python-wechat-pay
Use Django, Django-rest-framework to achieve wechat payment.
微信支付、服务器异步通知、订单查询、退款
统一下单
应用场景
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里调起支付
接口连接
https://api.mch.weixin.qq.com/pay/unifiedorder
是否需要证书
不需要
请求参数(必传字段)
body (商品描述)
total_fee (订单总金额,单位为分)
appid(放置于服务端)
mch_id (放置于服务端)
nonce_str (随机字符串,不长于32位)
trade_type = "APP" (交易类型)
spbill_create_ip (ip地址)
out_trade_no (商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一)
notify_url (接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。)
sign (签名)
随机字符串的生成Python代码
import string
import random
def nonce_str(size=32):
charsets = string.ascii_uppercase + string.digits
result = []
for index in range(0, size):
result.append(random.choice(charsets))
return "".join(result)
签名的生成Python代码
sign_key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
设置图片url: https://img-blog.csdn.net/20151126154045054?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
payload即为支付请求参数的字典集合
# payload
payload = {
"appid": appid,
"mch_id": mch_id,
"nonce_str": nonce_str,
"body": description,
"out_trade_no": instance.out_trade_no,
"total_fee": total_fee,
"spbill_create_ip": spbill_create_ip,
"notify_url": notify_url,
"trade_type": trade_type,
}
import hashlib
def sign(payload, sign_key=None):
lst = []
for key, value in payload.items():
lst.append("%s=%s" % (key, value))
lst.sort()
raw_str = "&".join(lst)
if sign_key:
raw_str += "&key=%s" % sign_key
md5 = hashlib.md5()
md5.update(raw_str.encode('utf8'))
return md5.hexdigest().upper()
返回支付信息调起微信APP进行支付
import dicttoxml
req_xml = dicttoxml.dicttoxml(payload, custom_root="xml")
# 微信统一支付接口
url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
res_xml = requests.post(url, req_xml, verify=False)
res_xml.encoding = 'utf8'
res_xml = res_xml.text
res_obj = xmltodict.parse(res_xml)["xml"]
return_code = res_obj["return_code"]
如果return_code == 'FAIL': 签名配置出现问题,res_obj["return_msg"]中会介绍出现问题的原因
如果return_code == 'SUCCESS':接口调用成功,返回如下格式代码(XML)
调用支付接口(必传字段)
appid
partnerid
prepayid(预支付交易会话ID)
package (扩展字段)
noncestr (随机字符串)
timestamp (时间戳)
sign (签名)
我们需要返回数据进行再次签名操作
prepay_id = res_obj["prepay_id"]
ts = str(int(time.time()))
nonce_str = generate_nonce_str()
package = "prepay_id=%s" % prepay_id
sign_type = "MD5"
sign_payload = {
'appid': appid,
'partnerid': mch_id,
'prepayid': prepay_id,
'noncestr': nonce_str,
'timestamp': ts,
'package': package
}
pay_sign = sign_func(
sign_payload, settings.WEIXIN_ENTERPRISE_APP["SIGN_KEY"])
向前端发送与支付信息,调起微信APP进行支付
res = {
"timestamp": ts,
"nonce_str": nonce_str,
"package": package,
"sign_type": sign_type,
"pay_sign": pay_sign,
"prepay_id": prepay_id,
"out_trade_no": out_trade_no,
"partner_id": mch_id
}
微信2次签名问题
sign_payload中key使用小写方式,并且字段为确定的6个字段,不能修改;生成签名时,将生成的签名再转化成大写,传递到前端;
关于时间戳,生成sign的时候,时间戳使用int类型;前端使用时,该字段应为str类型的;