乐趣区

关于vue.js:Win10环境前后端分离项目基于VuejsDjangoPython3实现微信扫码支付流程2021年最新攻略

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_182

之前的一篇文章:mpvue1.0+python3.7+Django2.0.4 实现微信小程序的领取性能,次要介绍了微信小程序外部领取的流程,然而实际上微信小程序有肯定的局限性,也就是用户范畴仅限于小程序外部生态圈,在生活中真正具备广泛性、高效性、应用方便性的领取形式还得是扫码领取,扫码的长处在于推广成本低,上至钓鱼台国宾馆,下至发廊地摊都能用,打印进去就完事了,而相比其余领取形式,现金的找零及假钞问题,信用卡的办理门槛、pos 机的沉没老本,就算微信可集成的 h5 领取和小程序领取,奈何很多老年人基本不会用小程序和手机浏览器,更别说再进行领取操作了,所以基于二维码的扫码领取确实是十分符合国情的。

本次咱们应用前后端拆散我的项目 Vue.js+Django 来集成微信的扫码领取性能,体验一下 21 世纪泛用性最高的领取形式,首先注册微信公众平台:https://mp.weixin.qq.com

取得开发者 id 和秘钥 (appid & appsecret)

同时确保获取微信领取接口的权限:

随后注册微信领取商户平台:https://pay.weixin.qq.com/

获取微信领取的商户号 (在账户信息页面):

获取微信领取接口的秘钥 (账户核心 ->api 平安):

同时在产品核心 -> 开发配置页面,将领取域名配置好:

这里不像微信小程序,小程序只能容许 https 协定接口,而扫码领取域名既反对 https 也反对 http,十分不便,同时留神域名必须是一个备案域名。

至此,微信领取的前置操作就搞定了,上面咱们来编写后盾接口 wx\_pay.py,首先导入依赖的库和一些工具办法:

import requests  
from django.http import HttpResponse, HttpResponseRedirect  
  
import random  
import time  
import hashlib  
  
import qrcode  
from bs4 import BeautifulSoup  
  
def trans_xml_to_dict(data_xml):  
    soup = BeautifulSoup(data_xml, features='xml')  
    xml = soup.find('xml')  # 解析 XML  
    if not xml:  
        return {}  
    data_dict = dict([(item.name, item.text) for item in xml.find_all()])  
    return data_dict  
  
def trans_dict_to_xml(data_dict):  # 定义字典转 XML 的函数  
    data_xml = []  
    for k in sorted(data_dict.keys()):  # 遍历字典排序后的 key  
        v = data_dict.get(k)  # 取出字典中 key 对应的 value  
        if k == 'detail' and not v.startswith('<![CDATA['):  # 增加 XML 标记  
            v = '<![CDATA[{}]]>'.format(v)  
        data_xml.append('<{key}>{value}</{key}>'.format(key=k, value=v))  
    return '<xml>{}</xml>'.format(''.join(data_xml))  # 返回 XML  
  
def get_sign(data_dict, key):  # 签名函数,参数为签名的数据和密钥  
    params_list = sorted(data_dict.items(), key=lambda e: e[0], reverse=False)  # 参数字典倒排序为列表  
    params_str = "&".join(u"{}={}".format(k, v) for k, v in params_list) + '&key=' + key  
    # 组织参数字符串并在开端增加商户交易密钥  
    md5 = hashlib.md5()  # 应用 MD5 加密模式  
    md5.update(params_str.encode())  # 将参数字符串传入  
    sign = md5.hexdigest().upper()  # 实现加密并转为大写  
    return sign

qrcode 模块用来生成二维码,bs4 模块用来将微信接口返回的 xml 解析成 json,在 21 世纪的第二十个年头,微信接口竟然还在应用原始的 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

编写逻辑:

def wx_pay(request):  
  
    url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'  # 微信扫码领取接口  
    key = '945bec********a8fbf7d7'  #商户 api 秘钥  
    total_fee = 1 #领取金额,单位分  
    body = '123123'  # 商品形容  
    out_trade_no = 'order_%s' % random.randrange(100000, 999999)  # 订单编号  
    params = {  
        'appid': 'wx09*****f',  # APPID  
        'mch_id': '16****08',  # 商户号  
        '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'  # 字符串  
    }  
    sign = get_sign(params, key)  # 获取签名  
    params.setdefault('sign', sign)  # 增加签名到参数字典  
    xml = trans_dict_to_xml(params)  # 转换字典为 XML  
    response = requests.request('post', url, data=xml)  # 以 POST 形式向微信公众平台服务器发动申请  
    data_dict = trans_xml_to_dict(response.content)  # 将申请返回的数据转为字典  
    print(data_dict)  
    qrcode_name = out_trade_no + '.png'  # 领取二维码图片保留门路  
    if data_dict.get('return_code') == 'SUCCESS':  # 如果申请胜利  
        img = qrcode.make(data_dict.get('code_url'))  # 创立领取二维码片  
        img.save('./' + qrcode_name)  # 保留领取二维码  
    return HttpResponse(qrcode_name)

