支付逻辑简介
跟微信和支付宝支付不一样,微信和支付宝都是先走统一下单接口,然后返回创建的下单会话id等然后传给前端,贝宝支付是我们先通过API发起个支付请求,然后贝宝给我们个支付链接,前端打开我们的支付链接:例如
https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-7K8740615W719670D
,然后该链接会有我们支付订单的部分信息,然后利用沙盒的账号支付确认要支付后,会返回到下单时设置的回调支付那里,如下图:
const accept_url = 'http://fumei.com:8082/service.php?action=paypal_callback&com_id=1210';//返回地址
$redirectUrls = new RedirectUrls();
$redirectUrls->setReturnUrl(self::accept_url.'&orderId='.$order->orderId.'&success=true')
->setCancelUrl(self::accept_url.'&orderId='.$order->orderId.'&success=false');
这里回调会带着订单号和发起是否成功或者失败,如果成功,其实到这里我们并没有完成支付,只有在回调地址里才能完成
payment
支付,不像微信和支付宝,前端调起真实的支付,支付完毕后就要跳转前端准备的支付成功或失败的页面。
PayPal账号设置
-
创建账号:
创建沙盒账号https://www.paypal.com/c2/webapps/mpp/account-selection
-
登录沙盒后台
https://developer.paypal.com/developer/applications/
-
创建项目APP 查看 APPID和Secret
-
设置沙盒支付测试账号,充值,创建
-
设置回调地址
代码实现 -
引入composer 管理包或者直接下载
-
引入文件
use PayPal\Api\Payer; use PayPal\Api\Item; use PayPal\Api\ItemList; use PayPal\Api\Details; use PayPal\Api\Amount; use PayPal\Api\Transaction; use PayPal\Api\RedirectUrls; use PayPal\Api\Payment; use PayPal\Auth\OAuthTokenCredential; use PayPal\Exception\PayPalConnectionException; use PayPal\Rest\ApiContext; use PayPal\Api\PaymentExecution;
-
初始化
protected $PayPal; public function __construct() { $this->PayPal = new ApiContext( new OAuthTokenCredential( self::clientId, self::clientSecret ) ); $this->PayPal->setConfig( array( 'mode' => 'sandbox',//线上的则配置为live 'log.LogEnabled' => true, 'log.FileName' => '../PayPal.log', 'log.LogLevel' => 'DEBUG', // PLEASE USE `INFO` LEVEL FOR LOGGING IN LIVE ENVIRONMENTS 'cache.enabled' => true ) ); }
-
创建支付链接(即下单接口)
/** * @param * $product 商品 * $price 价钱 * $shipping 运费 * $description 描述内容 */ public function pay() { global $db,$request,$comId; $fenbiao = getFenbiao($comId,20); $userId = (int)$request['user_id']; $oid = $request['order_id']; $order = $db->get_row("select * from order$fenbiao where id = $oid"); $product_json = json_decode($order->product_json); $subject = ''; $daizhifu = $order->price; $item = []; foreach ($product_json as $pdt) { $product_item = array( 'title' => $pdt->title, 'sn' => $pdt->sn, 'num' => (int)$pdt->num, 'price' => (int) $pdt->price_sale ); $subject.=','.$pdt->title.'*'.$pdt->num; } $body = substr($subject,1); $subject = sys_substr($body,30,true); $subject = str_replace('_','',$subject); $num = (int)$order->pdtNums; $price = (int)$order->price; try { $payer = new Payer(); $payer->setPaymentMethod('paypal'); $item = new Item(); $item->setName($product_item['title']) // 子订单的名称 ->setDescription($product_item['sn']) // 子订单描述 ->setCurrency(self::Currency) // 币种 ->setQuantity($product_item['num']) // 数量 ->setPrice($product_item['price']); // 价格 $itemList = new ItemList(); $itemList->setItems([$item]); // 设置子订单列表 // 这里是设置运费等 $details = new Details(); $details->setShipping(0) ->setSubtotal($price); // 设置总计费用 $amount = new Amount(); $amount->setCurrency(self::Currency) ->setTotal($price) ->setDetails($details); // 创建交易 $transaction = new Transaction(); $transaction->setAmount($amount) ->setItemList($itemList) ->setDescription($subject) ->setInvoiceNumber($order->orderId); // 这里设置支付成功和失败后的跳转链接 $redirectUrls = new RedirectUrls(); $redirectUrls->setReturnUrl(self::accept_url.'&orderId='.$order->orderId.'&success=true') ->setCancelUrl(self::accept_url.'&orderId='.$order->orderId.'&success=false'); $payment = new Payment(); $payment->setIntent('sale') ->setPayer($payer) ->setRedirectUrls($redirectUrls) ->setTransactions([$transaction]); $payment->create($this->PayPal); // 得到支付链接 $link = $payment->getApprovalLink(); $return['code'] = 1; $return['message'] = ''; $return['data'] = array(); $return['data']['link'] = $link; return json_encode($return,JSON_UNESCAPED_UNICODE); } catch (HttpException $e) { $data = (['msg' => $e->getMessage(), 'code' => $e->getStatusCode(), 'data' => ['order' => ['no' => '1112323231']]]); return null; } }
-
前端支付密码输入完毕后,发起支付
/** * 回调 */ public function callback() { global $db,$request,$comId; $fenbiao = getFenbiao($comId,20); $success = trim($_GET['success']); if ($success == 'false' && !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) { echo '取消付款';die; } $paymentId = trim($_GET['paymentId']); $PayerID = trim($_GET['PayerID']); $orderId = trim($_GET['orderId']); if (!isset($success, $paymentId, $PayerID)) { echo '支付失败';die; } if ((bool)$_GET['success'] === 'false') { echo '支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; } $payment = Payment::get($paymentId, $this->PayPal); $execute = new PaymentExecution(); $execute->setPayerId($PayerID); try { $payment->execute($execute, $this->PayPal); //todo 记录 $db->query("update order10 set paypal_no = '$paymentId', paypal_payerId ='$PayerID' where orderId = '$orderId'"); //todo 直接跳转前端页面 $url = ''; header("localtion:$url"); } catch (Exception $e) { echo ',支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; } echo '支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; }
-
支付后台回调,更改订单状态等
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; }
-
代码总:
namespace Zhishang; use PayPal\Api\Payer; use PayPal\Api\Item; use PayPal\Api\ItemList; use PayPal\Api\Details; use PayPal\Api\Amount; use PayPal\Api\Transaction; use PayPal\Api\RedirectUrls; use PayPal\Api\Payment; use PayPal\Auth\OAuthTokenCredential; use PayPal\Exception\PayPalConnectionException; use PayPal\Rest\ApiContext; use PayPal\Api\PaymentExecution; class Paypal{ const clientId = 'AV-SNw2j-1wNqAb1ygg0I4iKT8E8zHT1QCh8eLEbeFZQN2slHhq4dX5pKn4mfz7CHGDhCaVX1N';//ID const clientSecret = 'EHjumNJEwf5lkurMyNqo1DJ1v64jsZ9mpFYlKIkVQBh6Kksygxx_u77F8G0rSQHW9Awezdqd4RCd';//秘钥 const accept_url = 'http://fumei.com:8082/service.php?action=paypal_callback&com_id=1210';//返回地址 const Currency = 'USD';//币种 protected $PayPal; public function __construct() { $this->PayPal = new ApiContext( new OAuthTokenCredential( self::clientId, self::clientSecret ) ); //如果是沙盒测试环境不设置,请注释掉 // $this->paypal->setConfig( // array( // 'mode' => 'sandbox', // ) // ); // $this->PayPal->setConfig([ // 'mode' => 'sandbox', // 'log.LogEnabled' => true, // 'log.FileName' => 'logs/PayPal.log', // 'log.LogLevel' => 'DEBUG', // PLEASE USE `INFO` LEVEL FOR LOGGING IN LIVE ENVIRONMENTS // 'cache.enabled' => true, // ]); $this->PayPal->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 ) ); } /** * @param * $product 商品 * $price 价钱 * $shipping 运费 * $description 描述内容 */ public function pay() { global $db,$request,$comId; $fenbiao = getFenbiao($comId,20); $userId = (int)$request['user_id']; $oid = $request['order_id']; $order = $db->get_row("select * from order$fenbiao where id = $oid"); $product_json = json_decode($order->product_json); $subject = ''; $daizhifu = $order->price; $item = []; foreach ($product_json as $pdt) { $product_item = array( 'title' => $pdt->title, 'sn' => $pdt->sn, 'num' => (int)$pdt->num, 'price' => (int) $pdt->price_sale ); $subject.=','.$pdt->title.'*'.$pdt->num; } $body = substr($subject,1); $subject = sys_substr($body,30,true); $subject = str_replace('_','',$subject); $num = (int)$order->pdtNums; $price = (int)$order->price; try { $payer = new Payer(); $payer->setPaymentMethod('paypal'); $item = new Item(); $item->setName($product_item['title']) // 子订单的名称 ->setDescription($product_item['sn']) // 子订单描述 ->setCurrency(self::Currency) // 币种 ->setQuantity($product_item['num']) // 数量 ->setPrice($product_item['price']); // 价格 $itemList = new ItemList(); $itemList->setItems([$item]); // 设置子订单列表 // 这里是设置运费等 $details = new Details(); $details->setShipping(0) ->setSubtotal($price); // 设置总计费用 $amount = new Amount(); $amount->setCurrency(self::Currency) ->setTotal($price) ->setDetails($details); // 创建交易 $transaction = new Transaction(); $transaction->setAmount($amount) ->setItemList($itemList) ->setDescription($subject) ->setInvoiceNumber($order->orderId); // 这里设置支付成功和失败后的跳转链接 $redirectUrls = new RedirectUrls(); $redirectUrls->setReturnUrl(self::accept_url.'&orderId='.$order->orderId.'&success=true') ->setCancelUrl(self::accept_url.'&orderId='.$order->orderId.'&success=false'); $payment = new Payment(); $payment->setIntent('sale') ->setPayer($payer) ->setRedirectUrls($redirectUrls) ->setTransactions([$transaction]); $payment->create($this->PayPal); // 得到支付链接 $link = $payment->getApprovalLink(); $return['code'] = 1; $return['message'] = ''; $return['data'] = array(); $return['data']['link'] = $link; return json_encode($return,JSON_UNESCAPED_UNICODE); } catch (HttpException $e) { $data = (['msg' => $e->getMessage(), 'code' => $e->getStatusCode(), 'data' => ['order' => ['no' => '1112323231']]]); return null; } } /** * 回调 */ public function callback() { global $db,$request,$comId; $fenbiao = getFenbiao($comId,20); $success = trim($_GET['success']); if ($success == 'false' && !isset($_GET['paymentId']) && !isset($_GET['PayerID'])) { echo '取消付款';die; } $paymentId = trim($_GET['paymentId']); $PayerID = trim($_GET['PayerID']); $orderId = trim($_GET['orderId']); if (!isset($success, $paymentId, $PayerID)) { echo '支付失败';die; } if ((bool)$_GET['success'] === 'false') { echo '支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; } $payment = Payment::get($paymentId, $this->PayPal); $execute = new PaymentExecution(); $execute->setPayerId($PayerID); try { $payment->execute($execute, $this->PayPal); //todo 记录 $db->query("update order10 set paypal_no = '$paymentId', paypal_payerId ='$PayerID' where orderId = '$orderId'"); } catch (Exception $e) { echo ',支付失败,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; } echo '支付成功,支付ID【' . $paymentId . '】,支付人ID【' . $PayerID . '】';die; } 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; } }