先说下paypal的支付流程,借用一张网上找到的的流程图。
大致流程为
1.调用创建订单接口(传入订单金额以及两个return url,成功以及失败的地址),然后将用户重定向到paypal返回的支付链接
2.在return url里捕获订单执行扣款操作
3.回调页面收到paypal的订单完成通知,此时支付完成
paypal v1 Api sdk https://github.com/paypal/PayPal-PHP-SDK
paypal v2 Api SDK https://github.com/paypal/Checkout-PHP-SDK/
目前v1版本的Api接口已经不推荐使用,一开始我在对接v1版本的时候,webhook的验签一直无法通过,随换成v2的接口了。
首先coponser 安装Checkout-PHP-SDK
composer require paypal/paypal-checkout-sdk
创建订单
$request = new OrdersCreateRequest();
$request->prefer('return=representation');
$request->body = [
"intent" => "CAPTURE",
"purchase_units" => [[
"reference_id" => $order->order_sn,//订单号
"amount" => [
"value" => $order->amount,//订单金额
"currency_code" => $order->currency//币种 usd
]
]],
"application_context" => [
"cancel_url" => route('payment.callback',['success'=>'false']),//用户取消支付重定向页面
"return_url" => route('payment.callback',['success'=>'true'])//用户同意支付重定向页面(此页面paypal会携带Token、PayerID参数,在这里捕获订单执行扣款操作)
]
];
try {
// Call API with your client and get a response for your call
$response = $this->client->execute($request);
$link='';
if($response->statusCode==201){
for ($i = 0; $i < count($response->result->links); ++$i)
{
$link = $response->result->links[$i];
if($link->rel=='approve'){//link rel =approve 就是将用户重定向的paypal的支付地址
break;
}
}
if($link->rel=='approve'){
$order->third_order_sn=$response->result->id;//这里记录下返回的id,回调通知接收到的id就是这个paypal的订单id
$order->save();
return $link->href;
}
}
throw new \Exception("create order fail");
}catch (HttpException $ex) {
Log::error('paypal create order fail:'.$ex->getMessage());
}
paypal将用户重定向到return url捕获订单执行扣款逻辑
$success = request()->get('success');
if ($success == 'false') {
echo 'cancel';die;
}
$token = request()->get('token');
$payerId = request()->get('PayerID');
if (!isset($success, $token, $payerId)||$success=='false') {
echo 'pay fail';die;
}
$request = new OrdersCaptureRequest($token);
$request->prefer('return=representation');
try {
// Call API with your client and get a response for your call
$response = $this->client->execute($request);
// If call returns body in response, you can get the deserialized version from the result attribute of the response
if($response->result->status=='COMPLETED'){
echo 'Payment successful!';
}else{
echo 'Order processing..';
}
}catch (HttpException $ex) {
echo 'error';
Log::info('paypal execute fail'.$ex->getMessage());
}
paypal webhook 通知处理
$dataUrlString = file_get_contents('php://input');
$json = json_decode($dataUrlString,true);
if(!empty($json)){
Log::debug("paypal notify info:\r\n".$dataUrlString);
}else{
Log::debug("paypal notify fail:null");
}
$webhookId = $this->config->webhookId;//这里的webhookid 在paypal的开发者面板里进入应用添加webhook地址后会有一个webhook id
$headers = request()->header();
$headers = array_change_key_case($headers, CASE_UPPER);
$paypalTransmissionId = $headers['PAYPAL-TRANSMISSION-ID'][0] ?? '';
$timeStamp = $headers['PAYPAL-TRANSMISSION-TIME'][0] ?? '';
$crc32 = crc32($dataUrlString);
if (!$paypalTransmissionId || !$timeStamp || !$webhookId || !$crc32) {
return false;
}
$sigString = "{$paypalTransmissionId}|{$timeStamp}|{$webhookId}|{$crc32}";
// 通过PAYPAL-CERT-URL头信息去拿公钥
$publicKey = openssl_pkey_get_public(file_get_contents($headers['PAYPAL-CERT-URL'][0]));
$details = openssl_pkey_get_details($publicKey);
$verifyResult = openssl_verify($sigString, base64_decode($headers['PAYPAL-TRANSMISSION-SIG'][0]), $details['key'], 'sha256WithRSAEncryption');
if ($verifyResult === 1) {
// 验证成功
if($json['event_type']=='PAYMENT.CAPTURE.COMPLETED'){//支付完成
$order_sn = $json['resource']['supplementary_data']['related_ids']['order_id'];//之前存的paypal订单id去查找我们系统里的订单
$total = $json['resource']['amount']['value'];
$payNum=$json['resource']['id'];
$order=OrderServices::getInstance()->getOrderByThirdOrderSn($order_sn);
if($order){
if($order->amount==$total){
//标记订单支付成功
OrderServices::getInstance()->markOrderPaid($order,$total,$payNum);
}
echo 'success';
}else{
echo 'Order not found';
}
}
} else {
Log::info('paypal sign error');
return 'sign error';
}
本文地址:https://www.blear.cn/article/laravel-paypal
转载时请以链接形式注明出处
评论