随后配置路由:

from myapp.wx_pay import wx_pay  
from django.contrib.staticfiles.urls import staticfiles_urlpatterns  
  
# ... the rest of your URLconf goes here ...  
urlpatterns = [  
    #定义超链接路由  
    re_path('^static/upload/(?P<path>.*)$',serve,{'document_root':'/static/upload/'}),  
    path('wx_pay/', wx_pay),  
]

启动 django 服务:

python manage.py runserver

拜访 http://localhost:8000/wx\_pay/

没有问题,查看后盾日志:

{'return_code': 'SUCCESS', 'return_msg': 'OK', 'appid': 'wx092344a76b9979ff', 'mch_id': '1602932608', 'nonce_str': 'bnJwGlXZ3eDSNgjs', 'sign': '2D81402DABEDF75E9A58F200FE7B6775', 'result_code': 'SUCCESS', 'prepay_id': 'wx1816114416896958d6f84177bd71da0000', 'trade_type': 'NATIVE', 'code_url': 'weixin://wxpay/bizpayurl?pr=JgBYgTS00'}

能够看到曾经下单胜利,不过订单状态处于预领取状态,同时查看二维码图片是否生成:

至此,后盾逻辑根本搞定,上面就是如何在前端进行调用,同时让用户进行扫描操作,编写 wx\_pay.vue 组件:

<template>  
  
  <div>  
  
    <center><h1> 扫码领取 </h1></center>  
  
   
    <a-form-item v-bind="formItemLayout" label="金额">  
      <a-input v-model="money"/>  
    </a-form-item>  
      
     
    <a-form-item v-bind="tailFormItemLayout">  
      <a-button type="primary" html-type="submit" @click="submit">  
        生成二维码  
      </a-button>  
    </a-form-item>  
  
  
    <a-form-item v-bind="formItemLayout" label="二维码">  
      
      <img :src="src" />  
  
    </a-form-item>  
  
  
   </div>  
  
</template>  
  
  
  
<script>  
  
export default {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: 'Edward King 0',  
          age: '32',  
          address: 'London, Park Lane no. 0',  
        },  
        {  
          key: '1',  
          name: 'Edward King 1',  
          age: '32',  
          address: 'London, Park Lane no. 1',  
        },  
      ],  
      columns: [  
        {  
          title: 'name',  
          dataIndex: 'name',  
        },  
        {  
          title: 'age',  
          dataIndex: 'age',  
        },  
        {  
          title: 'address',  
          dataIndex: 'address',  
        },  
        {  
          title: 'operation',  
          dataIndex: 'operation',  
          scopedSlots: {customRender: 'operation'},  
        },  
      ],  
    };  
  },  
  methods: {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]);  
    }  
  
  },  
};  
</script>

当用户点击按钮之后,旋即申请后端领取接口,将接口生成的二维码返回给前端,成果是这样的:

随后应用微信扫一扫性能进行扫码领取,须要留神的是,该二维码有效期只有五分钟,所以最好加上刷新性能。

领取胜利之后,咱们还须要对交易进行确认,所以依据微信官网文档,调用对立查问接口:

https://pay.weixin.qq.com/wik…\_2,依据接口文档编写逻辑:

def wx_check(request):  
  
    #对立订单查问接口  
  
    url = "https://api.mch.weixin.qq.com/pay/orderquery"  
  
    out_trade_no = "order_537236" #领取后的商户订单号  
    key = '945b******d7'  # 商户 api 密钥  
  
    params = {  
        'appid': 'wx0*****ff',  # APPID  
        'mch_id': '16*****08',  # 商户号  
        'out_trade_no': out_trade_no,  # 订单编号  
        'nonce_str': 'ibuaiVcKdpRxkhJA'  # 随机字符串  
    }  
    sign = get_sign(params, key)  # 获取签名  
    params.setdefault('sign', sign)  # 增加签名到参数字典  
    xml = trans_dict_to_xml(params)  # 转换字典为 XML  
    response = requests.request('post', url, data=xml)  # 以 POST 形式向微信公众平台服务器发动申请  
    data_dict = trans_xml_to_dict(response.content)  # 将申请返回的数据转为字典  
    print(data_dict)  
  
    return HttpResponse('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 上的限度,所以退款流程应该会须要音讯队列的染指。

原文转载自「刘悦的技术博客」https://v3u.cn/a_id_182

退出移动版