本来以为微信支付是几行代码的事儿,没想到还要前后端配合,有点深。所以我还是记下来吧。(python环境下)
具体的业务流程就从百度上copy一下咯。
首先就要在公众平台申请微信支付,具体做法见https://jingyan.baidu.com/article/ad310e80f7a5c01849f49e9b.html,搞好了就进行下一步。
获取openid,步骤见上一篇。
之后就是生成商户订单了,来获取prepayid了,具体要求见官方说明https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3:
第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
有几个规则很重要:
- ◆ 参数名ASCII码从小到大排序(字典序);
- ◆ 如果参数的值为空不参与签名;
- ◆ 参数名区分大小写;
- ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
- ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
这里我需要发送的数据集合M如下;
data = {
'appid':env_dist['appid'],
'mch_id':env_dist['mch'],
'nonce_str': get_nonce_str(), #自己写的一个随机字符串,防止重放攻击。
'body': '上链费用', # 商品描述
'out_trade_no': str(int(time.time())), # 商户订单号
'spbill_create_ip': '127.0.0.1', # APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
'notify_url': ' http://www.weixin.qq.com/wxpay/pay.php', # 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
'trade_type': 'JSAPI',
'total_fee': '1', #支付费用,单位为分。
'openid' : openid,
}
用如下语句进行排序拼接: stringA = '&'.join(["{0}={1}".format(k, data.get(k)) for k in sorted(data)])
其中生成随机字符串函数:
def get_nonce_str():
string=[]
for i in range(32):
x = random.randint(1, 2)
if x == 1:
y = str(random.randint(0, 9))
else:
y = chr(random.randint(97, 122))
string += y
string = ''.join(string)
return string
接下来将stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。merchant_key到 https://pay.weixin.qq.com -->账户中心-->API安全-->设置API密钥
自行设置一个32位的密钥
stringSignTemp = '{0}&key={1}'.format(stringA, merchant_key)
stringSignTemp = stringSignTemp.encode("utf8") #utf-8编码很重要
sign = hashlib.md5(stringSignTemp).hexdigest().upper()
data['sign'] = sign
将data变为xml格式,为发送到微信后台做准备。
data = trans_dict_to_xml(data).encode('utf-8') #utf-8编码,很重要。
将字典变为xml的方法如下:
def trans_dict_to_xml(data):
xml = []
for k in data.keys():
v = data.get(k)
if k == 'detail' and not v.startswith('<![CDATA['):
v = '<![CDATA[{}]]>'.format(v)
xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))
return '<xml>{}</xml>'.format(''.join(xml))
然后使用post方法将数据传输到后台换取prepayid。
url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' #换取的url
req = urllib.request.Request(url=url, data=data, method='POST', headers={'Content-Type': 'application/xml'})
得到返回值: result = result.decode('utf-8'),返回的数据大致如下:
<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>
<openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>
这里我只需要得到prepayid,所以使用了正则,不过使用dom解析xml是更加通用的做法,代码如下:
result = re.findall(r"<prepay_id><!\[CDATA\[(.*)]]></prepay_id>", result)
获得perpayid后进行第二次签名。
首先定义需要传输的数据包,包括:
package="prepay_id="+prepay_id
t = time.time()
data2={
'appId': 'wx04c066bae099852d',
'timeStamp': int(t), #时间戳,为整数秒
'nonceStr': get_nonce_str(),
'package': package,
'signType':'MD5',
}
获得数据包后和上面步骤一样进行签名。获得paysign,和data2一并传到前端。
stringB = '&'.join(["{0}={1}".format(k, data2.get(k))for k in sorted(data2)])
stringB = '{0}&key={1}'.format(stringB, merchant_key)stringB=stringB.encode("utf8")
paySign = hashlib.md5(stringB).hexdigest().upper()#获得paysign
data2['paySign']=paySign#将paysign加入到data2中
前端得到以上的参数,调用wx.requestPayment就实现了微信支付,具体的前端代码等到写前端时再写。
最后有几个官方的关于微信支付的文档可以看看,上面的应该是最简单的一个应用了,剩下的还是要靠自己继续发掘了。
微信支付统一下单接口文档:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1
微信支付签名算法:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3
微信支付开发教程:https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course3_tmpl&lang=zh_CN
来源:https://www.cnblogs.com/yzc0709/p/9752688.html