微信小程序云开发订单微信支付与小票和标签打印的完整高效流程

一个字“全”!!!

  • 前言
  • 一、流程设定
    • 1、如何开通云支付流程
    • 2、以订单下单为例的支付流程
      • 2.1 业务场景介绍
      • 2.2 业务场景流程图
  • 二、代码与代码文件组成
    • 1、页面JS
    • 2、云函数payPre
    • 3、支付回调函数pay_cb
      • 3.1 准备条件
      • 3.2 必要认知
      • 3.3 pay_cb 完整函数如下
    • 4、标签打印函数
    • 5、小票打印函数
  • 三、回调函数常见问题
    • 1、提高云开发数据库订单更新与查询效率
    • 2、避免阻塞与防止回调函数一直回调
  • 五、参考文档

前言

本片文章全文12000字,细致讲述从申请账户到小程序注册到打印机的选型最后到实现微信小程序云开发订单支付更新与小票,标签打印完整的高效流程。

一、流程设定

1、如何开通云支付流程

无论是云支付还是其他平台语言的原生支付接入都有共同一点:非个人,是组织,企
业,个体商家[1],说白了就是有合理合法经营的营业执照,具体可参考[1]。

1.1 开通商户,获取商户号,商户号获取指引文档[2]

1.2 注册微信小程序,微信小程序注册要是“组织,企业,个体商家”,并且主体要与注册
商户号的主体保持一致。注册完成后等待认证完成即可进入微信公众平台[3]

微信公众平台

1.3 进入微信小程序开发者平台后填写相关的资料并进行类目确定。再次有一次非常需要注意的事项。

注意:根据交易类小程序运营规范,对于小程序内提供珠宝玉石、3C 数码、盲盒、服饰内衣、海淘、美妆、酒类、家用电器、玩具、箱包皮具、鞋靴、运动户外等商品在线销售及配送服务时,不可使用云调用支付能力。

1.4 对接商户号,进行JASP支付相关确认,接入准备文档[4]

注意:微信小程序云开发支付是一种免鉴权,免密钥配置的安全的支付方式,所以不
需要配置相关的密钥,直接进行到下一步1.5

云函数支付的官网流程图

1.5 云开发控制台确认

此处请参考微信开发者文档[5],文档内容详细。

2、以订单下单为例的支付流程

2.1 业务场景介绍

客户A在我们的点餐平台上点了一杯咖啡,所以客户要在选好后付款,在付款完成后商家的小票打印机和标签打印机打印出本订单的相关信息。如下:图2.1为小票打印机,图2.2为标签打印机。

小票打印机
图2.1 小票打印机
在这里插入图片描述
图2.2 标签打印机

2.2 业务场景流程图

在这里插入图片描述

二、代码与代码文件组成

1、页面JS

作用:发起支付请求与回调后续相关逻辑操作,例如:返回上一级,或跳转到相关页面。

