微信小程序三板斧实战:登录、支付、订阅消息一次讲透

Published on with 0 views and 0 comments

微信小程序三板斧实战:登录、支付、订阅消息一次讲透

做小程序有段时间了,回头看不难,但第一次接这三个功能的时候,每个都踩过坑。微信的文档写得挺全,就是太散了,东一块西一块,真到自己串起来用,总会卡在文档里一句话带过的地方。

这篇把登录、微信支付、订阅消息这三块最常见的功能串一遍,代码都是能直接跑的那种,顺手把坑标出来。

登录:code 换 openid,别在前端碰 secret

小程序登录的核心就一步:前端拿 code,后端换 openid。整个流程长这样:

小程序 wx.login 拿 code 发给自家后端 后端调 jscode2session 拿到 openid + session_key

前端代码:

// app.js 或者登录页
wx.login({
  success: async (res) => {
    // 注意:code 只有 5 分钟有效期,拿到马上发后端,别存着以后用
    const result = await wx.request({
      url: 'https://你的域名/api/wx/login',
      method: 'POST',
      data: { code: res.code }
    })
    // 后端返回自家签发的 token,后续请求都带它
    wx.setStorageSync('token', result.data.token)
  }
})

后端(Node 版):

// 用 code 换 openid,这一步必须在服务端做
// appsecret 放前端等于把家门钥匙贴门口
async function wxLogin(code) {
  const url = 'https://api.weixin.qq.com/sns/jscode2session' +
    `?appid=${APPID}&secret=${SECRET}&js_code=${code}` +
    '&grant_type=authorization_code'
  const res = await fetch(url).then(r => r.json())
  // res: { openid, session_key, unionid? }
  if (!res.openid) throw new Error('微信登录失败: ' + JSON.stringify(res))
  // 这里查库/建用户,签发自家 token 返回给前端
  return createSession(res.openid)
}

几个坑提前说:

  • wx.getUserInfo 早就废了(2021 年起),现在拿头像昵称得用 <button open-type="chooseAvatar"> 让用户主动选,别再去翻老教程了。
  • 手机号登录要 <button open-type="getPhoneNumber">,用户点按钮授权后,后端拿 code 去换手机号,2023 年后还开始按次收费,一毛钱一次,预算里别忘了。
  • session_key 别传给前端,留在服务端解密数据用。

微信支付:大头在后端,前端就三行

支付这块前端反而是最简单的,真正的工作量全在后端。前端拿到的五个参数全是后端算好的:

wx.requestPayment({
  timeStamp: data.timeStamp,
  nonceStr: data.nonceStr,
  package: data.package,      // 形如 prepay_id=wx201410272009395522657a690399285100
  signType: 'RSA',            // 现在推荐 RSA,别再用 MD5 了
  paySign: data.paySign,
  success: () => wx.showToast({ title: '支付成功' }),
  fail: (err) => {
    // errMsg 是 'requestPayment:fail cancel' 就是用户自己取消的
    // 不算错误,别弹报错吓用户
    if (err.errMsg.includes('cancel')) return
    wx.showToast({ title: '支付失败', icon: 'none' })
  }
})

后端的核心流程,用伪代码捋一遍:

async function createOrder(userId, goodsId) {
  // 1. 自己库里先落一单,状态 = 待支付
  const order = await db.orders.insert({ userId, goodsId, status: 'UNPAID' })

  // 2. 调微信 V3 统一下单(JSAPI 下单)
  const prepay = await wxpay.transactions_jsapi({
    appid: APPID,
    mchid: MCH_ID,
    description: '商品名称',
    out_trade_no: order.id,        // 自家订单号,回调靠它对上
    notify_url: 'https://你的域名/api/wx/pay/notify',
    amount: { total: 1, currency: 'CNY' },  // 单位:分
    payer: { openid: user.openid }
  })

  // 3. 用商户私钥把五个参数签名,返回给前端
  return buildPayParams(prepay.prepay_id)
}

支付这块的坑比登录多得多:

  • 回调必须验签 + 幂等。微信的支付通知会重试,网络抖一下你的接口就可能收到两次。处理逻辑开头先查订单状态,已支付就直接返回成功,别重复发货。
  • 回调里信 notify 数据,别信前端。前端 success 回调只是"调起支付这个动作结束了",不能作为发货依据。钱到没到,只认服务端收到的验签通过的回调。
  • 金额单位是分,见过不止一次有人把元直接传上去,一分钱的东西卖成一块。
  • 沙箱环境测不出所有问题,上线前一定用真实金额(一分钱)跑一遍完整链路。
  • 证书申请要时间,商户号下来就早点去申请 API 证书,别卡在上线前一天。

订阅消息:一次订阅一次发送

老的模板消息已经没了,现在是订阅消息。逻辑变了:用户每点一次授权,你才有发一条消息的额度。

前端引导订阅:

// 必须由用户点击触发,不能进页面就弹
wx.requestSubscribeMessage({
  tmplIds: ['模板ID1', '模板ID2'],  // 最多 3 个,提前在公众平台申请
  success: (res) => {
    // res 里是每个模板的订阅结果:accept / reject / ban
    // 用户拒了也别追着弹,体验分和审核都过不了
    if (res['模板ID1'] === 'accept') {
      // 告诉后端:这个用户欠他一条通知
    }
  }
})

后端发送:

async function sendSubscribeMsg(openid) {
  const token = await getAccessToken()  // 记得缓存,7200 秒有效期
  await fetch(
    `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${token}`,
    {
      method: 'POST',
      body: JSON.stringify({
        touser: openid,
        template_id: '模板ID1',
        page: 'pages/order/detail?id=123',  // 点了消息跳哪
        data: {
          thing1: { value: '您的订单已发货' },
          time2: { value: '2026-07-30 14:00' }
        }
        // 注意:data 里每个字段的类型和长度都受模板限制
        // thing 类型最长 20 个字符,超了直接报错
      })
    }
  )
}

最大的认知转变:订阅是一次性的。用户今天点了"允许",你只获得了一条消息的发送资格。想长期触达,就得在关键操作节点(下单、预约、报名)都埋订阅引导,让用户每次顺手点一下。这不是 bug,是微信故意这么设计的,防止你拿用户当营销靶子。

上线前 checklist

功能跑通只是第一步,送审之前过一遍这个清单,能省掉 80% 的驳回:

  • app.json 里配了 "__usePrivacyCheck__": true,首次进入弹了隐私协议
  • 隐私协议里写清了收集什么信息、第三方 SDK 有哪些
  • 用户产生文字/图片的地方接了 security.msgSecCheck 内容安全检测
  • 后端域名全部 HTTPS 且已备案,request 域名在小程序后台配好了
  • 有登录功能的,审核备注里写了测试账号或"游客可体验"
  • iOS 端没有虚拟物品支付入口(课程、会员、币这些,iOS 上要么关要么走 IAP)

三个功能串起来就是小程序的基本骨架了:登录解决"你是谁",支付解决"钱怎么收",订阅消息解决"怎么再联系到你"。剩下的都是业务细节,慢慢来。