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

写在前面

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

一、准备工作

  1. 登录 https://www.paypal.com/ 注册一个主账号(选择个人账号、企业账后都可)
  2. 申请完成后登录https://developer.paypal.com/ 在后台右侧菜地点击“Accounts”,可以看到系统自动给分配的两个沙箱环境的账号。类型为Personal是个人账号、类型为Business是商家账号。点进去可以看到密码
    image.png

3.开启PDT设置同步回调地址(这步拿到at_token return回调使用)

  • 用系统分配的卖家账号登录沙箱地址https://www.sandbox.paypal.com/

  • 右上角用户名下来会弹出一个"Account Settings"

  • 点击“网站付款”->“网站习惯设定”->“更新”
    image.png
  • 自动返回:开启,url填写return(同步回调地址)

  • 付款数据传输:开启
    image.png
  1. 激活沙箱企业帐户。注意:这步非常重要否则无法收到收款信息。
  • 用系统分配给你的卖家账号登录沙箱环境地址https://www.sandbox.paypal.com/
  • 右上角用户名下来会弹出一个"Profile Settings"
    image.png
  • 点击“登录和安全”->“邮箱地址”->“更新”
    image.png
  • 点击邮箱进行验证
    image.png
  • 登录你的paypal真实主账号https://developer.paypal.com/
    左侧菜单中选择“notifications”,会看到一条激活的邮件,点击进行激活就可以了

    image.png

二、支付开发

  1. 项目根目录下创建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@usr.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文件
<?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的方式,这两种有点小区别但是整体的流程还是一样是。等后续会和大家分享另一种方式。还请各位大佬多多指教。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容