
这一篇教程,我们继续了解关于微信公众平台扫码支付接口的调用以及回调验签。
本教程是在沙箱环境下完成,需要准备可用的商户号和沙箱交易密钥。
其中,商户号必须是真实的商户号,沙箱交易密钥通过真实商户交易密钥获取。
提示:获取沙箱交易密钥请参考《微信公众平台开发基本配置Token验证与接口请求验签》
作者使用的商户号和沙箱密钥不便公开,大家可以尝试在网上搜索一下,万一能找到一个呢?(注意潜台词)
接下来我们先看一下扫码支付的过程:
- 组织订单数据发起请求
- 返回二维码链接
- 用户扫描二维码支付(沙箱环境不需要扫码,会在5秒钟后自动返回回调数据)
- 返回回调数据
- 回调数据验签
参考上方的过程,我们完成项目核心代码的编写。
在编写核心代码之前,我们先做一些准备工作。
1、创建页面模板
需要两个页面,提交购买商品的页面和显示支付二维码的页面。
示例代码:(buy.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>结算</title>
</head>
<body>
<form action="../to_pay/" method="get">
<p>商品:Python 入门视频</p>
<input type="hidden" name="goods_name" value="Python 入门视频">
<p>价格:¥1.01</p>
<input type="hidden" name="price" value="1.01">
数量:<input type="number" name="quantity" value="1">
<p><input type="submit"></p>
<p><strong>注意:数量与价格计算后的总金额只能是1.01或者1.02以及验收实例中的金额,否则交易失败!</strong></p>
</form>
</body>
</html>
示例代码:(qrcode.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>扫码支付</title>
</head>
<body>
{% load static %}
<img src="{%%20static%20qrcode_img%20%}"><!--显示支付二维码-->
</body>
</html>
2、添加URL配置
示例代码:(urls.py)
from django.urls import path
from website import views as site_view
from django.views.static import serve
from myproject import settings
urlpatterns = [
path('weixin/', site_view.check_token), # 用于Token验证
path('buy/', site_view.buy), # 打开购买页面
path('to_pay/', site_view.wxpay), # 跳转二维码扫描页面
path('check_wxpay/', site_view.check_wxpay), # 支付结果验签
path('static/<path:path>', serve, {'document_root': settings.STATIC_ROOT}), # 静态文件访问配置
]
3、添加打开购买页面的视图函数
示例代码:(views.py)
from django.shortcuts import render
def buy(request):
return render(request, 'buy.html')
4、创建“static”文件夹
项目根目录下创建“static”文件夹用于保存支付二维码图片。
示例代码:(settings.py)
STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static')
5、安装需要使用的库。
pip install python-wechat # 微信库
pip install beautifulsoup4 # 解析XML的库
pip install qrcode # 生成二维码图片的库
接下来,我们在视图(views.py)中进行核心代码的编写。
因为不管是发送请求数据还是接收回调数据,数据都是以XML格式进行发送。
而我们对数据进行读取的时候,通过字典读取比较方便,所以先完成两个格式转换的函数。
1、XML转Dict的函数
示例代码:
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
2、Dict转XML的函数
示例代码:
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
3、编写签名函数
不管是请求还是回调都需要签名以及对签名进行验证,我们可以把签名函数独立出来。
验签规则可以参考:《微信公众平台开发基本配置Token验证与接口请求验签》
示例代码:
import hashlib
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
4、编写发起支付请求并生成支付二维码的函数
向微信公众平台服务器发起请求时会返回数据,包含请求状态等信息,如果请求成功会返回二维码链接“code_url”,通过这个链接生成支付二维码图片,供用户扫描支付。
示例代码:
from django.http.response import HttpResponse from myproject import settings import random import requests import qrcode
def wxpay(request):
url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder' # 微信扫码支付接口
key = 'c4966d17d71********b62450d3069097' # 沙箱交易密钥
total_fee = int(float(request.GET['price']) * int(request.GET['quantity']) * 100) # 订单总金额(注意是分不是元)
body = request.GET.get('goods_name').encode() # 商品描述
out_trade_no = 'order_%s' % random.randrange(100000, 999999) # 订单编号
params = {
'appid': 'wx77d670a870709aab', # 沙箱环境的APPID
'mch_id': '14******32', # 商户号
'notify_url': 'http://d0a10b0e.ngrok.io/check_wxpay/', # 回调地址
'product_id': 'goods_%s' % random.randrange(100000, 999999), # 商品编号
'trade_type': 'NATIVE', # 支付类型(扫码支付)
'spbill_create_ip': '1.203.76.199', # 发送请求服务器的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) # 将请求返回的数据转为字典
qrcode_name = out_trade_no + '.png' # 支付二维码图片保存路径
if data_dict.get('return_code') == 'SUCCESS': # 如果请求成功
img = qrcode.make(data_dict.get('code_url')) # 创建支付二维码片
img.save(settings.STATIC_ROOT + '/' + qrcode_name) # 保存支付二维码
return render(request, 'qrcode.html', {'qrcode_img': qrcode_name}) # 为支付页面模板传入二维码图像
return HttpResponse('交易请求失败!')
5、编写回调数据验签函数
示例代码:
from django.views.decorators.csrf import csrf_exempt # 解除csrf验证
@csrf_exempt # 去除csrf验证
def check_wxpay(request):
data_dict = trans_xml_to_dict(request.body) # 回调数据转字典
sign = data_dict.pop('sign') # 取出签名
key = 'c4966d17**************50d3069097' # 商户交易密钥
back_sign = get_sign(data_dict, key) # 计算签名
if sign == back_sign: # 验证签名是否与回调签名相同
'''
检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。
'''
print('支付成功!')
return HttpResponse('SUCCESS')
else:
'''
此处编写支付失败后的业务逻辑
'''
return HttpResponse('')
提示:微信和支付宝不一样,回调会在30分钟内反复发送,无需返回数据;所以,每次回调都要对业务状态进行判断,如果已经处理过就不再处理。
启动开发服务器,进行测试。
如果支付二维码图片没有正常显示,将“settings.py”文件中的“DEBUG”项改为“False”。
项目源代码下载:【点此下载】
神龙|纯净稳定代理IP免费测试>>>>>>>>天启|企业级代理IP免费测试>>>>>>>>IPIPGO|全球住宅代理IP免费测试



