微信小程序:用户从扫码到开门的完整流程
用户掏出手机,扫一下柜子上的二维码,柜门弹开,拿完关门自动扣款——整个过程不过五秒钟。这篇文章把小程序端的完整实现拆开,从扫码开门、免密支付到订单推送,附上核心代码和避坑指南。
大家好,我是黒漂技术佬。
上一篇把后端架构讲完了,今天进入用户最直接感知的一层——微信小程序。这个环节做好了,用户觉得「方便」;做不好,用户觉得「这什么破玩意儿」然后卸载。
一、整体流程:用户视角的五秒体验
用户扫柜子二维码
↓(1 秒)
小程序获取设备信息 + 请求开锁
↓(2 秒)
后端校验 → MQTT 下发开锁指令 → 电磁锁弹开
↓(1 秒)
用户取货 → 关门 → 自动识别 → 扣款
↓(1 秒)
小程序推送订单详情
核心指标:从扫码到开门 < 3 秒,从关门到收到账单 < 2 秒。
二、技术选型
- 框架:UniApp(Vue 3)+ uView UI
- 地图:腾讯地图 SDK
- 支付:微信支付 V3 + 微信支付分(免密代扣)
为什么 UniApp?一次开发能同时跑微信小程序和 H5,后续如果要出支付宝小程序或者 App,复用 90% 的代码。
三、扫码开门——核心流程
3.1 二维码里有什么
柜子上的二维码就是一个 URL:
https://m.example.com/open?sn=DEVICE20240001
小程序扫码后解析出设备序列号 sn,然后用这个 sn 去请求后端。
3.2 小程序端代码
// pages/scan/scan.vue
export default {
methods: {
// 扫码入口
onScanCode() {
uni.scanCode({
success: async (res) => {
const sn = this.parseSN(res.result); // 从 URL 解析设备编号
await this.openCabinet(sn);
}
});
},
// 开门流程
async openCabinet(sn) {
uni.showLoading({ title: '开门中...' });
try {
// 1. 请求后端开锁
const res = await uni.request({
url: `${API_BASE}/cabinet/open`,
method: 'POST',
data: { sn, timestamp: Date.now() }
});
if (res.data.code === 0) {
uni.showToast({ title: '柜门已打开', icon: 'success' });
// 2. 建立 WebSocket 等待关门通知
this.listenForOrder(sn);
} else if (res.data.code === 1001) {
// 用户有未支付订单
uni.showModal({
title: '提示',
content: '您有未支付的订单,请先完成支付',
success: () => this.navigateToPay()
});
}
} catch (e) {
uni.showToast({ title: '开门失败,请重试', icon: 'error' });
} finally {
uni.hideLoading();
}
},
// 等待关门后推送订单
listenForOrder(sn) {
const socket = uni.connectSocket({
url: `wss://api.example.com/ws/order?sn=${sn}`
});
socket.onMessage((res) => {
const order = JSON.parse(res.data);
if (order.status === 'paid') {
uni.navigateTo({
url: `/pages/order/detail?id=${order.id}`
});
}
});
}
}
}
3.3 避坑
- 扫码白名单:加时间戳 + 随机数校验,5 秒内有效,防止用户截图二维码发到群里被人乱扫
- 重复开门:后端用 Redis 记录
open:lock:{sn},同一设备 3 秒内不重复下发开锁指令 - 开门失败兜底:MQTT 指令下发后 3 秒没收到设备的开门确认,提示用户重试
四、免密支付——微信支付分
售货柜的核心优势之一就是拿完就走,自动扣款。这依赖微信支付分(免密代扣)。
4.1 开通流程
用户首次使用 → 授权微信支付分
↓
小程序调起 wx.openBusinessView 跳转签约页
↓
用户确认 → 微信返回签约令牌
↓
后端保存签约信息 → 后续自动扣款无需用户确认
4.2 扣款流程
用户关门 → 后端生成订单 → 调用微信代扣接口
↓
微信扣款 → 回调通知后端 → 更新订单状态
↓
小程序收到 WebSocket 推送 → 展示账单
4.3 避坑
- 扣款金额上限:单笔不超过用户签约时设置的上限(默认 100 元),超出需要用户确认
- 扣款失败处理:扣款失败后尝试 3 次,间隔递增(1 分钟、5 分钟、30 分钟),仍失败则冻结用户下次使用权限
- 支付分门槛:微信支付分 550 以上才能开通免密代扣,不满足的用户走普通支付流程
五、订单页面
关门后自动跳转到订单详情页,核心信息:
| 字段 | 示例 |
|---|---|
| 取货时间 | 2024-03-15 18:32 |
| 商品明细 | 可口可乐 500ml ×1 ¥3.00 |
| 实付金额 | ¥3.00 |
| 支付方式 | 免密支付(微信支付分 720) |
| 订单编号 | OD202403150001234 |
退款入口
每个订单详情页底部有「申请退款」按钮,用户点击后:
- 提交退款原因
- 后端查询设备的开门/关门录像片段
- 人工审核(误判则退款,正常则驳回)
- 退款走微信退款接口,原路退回
六、性能优化
| 优化项 | 方案 | 效果 |
|---|---|---|
| 首页加载 | 分包加载,首页包 < 500KB | 首屏 < 1.5 秒 |
| 开门请求 | 预连接 WebSocket,减少握手时间 | 开门延迟 -300ms |
| 列表渲染 | 虚拟列表,一次只渲染 20 条 | 滑动不卡顿 |
| 图片加载 | 商品图用 WebP + CDN | 图片体积 -60% |
七、一个教训:用户体验的最后一公里
上线第一版时,用户扫码开门很流畅,但有一个场景没考虑到——用户开门后手机没电了。门开着,用户拿完东西关门,但小程序没连上 WebSocket,账单弹不出来,用户不知道扣没扣钱。
解决方案很简单:关门后除了推送 WebSocket,后端同时发一条微信服务通知,即使小程序没打开也能收到。
细节决定用户会不会用第二次。
下一篇预告
小程序讲完了,下一篇回到边缘端——《边缘计算:RK3588 上跑 AI 模型的性能优化指南》,NPU 从 2fps 到 30fps 的完整优化过程。
💬 互动:你做过小程序吗?遇到过什么坑?