通用技术 支付笔记 (1) 之 PayPal 支付原理

Ningxw · 2017年09月08日 · 最后由 securitytest 回复于 2017年09月26日 · 3644 次阅读
本帖已被设为精华帖!

此系列作为我学习支付过程的记录文,第一次接触支付,最终目的是实现模拟第三方支付平台 Mock。有啥不对的欢迎各路英雄好汉指正哈~~~☀ ☀ ☀

第一部分,弄清支付的实现原理

要想对支付进行全面深入的测试,弄清支付系统的实现原理是必不可少哒~在此我就不罗里吧嗦的了,给大家强行安利一个大神精华帖,传送门 -> 关于支付、关于安全的一些总结 (GOOGLE, APPLE, PAYPAL) ,里面对支付和安全的一些内容都讲解得非常清楚,受益匪浅哦~我是不会告诉你我连第一句话都是抄袭过来的~😝 😝 😝

第二部分,阅读支付平台的开发文档

这么快就来到这么难的一步了吗~😂 😂 😂

大牛说了,给支付平台做 Mock 前,要想找到最佳的实现方式,阅读支付平台的开发文档是个不错的选择~阅读文档后,你会对每个接口的请求和返回的内容格式都很清晰,清楚接下来你要拿什么数据做什么事。因此我也老老实实的去阅读了 PayPal 的开发文档,结果是脑子果然大了两倍!😮 😮 😮

我公司目前的项目只支持 PayPal 第三方支付平台,且业务需求是实现给项目打赏,功能比较单一,因此下列的内容都是针对自己项目展开的一些学习过程,从而入入支付的坑,所以大牛可以绕道了哈哈哈~~

PayPal Payments API

本项目支付调用的是 PayPal 的 SDK,主要关注 Create 和 Execute 两个接口,使用 SDK 的好处是通信过程中只要按照格式要求去请求接口就 OK 了,不需要牵扯到加密等复杂部分。以下是项目主要请求的内容和格式,以及返回的内容和格式。(主要来自 Payments API,为图方便而记录下来,有需要的可以去看文档哈~)

  • Create payment

Create 请求的格式和内容:

curl -v -X POST https://api.sandbox.paypal.com/v1/payments/payment   #请求地址
-H "Content-Type:application/json"        #请求头
-H "Authorization: Bearer <Access-Token>"     #请求头,带Access-Token
-d '       #请求数据内容
{
    "intent":"sale",
    "payer":{
        "payment_method":"paypal"
    },
    "transactions":[
        {
            "amount":{
                "total":"30.11",
                "currency":"USD"
            },
            "description":"The payment transaction description."
        }
    ],
    "redirect_urls":{
        "return_url":"https://www.paypal.com/return",      #可以设置为任一可以访问的url
        "cancel_url":"https://www.paypal.com/cancel"      #可以设置为任一可以访问的url
    }
}'

Create 返回的格式和内容:

{
    "id":"PAY-1B56960729604235TKQQIYVY",          #作为execute请求的参数
    "intent":"sale",
    "state":"created",
    "payer":{
        "payment_method":"paypal"
    },
    "transactions":[
        {
            "amount":{
                "total":"30.11",
                "currency":"USD"
            },
            "description":"The payment transaction description.",
            "related_resources":[

            ]
        }
    ],
    "create_time":"2017-09-07T08:51:59Z",
    "links":[
        {
            "href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-1B56960729604235TKQQIYVY",
            "rel":"self",
            "method":"GET"
        },
        {
            "href":"https://api.sandbox.paypal.com/v1/payments//cgi-bin/webscr?cmd=_express-checkout&token=EC-60385559L1062554J",                        #请求payer_id的approval_url
            "rel":"approval_url",
            "method":"REDIRECT"
        },
        {
            "href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-1B56960729604235TKQQIYVY/execute",
            "rel":"execute",
            "method":"POST"
        }
    ]
} 
  • Execute approved PayPal payment

Execute 请求的格式和内容:

curl -v -X POST https://api.sandbox.paypal.com/v1/payments/payment/PAY-9N9834337A9191208KOZOQWI/execute 
-H "Content-Type:application/json" 
-H "Authorization: Bearer <Access-Token>"        #请求头,带Access-Token
-d '{
  "payer_id": "CR87QHB7JTRSC"                   #确认支付的id标识
}'

Execute 返回的格式和内容:

{
  "id": "PAY-9N9834337A9191208KOZOQWI",
  "create_time": "2017-07-01T16:56:57Z",
  "update_time": "2017-07-01T17:05:41Z",
  "state": "approved",                          #支付后的状态
  "intent": "order",
  "payer": {
    "payment_method": "paypal",
    "payer_info": {
      "email": "qa152-biz@paypal.com",
      "first_name": "Thomas",
      "last_name": "Miller",
      "payer_id": "PUP87RBJV8HPU",
    ……
      }
    }
  },
………………参考官方示例response

获取到 execute 的返回值,主要关注 state 关键字。

返回的值有 created,approved,failed,以此来判断支付的状态。到了这一步,支付的整个流程就全部走通啦!以上只展示了成功的流程,对于其他异常的流程,可以自己修改请求去获取,这里就不累赘介绍啦!

第三部分,有关参数和细节

1.关于Access-Token 的获取

PayPal 后台有两个关键参数 clientId 和 clientSecret,这些可以在 Paypal 后台配置,把这两个参数请求给 PayPal,然后会返回一个 access token。(前提是你需要登录 PayPal 的后台哦~关于账号密码,可以找你们的开发哥哥拿,简单粗暴!)

