paypal支持全球收款,在国外的市场非常大,如果你的客户为国外用户,这个是必不可少的,今天我们来申请一个paypal账号,并通过js和php来接入到我们的网站业务中,让用户可以使用paypal来支付购买商品。
一、申请paypal
首先申请一个paypal账号,申请地址:https://www.paypal.com/c2/webapps/mpp/account-selection
选择个人或企业账户,这里为了演示我选了个人
选择中国
输入自己手机号码
输入手机验证码
设置邮箱姓名及登录密码
输入自己的身份证号码及身份证上的地址
填完之后进入paypal的开发者中心页面,地址:https://developer.paypal.com/developer/applications/
我们可以看到有两个环境,一个是sandbox沙箱环境,是给开发者开发调试的,还有个一个live是生产版本,我们选择sandbox,点击creat app,我们来常见一个app
创建完成后,我们可以看到client-id及秘钥
好了,我们下一步就编写代码吧
二、使用js编写收款及回调通知
paypal为了方便商户网站集成paypay支付,推出了一个Smart Payment Buttons,只需要几段js代码就完成了paypal的支付接入,让我先看看沙箱环境下的支付接入代码吧:<!DOCTYPE html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. --> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility --> </head> <body> <script src="https://www.paypal.com/sdk/js?client-id=AYzWHRsN8oi4phBwL1VGT6F3AE80VlleicXEG03vtrZOXJE5MWyn-3_C3hTJGe0Ps9q5rVm_DfEXPZhw"> //这里的client-id要替换你在沙箱里新建app时的app的client-id. </script> <div id="paypal-button-container"></div> <script> paypal.Buttons({ createOrder: function(data, actions) { //设置交易数据. return actions.order.create({ purchase_units: [{ amount: { value: '0.01' } }] }); }, onApprove: function(data, actions) { //交易成功后触发 return actions.order.capture().then(function(details) { console.log(data); console.log(details); // This function shows a transaction success message to your buyer. alert('交易完成 ' + details.payer.name.given_name); }); } }).render('#paypal-button-container'); </script> </body> </html>是不是很简单,设置收款金额,收款成功后直接执行回调函数,这里可以通过ajax去更新订单信息
如果是发布到线上live的话我们要进行修改。
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
</head>
<body>
<script
<script
src="https://www.paypal.com/sdk/js?client-id=LIVE_CLIENT_ID"> //更换live下的app的client_id
</script>
<div id="paypal-button-container"></div>
<script>
// 设置clientid及秘钥
var PAYPAL_CLIENT = 'PAYPAL_LIVE_CLIENT';
var PAYPAL_SECRET = 'PAYPAL_LIVE_SECRET';
// Point your server to the PayPal API
var PAYPAL_ORDER_API = 'https://api-m.paypal.com/v2/checkout/orders/';
paypal.Buttons({
createOrder: function(data, actions) {
//设置交易数据.
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01'
}
}]
});
},
onApprove: function(data, actions) {
//交易成功后触发
return actions.order.capture().then(function(details) {
console.log(data);
console.log(details);
// This function shows a transaction success message to your buyer.
alert('交易完成 ' + details.payer.name.given_name);
});
}
}).render('#paypal-button-container');
</script>
</body>
</html>
好了,这里有个问题,现在所有的数据都放在前端,全部给暴露了,要是客户懂代码,直接在浏览器找到回调地址,直接更新地址不就麻烦了吗,那么还有一个方法就是通过后端来生成支付按钮并回调
三、使用php编写收款及回调通知
我们这里选择composer 安装,打开cmd,输入composer require "paypal/rest-api-sdk-php:*" ,回车
下载成功后,我们可以在目录中看到
在当前目录下再新建四个php文件,common.php、payment.php、notify.php、cancel.php
首先是common.php,这里主要设置clientid及秘钥等信息的,代码如下:
<?php require_once('vendor/autoload.php'); use PayPal\Rest\ApiContext; use PayPal\Auth\OAuthTokenCredential; // 下面为申请app获得的clientId和clientSecret,必填项,否则无法生成token。 $clientId = 'AYzWHRsN8oi4phBwL1VGT6F3AE80VlleicXEG03vtrZOXJE5MWyn-3_C3hTJGe0Ps9q5rVm_DfEXPZhw'; $clientSecret = 'EMc6umgnl-stAMQqGP25jLmhKMSrWf3wPkm-nJ5uzp2d7TUuXrfLdfKVzcjzgYt3n5gRgvN1uttZOyE4'; $apiContext = new ApiContext( new OAuthTokenCredential( $clientId, $clientSecret ) ); $apiContext->setConfig( array( 'mode' => 'sandbox', 'log.LogEnabled' => false, 'log.FileName' => 'PayPal.log', 'log.LogLevel' => 'DEBUG', 'cache.enabled' => false ) );
然后是payment.php,这个是设置商品信息及价格参数生成支付链接的,代码如下:
<?php 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; function isHTTPS() { if (defined('HTTPS') && HTTPS) return true; if (!isset($_SERVER)) return FALSE; if (!isset($_SERVER['HTTPS'])) return FALSE; if ($_SERVER['HTTPS'] === 1) { //Apache return TRUE; } elseif ($_SERVER['HTTPS'] === 'on') { //IIS return TRUE; } elseif ($_SERVER['SERVER_PORT'] == 443) { //其他 return TRUE; } return FALSE; } function getscheme(){ return (isHTTPS() ? 'https://' : 'http://'); } // Create new payer and method $payer = new Payer(); $payer->setPaymentMethod("paypal"); // Set redirect URLs $basepath=dirname(getscheme().$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]); $redirectUrls = new RedirectUrls(); $redirectUrls->setReturnUrl("{$basepath}/notify.php?success=true") ->setCancelUrl("{$basepath}/cancel.php?success=false"); // Set payment amount $amount = new Amount(); $amount->setCurrency("USD") ->setTotal(10); // Set transaction object $transaction = new Transaction(); $transaction->setAmount($amount) ->setDescription("Payment description"); // Create the full payment object $payment = new Payment(); $payment->setIntent('sale') ->setPayer($payer) ->setRedirectUrls($redirectUrls) ->setTransactions(array($transaction)); // Create payment with valid API context try { $payment->create($apiContext); //$payid= $payment->getId(); //这个payid就相当于交易流水号,可以存在本地数据库,回调的时候可以根据这个来更新数据库的支付状态 //生成地址 $approvalUrl = $payment->getApprovalLink(); // var_dump($approvalUrl); //跳转 header("location:" . $approvalUrl); // Redirect the customer to $approvalUrl } catch (PayPal\Exception\PayPalConnectionException $ex) { echo $ex->getCode(); echo $ex->getData(); die($ex); } catch (Exception $ex) { die($ex); }再看看notify.php,这个是支付成功后的跳转地址,代码如下:
<?php 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; // Get payment object by passing paymentId $paymentId = $_GET['paymentId']; $payment = Payment::get($paymentId, $apiContext); $payerId = $_GET['PayerID']; // Execute payment with payer ID $execution = new PaymentExecution(); $execution->setPayerId($payerId); try { // Execute payment $result = $payment->execute($execution, $apiContext); echo $result->getId();//获取payid,可以去更新数据库里的支付信息 echo $result->getState();//支付结果,可以判断 echo "支付成功"; var_dump($result); } catch (PayPal\Exception\PayPalConnectionException $ex) { echo "支付出错"; echo $ex->getCode(); echo $ex->getData(); die($ex); } catch (Exception $ex) { echo "支付失败"; die($ex); }
好了,整个的支付流程就完成了,是不是很简单。
这个项目的源码地址为https://studio.bfw.wiki/Studio/Open.html?projectid=16241458805893060050,可以打开后直接编辑运行,查看效果。
四、总结
paypal为了精简流程,方便更多的网站接入,采用js的一键接入方式,虽然好用,但是数据全部暴露给客户,不太安全,是的,paypal也注意到这个问题,推出了前后端搭配的支付接入方案,代码如下:<!DOCTYPE html> <html lang="en"> <head> <!-- Add meta tags for mobile and IE --> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title> PayPal Smart Payment Buttons Integration | Server Demo </title> </head> <body> <!-- Set up a container element for the button --> <div id="paypal-button-container"></div> <!-- Include the PayPal JavaScript SDK --> <script src="https://www.paypal.com/sdk/js?client-id=test¤cy=USD"></script> <script> // Render the PayPal button into #paypal-button-container paypal.Buttons({ // Call your server to set up the transaction createOrder: function(data, actions) { return fetch('/demo/checkout/api/paypal/order/create/', { method: 'post' }).then(function(res) { return res.json(); }).then(function(orderData) { return orderData.id; }); }, // Call your server to finalize the transaction onApprove: function(data, actions) { return fetch('/demo/checkout/api/paypal/order/' + data.orderID + '/capture/', { method: 'post' }).then(function(res) { return res.json(); }).then(function(orderData) { // Three cases to handle: // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart() // (2) Other non-recoverable errors -> Show a failure message // (3) Successful transaction -> Show confirmation or thank you // This example reads a v2/checkout/orders capture response, propagated from the server // You could use a different API or structure for your 'orderData' var errorDetail = Array.isArray(orderData.details) && orderData.details[0]; if (errorDetail && errorDetail.issue === 'INSTRUMENT_DECLINED') { return actions.restart(); // Recoverable state, per: // https://developer.paypal.com/docs/checkout/integration-features/funding-failure/ } if (errorDetail) { var msg = 'Sorry, your transaction could not be processed.'; if (errorDetail.description) msg += '\n\n' + errorDetail.description; if (orderData.debug_id) msg += ' (' + orderData.debug_id + ')'; return alert(msg); // Show a failure message } // Show a success message alert('Transaction completed by ' + orderData.payer.name.given_name); }); } }).render('#paypal-button-container'); </script> </body> </html>后端代码如下:
creatorder.php
<?php require __DIR__ . '/vendor/autoload.php'; //1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`. use PayPalCheckoutSdk\Orders\OrdersCreateRequest; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\SandboxEnvironment; ini_set('error_reporting', E_ALL); // or error_reporting(E_ALL); ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); class PayPalClient { /** * Returns PayPal HTTP client instance with environment that has access * credentials context. Use this instance to invoke PayPal APIs, provided the * credentials have access. */ public static function client() { return new PayPalHttpClient(self::environment()); } /** * Set up and return PayPal PHP SDK environment with PayPal access credentials. * This sample uses SandboxEnvironment. In production, use LiveEnvironment. */ public static function environment() { $clientId = 'AYzWHRsN8oi4phBwL1VGT6F3AE80VlleicXEG03vtrZOXJE5MWyn-3_C3hTJGe0Ps9q5rVm_DfEXPZhw'; $clientSecret = 'EMc6umgnl-stAMQqGP25jLmhKMSrWf3wPkm-nJ5uzp2d7TUuXrfLdfKVzcjzgYt3n5gRgvN1uttZOyE4'; return new SandboxEnvironment($clientId, $clientSecret); } } function createOrder($debug = false) { $request = new OrdersCreateRequest(); $request->prefer('return=representation'); $request->body = buildRequestBody(); // 3. Call PayPal to set up a transaction $client = PayPalClient::client(); $response = $client->execute($request); if ($debug) { print "Status Code: {$response->statusCode}\n"; print "Status: {$response->result->status}\n"; print "Order ID: {$response->result->id}\n"; print "Intent: {$response->result->intent}\n"; print "Links:\n"; foreach ($response->result->links as $link) { print "\t{$link->rel}: {$link->href}\tCall Type: {$link->method}\n"; } // To print the whole response body, uncomment the following line // echo json_encode($response->result, JSON_PRETTY_PRINT); } //订单id $orderid= $response->result->id; // 4. Return a successful response to the client. echo json_encode($response->result); return ; } /** * Setting up the JSON request body for creating the order with minimum request body. The intent in the * request body should be "AUTHORIZE" for authorize intent flow. * */ function buildRequestBody() { return array( 'intent' => 'CAPTURE', 'application_context' => array( 'return_url' => 'https://example.com/return', 'cancel_url' => 'https://example.com/cancel' ), 'purchase_units' => array( 0 => array( 'amount' => array( 'currency_code' => 'USD', 'value' => '220.00' ) ) ) ); } createOrder(false); ?>captureorder.php
<?php require __DIR__ . '/vendor/autoload.php'; //1. Import the PayPal SDK client that was created in `Set up Server-Side SDK`. use PayPalCheckoutSdk\Orders\OrdersCaptureRequest; use PayPalCheckoutSdk\Core\PayPalHttpClient; use PayPalCheckoutSdk\Core\SandboxEnvironment; ini_set('error_reporting', E_ALL); // or error_reporting(E_ALL); ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); class PayPalClient { /** * Returns PayPal HTTP client instance with environment that has access * credentials context. Use this instance to invoke PayPal APIs, provided the * credentials have access. */ public static function client() { return new PayPalHttpClient(self::environment()); } /** * Set up and return PayPal PHP SDK environment with PayPal access credentials. * This sample uses SandboxEnvironment. In production, use LiveEnvironment. */ public static function environment() { $clientId = 'AYzWHRsN8oi4phBwL1VGT6F3AE80VlleicXEG03vtrZOXJE5MWyn-3_C3hTJGe0Ps9q5rVm_DfEXPZhw'; $clientSecret = 'EMc6umgnl-stAMQqGP25jLmhKMSrWf3wPkm-nJ5uzp2d7TUuXrfLdfKVzcjzgYt3n5gRgvN1uttZOyE4'; return new SandboxEnvironment($clientId, $clientSecret); } } $orderId = $_GET['id']; //订单号,可以在数据库里处理 // 2. Set up your server to receive a call from the client /** *This function can be used to capture an order payment by passing the approved *order ID as argument. * *@param orderId *@param debug *@returns */ function captureOrder($orderId, $debug = false) { $request = new OrdersCaptureRequest($orderId); // 3. Call PayPal to capture an authorization $client = PayPalClient::client(); $response = $client->execute($request); // 4. Save the capture ID to your database. Implement logic to save capture to your database for future reference. if ($debug) { print "Status Code: {$response->statusCode}\n"; print "Status: {$response->result->status}\n"; print "Order ID: {$response->result->id}\n"; print "Links:\n"; foreach ($response->result->links as $link) { print "\t{$link->rel}: {$link->href}\tCall Type: {$link->method}\n"; } print "Capture Ids:\n"; foreach ($response->result->purchase_units as $purchase_unit) { foreach ($purchase_unit->payments->captures as $capture) { print "\t{$capture->id}"; } } // To print the whole response body, uncomment the following line // echo json_encode($response->result, JSON_PRETTY_PRINT); } echo json_encode($response->result); return ; } captureOrder($orderId, false); ?>
网友评论0