paypal集成到网站php与js的实现教程

paypal支持全球收款,在国外的市场非常大,如果你的客户为国外用户,这个是必不可少的,今天我们来申请一个paypal账号,并通过js和php来接入到我们的网站业务中,让用户可以使用paypal来支付购买商品。

一、申请paypal

首先申请一个paypal账号,申请地址:https://www.paypal.com/c2/webapps/mpp/account-selection

paypal集成到网站php与js的实现教程

选择个人或企业账户,这里为了演示我选了个人

paypal集成到网站php与js的实现教程

选择中国

paypal集成到网站php与js的实现教程

输入自己手机号码

paypal集成到网站php与js的实现教程

输入手机验证码

paypal集成到网站php与js的实现教程

设置邮箱姓名及登录密码

paypal集成到网站php与js的实现教程

输入自己的身份证号码及身份证上的地址

填完之后进入paypal的开发者中心页面,地址:https://developer.paypal.com/developer/applications/

paypal集成到网站php与js的实现教程

我们可以看到有两个环境,一个是sandbox沙箱环境,是给开发者开发调试的,还有个一个live是生产版本,我们选择sandbox,点击creat app,我们来常见一个app

paypal集成到网站php与js的实现教程

创建完成后,我们可以看到client-id及秘钥

paypal集成到网站php与js的实现教程

好了,我们下一步就编写代码吧

二、使用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:*" ,回车

下载成功后,我们可以在目录中看到

paypal集成到网站php与js的实现教程

在当前目录下再新建四个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);

?>


{{collectdata}}

网友评论0