wx.cloud.database().collection('orderUser').add({data:{subTime:this.getDateandTime().time,/* 下单时间 */subDate:this.getDateandTime().date,/* 下单日期 */orderCode:hexiaoCode,//核销码orderRows:[this.data.rows],orderGroups:tempInfo,//点单详情status:'制作中', // 出餐情况style:"点餐", //用餐方式pre_title:'**** 微信支付点单',pay_status:'wait', //交易状态orderNumber:orderTemp, //交意单号order_price:this.data.priceAll, //价格userName:wx.getStorageSync('userInfo').userName,phone:wx.getStorageSync('userInfo').phone,orderRemarks:this.data.orderRemarks},success:(event)=>{//支付接口/* 价格处理 */var price = parseFloat(this.data.priceAll)// 订单处理wx.cloud.callFunction({name:'payPre',data:{pro_name:'***店自取订单',pro_codeNum:orderTemp,pro_price:price*100,},success:(res_3)=>{wx.hideLoading()console.log("支付接口",res_3);// 调起支付wx.requestPayment({timeStamp: res_3.result.payment.timeStamp,nonceStr: res_3.result.payment.nonceStr,package: res_3.result.payment.package,signType: 'MD5',//加密方式paySign: res_3.result.payment.paySign,success (res_4) {//这里写付款完成后的逻辑函数},fail (res_4) {console.log('发起支付窗口失败');}})},fail(res_3){console.log('调用payPre失败:',res_3);}})},fail:(res)=>{console.log("创建订单出错误",res);}})

2、云函数payPre

作用调用云函数支付接口与返回支付的信息

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({ env: '' }) // 使用当前云环境
// 云函数入口函数
exports.main = async (event, context) => {const res = await cloud.cloudPay.unifiedOrder({"body" :event.pro_name, // 订单名称"outTradeNo" :event.pro_codeNum, // 订单号"spbillCreateIp" : "127.0.0.1","subMchId" : "*****", //商户号ID"totalFee" : event.pro_price, // 价格"envId": "",//云环境ID"functionName": "pay_cb" //回调函数必须有})return res
}

3、支付回调函数pay_cb

作用:支付完成后的回调函数,这个函数很重要,所有的支付完成的云操作都在这里边进行。

3.1 准备条件

由于用到了第三方库:flatted,所有要下载好Node.js并且使用NPM命令进行远程下载

return {errcode: 0, //必须要写,否则微信服务器没有响应则会一直调用errmsg: "订单未找到",
};

3.2 必要认知

pay_cb作为回调函数,则必须要有一个固定返回形式,参考文档如[6]

在这里插入图片描述

npm install flatted

3.3 pay_cb 完整函数如下

const cloud = require('wx-server-sdk');
const { stringify, parse } = require('flatted');// 此处的作用是:设置此函数的超时,主要是防止大量数据的处理时间造成的报错cloud.init({ env: '', // 云环境环境IDtimeout: 10000 // 设置超时时间为10秒
});const db = cloud.database();// 更新订单状态并查询订单信息函数
const updateOrderStatusAndFetch = async (outTradeNo) => {try {// 更新订单状态await db.collection("orderUser").where({orderNumber: outTradeNo}).update({data: {pay_status: "订单完成"}});// 查询订单信息const res = await db.collection("orderUser").where({orderNumber: outTradeNo}).get();return res.data[0];} catch (error) {console.error("订单更新或查询出错", error);throw error;}
};// 标签打印函数
const printLabel = async (allOrder) => {try {const result = await cloud.callFunction({name: 'printBiaoQian',data: {allOrder}});return { name: "标签打印", value: result.result };} catch (error) {console.error("标签打印出错", error);throw error;}
};// 小票打印函数
const printTicket = async (printLabelObj) => {try {const result = await cloud.callFunction({name: 'printer',data: parse(stringify({payStyle: printLabelObj.payStyle,time: printLabelObj.time,ordercode: printLabelObj.ordercode,takecode: printLabelObj.takecode,info: printLabelObj.info,price: printLabelObj.price,phone: printLabelObj.phone,name: printLabelObj.name,remarks: printLabelObj.remarks}))});return { name: "小票打印", value: result.result };} catch (error) {console.error("小票打印出错", error);throw error;}
};// 云函数入口函数
exports.main = async (event, context) => {const { outTradeNo, resultCode } = event;let resultAll = [];if (!outTradeNo || outTradeNo.length === 0) {return {errcode: 0, //必须要写,否则微信服务器没有响应则会一直调用errmsg: "订单未找到",};}try {console.log("开始处理订单支付");// 立即响应微信服务端const response = {errcode: 0,errmsg: event.returnCode,};// 异步处理订单更新和打印逻辑updateOrderStatusAndFetch(outTradeNo).then(async (order) => {if (!order) {console.error("订单未找到");return;}console.log("订单信息查询完成");// 准备标签打印数据console.log("准备标签打印数据中...");const allOrderGroups = order.orderGroups;const tempNowTemp = allOrderGroups.map(element => {const orderShuXingStr = element.orderShuXing.map(el => `#${el.value}`).join('');return {sumNum: element.orderNum,title: element.goodGroups.shopTitle,tuple: `(${orderShuXingStr})`,order: order.orderCode,time: order.subTime,price: element.priceAll,style: "小程序点单"};});// 准备小票打印数据console.log("准备小票打印数据中...");const tupleInfoArray = allOrderGroups.map(element => {const orderShuXingStr = element.orderShuXing.map(el => `#${el.value}`).join('');return {tuple: orderShuXingStr,title: element.goodGroups.shopTitle,num: element.orderNum};});const printLabelObj = {payStyle: "小程序微信支付",time: order.subTime,ordercode: order.orderNumber,takecode: order.orderCode,info: tupleInfoArray,price: order.order_price,phone: order.phone,name: order.userName,remarks: order.orderRemarks};// 标签打印console.log("标签打印中...");const printBiaoQianResult = await printLabel(tempNowTemp);resultAll.push(printBiaoQianResult);console.log("标签打印完成");// 小票打印console.log("小票打印中...");const printerResult = await printTicket(printLabelObj);resultAll.push(printerResult);console.log("小票打印完成");// 打印完成后的处理console.log("支付处理完成");}).catch((error) => {console.error("异步处理订单出错", error);});// 立即返回响应return response;} catch (error) {console.error("支付处理出错", error);return {errcode: 0,errmsg: "支付处理出错",};}
};

4、标签打印函数

在本文章当中,我所使用的标签打印机是“芯烨云”的打印机,SDK比较明确,主流打印机,使用后端语言比较多,具体请参考官网文档[7]。

// 引入云函数 SDK 和 axios
const cloud = require('wx-server-sdk');
const axios = require('axios'); //提前用npm下载此库// 初始化云环境
cloud.init({env: '' //云开发云函数环境ID
});// 生成签名的函数
function generateSignature(user, userKey, timestamp) {const crypto = require('crypto');const str = user + userKey + timestamp;return crypto.createHash('sha1').update(str).digest('hex');
}// 打印标签的云函数
exports.main = async (event, context) => {// 获取传入的订单数组const allOrder = event.allOrder;// 设置相关参数const user = ''; //开放用户账户名const userKey = ''; // 用户密钥const timestamp = Math.floor(Date.now() / 1000);const sign = generateSignature(user, userKey, timestamp);// 添加打印机const addPrinterResponse = await axios.post('https://open.xpyun.net/api/openapi/xprinter/addPrinters', {user: user,timestamp: timestamp,sign: sign,items: [{sn: '打印机sn号', name: '店铺名称'}]}, {headers: {'Content-Type': 'application/json;charset=UTF-8'}});if (addPrinterResponse.data.code !== 0) {// 添加打印机失败return addPrinterResponse.data;}// 遍历订单数组,生成打印内容并发送打印请求for (const order of allOrder) {for (let i = 1; i <= order.sumNum; i++) {// 初始化单个订单的打印内容let printContent = '';printContent += `<TEXT x="10" y="10" font="9" w="1" h="1" r="0">${order.style} #${i}/${order.sumNum}</TEXT>\n`;printContent += `<TEXT x="10" y="40" font="9" w="1" h="1" r="0">取餐号:${order.order}</TEXT>\n`;printContent += `<TEXT x="10" y="70" font="9" w="1" h="1" r="0">${order.title}</TEXT>\n`;printContent += `<TEXT x="10" y="100" font="9" w="1" h="1" r="0">${order.tuple}</TEXT>\n`;printContent += `<TEXT x="10" y="130" font="9" w="1" h="1" r="0">${order.price}元</TEXT>\n`;printContent += `<TEXT x="10" y="160" font="9" w="1" h="1" r="0">${order.time}</TEXT>\n`;printContent += `<TEXT x="10" y="190" font="9" w="1" h="1" r="0">NFIVE COFFEE</TEXT>\n`;// 构建打印请求体const printRequestBody = {sn: '',content: printContent,user: user,timestamp: timestamp,sign: generateSignature(user, userKey, timestamp), // 重新生成签名debug: '0'};// 发送打印请求const printResponse = await axios.post('https://open.xpyun.net/api/openapi/xprinter/printLabel', printRequestBody, {headers: {'Content-Type': 'application/json;charset=UTF-8'}});// 检查打印结果if (printResponse.data.code !== 0) {// 打印失败return printResponse.data;}}}// 所有订单打印成功return { code: 0, msg: 'All orders printed successfully' };
};

5、小票打印函数

在本文章当中,我所使用的小票打印机是“优声云”的打印机,SDK比较明确,使用后端语言比较多,具体请参考官网SDK文档链接[8]。

// 云函数入口文件
const cloud = require('wx-server-sdk')
const crypto = require('crypto');  //提前用npm下载此库
//axios版本
const axios = require('axios')  //提前用npm下载此库
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
// 云函数入口函数
exports.main = async (event, context) => {//event为客户端上传的参数console.log('event : ', event)//通用参数var appid = ''; //应用ID,申请的ID,值为number类型var appsecret = ''; //申请的appsecretvar sign = ''; //签名,不需要填写,会自动生成。var timestamp = parseInt((new Date().getTime()) / 1000); //当前时间戳,不需要填写var deviceid = ''; //设备号var devicesecret = ''; //设备密钥// 构建打印内容var printdata = '<B1><C>小程序订单</C></B1>\n';printdata += '<B1><C></C></B1>\n';printdata += '--------------------------------\n';printdata += '<B1><C>' + event.payStyle + '</C></B1>\n';printdata += '下单时间:' + event.time + '\n';printdata += '订单编号:' + event.ordercode + '\n';printdata += '取餐码:' + event.takecode + '\n';printdata += '--------------------------------\n';event.info.forEach(item => {printdata += item.title + ' x ' + item.num + '\n';printdata += item.tuple + '\n';});printdata += '--------------------------------\n';printdata += '实付金额:' + event.price + '\n';printdata += '下单手机号:' + event.phone + '\n';printdata += '客户姓名:' + event.name + '\n';printdata += '备注:' + event.remarks + '\n';//打印var parameter = {appid: appid,timestamp: timestamp,deviceid: deviceid,devicesecret: devicesecret,printdata: printdata,//请在此添加您需要的参数,格式为上面定义的变量名为key值和value值}//键值排序function objKeySort(obj) { //排序的函数var newkey = Object.keys(obj).sort();//先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组var newObj = {}; //创建一个新的对象,用于存放排好序的键值对for (var i = 0; i < newkey.length; i++) { //遍历newkey数组newObj[newkey[i]] = obj[newkey[i]]; //向新创建的对象中按照排好的顺序依次增加键值对}return newObj; //返回排好序的新对象}var newObj = objKeySort(parameter); //函数执行var valueJian = '';for (var x in newObj) {if (!newObj[x]) {break;}valueJian += x + newObj[x]}valueJian += appsecret;//console.log(valueJian);sign = crypto.createHash('md5').update(valueJian).digest("hex")//需要的参数对象列表parameter.sign = sign;try {const response = await axios.post('https://open-api.ushengyun.com/printer/print', parameter);// 处理循环引用const data = JSON.parse(JSON.stringify(response, (key, value) => {if (typeof value === 'object' && value !== null) {const seen = new WeakSet();if (seen.has(value)) {return;}seen.add(value);}return value;}));console.log(data);return data;} catch (error) {console.error('Error:', error);return { errorCode: 1, errorMessage: error.message };}
}

三、回调函数常见问题

在接下来的回调函数我们统一使用pay_cb表示

1、提高云开发数据库订单更新与查询效率

面临大量的数据并发,会造函数执行超时,对于这个云数据库操作我们有以下方法

1.1 在云开发数据库集合当中增加索引,对于经常使用的查询键,可以增加到索引当中去。如图
在这里插入图片描述 1.2 减少数据库调用

将查询和更新操作合并为一个操作,减少数据库调用的次数。具体请仔细查看pay_cb

回调函数中的订单更新与查询操作语句写的方法。

1.3 提高云函数超时时间

在云函数的配置中增加超时时间,可以在调用 cloud.init 时设置 timeout 参数。

cloud.init({ env: '',timeout: 10000 // 设置超时时间为10秒
});

2、避免阻塞与防止回调函数一直回调

由于在付款完成后,回调函数执行当中很多时间浪费在云数据库操作查询更新上,更新的时候使用了await,阻塞了应答微信服务端的应答,应答晚了,微信服务端认为你没有应答,所以就造成微信服务器一直在回调
所以我们可以按照pay_cb完整代码当中那样的结构去写,通过在接收支付回调时立即返回响应,然后异步更新数据库,这样做可以避免阻塞微信服务端的响应时间。

五、参考文档

1. 云调用对接微信支付
2. 商户号获取指引文档
3. 微信公众平台
4. 接入准备文档
5. 微信开发者文档云支付确认
6. 芯烨云标签打印机参考官网文档
7. 优声云小票打印机参考官网文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3280000.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Python使用pytest-benchmark做基准测试

安装pytest-benchmark pip install pytest-benchmark代码编写 导入pytest import pytest我们以“在120以内的正整数中找出和120互质的正整数&#xff0c;并统计个数和求和”为例&#xff0c;比较穷举和使用数学技巧完成该任务的性能。 def coprime_120_1():count 0sum 0f…

Java 8-函数式接口

目录 一、概述 二、 函数式接口作为方法的参数 三、函数式接口作为方法的返回值 四、 常用的函数式接口 简单总结 简单示例 4.1 Consumer接口 简单案例 自我练习 实际应用场景 多线程处理 4.2 Supplier接口 简单案例 自我练习 实际应用场景 配置管理 4.3 Func…

MindIE Service服务化集成部署通义千问Qwen模型

一、昇腾开发者平台申请镜像 登录Ascend官网昇腾社区-官网丨昇腾万里 让智能无所不及 二、登录并下载mindie镜像 #登录docker login -u XXX#密码XXX#下载镜像docker pull XXX 三、下载Qwen的镜像 使用wget命令下载Qwen1.5-0.5B-Chat镜像&#xff0c;放在/mnt/Qwen/Qwen1.5-…

将项目部署到docker容器上

通过docker部署前后端项目 前置条件 需要在docker中拉去jdk镜像、nginx镜像 docker pull openjdk:17 #拉取openjdk17镜像 docker pull nginx #拉取nginx镜像部署后端 1.打包后端项目 点击maven插件下面的Lifecycle的package 对后端项目进行打包 等待打包完成即可 2.将打…

源码搭建国内微短剧系统上架(微信抖音)所需资质全面解析

随着短视频和微短剧市场的持续升温&#xff0c;越来越多的企业和个人开始关注并尝试进入这一领域。微短剧以其短小精悍、内容丰富的特点&#xff0c;吸引了大量用户的关注。对于想要搭建并运营自己的微短剧系统的创业者来说&#xff0c;选择合适的平台以及准备必要的资质成为了…

Linux下文件编译器-GCC/G++

前言 本文介绍了c/c的编译过程以及gcc/g的时使用 一.c/c翻译的本质&#xff1a;将高级语言翻译成二进制 1&#xff09;程序翻译过程&#xff1a; &#xff08;1&#xff09;预处理&#xff08;头文件展开、宏替换、去注释、条件编译&#xff09;还是C语言代码 ​ …

ad9361 CTRL_OUT0~7对应能读到的状态

ad9361 CTRL_OUT对应能读到的状态

新手小白如何投放知乎信息流广告推广?

随着越来越多的企业开始寻求更有效的方式来触达目标客户&#xff0c;知乎作为一个集知识分享、社交互动于一体的平台&#xff0c;已经成为众多品牌青睐的广告投放渠道之一。特别是知乎的信息流广告&#xff0c;因其高度融合的内容形式和精准的目标用户定向&#xff0c;成为了品…

《计算机网络》(第8版)第五章 运输层 复习笔记

第五章 运输层 一、运输层协议概述 1 运输层的功能 从通信和信息处理角度讲&#xff0c;传输层向应用层提供服务&#xff0c;是面向通信的最高层&#xff0c;也是面 向用户功能的最底层&#xff0c;它的主要功能有&#xff1a; &#xff08;1&#xff09;提供用户进程之间的逻…

HTML基础1-文本级元素

HTML 简介 什么是 HTML&#xff1f; HTML (HyperText Markup Language) 是一种用于创建网页的标准标记语言。它通过使用一系列预定义的元素来描述文档的结构和外观&#xff0c; 您可以使用 HTML 来建立自己的 WEB 站点。 HTML 的作用 HTML 用于定义网页的结构&#xff0c;…

Openwrt接UVC摄像头丢帧分析

Openwrt接UVC摄像头&#xff0c;使用mjpg-streamer&#xff0c;此处默认已经移植成功。这里以Skylab的SKW99为例进行说明&#xff0c;SKW99为高通QCA9531的方案&#xff0c;CPU的频率为650MHz。最近在应用过程中&#xff0c;需求完成后&#xff0c;发现视频码流下降了&#xff…

入门 PyQt6 看过来(案例)16~ 竖状菜单

本文依照上一篇文章进行简单的拓展&#xff0c;做一个竖状的堆栈菜单。效果如下&#xff1a; ​ 首先我们先进行分析以&#xff0c;页面左侧是菜单栏&#xff0c;按照堆栈式列表展示&#xff0c;包含基本信息和编程语言&#xff08;仔细的你是否发现路老师文字写错了&#xff1…

【数据结构】链式二叉树的实现和思路分析及二叉树OJ

【数据结构】链式二叉树的实现和思路分析及二叉树OJ &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;数据结构 文章目录 【数据结构】链式二叉树的实现和思路分析及二叉树OJ前言一.链式二叉树的定义及结构二.链式二叉树的遍历2.1前序遍历2.2中…

汇昌联信数字做拼多多运营实力强吗?

拼多多作为中国领先的电商平台之一&#xff0c;其运营实力一直是业界关注的焦点。汇昌联信数字公司作为一家专注于电商运营的企业&#xff0c;其在拼多多平台上的表现如何&#xff0c;是否具备强大的运营能力&#xff0c;是本篇文章探讨的主题。 一、答案是肯定的&#xff0c;汇…

C++高性能通信:图形简述高性能中间件Iceoryx

文章目录 1. 概述2. 支持一个发布者多个订阅者2.2 Iceoryx为何不支持多个发布者发布到同一个主题 3. Iceoryx的架构和数据传输示意图3.1 发布者与订阅者的通信机制3.2 零拷贝共享内存通信机制 4. 使用事件驱动机制4.1 WaitSet机制4.2 Listener机制 5. 已知限制6. 参考 1. 概述 …

Python .whl 独立安装和全部依赖安装命令

以安装 Flask 为例&#xff1a; 1. 独立安装 pip install whl_files/Flask-1.1.2-py2.py3-none-any.whl 2. 安装 Flask 全部依赖包和自己 cd /path/to/flask/1.0 pip install --no-index --find-links/path/to/downloaded/files Flask1.1.2 cd /path/to/flask/2.0 pip install …

批量输出文件夹内所有文件名和文件——vba实现

导出一个文件夹下所有文件名&#xff0c;可用vba插件实现&#xff0c;如图 如下图&#xff0c;已在桌面生成一个txt文本&#xff0c;但此方法只可输出一级目录下的文件&#xff0c;若输出所有文件&#xff0c;则需修改插件代码 &#xff08;若想导出硬盘下所有文件和文件夹&…

网络通信HTTP

学习内容 这是昨日学习内容&#xff0c;之后花费昨晚和今天一整天的时间做了个小项目 项目&#xff1a;基于网络爬虫的天气查询系统 其中用了cJSON库来解析相关内容&#xff0c;感兴趣的朋友也可以做一做

SM2在线解密工具

SM2加密算法&#xff0c;采用公钥加密、私钥解密&#xff0c;在上一篇文章提到SM2加密工具&#xff0c;对应的这里再次提供SM2的在线解密工具 在线SM2解密工具 这个工具非常强大&#xff0c;不管什么加密模式都能无需指定的直接解密。

yolov10在地平线旭日X3派上的部署和测试(Python版本和C++版本)

0、搭建开发环境 当前的测试根据一下的步骤并修改源码是可以实现yolov8的板端运行&#xff0c;如果不想再搭建环境和测试代码bug上浪费更多的时间可以直接获取本人的测试虚拟机&#xff0c;所有的测试代码、虚拟环境和板端测试工程以全部打包到了虚拟机&#xff0c;需要的可以…