1.字段概念
APPID:收款账号既是您的APPID对应支付宝账号 商户私钥 merchant_private_key,您的PKCS8格式RSA2私钥 支付宝公钥 alipay_public_key,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应 APPID下的支付宝公钥 服务器异步通知页面路径 notify_url post 页面跳转同步通知页面路径 return_url get 签名方式 sign_type = "RSA2" 沙箱网关 gatewayUrl ="https://openapi.alipaydev.com/gateway.do"
2.同步回调与异步回调
同步回调:把函数b传递给函数a。执行a的时候,回调了b,a要一直等到b执行完才能继续执行;
异步回调:把函数b传递给函数a。执行a的时候,回调了b,然后a就继续往后执行,b独自执行。
异步回调指的是:在发起一个异步任务的同时指定一个函数,在异步任务完成时会自动的调用这个函数。
通常情况下,异步都会和回调函数一起使用,使用方法即是add_done_callback(),给Future对象绑定一个回调函数。
3.概述
当一个支付请求被发送到支付渠道方,支付渠道(支付宝同步回调向系统发送一个get请求)会很快返回一个结果。但是这个结果,只是告诉你调用成功了,不是扣款成功,这叫同步调用。
很多新手会拿这个结果 当作支付成功了,那就会被坑死,结果就是支付成功率特别高,伴随着一堆无法解释的坏账率,
测试人员尤其要注意测试数据的篡改:金额,同步返回结果,订单号等。
同步请求参数里面会有一个回调地址,这个地址是支付渠道在扣款成功后调用的,这叫异步调用。
一般同步接口仅检查参数是否正确,签名是否无误等。异步接口才告诉你扣款结果。
一般异步接口有5秒以内的延迟。调用不成功会重试。有时候是这边成功了,但支付渠道侧没收到返回,于是会继续调。
当天的支付到第二天还在 被异步调用也都是正常的。这也是开发人员需要特别注意的地方,不要当做重复支付。
测试人员也要对重复回调进行测试,应只有一次有效。这还不是最坑的,一般 支付渠道侧,只有支付成功了才通知你。
要是支付失败了,压根儿都不告诉你。
另一方面,如何老收不到异步结果呢?那就得查查了。同步结果不可靠,异步调用不可靠,那怎么确定支付结果?最终的杀招就是查单了,
反查,一般支付渠道侧都 会提供反查接口,定时获取DB中待支付的订单调用支付渠道侧的反查接口,最终把支付渠道侧扣款成功的订单完成掉。
4.第三方支付过程分析
1)支付接口(需要登录认证:是谁):前台提交商品等信息,得到支付链接(或二维码) post方法 分析:支付宝回调 同步:支付宝get给前台 => 前台可以在收到支付宝同步get回调时,前台ajax异步在给消息同步给后台,也采用get,后台处理前台的get请求 异步:post给后台 => 后台直接处理支付宝的post请求 2)支付回调接口(不需要登录认证:哪个订单(订单信息中有非对称加密)、支付宝压根不可能有你的token): get方法:处理前台来的同步回调(不一定能收得到,所有不能在该方法完成后台订单状态等信息操作) post方法:处理支付宝来的异步回调 3)订单状态确认接口:随你前台任何时候来校验订单状态的接口
5.第三方支付宝接口实现过程
1.支付宝开放平台
https://open.alipay.com/platform/home.htm
2. alipay六大接口
3. alipay.trade.page.pay
非对称加密:公钥签发加密,私钥解密
开发助手生成本地应用公钥私钥
拿生成的本地应用公钥生成阿里云公钥
基于python alipay SDK 应用私钥 支付宝公钥
生成支付对象 alipay 包含商户的应用私钥(2048个字节)只有商户自己知道,支付宝公钥
调用对象alipay的方法 apipay.api_alipay_trade_page_pay 方法生成支付连接二维码页面
输入pay_url 即可
同步回调页面
二次封装 其它写道公共方法和配置文件
店员动作:生成支付码
过程:生成支付连接(网关+字符串)==> 向支付宝发送get请求获得支付二维码页面
接口1:向后台发送订单信息,获取支付连接()
参数: 订单号,总金额,商品,(同步回调地址,异步回调地址 在配置文件中读取,接口中无此参数)
接口2: 向支付宝发送get请求,获取支付页面
生成连接分析:
https://openapi.alipaydev.com/gateway.do?参数1=...&参数2=...&sign_type=RSA2&sign=mZsBK....(很长)
说简单点即是:支付宝网关?必要的参数&签名参数
商户私钥
参考此博客 验签
用户动作:扫码支付
1.前奏:
订单模块表分析
1)支付接口(需要登录认证:是谁):前台提交商品等信息,得到支付链接(或二维码) post方法
发起支付 分析:支付宝回调 同步:支付宝get给前台 => 前台可以在收到支付宝同步get回调时,前台ajax异步在给消息同步给后台,也采用get,后台处理前台的get请求 异步:post给后台 => 后台直接处理支付宝的post请求 2)支付回调接口(不需要登录认证:哪个订单(订单信息中有非对称加密)、支付宝压根不可能有你的token): get方法:处理前台来的同步回调(不一定能收得到,所有不能在该方法完成后台订单状态等信息操作) post方法:处理支付宝来的异步回调 3)订单状态确认接口:随你前台任何时候来校验订单状态的接口
支付接口
支付回调接口
1.支付宝同步回调接口
return_url + 回调参数
回调参数
` charset=utf-8& out_trade_no=7f7c7d12d57d45b693e1b49a6b01e1dd& method=alipay.trade.page.pay.return& total_amount=39.00& sign=FUmceqiNMWvxcD%2BUPCHiOTaEwlJ%2FXIXL5UwZWOSI1TwRjPIZVzjRLB4j2G5CQpn472JO8X%2BwMx04dHqjLxqLcY3TRu0XurQ%2FwKTNpyfDrtNuNv0rfGPuVHw52y3blbS7%2FKFVsWryw4%2BBuF2fCrJ4qWH8Zg14Rct7qoMbu73N74WkQtDyzXefiKDbkMMRMfLbelE9TFyeIeygeMId8%2B58mcJMUOh6aQqwpr9bzuBbfJ17fkqU%2F0ys9zGr%2FlDtLL7aAh6BPViqZN%2F9T7byCoferD1BhcSzJNR6V6VuhOdTq8iEaH2XgJT9aIiyHgg3GT1taBBvZX2gK41FSmkguk%2BfsA%3D%3D& trade_no=2020030722001464020500585462& auth_app_id=2016093000631831& version=1.0& app_id=2016093000631831& sign_type=RSA2& seller_id=2088102177958114& timestamp=2020-03-07%2014%3A47%3A48 ` // 同步回调没与订单状态
return_url 返回的字段信息解析到界面 显示购买成功,此时并没有回调订单状态,所以不能以此判断支付成功
同步回调异步回调处理
* 第一步:验证签名,签名通过后进行第二步 * 第二步:按一下步骤进行验证 * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), * 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方
(有的时候,一个商户可能有多个seller_id/seller_email), * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。 * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。
测试分析: 验签只能说明支付宝给你的数据没有问题,支付宝只认订单号
1.直接修改支付宝post接口数据,验签则无法通过,那么数据是否可以在前面的步骤中修改
2. 在生成支付二位码的时候修改支付数量,金额,订单号(不存在,重复),那么异步回调时候你就要效验订单,金额了
3.如果使用别的商户私钥和公钥去生成支付二维码,那么这时候异步回调的时候你就要效验appid(商户id)
4.在生成支付二维码,填入非法字段,不存在的,或者trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED")
5.并发单号会不会重复 :如果做了服务器集群,考虑使用分布式锁,锁住outTradeNo后再处理业务,不然有可能重复处理
6.服务器弱网断网: 支付宝一天8次异步回调
from utils.logging import logger # 支付回调接口 class SuccessViewSet(ViewSet): authentication_classes = () permission_classes = () # 支付宝同步回调给前台,在同步通知给后台处理 def get(self, request, *args, **kwargs): # return Response('后台已知晓,Over!!!') # 不能在该接口完成订单修改操作 # 但是可以在该接口中校验订单状态(已经收到支付宝post异步通知,订单已修改),告诉前台 # print(type(request.query_params)) # django.http.request.QueryDict # print(type(request.query_params.dict())) # dict out_trade_no = request.query_params.get('out_trade_no') try: #去数据判断有此订单,且此订单状态为1 models.Order.objects.get(out_trade_no=out_trade_no, order_status=1) return APIResponse(result=True) except: return APIResponse(1, 'error', result=False) # 支付宝异步回调处理 def post(self, request, *args, **kwargs): try: result_data = request.data.dict() out_trade_no = result_data.get('out_trade_no') signature = result_data.pop('sign') from libs import iPay # 先把返回的字符串用explode()解析成数组,去掉多余的数组元素,然后在用支付宝给你们约定的加密
# 方法重新加密一次,然后在把加密出来的字符串重新和你的sign匹配一下 result = iPay.alipay.verify(result_data, signature) if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"): # 完成订单修改:订单状态、流水号、支付时间 models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1) # 完成日志记录 logger.warning('%s订单支付成功' % out_trade_no) return Response('success') else: logger.error('%s订单支付失败' % out_trade_no) except: pass return Response('failed')
支付宝同步验签 与 异步验签
6.第三方支付并发数据一致性问题
第三方支付同步和异步回调并发数据一致性的问题 在第三方交易系统中的支付场景下,一般的步骤是,用户在系统产生订单,支付系统向第三方发起支付请求创建支付订单,用户跳转至第三方系统进行支付,
支付完成后会产生 同步回调 和 异步回调。系统一般根据 第三方异步回调 来确认本次交易结果 特殊业务场景 由于目前存在特殊的业务场景,本次交易结果必须依赖前端的 同步回调 请求来决定用户是否能进入下一步的产品交付场景。简单来说,在特殊的业务场景下,
必须依赖 同步回调 的结果来判定本次交易状态 并发一致性问题 当同步回调和异步回调同时发生的时候,由于实现细节的不同,两个流程需要将订单状态分别流转到不同的状态,由于查询数据库使用的是快照读,
在某一个流程中修改状态的事务未提交的时候,查询到这条记录一样的事务提交之前的状态,所以会存在数据没有被流转到正确的处理状态 原因 在 A B 事务并发的情况下,A B 两个事务都依赖同一行数据中数据字段的状态进行下一步的操作,当 A 事务决定将状态更新到 x 时的事务还未提交的时候,
B 查询到的数据状态和 A 事务是相同的,导致 B 事务会将数据字段更新到 y 导致数据结果和预期不一致 解决方式 事务 + 当前读 保证两个业务的查询,更新都在一个事务中,且使用当前读查询数据的最新状态 利用分布式锁串行化接口 由于业务强依赖同步回调,所以将同步回调与异步回调串行,使同步回调保持在异步回调之前,异步回调在同步回调请求未产生之前不进行处理 第三方异步回调返回问题 由于第三方依赖异步回调的结果来分析我方业务是否正常,如果在异步场景下直接返回异常那么在第三方的业务系统中会存在大量错误,这是我们不希望看到的。 由于不强依赖第三方的异步回调,系统可以使用消息中间件将回调消息收取,返回第三方正常,利用消息 + Job 脚本的方式对数据没有正常流转的订单进行补偿重试