PHP | 开发Paypal支付,支付流程和接口实现方案

写在前面
近期由于要开发公司外贸商城,需要对接Paypal支付。在开发过程中发现有好多坑。文档都是英文文档(主要还是自己英文水平不过关)、网上找的那些翻译过的文档老旧、沙箱环境网站卡的要让人崩溃。整个过程真是让人头大,经过各种翻天覆地的百度、google、论坛终于功夫不负有心人跑通了。下面就结合网上的文档给大家说说如何开发Paypal支付(一个Demo),避免大家在踩坑。

一、准备工作
登录 https://www.paypal.com/ 注册一个主账号(选择个人账号、企业账后都可)
申请完成后登录https://developer.paypal.com/ 在后台右侧菜地点击“Accounts”,可以看到系统自动给分配的两个沙箱环境的账号。类型为Personal是个人账号、类型为Business是商家账号。点进去可以看到密码
开启PDT设置同步回调地址(这步拿到at_token return回调使用)
用系统分配的卖家账号登录沙箱地址https://www.sandbox.paypal.com/
右上角用户名下来会弹出一个"Account Settings"
点击“网站付款”->“网站习惯设定”->“更新”
自动返回:开启,url填写return(同步回调地址)
付款数据传输:开启
激活沙箱企业帐户。注意:这步非常重要否则无法收到收款信息。
用系统分配给你的卖家账号登录沙箱环境地址https://www.sandbox.paypal.com/
右上角用户名下来会弹出一个"Profile Settings"
点击“登录和安全”->“邮箱地址”->“更新”
点击邮箱进行验证
登录你的paypal真实主账号https://developer.paypal.com/
左侧菜单中选择“notifications”,会看到一条激活的邮件,点击进行激活就可以了
二、支付开发
项目根目录下创建index.html文件

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Paypal订单支付</title>
</head>
<body>
<form action="https://www.sandbox.paypal.com/cgi-bin/webscr" method="POST"  name="form_starPay"> <!-- // Live https://www.paypal.com/cgi-bin/webscr -->
    <input type='hidden' name='cmd' value='_xclick'>  <!-- //告诉paypal该表单是立即购买 -->
    <input type='hidden' name='business' value='paypal-facilitator@xx.cn'> <!-- //卖家帐号 也就是收钱的帐号 -->
    <input type='hidden' name='item_name' value='支付订单:20180828080706000039'> <!-- //商品名称 item_number -->
    <input type='hidden' name='item_number' value='20180828080706000033'> <!-- //物品号 item_number -->
    <input type='hidden' name='amount' value='0.01'> <!-- .// 订单金额 -->
    <input type='hidden' name='shipping' value='0.01'> <!-- .// 运费 -->
    <input type='hidden' name='currency_code' value='USD'> <!-- .// 货币 -->
    <input type='hidden' name='return' value='http://XXX.XXX.XX/paypal2/return.php'> <!-- .// 支付成功后网页跳转地址 -->
    <input type='hidden' name='notify_url' value='http://XXX.XXX.XX/paypal2/notify.php'> <!-- .//支付成功后paypal后台发送订单通知地址 -->
    <input type='hidden' name='cancel_return' value='http://XXX.XXX.XX/paypal2/cancel.php'> <!-- .//用户取消交易返回地址 -->
    <input type='hidden' name='invoice' value='20180828080706000026'> <!-- .//自定义订单号 -->
    <input type='hidden' name='charset' value='utf-8'> <!-- .// 字符集 -->
    <input type='hidden' name='no_shipping' value='1'> <!-- .// 不要求客户提供收货地址 -->
    <input type='hidden' name='no_note' value='1'> <!-- .// 付款说明 -->
    <input type='hidden' name='rm' value='2'> <!-- 不知道是什么 -->
    <input type='hidden' name='custom' value='paypal'> <!-- 自定义字段 -->
</form>
正在跳转Paypal支付,请稍等。。。
<script>
    function sub(){
        document.form_starPay.submit();
    }
    onload(sub());
