微信小程序:用户从扫码到开门的完整流程

0 阅读4分钟

微信小程序:用户从扫码到开门的完整流程

用户掏出手机,扫一下柜子上的二维码,柜门弹开,拿完关门自动扣款——整个过程不过五秒钟。这篇文章把小程序端的完整实现拆开,从扫码开门、免密支付到订单推送,附上核心代码和避坑指南。


大家好,我是黒漂技术佬。

上一篇把后端架构讲完了,今天进入用户最直接感知的一层——微信小程序。这个环节做好了,用户觉得「方便」;做不好,用户觉得「这什么破玩意儿」然后卸载。


一、整体流程:用户视角的五秒体验

用户扫柜子二维码
  ↓(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

退款入口

每个订单详情页底部有「申请退款」按钮,用户点击后:

  1. 提交退款原因
  2. 后端查询设备的开门/关门录像片段
  3. 人工审核(误判则退款,正常则驳回)
  4. 退款走微信退款接口,原路退回

六、性能优化

优化项方案效果
首页加载分包加载,首页包 < 500KB首屏 < 1.5 秒
开门请求预连接 WebSocket,减少握手时间开门延迟 -300ms
列表渲染虚拟列表,一次只渲染 20 条滑动不卡顿
图片加载商品图用 WebP + CDN图片体积 -60%

七、一个教训:用户体验的最后一公里

上线第一版时,用户扫码开门很流畅,但有一个场景没考虑到——用户开门后手机没电了。门开着,用户拿完东西关门,但小程序没连上 WebSocket,账单弹不出来,用户不知道扣没扣钱。

解决方案很简单:关门后除了推送 WebSocket,后端同时发一条微信服务通知,即使小程序没打开也能收到。

细节决定用户会不会用第二次。


下一篇预告

小程序讲完了,下一篇回到边缘端——《边缘计算:RK3588 上跑 AI 模型的性能优化指南》,NPU 从 2fps 到 30fps 的完整优化过程。


💬 互动:你做过小程序吗?遇到过什么坑?