原文转载自「刘悦的技术博客」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/wiki/doc/api/native.php?chapter=6_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/wiki/doc/api/native.php?chapter=9_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/wiki/doc/api/native.php?chapter=9_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

Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)的更多相关文章

  1. Vue学习笔记-基于CDN引入方式简单前后端分离项目学习(Vue+Element+Axios)

    一  使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7  (Windows x86- ...

  2. abp框架运行——前后端分离(基于VUE)

    目录 1.介绍abp 2.abp如何工作 3.运行Domo 3.1官网点击 创建Demo 3.2 配置NetCore,选择Vue 3.3 输入系统名称验证码 4.官方手册文档 5.VUE项目 6. S ...

  3. ============================================ 新的开始(前后端分离项目学习Vue+DRF)

  4. 海纳百川无所不容,Win10环境下使用Docker容器式部署前后端分离项目Django+Vue.js

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_179 随着现代化产品研发的不断推进,我们会发现,几乎每个产品线都会包含功能各异的服务,而且服务与服务之间存在也会存在着错综复杂的依 ...

  5. 基于Vue的前后端分离项目实践

    一.为什么需要前后端分离 1.1什么是前后端分离  前后端分离这个词刚在毕业(15年)那会就听说过,但是直到17年前都没有接触过前后端分离的项目.怎么理解前后端分离?直观的感觉就是前后端分开去做,即功 ...

  6. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  7. Yii框架和Vue的完美结合完成前后端分离项目

    背景说明 本文假设你对Yii和Vue都比较熟悉,至少都在项目里用过,另外笔者新人,以后不定时放一些干货,欢迎程序媛关注 Yii是一个PHP全端框架,典型的mvc的项目结构,后端接口都是一个控制器里放了 ...

  8. docker-compose 部署 Vue+SpringBoot 前后端分离项目

    一.前言 本文将通过docker-compose来部署前端Vue项目到Nginx中,和运行后端SpringBoot项目 服务器基本环境: CentOS7.3 Dokcer MySQL 二.docker ...

  9. Docker环境下的前后端分离项目部署与运维

    本教程将从零开始部署一个前后端分离的开源项目,利用docker虚拟机的容器技术,采用分布式集群部署,将项目转换成为高性能.高负载.高可用的部署方案.包括了MySQL集群.Redis集群.负载均衡.双机 ...

随机推荐

  1. 559. Maximum Depth of N-ary Tree - LeetCode

    Question 559. Maximum Depth of N-ary Tree Solution 题目大意:N叉树求最大深度 思路:用递归做,树的深度 = 1 + 子树最大深度 Java实现: / ...

  2. 使用MinIO搭建对象存储服务

    1.MinIO是什么? MinIO 是一款高性能.分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件.即X86等低成本机器也能够很好的运行MinIO. MinIO与传统的存储和其 ...

  3. 深度学习可视化工具--tensorboard的使用

    tensorboard的使用 官方文档 # writer.add_scalar() # 添加标量 """ Args: tag (string): Data identif ...

  4. 论文解读(ARVGA)《Learning Graph Embedding with Adversarial Training Methods》

    论文信息 论文标题:Learning Graph Embedding with Adversarial Training Methods论文作者:Shirui Pan, Ruiqi Hu, Sai-f ...

  5. 前端2CSS

    内容概要 form表单 网络请求方式 CSS简介 CSS查找标签之基本选择器(重要) CSS查找标签之组合选择器(重要) 属性选择器 分组与嵌套 伪类选择器 内容详情 form表单 "&qu ...

  6. 商户编号[Merchant Id]是什么

    1. Merchant Id是什么 2. Merchant Id 是有哪几个部分构成的 2.1 收单机构代码 2.2 商户地区代码 2.3 Merchant Category Code(MCC) 本文 ...

  7. 开发工具-在线JSON相关的工具

    更新记录: 2022年6月7日 新增链接. 2022年6月1日 开始. https://www.sojson.com/json2entity.html URL参数互转JSON https://tool ...

  8. 如何利用 RPA 实现自动化获客?

    大家好,我是二哥.前高级技术专家 & 增长黑客,现一枚爱折腾的小小创业者,专注于 RPA & SaaS 软件这块.这次给大家带来如何利用 RPA 实现自动化获客 一.RPA 是什么?难 ...

  9. 2.如何正确理解古典概率中的条件概率《zobol的考研概率论教程》

    写本文主要是帮助粉丝理解考研中的古典概率-条件概率的具体定义. "B事件发生的条件下,A事件发生的概率"? "在A集合内有多少B的样本点"? "在B约 ...

  10. 针对elementUI 中InfiniteScroll按需引入的一点注意事项

    大家为了节省空间,常常进行按需引入来节省空间,这里我给大家来介绍一下element中按需引入无限滚动指令注意的事项. 针对前面element 按需引入的一些配置这里就不再详细介绍了. 那么这里讲的是在 ...