</script>
</body>
</html>
  1. 项目根目录下创建notify.php文件
  2. <?php
    
        if(!$_POST) die();
    
        //记录支付回调信息
        if(!empty($_POST)){
            $notify_str = "paypal:\r\n";
            foreach ($_POST as $key => $value) {
                $notify_str.=$key."=".$value.";\r\n";
            }
        }
        writelog($notify_str);
    
        //ipn验证
        $data = $_POST;
        $data['cmd'] = '_notify-validate';
        //$url = 'https://www.paypal.com/cgi-bin/webscr';//支付异步验证地址
        $url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';//支付异步验证地址
        writelog('data:'.$data);
        $res = https_request($url,$data);
        //记录支付ipn验证回调信息
        writelog('info:'.$res);
    
        if (!empty($res)) {
            if (strcmp($res, "VERIFIED") == 0) {
    
                if ($_POST['payment_status'] == 'Completed' || $_POST['payment_status'] == 'Pending') {
                    //付款完成,这里修改订单状态
    //                $order_res = $this->order_pay($_POST);
                    $order_res = 1;
                    if(!$order_res){
                        writelog('update order result fail');
                    }
                    writelog('success');
                }
            } elseif (strcmp($res, "INVALID") == 0) {
                //未通过认证,有可能是编码错误或非法的 POST 信息
                writelog('fail');
            }
        } else {
            //未通过认证,有可能是编码错误或非法的 POST 信息
    
            writelog('fail');
    
        }
    
    
    function writelog($loginfo)
    {
        $file = 'log/tongbu_' . date('y-m-d') . '.log';
        if (!is_file($file)) {
            file_put_contents($file, '', FILE_APPEND);//如果文件不存在,则创建一个新文件。
        }
        $contents = $loginfo . "\r\n";
        file_put_contents($file, $contents, FILE_APPEND);
    }
    
    function https_request($url,$data=null){
    
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $tmpInfo = curl_exec($ch);
        if (curl_errno($ch)) {
            return curl_error($ch);
        }
    
        curl_close($ch);
        return $tmpInfo;
    
    }
    ?>
    
    

    3.项目根目录下创建return.php文件

    <?php
    /**
     * Created by PhpStorm.
     * User: yangyi
     * Date: 2020/8/14
     * Time: 15:31
     */
    //获取 PayPal 交易流水号 tx
    
    $tx = $_GET['tx'];
    
    $at_token = '身份标记'; //your_pdt_identity_token
    
    
    
    // Init cURL
    
    $request = curl_init();
    
    // Set request options
    
    curl_setopt_array($request, array
    
    (
    
    // CURLOPT_URL => 'https://www.sandbox.paypal.com/cgi-bin/webscr',//测试
    
        CURLOPT_URL => 'https://www.sandbox.paypal.com/cgi-bin/webscr',//正式
    
        CURLOPT_POST => TRUE,
    
        CURLOPT_POSTFIELDS => http_build_query(array
    
        (
    
            'cmd' => '_notify-synch',
    
            'tx' => $tx,
    
            'at' => $at_token,
    
        )),
    
        CURLOPT_RETURNTRANSFER => TRUE,
    
        CURLOPT_HEADER => FALSE//,
    
    // CURLOPT_SSL_VERIFYPEER => TRUE,
    
    // CURLOPT_CAINFO => 'cacert.pem',
    
    ));
    
    
    
    // Execute request and get response and status code
    
    $response = curl_exec($request);
    
    $status = curl_getinfo($request, CURLINFO_HTTP_CODE);
    
    
    
    // Close connection
    
    curl_close($request);
    
    // echo $status;
    
    // var_dump($response);
    
    if($status == 200 AND strpos($response, 'SUCCESS') === 0)
    
    {
    
    // Remove SUCCESS part (7 characters long)
    
        $response = substr($response, 7);
    
    
    
    // Urldecode it
    
        $response = urldecode($response);
    
    
    
    // Turn it into associative array
    
        preg_match_all('/^([^=\r\n]++)=(.*+)/m', $response, $m, PREG_PATTERN_ORDER);
    
        $response = array_combine($m[1], $m[2]);
    
    
    
    // Fix character encoding if needed
    
        if(isset($response['charset']) AND strtoupper($response['charset']) !== 'UTF-8')
    
        {
    
            foreach($response as $key => &$value)
    
            {
    
                $value = mb_convert_encoding($value, 'UTF-8', $response['charset']);
    
            }
    
    
    
            $response['charset_original'] = $response['charset'];
    
            $response['charset'] = 'UTF-8';
    
        }
    
    
    
    // Sort on keys
    
        ksort($response);
    
    // Done!
    
    }else{
    
        echo 'pay fail curl';
    
        die();
    
    }
    

    4.项目根目录下创建cancel.php文件

    <?php
    //具体取消逻辑可以根据自己业务写
    echo '已取消';
    

    三、友情提示
    使用沙箱地址请求的时候,有时候可能会请求失败。多试几次就可以了
    在配置PDT的回调地址,和发起请求的html的return字段效果是一样的,两个地方填一个即可
    本文章涉及的代码只是demo,让大家明白流程而已,具体业务代码还得根据实际情况编写
    总结
    目前网上的文档感觉没有一个整体流程详细的介绍,而且有些比较老。本篇文章是汇总整理并加入了一些自己的改造和踩坑分享而已。文介绍了paypal支付的其中一种方式,还有一种SDK的方式,这两种有点小区别但是整体的流程还是一样是。等后续会和大家分享另一种方式。还请各位大佬多多指教。
    ————————————————
    版权声明:本文为CSDN博主「杨有财~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/m0_37572422/article/details/108338005

THE END