curl -v https://api.sandbox.paypal.com/v1/oauth2/token \   
   -H "Accept: application/json" \
   -H "Accept-Language: en_US" \
   -u "<client_id>:<secret>" \
   -d "grant_type=client_credentials"

PayPal 后台关键参数入口在My Apps & Credentials -> REST Api apps -> 你的项目,点进去之后就可以看到了,具体的页面我就不展示啦,我害怕泄露天机然后被老板炒鱿鱼!😂 😂

2.关于 payer_id 的获取
payer_id 的获取需要从 Create 请求的返回结果 approval_url 中请求得到,需要在 PayPal 确认支付后才会返回给页面,直接一点的方法是在浏览器中输入 approval_url,使用抓包工具截取返回结果,从而获取到 payer_id,如图所示:

3.关于 curl 命令的运行工具
墙裂推荐使用Babun,一个 Windows 上的开箱即用的壳程序,安装 Babun 十分简单,解压缩包之后,执行里面的 install.bat 批处理脚本,然后静静等待执行结束即可,过程有些长,但一定要等待执行结束哦,安装结束后 Babun 会自动运行,且在桌面上生成快捷方式,谁用谁知道哈~~😎 😎 😎

4.使用 python 运行 curl 命令
是不是很头疼啊现在,不知道怎么转化,谷歌来谷歌去的,一会使用 pycurl,一会想将 curl 命令转化为 requests 方式,就是找不到最佳的实现方式,就是找不到一种简单粗暴又无脑的方式去转化呢?!!!😭 😭 😭

别着急,甩你一个拯救苍生的快捷路径 -> 转 curl 为 python ,还不赶快谢主隆恩哈哈哈😎 😎 😎

这样的话,我们就可以进一步把上面的步骤一一整合成 python 的方式去实现啦,不过在整合之前,最好还是一步步来,体会一下这个实现的过程,收获颇多哦~~

5.将 curl 命令转化为 requests 的样子

以下展示获取 access_token 的 python 文件,以作观赏!想要将整个流程合为一个 py,这个任务就交给聪明的小伙伴们啦~

import requests

def get_accesstoken():
    clientId = 'clientIdxxx'
    clientSecret = 'clientSecretxxx'
    headers = {
        'Accept': 'application/json',
        'Accept-Language': 'en_US',
    }

    data = [('grant_type', 'client_credentials')]

    res = requests.post('https://api.sandbox.paypal.com/v1/oauth2/token',
                headers=headers,
                data=data,
                auth=(clientId, clientSecret))

    result = res.json()
    print(result['access_token'])

if __name__=="__main__":
    get_accesstoken()

总结

理顺完支付的过程,清楚了支付时请求了什么东东给第三方支付平台,也清楚了第三方支付平台支付完成后回调了什么东东给服务器,这样的话,就可以开始起一个 mock 服务,写几个 function 专门来处理这些支付回调的东西,然后再去项目接口内部修改注释掉相应的支付部分,通过 hosts 指向自己部署的服务,模拟返回第三方支付端参数达到支付 mock 效果。

以上是我这个星期来研究各大论坛得出的一个结论,时间比较短,可能理解的也很浅,具体的工作还没开始做,就只是理清了这个 PayPal 的支付过程和支付原理,有够笨的我😂 😂 😂 也不知道是否是这样做的,如果有做过的大牛小牛,欢迎来批评指正~没人阻止我的话我就这样钻下去了😳 😳 😳

还有话要说

我上段时间没事做,开通了个人博客啦 -> 宁大大噢耶的博客,里面会发布一些零散的学习笔记,希望能帮助到更多的初学者,觉得好的话给我点赞哦哈哈😁 😁 😁

共收到 10 条回复 时间 点赞

理顺完支付的过程,清楚了支付时请求了什么东东给第三方支付平台,也清楚了第三方支付平台支付完成后回调了什么东东给服务器,这样的话,就可以开始起一个 mock 服务,写几个 function 专门来处理这些支付回调的东西,然后再去项目接口内部修改注释掉相应的支付部分,通过 hosts 指向自己部署的服务,模拟返回第三方支付端参数达到支付 mock 效果。

这段精华。有个问题,既然可以用 sandbox,为啥还要 mock 啊?可以畅快的付钱啊。。

恒温 回复

哈哈,你说的对,实际上是不需要 mock 的,但是支付宝微信那些没有呀,但目前项目先上的是 PayPal,所以拿 PayPal 来练练手~看看这个过程是怎么实现的,学学习~有啥好建议吗😁 😁

Ningxw 回复

支付宝如果没有沙箱,那估计 mock 不了,

思寒_seveniruby 将本帖设为了精华贴 09月08日 21:03
恒温 回复

我的想法是,把返回值模拟掉,然后自己本地搭建一套支付环境,或者自己拉一套代码分支,在本地做支付接口的测试~你看这个想法可实行吗?有什么其它比较好的建议你也可以说说看哦😁 😁 😁

debugtalk 回复

😁 😁 谢谢称赞~

Ningxw 回复

这个思路是可以的,我们目前就是这么做国内支付源的 mock 的, https://github.com/jacexh/grandet

jacexh 回复

哇好棒!有没有一些文章系列说明呀,代码量还是有点多的,希望能快速了解其中实现原理,然后自己实现出来~💪 💪

Ningxw 回复

核心代码没多少,主要工作花在了整理支付源的各种返回值上

Ningxw 2017 末,两年半 中提及了此贴 12月29日 15:40
需要 登录 後方可回應,如果你還沒有帳號按這裡 注册