前言
我们现在动辄就要对接微信,但是微信的文档一个是入口隐藏太深,第二错误码也太草率了,最近正好项目不太忙,就把微信相关的东西整理出来,我踩过的坑希望大家就不要去浪费时间了。
一、微信支付流程
流程图画这么多,简单的概括就是三步:
<1>服务端生成支付参数(服务端)
<2>移动端调起sdk进行支付(移动端)
<3>检查支付结果(服务端)
二、生成支付参数
1.获取code链接
1.主方法
# pyton代码
from collections import OrderedDict
import xml.etree.ElementTree as et
import xmltodict
def get_pay_param():
try:
url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
data = OrderedDict()
data['appid'] = 'appid' # 这里填你的appid
data['body'] = 'title' # 这里是支付时候显示的名字, 如"增值服务"
data['mch_id'] = 'mch_id' # 商户号
data['nonce_str'] = verification_code() # 随机字符串, 详见verification_code方法
data['notify_url'] = notify_url # 回调地址, 我们这里不用这个, 但是是必传参数, 传空字符串就行
data['openid'] = 'open_id' # openid js支付必传, app支付不传, 后续解释什么openid
data['out_trade_no'] = 'order_id' # 需要支付订单的订单号
data['spbill_create_ip'] = 'ip' # 调用微信支付API的机器IP
data['total_fee'] = 'amount' # 支付金额, 单位是分
data['trade_type'] = 'JSAPI' # openid js支付必传, app支付不传
data['sign'] = set_sign(data) # 签名, 详见set_sign_js方法
xml_data = xmltodict.unparse({'xml': data}, pretty=True, full_document=False).encode('utf-8')
response = requests.post(url, data=xml_data)
res_data = response.content.decode()
data_dict = xmltodict.parse(res_data)
prepayid = data_dict.get('xml').get('prepay_id')
noncestr = data_dict.get('xml').get('nonce_str')
# 计算app_sign
app_data = app_sign(data['appid'], data['mch_id'], prepayid, noncestr)
exceptException as e:
print(e)
return dict()
return app_data
从上往下,这里有几个注意点
<1>这里字典用的是OrderedDict,因为生成签名要求参数按照参数名ASCII字典序排序。
<2>支付金额用分啊!敲黑板,当然这里微信要求的就是分,我们项目里设计最好也要分,千万不要用float,丢精度丢精度!!!
<3>这里是把js支付和app支付混在一起写的,用的时候注意自行去掉。
<4>我这里是只取了必填参数,如果需要扩展还要看微信文档。
# 如果参数正确的话就会收到如下的响应
"app_data": {
"appId": "wx3c2003853017fb67",
"nonceStr": "4yuBi0T1FVHuvKlT",
"package": "prepay_id=None",
"signType": "MD5",
"timeStamp": 1606982275,
"sign": "5C827985D58D8BEC5E0FF893F105578F"
}
2.工具方法
生成随机码 verification_code()
# 我这里是6位随机码
def verification_code():
return str("%06d"%random.randint(0, 999999))
生成签名 set_sign()
# key 后面的xxxx是对应商户的secret, 换成你们自己的
# data_dict就是上面的有序字典
def set_sign(data_dict):
stringA = ''
for key, value in data_dict.items():
stringA += key + '=' + str(value) + '&'
stringSignTemp = stringA + 'key=xxxxxx'
sign = md5(stringSignTemp).upper()
return sign
md5 加密 md5()
import hashlib
def md5(str):
return hashlib.md5(str.encode('utf-8')).hexdigest()
三、app调起sdk进行支付
封装好的代码,直接下载传参数就可以了
这里是app的
这里是js的
四、检查支付结果
如果我们有外网可以访问到的服务器,还是推荐notify_url填我们服务器地址,这样可靠性高一点,如果没有也没关系,我们可以主动查询。
1.理想状态
def check_payment():
try:
url = "https://api.mch.weixin.qq.com/pay/orderquery"
data = OrderedDict()
data['appid'] = app_id
data['mch_id'] = mch_id
data['nonce_str'] = verification_code()
data['out_trade_no'] = order_id
data['sign'] = set_sign(data)
xml_data = trans_dict_to_xml(data)
response = requests.post(url, data=xml_data)
res_data = response.content.decode()
tree = et.fromstring(res_data)
return_code = tree.find("return_code").text
if return_code != 'SUCCESS':
return False
result_code = tree.find("result_code").text
if result_code != 'SUCCESS':
return False
trade_state = tree.find("trade_state").text
if trade_state != 'SUCCESS':
return False
except Exception as e:
print(e)
return False
return True
参数的话生成支付参数的时候都见过了,就不赘述了
2.异常状态
用户正常流程当然会不异常,我们支付完调用这个接口,确定支付成功,刷新订单状态,但是我们同时也要保证程序的健壮性,对下面的异常进行处理:
1.app支付完不点返回,我们调不到这个接口。
2.app支付完直接上滑杀进程。
3.网络请求时断网或者网络异常。
用户的使用条件我们无法估计,就是有好多我们想得到或者想不到的意外发生,所以订单支付了我们没有及时更新怎么办?
做一个定时任务轮询未支付的订单呀,定时任务的时间根据业务来定,批量处理未支付的订单,确定是真的没有支付还是异常状态!
这也是为什么推荐用微信主动回调的原因了,因为我们能想到的异常在用被动回调的时候就不存在了。
总结
1. 上面的代码全部写在函数里,基本上属于完成度比较高的伪代码,小伙伴们用到的时候可以进一步封装。
2. 是不是又复习到前面的知识点,字典添加值,遍历,字符串拼接等。
3. 因为python中的字典是无序的,这里用到了有序字典,有序字典使用和我们前面说的字典用法是一样的,他只是额外记录了元素的插入顺序,当然也有额外的内存开销。
4. 为什么用python写支付demo,因为文档上有现成的java,php代码,拿来就能用。
5. 支付其实很简单的,主要耗时的可能就是前后端联调,作为一个合格后端开发,我们生成参数后尽量先去自检参数是否正确,这样可以大大节省调试时间。