paypal支付接口准备工作
- 首先去申请一个paypal账号,https://www.paypal.com/。
- 申请完毕并登录,进入https://developer.paypal.com/developer/accounts/。即可看到你申请账号自动配属的两个测试账号,账号类别分别是:BUSINESS和PERSONAL,PERSONAL的账号里面有$9999,测试费用,表激动。
- 下面去给两个账号设置密码,点击账号展开,然后点击Profile,会弹出账号信息框,里面可以设置密码等一堆属性。如果点击账号始终无法展开,请吐槽下paypal,然后F5再点。
- 下面进入https://developer.paypal.com/developer/applications/申请APP,点击REST API apps栏目下面的Create App按钮,写进一个APP名称,然后选择一个测试账户作为此APP绑定的账号,如果你在上一步没有申请新的测试账号,那么这里默认就是选择了BUSINESS账号。
- 然后打开创建的APP,可以看到APP的clientId和clientSecret。
- paypal的测试环境域名为sandbox.paypal.com,正式域名为www.paypal.com。一下测试均为测试环境。
PayPal-PHP-SDK下载
- 通过composer(composer安装教程)下载PayPal-PHP-SDK,具体的composer.json如下:
{
"require" : {
"paypal/rest-api-sdk-php" : "1.7.4"
},
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 切换至项目目录并执行
composer install
,PayPal-PHP-SDK安装完毕。 - 因为PayPal-PHP-SDK里面的composer.json里面的require有psr/log,所以在在目录vendor下有三个文件夹:composer,paypal和psr。
- 此时项目的目录结构如下,其中的app文件夹是下面实例化创建的文件夹。
文章图片
打开浏览器,输入http://~/rest-api-sdk-php/sample/index.php,“~”符号改为你自己的路径。
可以看到sample了,如下图:
文章图片
图中的PayPal Payments - similar to Express Checkout in Classic APIs即为支付接口,对应的代码路径为~/rest-api-sdk-php/sample/payments/CreatePaymentUsingPayPal.php。
【paypal|PayPal-PHP-SDK(V1.7.4)支付接口实现】PayPal Payments的逻辑大致如下:
- 创建一个支付,发送到paypal服务端
- paypal服务端返回一个用户授权地址
- 转链到用户授权地址,用户授权
- 用户授权完毕,paypal返回到客户端设置的execute地址,付款实现。
~/rest-api-sdk-php/sample/payments/CreatePaymentUsingPayPal.php有如下代码
// ### Get redirect url
// The API response provides the url that you must redirect
// the buyer to. Retrieve the url from the $payment->getApprovalLink()
// method
// 获取到重定向地址
$approvalUrl = $payment->getApprovalLink();
// NOTE: PLEASE DO NOT USE RESULTPRINTER CLASS IN YOUR ORIGINAL CODE. FOR SAMPLE ONLY
// 将重定向地址打印出来
ResultPrinter::printResult("Created Payment Using PayPal. Please visit the URL to Approve.".$approvalUrl, "Payment", "$approvalUrl", $request, $payment);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
文章图片
页面执行完毕,既看到paypal服务端返回的授权地址
文章图片
转链到授权地址
文章图片
用户登录授权之后
文章图片
点击继续,返回用户设置的execute地址
文章图片
至此,PayPal-PHP-SDK支付接口sample支付过程完毕。
支付接口实例化 在项目根目录创建app文件夹,创建几个必须的文件如下:
文章图片
其中:
- payment.php,创建支付
- exec.php,执行支付,用户授权返回地址
- cancel.php,用户取消支付
- common.php,公共文件
payment.php文件
// +----------------------------------------------------------------------
// | Datetime: 2016-07-28 10:56:40
// +----------------------------------------------------------------------
// | Copyright: Perfect Is Shit
// +----------------------------------------------------------------------
require_once('./common.php');
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\RedirectUrls;
use PayPal\Api\Transaction;
use PayPal\Api\ShippingAddress;
// ### Payer
// A resource representing a Payer that funds a payment
// For paypal account payments, set payment method
// to 'paypal'.
$payer = new Payer();
$payer->setPaymentMethod("paypal");
// ### Itemized information
// (Optional) Lets you specify item wise
// information
$item1 = new Item();
$item1->setName('test pro 1')
->setCurrency('USD')
->setQuantity(1)
->setSku("testpro1_01") // Similar to `item_number` in Classic API
->setPrice(20);
$item2 = new Item();
$item2->setName('test pro 2')
->setCurrency('USD')
->setQuantity(5)
->setSku("testpro2_01") // Similar to `item_number` in Classic API
->setPrice(10);
$itemList = new ItemList();
$itemList->setItems(array($item1, $item2));
// 自定义用户收货地址,避免用户在paypal上账单的收货地址和销售方收货地址有出入
// 这里定义了收货地址,用户在支付过程中就不能更改收货地址,否则,用户可以自己更改收货地址
$address = new ShippingAddress();
$address->setRecipientName('什么名字')
->setLine1('什么街什么路什么小区')
->setLine2('什么单元什么号')
->setCity('城市名')
->setState('浙江省')
->setPhone('12345678911')
->setPostalCode('12345')
->setCountryCode('CN');
$itemList->setShippingAddress($address);
// ### Additional payment details
// Use this optional field to set additional
// payment information such as tax, shipping
// charges etc.
$details = new Details();
$details->setShipping(5)
->setTax(10)
->setSubtotal(70);
// ### Amount
// Lets you specify a payment amount.
// You can also specify additional details
// such as shipping, tax.
$amount = new Amount();
$amount->setCurrency("USD")
->setTotal(85)
->setDetails($details);
// ### Transaction
// A transaction defines the contract of a
// payment - what is the payment for and who
// is fulfilling it.
$transaction = new Transaction();
$transaction->setAmount($amount)
->setItemList($itemList)
->setDescription("Payment description")
->setInvoiceNumber(uniqid());
// ### Redirect urls
// Set the urls that the buyer must be redirected to after
// payment approval/ cancellation.
$baseUrl = getBaseUrl();
$redirectUrls = new RedirectUrls();
$redirectUrls->setReturnUrl("$baseUrl/exec.php?success=true")
->setCancelUrl("$baseUrl/cancel.php?success=false");
// ### Payment
// A Payment Resource;
create one using
// the above types and intent set to 'sale'
$payment = new Payment();
$payment->setIntent("sale")
->setPayer($payer)
->setRedirectUrls($redirectUrls)
->setTransactions(array($transaction));
$payment->create($apiContext);
$approvalUrl = $payment->getApprovalLink();
// 打印出用户授权地址,这里仅仅实现支付过程,流程没有进一步完善。
dump($approvalUrl);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
// +----------------------------------------------------------------------
// | Datetime: 2016-07-28 11:53:10
// +----------------------------------------------------------------------
// | Copyright: Perfect Is Shit
// +----------------------------------------------------------------------
set_time_limit(3600);
require_once('./common.php');
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\ExecutePayment;
use PayPal\Api\Payment;
use PayPal\Api\PaymentExecution;
use PayPal\Api\Transaction;
// ### Approval Status
// Determine if the user approved the payment or not
if (isset($_GET['success']) && $_GET['success'] == 'true') {// Get the payment Object by passing paymentId
// payment id was previously stored in session in
// CreatePaymentUsingPayPal.php
$paymentId = $_GET['paymentId'];
$payment = Payment::get($paymentId, $apiContext);
// ### Payment Execute
// PaymentExecution object includes information necessary
// to execute a PayPal account payment.
// The payer_id is added to the request query parameters
// when the user is redirected from paypal back to your site
$execution = new PaymentExecution();
$execution->setPayerId($_GET['PayerID']);
// ### Optional Changes to Amount
// If you wish to update the amount that you wish to charge the customer,
// based on the shipping address or any other reason, you could
// do that by passing the transaction object with just `amount` field in it.
// Here is the example on how we changed the shipping to $1 more than before.
$transaction = new Transaction();
$amount = new Amount();
$details = new Details();
$details->setShipping(5)
->setTax(10)
->setSubtotal(70);
$amount->setCurrency('USD');
$amount->setTotal(85);
$amount->setDetails($details);
$transaction->setAmount($amount);
// Add the above transaction object inside our Execution object.
$execution->addTransaction($transaction);
try {
// Execute the payment
$result = $payment->execute($execution, $apiContext);
echo "支付成功";
} catch (Exception $ex) {
echo "支付失败";
exit(1);
}return $payment;
} else {
echo "PayPal返回回调地址参数错误";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
// +----------------------------------------------------------------------
// | Datetime: 2016-07-29 11:31:32
// +----------------------------------------------------------------------
// | Copyright: Perfect Is Shit
// +----------------------------------------------------------------------
echo "用户取消支付";
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
// +----------------------------------------------------------------------
// | Datetime: 2016-07-28 11:55:30
// +----------------------------------------------------------------------
// | Copyright: ShowMore
// +----------------------------------------------------------------------
require_once('../vendor/autoload.php');
use PayPal\Rest\ApiContext;
use PayPal\Auth\OAuthTokenCredential;
// 下面为申请app获得的clientId和clientSecret,必填项,否则无法生成token。
$clientId = '';
$clientSecret = '';
$apiContext = new ApiContext(
new OAuthTokenCredential(
$clientId,
$clientSecret
)
);
$apiContext->setConfig(
array(
'mode' => 'sandbox',
'log.LogEnabled' => true,
'log.FileName' => '../PayPal.log',
'log.LogLevel' => 'DEBUG', // PLEASE USE `INFO` LEVEL FOR LOGGING IN LIVE ENVIRONMENTS
'cache.enabled' => true,
// 'http.CURLOPT_CONNECTTIMEOUT' => 30
// 'http.headers.PayPal-Partner-Attribution-Id' => '123123123'
//'log.AdapterFactory' => '\PayPal\Log\DefaultLogFactory' // Factory class implementing \PayPal\Log\PayPalLogFactory
)
);
// 浏览器友好的变量输出
function dump($var, $echo=true, $label=null, $strict=true) {
$label = ($label === null) ? '' : rtrim($label) . ' ';
if (!$strict) {
if (ini_get('html_errors')) {
$output = print_r($var, true);
$output = "" . $label . htmlspecialchars($output, ENT_QUOTES) . "
";
} else {
$output = $label . print_r($var, true);
}
} else {
ob_start();
var_dump($var);
$output = ob_get_clean();
if (!extension_loaded('xdebug')) {
$output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
$output = '' . $label . htmlspecialchars($output, ENT_QUOTES) . '
';
}
}
if ($echo) {
echo($output);
return null;
}else
return $output;
}/**
* ### getBaseUrl function
* // utility function that returns base url for
* // determining return/cancel urls
*
* @return string
*/
function getBaseUrl()
{
if (PHP_SAPI == 'cli') {
$trace=debug_backtrace();
$relativePath = substr(dirname($trace[0]['file']), strlen(dirname(dirname(__FILE__))));
echo "Warning: This sample may require a server to handle return URL. Cannot execute in command line. Defaulting URL to http://localhost$relativePath \n";
return "http://localhost" . $relativePath;
}
$protocol = 'http';
if ($_SERVER['SERVER_PORT'] == 443 || (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on')) {
$protocol .= 's';
}
$host = $_SERVER['HTTP_HOST'];
$request = $_SERVER['PHP_SELF'];
return dirname($protocol . '://' . $host . $request);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
文章图片
转链到用户授权地址
文章图片
用户登录授权,可以看到在我们设置过收货地址之后,支付收货地址是默认无法更改的。
文章图片
用户点击继续,会转链到我们的支付成功回调地址exec.php。
文章图片
用户点击取消并返回,会转链到我们的支付失败回调地址cancel.php
文章图片
支付过后可进入sandbox账号中心查看是否有交易。
文章图片
测试的款项都在这里,有个问题,sandbox上面的交易时间总比我这边晚一天,比如今天是7月29日完成的交易,但是在sandbox里面显示的都是7月28日。不知道是不是个BUG。
至此,PayPal-PHP-SDK的支付接口OK。
可以对PayPal-PHP-SDK底层代码进行进一步研究,有时间再弄。
- 顶
- 1
- 踩
- 0
- 上一篇composer安装,速度慢
- 下一篇PHP递归获取目录内所有文件
- ?PHP SESSION PHPSESSID session_id()2016-09-26阅读75
- ?正则去除php代码中的注释2016-08-05阅读127
- ?PHP递归获取目录内所有文件2016-08-01阅读154
- ?php_lang_ref:classes and objects >> object interface2016-07-21阅读38
- ?php_lang_ref:Classes_and_Objects>>Trait2016-07-18阅读38
- ?php curl https代码示例2016-08-22阅读51
- ?thinkphp的dump函数无输出2016-08-03阅读146
- ?composer安装,速度慢2016-07-28阅读227
- ?php_lang_ref:classes and objects>>class abstraction2016-07-21阅读39
- ?nginx环境搭建(windows)2016-06-21阅读54
- 猜你在找
- 7楼 飞狐or 2016-10-26 16:57发表 [回复]
-
文章图片
- 楼主你好 我想 在 会掉的时候 获取一些额外的数据 这些数据怎么写进去 又可以不用在页面上体现 还有就是 paypal 回调 怎么验签 呢
- 6楼 cdk0426 2016-09-21 20:38发表 [回复]
-
文章图片
-
请问切换到 live模式还需要配置哪些信息呢?
- Re: alexander_phper 2016-09-22 14:49发表 [回复]
-
文章图片
- 回复cdk0426:用正式app的clientId和clientSecret。
- 5楼 caiqihuang 2016-09-09 15:40发表 [回复]
-
文章图片
-
exce.php页面
$result = $payment->execute($execution, $apiContext);
echo "支付成功";
$result是一个对象,怎么把里面的支付信息取出来,想取出订单id去修改数据表订单信息- Re: alexander_phper 2016-09-13 12:12发表 [回复]
-
文章图片
-
回复caiqihuang: [php] view plain
copy
- // exec.php文件里面这个对象
- $result = $payment->execute($execution, $apiContext);
- // 账单唯一交易号,paypal账户交易记录里面的标示
- $result->transactions[0]->related_resources[0]->sale->id;
- // 账单号,paypal账户交易记录里面的标示
- $result->transactions[0]->invoice_number;
- // payid,接口查询使用的账单id
- $result->id;
- Re: caiqihuang 2016-09-18 17:05发表 [回复]
-
文章图片
-
回复alexander_phper:Samples 里有信用卡付款,但是跳转老是出错。
现在付款方式只有PayPal余额,怎么添加信用卡付款。- Re: alexander_phper 2016-09-19 17:11发表 [回复]
-
文章图片
-
回复caiqihuang:paypal付款的时候可以选择信用卡支付的,不需要配置。也有信用卡支付的专用接口,不过那个我还没有试过。
- Re: caiqihuang 2016-09-21 09:32发表 [回复]
-
文章图片
-
回复alexander_phper:给paypal提交了问题,直接调用信用卡接口,目前不能再亚太地区使用。
只有在账户添加信用卡,在能在付款方式里罗列出来。
- 4楼 caiqihuang 2016-09-09 11:34发表 [回复]
-
文章图片
-
回复alexander_phper:payment.php
$item1 = new Item();
$item1->setName('test pro 1')
->setCurrency('USD')
->setQuantity(1)
->setSku("testpro1_01") // Similar to `item_number` in Classic API
->setPrice(20);
$item2 = new Item();
$item2->setName('test pro 2')
->setCurrency('USD')
->setQuantity(5)
->setSku("testpro2_01") // Similar to `item_number` in Classic API
->setPrice(10);
$item3 = new Item();
$item3->setName('test pro 3')
->setCurrency('USD')
->setQuantity(2)
->setSku("testpro3_01") // Similar to `item_number` in Classic API
->setPrice(10);
$itemList = new ItemList();
$itemList->setItems(array($item1, $item2, $item3));
这里写3个item和1个item都会报错
Fatal error: Uncaught exception 'PayPal\Exception\PayPalConnectionException' with message 'Got Http response code 400 when accessing- Re: caiqihuang 2016-09-09 12:10发表 [回复]
-
文章图片
- 回复caiqihuang:因为金额对不上的原因,才导致出错的。。把金额重新填好后就没问题了
- 3楼 caiqihuang 2016-09-05 16:22发表 [回复]
-
文章图片
-
换了一个没的浏览器可以了,支付成功。
这种能做成ecshop的那种插件吗?- Re: alexander_phper 2016-09-07 10:26发表 [回复]
-
文章图片
- 回复caiqihuang:我ecshop不熟,这个需要同学自己去探索下了...
- 2楼 caiqihuang 2016-09-05 16:16发表 [回复]
-
文章图片
-
博主你好,我再本地上测试
http://www.paydemo.com/app/exec.php?success=true&paymentId=PAY-67495016C61098847K7GSMYI&token=EC-5LC31251DU669893W&PayerID=SCVUJL3MN9ANA
支付失败
- 1楼 qq_35099956 2016-09-01 17:56发表 [回复]
-
文章图片
-
博主你好,不知你是否留意到
官方demo的用户授权地址页面跟你自己的授权页面不一样。
请问下怎么设置可以跳转到官方demo一样得地址- Re: alexander_phper 2016-09-04 21:04发表 [回复]
-
文章图片
- 回复qq_35099956:正式环境的支付页面是和官方一样的,不知道是不是paypal的sandbox版本和正式版不一样还是支付方式不同支付页面样式不同,在几个购物网站上面看到他们的支付页面是和这个sandbox一样的,也有可能他们使用的是支付按钮。
推荐阅读
- 程序员|程序员“真实”日常(每天敲代码不到 1 小时)
- Java|angular.js 翻页组件
- Java|java--NoSuchMethodError解决办法
- Flutter|Flutter 基于NestedScrollView+RefreshIndicator完美解决滑动冲突
- andorid|Android属性动画Property Animation系列一之ValueAnimator
- Android|无限自动轮播+自定义小圆点
- 技术|报表开发之批量导入导出Excel