推荐资料
主要是参考官方的 SDK PayPal-PHP-SDK
wiki 操作的 https://github.com/paypal/PayPal-PHP-SDK/wiki,按照上面的步骤一步步操作
以下三点也很重要
1.下载 SDK
composer require "paypal/rest-api-sdk-php:*"
2.创建支付订单。比如我们使用first.php
页面
// 1.引入 sdk 包
<?php
// 1. Autoload the SDK Package. This will include all the files and classes to your autoloader
// Used for composer based installation
require __DIR__ . '/vendor/autoload.php';
// Use below for direct download installation
// require __DIR__ . '/PayPal-PHP-SDK/autoload.php';
// 2. 添加你的 clientId 和 secret
// After Step 1
$apiContext = new \PayPal\Rest\ApiContext(
new \PayPal\Auth\OAuthTokenCredential(
'ClientID', // ClientID
'ClientSecret' // ClientSecret
)
);
// 3. 创建订单
// 付款人信息
$payer = new \PayPal\Api\Payer();
$payer->setPaymentMethod('paypal'); // 付款方式
// 金额信息
$amount = new \PayPal\Api\Amount();
$amount->setTotal('1.00');
$amount->setCurrency('USD');
// 交易信息
$transaction = new \PayPal\Api\Transaction();
$transaction->setAmount($amount);
// 回调信息
$redirectUrls = new \PayPal\Api\RedirectUrls();
$redirectUrls->setReturnUrl("https://xxx.com/callback.phpl") //支付后, 同步回调 url
->setCancelUrl("https://xxx.com/cancel.php");// 取消支付后跳转的的url
// 创建支付实例,并赋值
$payment = new \PayPal\Api\Payment();
$payment->setIntent('sale')
->setPayer($payer)
->setTransactions(array($transaction))
->setRedirectUrls($redirectUrls);
// 4. 请求支付
try {
$payment->create($apiContext);
如果支付成功,则肯定有第三方的订单号,我们应该记录此单号。因为paypal 并没有记录我们本地的单号
// 获取订单id
$arr = json_decode($payment, true);
// 订单id 赋值
$paypal_order_id = isset($arr['id']) ? $arr['id'] : '';
echo $payment; // 支付信息
echo $payment->getApprovalLink(); //客户支付,需要跳转的连接
}
catch (\PayPal\Exception\PayPalConnectionException $ex) {
// This will print the detailed information on the exception.
//REALLY HELPFUL FOR DEBUGGING
echo $ex->getData();
}
2.1 请求支付,返回的数据
> php -f first.php // 终端执行,也可以直接访问 first.php 路由
{
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"transactions": [
{
"amount": {
"total": "1.00",
"currency": "USD"
},
"related_resources": []
}
],
"redirect_urls": {
"return_url": "https://example.com/your_redirect_url.html",
"cancel_url": "https://example.com/your_cancel_url.html"
},
"id": "PAY-3MC96102SY030652JLHXXPMA",
"state": "created",
"create_time": "2017-10-24T17:26:07Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-3MC96102SY030652JLHXXPMA",
"rel": "self",
"method": "GET"
},
{
"href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-1NT485541R0509947",
"rel": "approval_url",
"method": "REDIRECT"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-3MC96102SY030652JLHXXPMA/execute",
"rel": "execute",
"method": "POST"
}
]
}
Redirect user to approval_url: https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-1NT485541R0509947
3.添加请求日志
在 $payment->create($apiContext); 之前添加以下代码。必须放在 try catch 之前
$apiContext->setConfig(
array(
'log.LogEnabled' => true,
'log.FileName' => 'PayPal.log',
'log.LogLevel' => 'DEBUG',
'mode' => 'live', // 默认是使用沙盒。开启这个,表示使用正式环境
)
);
以下是执行结构,我吧 client 写错,会出现 ERROR 的报错
[11-07-2020 07:14:37] PayPal\Core\PayPalHttpConnection : INFO: POST https://api.sandbox.paypal.com/v1/oauth2/token
[11-07-2020 07:14:39] PayPal\Core\PayPalHttpConnection : INFO: Response Status : 200
[11-07-2020 07:14:39] PayPal\Core\PayPalHttpConnection : INFO: POST https://api.sandbox.paypal.com/v1/payments/payment
[11-07-2020 07:14:40] PayPal\Core\PayPalHttpConnection : INFO: Response Status : 201
[11-07-2020 07:15:58] PayPal\Core\PayPalHttpConnection : INFO: POST https://api.sandbox.paypal.com/v1/oauth2/token
[11-07-2020 07:16:00] PayPal\Core\PayPalHttpConnection : INFO: Response Status : 401
[11-07-2020 07:16:00] PayPal\Core\PayPalHttpConnection : ERROR: Got Http response code 401 when accessing https://api.sandbox.paypal.com/v1/oauth2/token. {"error":"invalid_client","error_description":"Client Authentication failed"}
4. 同步回调
这个是按照支付流程,付款后,paypal 跳转的 url格式如下
http://pay.xxx.com/callback.php?r=index/callback&paymentId=PAYID-L4GSHBA2UL56165LM176404F&token=EC-7R537316F06356329&PayerID=HNANHU3TEQETQ
可以看到看到有 paymentId,token ,PayerID 三个参数。但是 paypal 并没有给支付是否成功的参数。以后我测试下正式环境,参数如果有变化,再做补充
如果取消,则会跳转到取消后的 url
/**
* 回调
*/
public function Callback()
{
if ( !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) {
echo '取消付款';die;
}
$paymentId = trim($_GET['paymentId']);
$PayerID = trim($_GET['PayerID']);
if (!isset( $paymentId, $PayerID)) {
echo '支付失败';die;
}
$payment = Payment::get($paymentId, $this->PayPal);
$execute = new PaymentExecution();
$execute->setPayerId($PayerID);
try {
$payment->execute($execute, $this->PayPal);
} catch (Exception $e) {
echo ',支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die;
}
echo '支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die;
}
5.异步回调
这个是在后台设置的 https://developer.paypal.com/developer/applications/->
My Apps & Crentials
->REST API apps 中的APP name
->页面的最下面选择Add Webhook
->输入Webhook URL
public function notify(){
//获取回调结果
$json_data = $this->get_JsonData();
if(!empty($json_data)){
Log::debug("paypal notify info:\r\n".json_encode($json_data));
}else{
Log::debug("paypal notify fail:参加为空");
}
//自己打印$json_data的值看有那些是你业务上用到的
//比如我用到
$data['invoice'] = $json_data['resource']['invoice_number'];
$data['txn_id'] = $json_data['resource']['id'];
$data['total'] = $json_data['resource']['amount']['total'];
$data['status'] = isset($json_data['status'])?$json_data['status']:'';
$data['state'] = $json_data['resource']['state'];
try {
//处理相关业务
} catch (\Exception $e) {
//记录错误日志
Log::error("paypal notify fail:".$e->getMessage());
return "fail";
}
return "success";
}
public function get_JsonData(){
$json = file_get_contents('php://input');
if ($json) {
$json = str_replace("'", '', $json);
$json = json_decode($json,true);
}
return $json;
}
网友评论