
摘要: 用云开发 CloudBase 做会员小程序,支付走云调用,不用处理证书和签名,回调也由云函数接收。整条链路四步:小程序下单、云函数生成订单、小程序调起支付、云函数接回调发货。 开通环境开始做会员小程序:云开发 CloudBase
先说清一件事:小程序接微信支付,技术上最费劲的从来不是写代码,而是证书管理、签名计算和回调服务器。云开发的云调用把这三件事全接走了——云函数里调一个 cloudPay.unifiedOrder 就能生成订单,回调直接指向另一个云函数,你不需要有公网服务器、不需要配 HTTPS 回调地址、不需要下载和保管商户证书。下面从数据建模到支付回调给完整实现,再把上线前必须核对的细节列出来。
会员小程序听起来复杂,其实就四块,每块对应云开发的一项能力:
模块 | 要解决的问题 | 用什么实现 |
|---|---|---|
用户身份 | 知道当前是谁 | 微信登录 + 云函数里取 OPENID |
会员数据 | 存等级、到期时间、权益 | 云数据库 users 集合 |
商品与订单 | 卖什么、卖了什么 | 云数据库 products / orders 集合 |
支付 | 收钱与发货 | 云调用 + 两个云函数 |
这四块的依赖顺序是固定的:身份 → 数据 → 订单 → 支付。不要跳着做,支付调不通的问题,有一半源头在前面三步没打牢(比如订单里没存 openid,回调时不知道给谁开会员)。
云开发侧:已开通云开发环境,拿到环境 ID。正式收钱的项目建议用付费套餐,别用免费环境承载真实交易——额度一停业务就断。
微信侧(这部分卡的是资质,不是技术):
资质审核的要求、可开通的支付类目、费率与结算周期,各主体类型不同,以微信支付官方规则为准。这块没过,代码写得再对也收不到钱,建议先去把资质办下来再动手写。
先建集合再写代码,顺序反了后面要返工。
products(商品 / 会员套餐)
字段 | 说明 |
|---|---|
name | 套餐名称,如「月度会员」 |
price | 价格,单位:分(和支付接口保持一致,避免换算错误) |
duration | 会员时长,单位:天 |
desc | 权益说明 |
status | 上下架状态 |
权限设为所有用户可读、仅创建者可写。商品要让所有人看到,但不能让用户改价格。
users(用户与会员状态)
字段 | 说明 |
|---|---|
openid | 用户唯一标识,从云函数取 |
level | 会员等级 |
expireAt | 会员到期时间 |
createTime | 首次注册时间 |
权限设为仅创建者可读写。更关键的一条:会员等级和到期时间这两个字段,绝不能让前端直接写,必须由支付回调云函数写入。否则用户可以自己改成永久会员。
orders(订单)
字段 | 说明 |
|---|---|
outTradeNo | 商户订单号,必须不重复 |
openid | 下单用户 |
productId | 购买的套餐 |
totalFee | 金额,单位:分 |
status | 订单状态:pending / paid / failed |
createTime / payTime | 下单与支付时间 |
权限设为仅创建者可读写,写入和状态流转都走云函数。
这里有个权限设计原则值得记住:凡是涉及钱和权益的写操作,一律收敛到云函数里做,前端只读不写。 前端能写的字段,就等于用户能改的字段。
云开发的云调用天然打通微信支付接口,无需关心证书、签名,基于微信私有协议和私有链路,并支持用云函数接收支付回调。下面四步是完整闭环。
用户在商品页点「购买」,前端不直接碰支付,而是先让云函数生成订单:
// 小程序端:pages/product/product.js
wx.cloud.callFunction({
name: "makeOrder",
data: {
productId: this.data.productId, // 买哪个套餐
},
});云函数 makeOrder 做三件事:查商品真实价格、写订单记录、调统一下单接口。价格必须从数据库查,不能用前端传过来的值,否则会被改价。
// 云函数 makeOrder/index.js
const cloud = require("wx-server-sdk");
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const db = cloud.database();
exports.main = async (event, context) => {
// 1. 取当前用户身份,不信任前端传入
const { OPENID } = cloud.getWXContext();
// 2. 从数据库查商品真实价格,避免前端改价
const product = await db.collection("products").doc(event.productId).get();
const totalFee = product.data.price; // 单位:分
// 3. 生成不重复的商户订单号
const outTradeNo = `${Date.now()}${Math.floor(Math.random() * 10000)}`;
// 4. 写入订单记录,状态为待支付
await db.collection("orders").add({
data: {
outTradeNo,
openid: OPENID,
productId: event.productId,
totalFee,
status: "pending",
createTime: db.serverDate(),
},
});
// 5. 调用统一下单,无需证书和签名
const res = await cloud.cloudPay.unifiedOrder({
body: product.data.name, // 商品描述
outTradeNo, // 商户订单号
spbillCreateIp: "127.0.0.1",
subMchId: "你的商户号",
totalFee, // 金额,单位:分
envId: "你的环境ID",
functionName: "payCallback", // 支付结果回调的云函数名
});
return res;
};几个参数容易写错,单独说明:totalFee 单位是分,1 元要传 100;outTradeNo 必须不重复,重复会下单失败,用时间戳加随机数是最省事的做法;functionName 填的是接收回调的云函数名,不是 URL。
拿到云函数返回的支付参数后,用 wx.requestPayment 调起支付界面:
// 小程序端
wx.cloud.callFunction({
name: "makeOrder",
data: { productId: this.data.productId },
success: (res) => {
const payment = res.result.payment; // 云函数返回的支付参数
wx.requestPayment({
...payment,
success(res) {
wx.showToast({ title: "支付成功" });
// 注意:这里只是用户侧支付成功,开通会员要等回调
},
fail(res) {
wx.showToast({ title: "支付未完成", icon: "none" });
},
});
},
});这里有个最容易犯的错:在前端 success 回调里直接给用户开会员。前端的成功回调只代表微信客户端支付动作完成,不是可信的支付凭证,用户完全可以伪造。开通权益必须在第 4 步的服务端回调里做。
用户付款完成后,微信后台会自动调用你指定的 payCallback 云函数,参数里带着订单信息。这里才是真正发货的地方:
// 云函数 payCallback/index.js
const cloud = require("wx-server-sdk");
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const db = cloud.database();
exports.main = async (event, context) => {
const {
return_code, // 状态码
out_trade_no, // 商户订单号
openid, // 付款用户
total_fee, // 实付金额
} = event;
// 1. 幂等校验:订单已处理过就直接返回,避免重复开通
const order = await db.collection("orders")
.where({ outTradeNo: out_trade_no })
.get();
if (!order.data.length) return { errcode: 0 };
if (order.data[0].status === "paid") return { errcode: 0 };
// 2. 金额校验:实付金额要和订单金额一致
if (total_fee !== order.data[0].totalFee) {
return { errcode: 0 }; // 金额不符不发货,另记日志排查
}
// 3. 更新订单状态
await db.collection("orders").doc(order.data[0]._id).update({
data: { status: "paid", payTime: db.serverDate() },
});
// 4. 开通会员权益:按套餐时长延长到期时间
const product = await db.collection("products")
.doc(order.data[0].productId).get();
const days = product.data.duration;
const now = Date.now();
const user = await db.collection("users").where({ openid }).get();
const base = user.data.length && user.data[0].expireAt > now
? user.data[0].expireAt // 未过期则续期
: now; // 已过期则从现在算
const expireAt = base + days * 24 * 60 * 60 * 1000;
if (user.data.length) {
await db.collection("users").doc(user.data[0]._id)
.update({ data: { level: "vip", expireAt } });
} else {
await db.collection("users").add({
data: { openid, level: "vip", expireAt, createTime: db.serverDate() },
});
}
// 5. 必须返回成功,否则微信后台会重复调用此函数
return { errcode: 0 };
};这段代码里有三处是生产环境必须有的,不能省:幂等校验(防止回调重复触发导致重复开通)、金额校验(防止金额被篡改)、返回 errcode: 0(不返回微信会一直重试)。
前面的结构想清楚之后,可以把下面这段整体交给接好云开发的 AI 工具,让它生成页面和基础代码,你再按第四节核对支付逻辑:
用云开发做一个会员制微信小程序。
功能:
1. 首页:会员套餐列表(名称、价格、权益说明、时长),数据来自云数据库 products 集合
2. 套餐详情:展示权益,点击「立即开通」发起支付
3. 我的:显示当前会员等级、到期时间、历史订单列表
4. 微信登录:首次进入静默获取身份,云函数里用 OPENID 认人
实现要求:
- 原生小程序 + 云开发,不引第三方 UI 库,浅色卡片风格
- 登录走云开发身份,不要自建账号体系
- 支付走 cloudPay 云调用,云函数 makeOrder 下单、payCallback 接回调
- 价格从数据库查,不信任前端传值
- 集合三个:products(所有用户可读、仅创建者可写)、users(仅创建者可读写)、orders(仅创建者可读写)
- 会员等级与到期时间只能由 payCallback 云函数写入
- 生成完成后用微信开发者工具打开项目,并列出需要我手动完成的步骤最后一句很重要——让 AI 明确告诉你哪些步骤它做不了(比如申请商户号、填商户号参数),避免你以为全自动完成了。
支付不能直接拿真钱反复测,按这个顺序验证:
第 1 步:先验证云函数能调通。 在开发者工具里直接调用 makeOrder,看是否返回支付参数。报错多数集中在商户号填错、环境 ID 填错、小程序未开通支付。
第 2 步:用 1 分钱测真实链路。 totalFee 传 1(即 1 分钱),走完整支付流程。这是验证回调是否真的触发的唯一可靠方式。
第 3 步:查回调是否执行。 到云开发控制台看 payCallback 的云函数日志。如果订单状态没变成 paid,99% 是回调没触发或回调里报错了,日志里能看到具体堆栈。
第 4 步:验证幂等。 手动重复调用一次 payCallback(传相同订单号),确认不会重复开通会员、不会重复延长到期时间。
回调必须返回 { errcode: 0 }。不返回或返回其他值,微信后台会认为回调失败并重复调用,可能造成重复发货。
订单号不能重复。outTradeNo 重复会直接导致下单失败,时间戳加随机数是最低成本的方案。
金额单位是分。前端展示用元,传给接口用分,中间的换算只在一处做,不要散落在多个文件里。
权益写入只走服务端。再强调一次:前端支付成功回调不等于支付完成,开权益必须在 payCallback 里做。
提审与套餐。小程序提审时如实选择类目并提供支付相关资质;正式环境建议升级到付费套餐,并在上线前再过一遍数据库权限——确认没有集合是「所有用户可写」。
云开发 CloudBase 把会员小程序的支付难点消掉了大半:云调用免去证书与签名,回调由云函数接收,不需要公网服务器。真正要你把握的是四件事——价格从数据库查、订单号不重复、权益只在服务端回调里写、回调做幂等和金额校验。按「身份 → 数据 → 订单 → 支付」的顺序建,用 1 分钱跑通真实链路,再去补提审资质,这条路是走得通的。支付资质、费率与结算规则以微信支付官方要求为准。
开通环境开始做你的会员小程序:云开发 CloudBase 购买页
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。