近期公司的APP打算上线,需要集成支付的功能。由于采用的是Python进行开发,因此无法直接使用官方提供的SDK。虽然也有一些集成的第3方可以使用,比如ping++、beecloud。
但是由于提供的时间比较充裕,于是就自己实现了1个。在这个过程中,难免遇到一些坑,而这些坑有时会困扰你很久。
最初,并没有打算写这么一篇文章,因为它的适用范围很窄。但是网上搜索到的关于APP支付方面的都是移动端iOS和Android的实现方式,对于服务端的实现寥寥无几。相比而言,python在当前毕竟是小众语言,而如果参考其他语言,比如php的实现,发现这个过程还是有不少地方是没有讲清楚的。
虽然对于很多开发者来说,支付这个功能涉及的知识点并不是很多,但是你会发现你却在这里耗费了很多的时间。有时1个签名的问题,就让你无法调用支付,比如支付宝的Alipay10问题,总是出现服务器繁忙的提示,其实就是你的签名出了问题。
在这里,由于涉及到公司的一些敏感信息的问题,因此下面代码中的签名用的都是测试数据,而签名是根据已经验证通过的函数调用计算出来的。当你发现自己签名不过时,可以直接复制这些字符串,然后比对下面计算出来的签名来查看你的签名函数及你的回调处理哪里出了问题。
首先为了避免耽误大家的时间,这里我们只实现了微信支付及支付宝的移动支付。对于微信公众支付及支付宝的其他支付场景是不适用的。
这里,限于篇幅,只对订单支付及异步回调的部分进行说明,因为如果把所有的接口都过一遍,太耗费时间,还不如直接在pypi上上传1个包,直接使用pip安装。
在这里,将用到的签名的方式单独提取出来进行讲解,对于相同产品其他的接口也是适用的,只是请求的参数有所变化而已。
在正式讲述APP支付之前,我有如下的建议:
下面我们需要理清我们要做的事情,避免不必要的工作。主要是如下2个方面:
另外,私钥必须放在服务端,签名过程也必须放在服务端。
在这2种支付方式中,我们需要对签名的信息(URL键值对,例如key1=value1&key2=valu2…)按照ASCII编码顺序进行排序后再进行签名,并且采用POST方式进行提交。
_input_charset来指定编码,官方建议我们采用UTF-8。下面我们正式进行APP支付流程的说明,在这个过程中,我们需要阅读官方提供的文档。这里我们从微信开始,因为相比支付宝,微信的支付调用更为简单些。
在进行模块代码编写之前,我们来看看官方提供的流程图。换句话说,在我们调用统一下单接口后,我们需要给APP客户端返回prepayid及生成的签名,另外还有APP端调起支付接口中的其他字段。
这里,假设我们统一下单时请求参数如下:
| 1 | appid=wx2421b1c4370ec43b&attach=支付测试&body=APP支付测试&mch_id=10000100&nonce_str=1add1a30ac87aa2db72f57a2375d8fec¬ify_url=http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php&out_trade_no=1415659990&spbill_create_ip=14.23.150.211&total_fee=1&trade_type=APP |
而我们的商户号假设为1900000109,那么我们需要将商户号与之前的请求参数拼接在一起:
| 1234 | data = \’appid=wx2421b1c4370ec43b&attach=支付测试&body=APP支付测试&mch_id=10000100&nonce_str=1add1a30ac87aa2db72f57a2375d8fec¬ify_url=http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php&out_trade_no=1415659990&spbill_create_ip=14.23.150.211&total_fee=1&trade_type=APP&key=1900000109\’>>> from hashlib import md5>>> md5(data).hexdigest().upper()\’F3D12D07612100A7F0DA652E97A766FA\’ |
这里我们拼接后的参数进行MD5加密后将其转换为大写字母,这样就得到我们需要的签名了。因此,在请求统一下单时,我们需要传递如下的字符串:
| 12345678910111213 | <xml> <appid>wx2421b1c4370ec43b</appid> <attach>支付测试</attach> <body>APP支付测试</body> <mch_id>10000100</mch_id> <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str> <notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url> <out_trade_no>1415659990</out_trade_no> <spbill_create_ip>14.23.150.211</spbill_create_ip> <total_fee>1</total_fee> <trade_type>APP</trade_type> <sign>F3D12D07612100A7F0DA652E97A766FA</sign></xml> |
关于签名校验,微信官方提供了1个校验工具,当在请求返回的err_code出现SIGNERROR时可以使用这个工具来辅助我们进行校验。
当我们成功请求统一下单接口后,返回的结果可能如下所示:
| 1234567891011 | <xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <mch_id><![CDATA[10000100]]></mch_id> <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str> <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id> <trade_type><![CDATA[APP]]></trade_type></xml> |
接下来,我们需要取出返回结果中的prepay_id参数,然后按照调起支付接口中组装请求参数,假设我们得到如下的请求参数: