(二十六)微信小程序支付流程
小程序前端页面
<radio-group bindchange="changeGoods"> <view class='row'wx:for="{{goodList}}" wx:key="index"> <text>{{item.title}}-{{item.price}}</text> <radio class="radio" value="{{item.id}}"></radio> </view> </radio-group> <button bindtap="doBuy">购买</button>
const app = getApp() Page({ data: { goodList:null, selectId:null }, /** * 你选着那件商品就知道是哪个id */ changeGoods:function(e){ this.setData({ selectId: e.detail.value }) }, /** * 一启动就展示商品列表 */ onLoad: function () { wx.request({ url: 'http://127.0.0.1:8000/goods/', method: 'GET', dataType: 'json', responseType: 'text', success: (res) =>{ console.log(res) this.setData({ goodList:res.data }) }, }) }, })
// 先写一部分 doBuy: function () { // 向后台发送一个请求,生成一大堆数据 wx.request({ url: 'http://127.0.0.1:8000/payment/', data: { goodId:this.data.selectId }, method: 'POST', dataType: 'json', responseType: 'text', success: (res)=> { console.log(res) }, }) // 获取这一大堆数据,然后弹出支付二维码 },
后端
url(r'^payment/', payment.PaymentView.as_view()),
# 根据上面 微信小程序官网提供的文档
from app01 import models from rest_framework.generics import ListAPIView from rest_framework.views import APIView import random from rest_framework import serializers from rest_framework.response import Response from xml.etree import ElementTree as ET # 操作xml import requests import time def md5(string): import hashlib m = hashlib.md5() m.update(string.encode('utf-8')) return m.hexdigest() class PaymentView(APIView): def post(self, request, *args, **kwargs): goods_id = request.data.get('goodId') '''模拟的订单号和用户''' order_random = str(int(time.time())) user_obj = models.UserInfo.objects.filter(id=1).first() # 有openid goods_object = models.Goods.objects.filter(id=goods_id).first() # 商品价格 models.Order.objects.create(goods=goods_object,user=user_obj,uid=order_random,status=1) # 生成一大堆数据 按照微信规则---官方文档 '''https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1''' # ###################### 1.调用支付统一下单 ###################### info = { 'appid': '', # 小程序id 'mch_id': '', # 商户号 'device_info': '', # 设备号 'nonce_str': "".join([chr(random.randint(65, 90)) for _ in range(12)]), 'sign_type': "MD5", # 加密类型 'body': "保证金", # 'detail': '这是一个商品详细描述信息.', 'attach': '微信小程序', 'out_trade_no': order_random, # 订单id 'total_fee': goods_object.price, # 总金额 'spbill_create_ip': request.META.get('REMOTE_ADDR'), # 终端里的ip # 终端IP(用户IP) remote_addr = request.META.get('REMOTE_ADDR') 'notify_url': "http://1.1.1.1:8012/pay/notify/", # 支付成功之后,微信异步通知 待会要用 'trade_type': 'JSAPI', 'openid': user_obj.openid # openid } # 1.1 签名 '''https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3''' # 对字典中的key按照ASCII码从小到大排序 # 将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051 # 让stringA和key拼接:stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key # MD5(stringSignTemp) # 将密文转换为大写 # 得到签名 sign # 把签名再添加到info中 info['sign'] = sign值 pay_key = "2SzCvaKgYExuItWBfYAqJFs72uUleD14" # 商户的key temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ]) sign = md5(temp).upper() info['sign'] = sign # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml) xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()])) prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder',data=xml_string.encode('utf-8')) # 1.3 从结果xml中提取 prepay_id # from xml.etree import ElementTree as ET root = ET.XML(prepay.content.decode('utf-8')) prepay_dict = {child.tag:child.text for child in root} prepay_id = prepay_dict['prepay_id'] # ####################### 2.再次签名 ####################### info_dict = { 'appId': "wx2a313db1501c7253", 'timeStamp': str(int(time.time())), # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]), # 随机字符串,长度为32个字符以下。 'package': 'prepay_id={0}'.format(prepay_id), # 统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=* 'signType': 'MD5', # 签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致 } temp = "&".join( ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ]) sign2 = md5(temp).upper() info_dict['paySign'] = sign2 return Response(info_dict)
在回到前端
doBuy: function () { // 向后台发送一个请求,生成一大堆数据 wx.request({ url: 'http://127.0.0.1:8000/payment/', data: { goodId:this.data.selectId }, method: 'POST', dataType: 'json', responseType: 'text', success: (res)=> { console.log(res.data) // 获取这一大堆数据,然后弹出支付二维码 wx.requestPayment( { 'timeStamp': res.data.timeStamp, 'nonceStr': res.data.nonceStr, 'package': res.data.package, 'signType': 'MD5', 'paySign': res.data.paySign, 'success': function (res) { // 要是成功了 就往上面info里的notify_url发请求 }, 'fail': function (res) { }, 'complete': function (res) { } }) }, })
notify_url发请求 就是告知我们的ip地址------结合info里的地址
url(r'^notify/', payment.NotifyView.as_view()),
class NotifyView(APIView): '''支付完后的通知''' def post(self, request, *args, **kwargs): # 1. 腾讯会发一个XML格式的数据 获取结果把结果XML转换为字典格式 root = ET.XML(request.body.decode('utf-8')) # 变json数据 result = {child.tag: child.text for child in root} # 2. 校验签名是否正确,防止恶意请求。 sign = result.pop('sign') key = "key为商户平台设置的密钥key" temp = "&".join( ["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["{0}={1}".format("key", key, ), ]) local_sign = md5(temp).upper() # 签名一致 if local_sign == sign: # 拿到订单号 out_trade_no = result.get('out_trade_no') # 根据订单号,把数据库的订单状态修改为支付成功 models.Order.objects.filter(uid=out_trade_no).update(status=2) response = """<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>""" return Response(response)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现