原文转载自“刘越的技术博客”https://v3u.cn/a_id_182之前有一篇文章:mpvue1.0+python3.7+Django2.0.4实现微信小程序的支付功能,主要介绍了微信小程序内部的支付流程。但实际上,微信小程序有一定的局限性,即用户范围仅限于小程序内部生态,生活中真正广泛、高效、便捷的支付方式还得是扫码支付.扫码的好处是推广成本低,从钓鱼台国宾馆到发廊街边摊都可以用,打印出来就完事了。相比于其他支付方式,零钱和假钞,刷卡的处理门槛,POS机的沉没成本,即使是微信集成的h5支付和小程序支付,但是很多老人并不知道如何使用小程序和手机浏览器都没有,更别说做支付操作了,所以扫码支付确实很符合国情。本次我们使用Vue.js+Django这个前后端分离的项目,集成微信的扫码支付功能,体验21世纪最全能的支付方式。首先在微信公众平台注册:https://mp.weixin.qq。com获取developerid和secretkey(appid&appsecret)同时保证获取微信支付接口的权限:然后注册微信支付商户平台:https://pay.weixin.qq.com/获取微信支付商户号(账户信息页):获取微信支付接口秘钥(账户中心->api安全):同时在产品中心->开发配置上配置支付域名page:与微信小程序不同,小程序只能允许https协议接口,扫码支付域名支持https和http,非常方便。同时注意域名必须是注册域名。至此,微信支付的前置操作就完成了。接下来我们来编写后台界面wx\_pay.py。首先导入依赖库和一些工具方法:importrequestsfromdjango.httpimportHttpResponse,HttpResponseRedirectimportrandomimporttimeimporthashlibimportqrcodefrombs4importBeautifulSoupdeftrans_xml_to_dict(data_xml):soup=BeautifulSoup(data_xml,features='xml')xml=soup.find('xml')#解析XML如果不是xml:return{}data_dict=dict([(item.name,item.text)foriteminxml.find_all()])returndata_dictdeftrans_dict_to_xml(data_dict):#定义字典转XML函数data_xml=[]forkinsorted(data_dict.keys()):#遍历字典的排序键v=data_dict.get(k)#取出值对应字典中的键ifk=='detail'andnotv.startswith(''.format(v)data_xml.append('<{key}>{value}{key}>'.format(key=k,value=v))return'{}'.format(''.join(data_xml))#返回XMLdefget_sign(data_dict,key):#签名函数,参数为签名后的数据和keyparams_list=sorted(data_dict.items(),key=lambdae:e[0],reverse=False)#参数字典倒序排列成alistparams_str="&".join(u"{}={}".format(k,v)fork,vinparams_list)+'&key='+key#整理参数串,添加商户交易keymd5attheend=hashlib.md5()#使用MD5加密方式md5.update(params_str.encode())#将参数字符串传入sign=md5.hexdigest().upper()#完成加密并转为大写返回signqrcode模块生成二维码,bs4模块用于将微信接口返回的xml解析为json。21世纪20年,微信界面还在用原来的xml。这种反人类的行为实在让人无法理解。接下来我们来写支付逻辑,参考微信官方文档:https://pay.weixin.qq.com/wik...\_5&index=3业务流程说明:(1)商户后台系统根据生成订单用户购买的产品。(2)用户确认支付后,调用微信支付【统一下单API】生成一笔预付款交易;(3)微信支付系统收到请求后生成预付款交易订单,返回交易会话的二维码链接code\_url。(4)商户后台系统根据返回的code\_url生成二维码。(5)用户打开微信扫描二维码,微信客户端将扫码内容发送至微信支付系统。(6)微信支付系统收到客户端请求后,验证链接有效性并发起用户支付,请求用户授权。(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。(8)微信支付系统根据用户授权完成支付交易。(9)支付交易完成后,微信支付系统将交易结果返回给微信客户端,并通过短信或微信消息提醒用户交易结果。微信客户端显示支付交易结果页面。(10)微信支付系统通过异步消息的方式将支付结果通知商户后台系统。商户后台系统需要回复收货状态,通知微信后台系统不要发送订单支付通知。(11)若未收到支付通知,商户后台系统调用【查询订单API】。(12)商户确认订单已付款后,将商品交付给用户。一看就知道需要调用微信统一下单接口,文档:https://pay.weixin.qq.com/wik...\_1写逻辑:defwx_pay(request):url='https://api.mch.weixin.qq.com/pay/unifiedorder'#微信扫码支付接口key='945bec*********a8fbf7d7'#商户api秘钥total_fee=1#支付金额,unitpointsbody='123123'#产品描述out_trade_no='order_%s'%random.randrange(100000,999999)#订单号params={'appid':'wx09*****f',#APPID'mch_id':'16****08',#商户ID'notify_url':'http://wxpay.v3u.cn/wx_back/',#支付域名回调地址'product_id':'goods_%s'%random.randrange(100000,999999),#商品编号'trade_type':'NATIVE',#支付类型(扫码支付)'spbill_create_ip':'114.254.176.137',#发送请求服务器IP地址'total_fee':total_fee,#订单总金额'out_trade_no':out_trade_no,#订单号'body':body,#产品描述'nonce_str':'ibuaiVcKdpRxkhJA'#string}sign=get_sign(params,key)#获取签名params.setdefault('sign',sign)#添加签名到参数字典xml=trans_dict_to_xml(params)#将字典转为XMLresponse=requests.request('post',url,data=xml)#通过POST方式向微信公众平台服务器发起请求data_dict=trans_xml_to_dict(response.content)#Return将请求的数据转化为字典print(data_dict)qrcode_name=out_trade_no+'.png'#支付二维码图片保存路径ifdata_dict.get('return_code')=='SUCCESS':#如果请求成功img=qrcode.make(data_dict.get('code_url'))#创建支付二维码img.save('./'+qrcode_name)#保存支付二维码returnHttpResponse(qrcode_name)然后配置路由:frommyapp.wx_payimportwx_payfromdjango.contrib.staticfiles.urlsimportstaticfiles_urlpatterns#...你的URLconf的其余部分放在这里...urlpatterns=[#Definehyperlinkroutingre_path('^static/upload/(?P.*)$',serve,{'document_root':'/static/upload/'}),path('wx_pay/',wx_pay),]启动django服务:pythonmanage.pyrunserver访问http://localhost:8000/wx\_pay/没问题,查看后台log:{'return_code':'SUCCESS','return_msg':'OK','appid':'wx092344a76b9979ff','mch_id':'1602932608','nonce_str':'bnjwglxz3edsngjs','sign':'2D81402DABEDF75E9A58F200FE7B6777B67775:'weixin://wxpay/bizpayurl?pr=JgBYgTS00'}可以看到已经下单成功,但是订单状态是预付状态。同时查看二维码图片是否生成:至此,后台逻辑基本完成,下面是如何在前端进行调用,让用户扫描的同时,编写wx\_pay.vue组件:<script>exportdefault{data(){return{money:"1",src:"",formItemLayout:{labelCol:{xs:{span:24},sm:{span:8},},wrapperCol:{xs:{span:24},sm:{span:16},},},tailFormItemLayout:{wrapperCol:{xs:{span:24,offset:0,},sm:{span:16,offset:8,},},},dataSource:[{key:'0',name:'EdwardKing0',age:'32',address:'London,ParkLaneno.0',},{key:'1',name:'EdwardKing1',age:'32',address:'London,ParkLaneno.1',},],列:[{title:'name',dataIndex:'name',},{title:'age',dataIndex:'age',},{title:'address',dataIndex:'address',},{title:'operation',dataIndex:'operation',scopedSlots:{customRender:'操作'},},],};},方法:{submit:function(){this.axios.get('http://localhost:8000/wx_pay/').then((result)=>{console.log(result.data.img);this.src="http://localhost:8000/static/upload/"+result.data.img});},onDelete(key){console.log(this.dataSource[key]);}},};当用户点击按钮后,立即请求后台支付接口,并将接口生成的二维码返回给前端。效果如下:然后使用微信扫一扫功能进行扫码支付,需要注意的是二维码的有效期只有五分钟,所以最好加上刷新功能支付成功后,我们还需要确认交易,所以根据微信官方文档,调用统一查询接口:https://pay.weixin.qq.com/wik...\_2,写逻辑根据接口文档:defwx_check(request):#统一订单查询接口url="https://api.mch.weixin.qq.com/pay/orderquery"out_trade_no="order_537236"#支付key后商户订单号='945b******d7'#商家api密钥参数={'appid':'wx0*****ff',#APPID'mch_id':'16*****08',#商家ID'out_trade_no':out_trade_no,#ordernumber'nonce_str':'ibuaiVcKdpRxkhJA'#randomstring}sign=get_sign(params,key)#获取签名params.setdefault('sign',sign)#添加签名到参数字典xml=trans_dict_to_xml(params)#字典转XMLresponse=requests.request('post',url,data=xml)#以POST方式向微信公众平台服务器发起请求data_dict=trans_xml_to_dict(response.content)#转换数据请求返回成字典print(data_dict)returnHttpResponse('ok')这里需要注意的是,查询的订单号可以是商家自己的订单号,也可以是微信订单号,必须选择其中之一:访问接口http://localhost:8000/wx\_check/返回结果:{'return_code':'SUCCESS','return_msg':'OK','appid':'wx092344a76b9979ff','mch_id':'1602932608','nonce_str':'BVoaDmxxADkpSFEl','sign':'23A86EB406B743E0C2C61C7E78DC9373','result_code':'SUCCESS','openid':'oy9q36f9Dpeokj9FWyN3j0znpIqE','is_subscribe':'N','trade_type':'NATIVE','bank_type':'OTHERS','total_fee':'1','fee_type':'CNY','transaction_id':'4200000806202012174121934231','out_trade_no':'order_537236','attach':'','time_end':'20201217231553','trade_state':'SUCCESS','cash_fee':'1','trade_state_desc':'支付成功','cash_fee_type':'CNY'}可以看出是没有问题的,但是既然涉及到钱业务,为了养成良好的测试习惯,最好登录商家后台再次确认:结论:至此,整个微信扫码支付流程运行的很流畅,流程比起微信小程序支付应该更简单一些。同时由于不需要在线用户的openid,所以不存在微信小程序获取不到openid这么大的坑。后面会分享一些关于微信扫码订单退款的逻辑。搞笑的是,我们统一山河查询接口没有并发限制,但是申请退款时有qps限制,所以退款过程应该需要消息队列的介入