最近接触到了微信支付,主要做的是用django实现PC端的Native支付模式,即商户系统页面生成支付二维码,用户用“扫一扫”完成支付的模式,下面简单了解一下微信支付的开发文档。
Native支付有两种模式,我们可以根据支付场景选择相应的模式:
模式一
商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。
模式二
商户后台系统调用微信支付统一下单API生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。该模式的预付单有效期为2小时,过期后无法支付。
相比之下,模式二较为简单,更容易上手,因此选择了模式二。
事先需获取到微信公众账号的appid和商户号mch_id
商户后台系统根据用户选购的商品生成订单;
关键参数:生成订单时需提供product_id,商户订单号out_trade_no以及订单金额total_fee(单位分);
用户确认支付后调用微信支付统一下单API生成预支付交易;
关键参数:UNIFIED_ORDER_URL统一下单地址,NOTIFY_URL自定义的支付回调地址,nonce_str随机字符串,sign签名,body商品描述,spbill_create_ip终端IP,trade_type此处为NATIVE。
以上所有参数(包含appid、mch_id等)均用来构建xml请求参数,xml示例如下:
xxxxxx
NATIVE支付测试
10000100
1add1a30ac87aa2db72f57a2375d8fec
xxx.com
1415659990
0.0.0.0
1
NATIVE
0CB01533B8C1EF103065174F50BCA001
代码实现
# 构建原始参数dict,之后转成xml格式
params = {
"appid": APP_ID,
"mch_id": MCH_ID,
"nonce_str": nonce_str,
"out_trade_no": out_trade_no,
"body": 'xxxx',
"total_fee": total_fee,
"spbill_create_ip": spbill_create_ip,
"notify_url": NOTIFY_URL,
"trade_type": "NATIVE",
"product_id": product_id,
}
sign = sign_func(params, API_KEY) # 调用签名生成函数
params["sign"] = sign
req_xml = re_dict_to_xml(params) # 调用函数将dict转为xml格式
res_xml = requests.post(UNIFIED_ORDER_URL, req_xml, verify=False) # 调用统一下单API
res_xml.encoding = 'utf-8'
_, res_obj = xml_to_dict(res_xml.text) # 调用函数将结果转成dict 方便后续的解析
code_url = res_obj["code_url"] # 此处获得二维码url
关于接口其他返回参数的详细信息及使用方法,可参考官方文档:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url,然后商户后台系统根据返回的code_url生成二维码。
此处返回的ur需自行调用JS(如QRCode)转成二维码图片;
QRCode使用可参考:https://www.runoob.com/w3cnote/javascript-qrcodejs-library.html
用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统, 微信支付系统根据用户授权完成支付交易,微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
异步通知接口需要自行撰写,返回return_code和return_msg给微信即可;
代码实现
@csrf_exempt
def wechatpay_notify(request):
xml = request.body
WECHAT_SUCCESS = re_dict_to_xml({
'return_code': 'SUCCESS',
'return_msg': 'OK'
})
WECHAT_FAIL = re_dict_to_xml({
'return_code': 'FAIL',
'return_msg': 'ERROR',
})
sign, xml_obj = xml_to_dict(xml)
check_sign = sign_func(xml_obj, API_KEY)
if sign != check_sign: # 校验签名
return HttpResponse(WECHAT_FAIL)
if xml_obj['result_code'] != 'SUCCESS':
return HttpResponse(WECHAT_FAIL)
else:
return HttpResponse(WECHAT_SUCCESS)
除了等待微信支付系统的通知,商户后台系统也可调用查询订单API来自主查询订单支付情况。
关键参数:QUERY_ORDER_URL统一查询地址,nonce_str随机字符串,sign签名。
代码实现
def query_order(request):
data = {
'out_trade_no': out_trade_no,
'pay_status': 0,
'err_msg': None
}
nonce_str = generate_nonce_str()
params = {
"appid": APP_ID,
"mch_id": MCH_ID,
"nonce_str": nonce_str,
"out_trade_no": out_trade_no,
}
sign = sign_func(params, API_KEY)
params["sign"] = sign
req_xml = re_dict_to_xml(params)
res_xml = requests.post(QUERY_ORDER_URL, req_xml, verify=False)
res_xml.encoding = 'utf8'
_, res_obj = xml_to_dict(res_xml.text)
if res_obj["return_code"] == "FAIL":
return Response(res_obj["err_code_des"])
else:
result_code = res_obj["result_code"]
trade_state = res_obj["trade_state"]
if result_code == "SUCCESS":
if trade_state == "SUCCESS":
data['pay_status'] = 1
else:
data['err_msg'] = res_obj['trade_state_desc']
else:
data['err_msg'] = res_obj['err_code_des']
return Response(data)
关于接口其他返回参数的详细信息及使用方法,可参考官方文档:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
注意:微信API接口均采用POST
微信支付API接口协议中使用的nonce_str,主要用来保证签名不可预测。
# 生成随机字符串
def generate_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)
关于生成签名的规则和参数拼接顺序可参考
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
# 生成签名
def sign_func(payload, sign_key=None):
params_list = []
for key, value in payload.items():
if value:
params_list.append("%s=%s" % (key, value))
params_list.sort()
raw_str = "&".join(params_list)
if sign_key:
raw_str += "&key=%s" % sign_key
md5 = hashlib.md5()
md5.update(raw_str.encode('utf8'))
return md5.hexdigest().upper()
微信支付API官方文档 https://pay.weixin.qq.com/wiki/doc/api/index.html