微信支付的主要操作流程
1.用户浏览app,选定商品然后下单。
2.服务器处理订单逻辑,开始正式发起支付流程
3.首先,后台服务器向weixin服务器发起请求,获取一个token。
4.后台服务器拿到token,使用和其他参数加密,再次向weixin服务器发起请求,获取一个预支付prepayid
5.后台服务器将该prepayid返回给app客户端
6.app调用手机上的微信控件,完成付款流程。
7.app向后台服务器发起一个回调请求,通知服务器交易完成。
8.weixin服务器处理完所有的流程后,向后台服务器发起一个post请求,正式通知后台服务器交易完毕
上面流程的一些注意点:
1.每次获取的token是有时效的,默认是7200s,而且每天最多获取200次,因此最好放到redis中缓存起来,等失效后再去重新获取
2.app发起的回调默认是不可靠的,后台应该尽可能(不是必须)向微信服务器发起订单查询,查询本次交易的结果。
3.weixin服务器向后台发起的notify,才是确保交易完成的最后屏障。后台服务器确认后必须返回“success”,否则weixin服务器会尝试重发请求。
获取token
这步很简单,发送一个get请求即可。只需配置正确参数。
‘‘‘从微信服务器获取token‘‘‘
def _getAccessTokenFromWeixin(self):
response = requests.get(self.tokenUrl % (self.appId, self.appSecret))
if response.status_code == 200:
text = response.text
tokenInfo = json.loads(text)
try:
token = tokenInfo[‘access_token‘]
expires_in = tokenInfo[‘expires_in‘]
self._writeWeixinTokenLog(token, self.order_no)
return token
except KeyError:
return None #token获取失败
return None #http请求失败
获取prepayid
在微信支付的开发流程中,最繁琐的就是获取prepayid。
这一步我们需要组装这样一个参数:
{ "appid":"wxd930ea5d5a258f4f", "traceid":"test_1399514976", "noncestr":"e7d161ac8d8a76529d39d9f5b4249ccb ", "timestamp":1399514976, "package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF% 95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8", "sign_method":"sha1", "app_signature":"7f77b507b755b3262884291517e380f8" }
组装package
这里的第一步就是组装package:
"package":"bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF% 95&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_ no=7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1& total_fee=1&sign=7F77B507B755B3262884291517E380F8",
组装package需要的参数如上面代码所示,所以我们需要准备一个params,然后准备签名,签名流程如下:
1.按照key的字典序,对params进行排序,然后拼接成字符串,注意这些key不包括sign
2.在上面的字符串后面拼接key=paternerKey,然后对整个字符串进行md5签名,然后转换成大写,此时我们就得到了签名
然后我们将所有params的value进行urlencode转码,然后后面拼接上sign=signValue,就得到了package字符串。
这里创建MD5的过如下:
def createMD5Signature(self, signParams): ‘‘‘先排序‘‘‘ sortedParams = sorted(signParams.iteritems(), key=lambda d:d[0]) ‘‘‘拼接‘‘‘ stringSignTemp = "&".join(["%s=%s" % (item[0], item[1]) for item in sortedParams if item[0] != ‘sign‘ and ‘‘ != item[1]]) #加上财付通商户权限密钥 stringSignTemp += ‘&key=%s‘ % (self.partnerKey) #使用MD5进行签名,然后转化为大写 stringSign = hashlib.md5(stringSignTemp).hexdigest().upper() #Upper return stringSign
组装package的代码:
def getPackage(self, packageParams): ‘‘‘先获取params的sign,然后将params进行urlencode,最后拼接,加上sign‘‘‘ sign = self.createMD5Signature(packageParams) packageParams = sorted(packageParams.iteritems(), key=lambda d:d[0]) stringParams = "&".join(["%s=%s" % (item[0], urllib.quote(str(item[1]))) for item in packageParams]) stringParams += ‘&sign=%s‘ % (sign) return stringParams
继续组装参数
得到package后,我们继续组装参数:
这里需要的参数为:
appid=wxd930ea5d5a258f4f appkey=L8LrMqqeGRxST5reouB0K66CaY A WpqhA Vsq7ggKkxHCOastWksvuX1uvmvQcl xaHoYd3ElNBrNO2DHnnzgfVG9Qs473M3DTOZug5er46FhuGofumV8H2FVR9qkjSlC5K noncestr=e7d161ac8d8a76529d39d9f5b4249ccb package=bank_type=WX&body=%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95 &fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fweixin.qq.com&out_trade_no =7240b65810859cbf2a8d9f76a638c0a3&partner=1900000109&spbill_create_ip=196.168.1.1&tot al_fee=1&sign=7F77B507B755B3262884291517E380F8 timestamp=1399514976
traceid=test_1399514976
注意这里有个坑:
参与签名的是上面的参数,但是最后的参数中不包括appKey,签名后要记得删除。
1.所有参数按照字典序排序,然后拼接
2.进行sha1签名,拼接到上面字符串的后面
3.注意这里要删除appKey,然后加上sign
获取sha1签名的代码如下: