首先先了解字节跳动小程序以及微信H5支付他们发起支付时所需要的参数
微信
H5支付
官网文档链接:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
流程图:
1、用户在商户侧完成下单,使用微信支付进行支付
2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB
3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页
4、中间页进行H5权限的校验,安全性检查(此处常见错误请见下文)
5、如支付成功,商户后台会接收到微信侧的异步通知
6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)
7、商户在展示页面,引导用户主动发起支付结果的查询
8,9、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态
10、展示最终的订单支付结果给用户
统一下单接口
https://api.mch.weixin.qq.com/pay/unifiedorder
参数
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付测试</attach>
<body>H5支付测试</body>
<mch_id>10000100</mch_id>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee> # 金额必须为整数,这里单位为分
<trade_type>MWEB</trade_type>
<scene_info>{"h5_info": {"type":"IOS","app_name": "王者荣耀","package_name": "com.tencent.tmgp.sgame"}}</scene_info>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
签名
将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
- ◆ 参数名ASCII码从小到大排序(字典序);
- ◆ 如果参数的值为空不参与签名;
- ◆ 参数名区分大小写;
- ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
key设置路径:微信商户平台(https://pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
### 微信支付接口签名校验工具(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=20_1)
# 使用MD5生成 m1 = hashlib.md5() m1.update(stringA) # stringA为拼接的字符串 key = m1.hexdigest().upper() # 拿到加密字符串,将他转化为大写
只要你生成的和用官方测试工具生成的签名一致,那签名这块就没问题了
然后再封装成xml请求微信统一支付接口
request = urllib2.Request(url="https://api.mch.weixin.qq.com/pay/unifiedorder",
headers={'Content-Type': 'application/xml', 'charset': 'UTF-8'},
data=xml)
f = opener.open(request)
data = f.read()
data = xml2dict.xml2dict(data)
请求成功后会返回以下参数
<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[MWEB]]></trade_type>
<mweb_url><![CDATA[https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx2016121516420242444321ca0631331346&package=1405458241]]></mweb_url>
</xml>
字节跳动
当你做完所有操作后,就得开始封装返回给字节跳动的数据,所以我们对于字节跳动的文档多多少少也还是要熟悉一点。
当我们返回出去后,前端就会调用字节跳动的tt.pay
接口,所以我们返回的参数还需要根据这个接口来规定
官方链接:https://microapp.bytedance.com/dev/cn/mini-app/develop/open-capacity/payment/pay
签名方案只验证 服务端签名前的支付数据 和 tt.pay 发送到字节跳动后端的支付数据 的一致性(因而,具体字段有哪些,以及字段的值是否正确并不由签名验证) ----只要验证对签名就好了
生成签名规则
Key 筛选(我之前就是错在这一步,搞了很久,有些参数是不要参加签名的)
去掉请求参数中的字节类型字段(如文件、字节流)、sign
与 risk_info
字段、value
为空的字段(某些接口请求参数中特殊标明不参与签名的字段也需要去掉)。
K-V 排序和拼接
把筛选后的 k-v 集合按照 key 的 ASCII 码升序排序,
k-v 按照 key=value
的格式链接,并且按照 key 排序使用 &
连接成一个字符串,生成待签名字符串
MD5 加签
MD5(待签名字符串 + app_secret); // 待签名字符串 + app_secret 是字符串直接拼接,待签名字符串在前,中间没有连接符。
sign = hashlib.md5(string)
然后将所有的数据返回给前端
data = {
'app_id': str(dy_utils.app_id), # 头条支付分配给商户 app_id
'merchant_id': str(dy_utils.partner_id), # 商户号
'timestamp': str(create_time), # 请求时间戳
'out_order_no': str(order_num), # 订单号
'total_amount': str(money), # 金额
'trade_type': 'H5', # 交易类型
'subject': '测试订单', # 商户订单名称
'body': '测试订单', # 商户订单详情
'uid': str(uid), # 用户id
'trade_time': str(create_time), # 下单时间戳
'notify_url': return_url,
'risk_info': client_ip,
'wx_url': mweb_url, # 微信支付跳转链接
'sign': sign, # 签名
}
有些固定值让前端添加
# 验证签名
可以先用官方给的数据来进行签名,如果签名对了,就将官方的数据换成自己的数据
{
app_id: '800000000001',
merchant_id: '1900000001',
timestamp: 1570694312,
sign_type: 'MD5',
out_order_no: '201900000000000001',
total_amount: 1,
product_code: 'pay',
payment_type: 'direct',
trade_type: 'H5',
version: '2.0',
currency: 'CNY',
subject: '测试订单',
body: '测试订单',
uid: '0000000000000001',
trade_time: 1570585744,
valid_time: 300,
notify_url: '',
risk_info: '{"ip":"120.230.0.0"}',
wx_type: 'MWEB',
wx_url: 'https://wx.tenpay.com/xxx',
alipay_url: 'app_id=2019000000000006&biz_content=xxxx'
}
则待签名字符串 unsigned_str
为:
alipay_url=app_id=2019000000000006&biz_content=xxxx&app_id=800000000001&body=测试订单¤cy=CNY&merchant_id=1900000001&out_order_no=201900000000000001&payment_type=direct&product_code=pay&sign_type=MD5&subject=测试订单×tamp=1570694312&total_amount=1&trade_time=1570585744&trade_type=H5&uid=0000000000000001&valid_time=300&version=2.0&wx_type=MWEB&wx_url=https://wx.tenpay.com/xxx
若你的 app_secret 是 'a'
,那么带入上述 unsigned_str
,最终 MD5(unsigned_str + app_secret)
后签名字符串 signed_str
应为:
0f1e3358a9898d7c4c6c23740251808a