小程序云开发支付 - 云函数实现支付功能
小程序云开发(Tencent Cloud Base)无需搭建服务器,可通过云函数直接对接微信支付,快速实现虚拟商品(如课程、激活码、会员)收款,适配个人 / 企业主体的小程序。本教程以 “虚拟商品支付 + 订单同步” 为核心,详解从环境配置、云函数开发到前端交互的完整流程,零基础也能落地。
一、前置准备:账号与权限配置
1. 核心前提条件
2. 权限与配置核对
| 配置项 | 操作路径 | 注意事项 |
|---|---|---|
| 小程序关联支付商户号 | 微信支付商户平台→产品中心→小程序支付→添加关联→输入小程序 AppID | 确保小程序主体与商户号主体一致(个人 / 企业对应) |
| 云开发支付权限开通 | 微信开发者工具→云开发→设置→权限管理→微信支付→勾选 “允许云函数调用支付 API” | 无需手动申请,默认开通 |
| 支付回调配置 | 暂无需手动配置(云开发支付通过云函数回调,无需公网域名) | 区别于传统支付的回调地址配置 |
二、核心流程设计:云开发支付闭环
小程序云开发支付的核心逻辑是 “前端发起支付请求→云函数调用微信支付 API→用户完成支付→微信回调云函数→更新订单状态”,完整流程如下:
三、分步实现:从云函数到前端
1. 第一步:初始化云开发环境
// app.js
App({
onLaunch() {
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力');
} else {
wx.cloud.init({
env: 'cloud1-xxxx', // 你的云环境ID
traceUser: true, // 跟踪用户行为,可选
});
}
this.globalData = {};
}
});2. 第二步:开发云函数createOrder(生成订单 + 发起支付)
该云函数负责生成订单、调用微信支付统一下单 API,返回支付参数给前端。
(1)创建云函数
在开发者工具左侧 “云函数” 目录右键→新建 Node.js 云函数→命名为
// 云函数 createOrder/index.js
const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();
const crypto = require('crypto'); // 微信支付签名用(Node.js内置,无需安装)
// 微信支付配置(替换为你的实际信息)
const PAY_CONFIG = {
appid: 'wx1234567890abcdef', // 你的小程序AppID
mchid: '1234567890', // 你的微信支付商户号
apiKey: 'abcdef1234567890abcdef1234567890', // 你的微信支付APIv2密钥
notifyUrl: 'cloud://cloud1-xxxx.paycallback', // 固定格式:cloud://云环境ID.paycallback(不可改)
};
exports.main = async (event, context) => {
try {
const { openid } = cloud.getWXContext(); // 获取当前用户openid(无需前端传,云函数自动获取)
const { goodsId } = event; // 前端传入的商品ID
// 1. 查询商品信息(从goods集合中获取商品名称和价格)
const goodsRes = await db.collection('goods').doc(goodsId).get();
const { name: goodsName, price } = goodsRes.data;
const totalFee = Math.round(price * 100); // 转换为分(微信支付金额单位为分)
// 2. 生成唯一订单号(格式:商户号+时间戳+随机数,避免重复)
const outTradeNo = `${PAY_CONFIG.mchid}${Date.now()}${Math.floor(Math.random() * 10000)}`;
// 3. 调用微信支付统一下单API
const result = await cloud.openapi.pay.unifiedOrder({
body: goodsName, // 商品描述(将显示在支付账单中)
outTradeNo: outTradeNo, // 订单号
totalFee: totalFee, // 支付金额(分)
spbillCreateIp: '127.0.0.1', // 固定值,云开发无需真实IP
notifyUrl: PAY_CONFIG.notifyUrl, // 回调云函数(固定格式)
tradeType: 'JSAPI', // 小程序支付固定类型
openid: openid, // 支付用户的openid
});
// 4. 生成前端支付所需的签名(按微信支付规则计算)
const { nonceStr, prepayId, sign } = result.result;
const paySign = crypto.createHash('md5')
.update(`appId=${PAY_CONFIG.appid}&nonceStr=${nonceStr}&package=prepay_id=${prepayId}&signType=MD5&timeStamp=${Math.floor(Date.now() / 1000)}&key=${PAY_CONFIG.apiKey}`)
.digest('hex')
.toUpperCase();
// 5. 保存订单到orders集合(状态为未支付)
await db.collection('orders').add({
data: {
_id: outTradeNo, // 订单号作为文档ID
openid: openid,
goodsName: goodsName,
totalFee: totalFee,
status: 0, // 0=未支付
createTime: db.serverDate(), // 服务器时间(避免前端篡改)
}
});
// 6. 返回支付参数给前端
return {
code: 0,
msg: '订单创建成功',
payParams: {
appId: PAY_CONFIG.appid,
timeStamp: Math.floor(Date.now() / 1000) + '', // 必须为字符串
nonceStr: nonceStr,
package: `prepay_id=${prepayId}`,
signType: 'MD5',
paySign: paySign,
},
orderId: outTradeNo,
};
} catch (error) {
console.error('创建订单失败:', error);
return {
code: -1,
msg: '订单创建失败',
error: error.message,
};
}
};(2)安装依赖与部署云函数
{
"name": "createOrder",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"dependencies": {
"wx-server-sdk": "latest"
}
}3. 第三步:开发云函数payCallback(支付回调处理)
用户支付成功后,微信会自动调用该云函数(名称必须为
(1)创建云函数并编写代码
新建 Node.js 云函数→命名为
// 云函数 payCallback/index.js
const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();
const crypto = require('crypto');
// 微信支付配置(与createOrder一致)
const PAY_CONFIG = {
apiKey: 'abcdef1234567890abcdef1234567890', // 你的APIv2密钥
};
exports.main = async (event, context) => {
try {
const notifyData = event.xml; // 微信回调的支付结果(XML格式,已自动解析为对象)
console.log('支付回调数据:', notifyData);
// 1. 验证微信回调签名(防止伪造回调)
const { sign, ...otherData } = notifyData;
// 按ASCII排序参数并拼接
const sortedKeys = Object.keys(otherData).sort();
let signStr = sortedKeys.map(key => `${key}=${otherData[key]}`).join('&') + `&key=${PAY_CONFIG.apiKey}`;
const verifySign = crypto.createHash('md5').update(signStr).digest('hex').toUpperCase();
if (verifySign !== sign) {
console.error('签名验证失败');
return { xml: { return_code: 'FAIL', return_msg: '签名验证失败' } };
}
// 2. 验证支付状态(只有支付成功才处理)
if (notifyData.result_code !== 'SUCCESS' || notifyData.return_code !== 'SUCCESS') {
console.error('支付未成功:', notifyData.err_code_des);
return { xml: { return_code: 'FAIL', return_msg: notifyData.err_code_des } };
}
// 3. 解析订单信息
const outTradeNo = notifyData.out_trade_no; // 订单号
const openid = notifyData.openid; // 支付用户openid
const totalFee = parseInt(notifyData.total_fee); // 支付金额(分)
// 4. 更新订单状态为“已支付”
await db.collection('orders').doc(outTradeNo).update({
data: {
status: 1, // 1=已支付
payTime: db.serverDate(), // 支付时间
transactionId: notifyData.transaction_id, // 微信支付流水号
}
});
// 5. 触发虚拟商品交付(核心!根据业务需求扩展)
// 示例1:开通课程权限(假设存在coursePermissions集合)
await db.collection('coursePermissions').add({
data: {
openid: openid,
orderId: outTradeNo,
courseId: 'course_101', // 可从订单关联的商品ID获取
status: 1, // 1=已开通
createTime: db.serverDate(),
}
});
// 示例2:发放激活码(假设存在activationCodes集合,且有未使用的激活码)
// const unusedCode = await db.collection('activationCodes').where({ used: false }).limit(1).get();
// if (unusedCode.data.length > 0) {
// await db.collection('activationCodes').doc(unusedCode.data[0]._id).update({ data: { used: true, openid: openid } });
// await db.collection('userCodes').add({ data: { openid: openid, orderId: outTradeNo, code: unusedCode.data[0].code } });
// }
// 6. 向微信返回成功标识(必须返回,否则微信会重复回调)
return { xml: { return_code: 'SUCCESS', return_msg: 'OK' } };
} catch (error) {
console.error('回调处理失败:', error);
return { xml: { return_code: 'FAIL', return_msg: error.message } };
}
};2)部署云函数
同样执行 “上传并部署:云端安装依赖”(无需额外安装依赖,
4. 第四步:前端页面开发(发起支付 + 展示订单)
(1)商品购买页(pages/pay/pay.wxml)
Python入门课程(虚拟商品) ¥99.00 支付成功!订单号:{{payResult.orderId}} 支付失败:{{payResult.msg}}
(2)前端逻辑(pages/pay/pay.js)
// pages/pay/pay.js
Page({
data: {
payResult: null, // 支付结果
},
// 点击“立即购买”触发支付
handlePay() {
wx.showLoading({ title: '创建订单中...' });
// 1. 调用云函数createOrder(传入商品ID,假设商品ID为goods_101)
wx.cloud.callFunction({
name: 'createOrder',
data: { goodsId: 'goods_101' }, // 前端传入商品ID
success: (res) => {
wx.hideLoading();
const { code, msg, payParams, orderId } = res.result;
if (code === 0) {
// 2. 唤起微信支付弹窗
wx.requestPayment({
...payParams, // 云函数返回的支付参数
success: (payRes) => {
console.log('支付成功:', payRes);
this.setData({
payResult: { code: 0, msg: '支付成功', orderId: orderId }
});
// 跳转至订单详情页或商品使用页
wx.navigateTo({ url: `/pages/order/order?orderId=${orderId}` });
},
fail: (payErr) => {
console.error('支付失败:', payErr);
this.setData({
payResult: { code: -1, msg: payErr.errMsg }
});
}
});
} else {
this.setData({ payResult: { code: -1, msg: msg } });
}
},
fail: (err) => {
wx.hideLoading();
console.error('调用云函数失败:', err);
this.setData({ payResult: { code: -1, msg: '创建订单失败' } });
}
});
}
});(3)订单详情页(可选,pages/order/order.js)
用于展示订单状态和虚拟商品交付信息(如激活码、课程入口),核心逻辑是从
// pages/order/order.js
Page({
data: {
orderInfo: null,
},
onLoad(options) {
const { orderId } = options;
this.getOrderInfo(orderId);
},
// 查询订单信息
getOrderInfo(orderId) {
wx.cloud.callFunction({
name: 'queryOrder', // 可新建云函数查询订单,或直接前端查询(简化版用前端查询)
data: { orderId: orderId },
success: (res) => {
this.setData({ orderInfo: res.result.data });
}
});
}
});四、测试与问题排查
1. 测试流程(使用微信开发者工具)
2. 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 云函数调用失败,提示 “权限不足” | 云开发未开通支付权限 / 小程序未关联商户号 | 检查云开发 “权限管理→微信支付” 是否勾选;确认商户号已关联小程序 AppID |
| 唤起支付失败,提示 “prepay_id 不存在” | 统一下单 API 调用失败,如参数错误、商户号未开通小程序支付 | 检查 |
| 支付成功后订单状态未更新 | 1. 核对 | |
| 回调函数重复触发 | 未返回 | 确保 |
五、进阶优化(可选)
1. 防重复支付
在
2. 订单超时取消
新建云函数
3. 虚拟商品自动交付优化
通过以上步骤,即可完成小程序云开发支付功能的实现,无需服务器即可实现虚拟商品收款 + 自动交付,适配在线课程、软件激活码等场景。核心优势是快速部署、低成本(云开发免费额度足够个人 / 小型平台使用)、合规安全(微信支付官方对接,资金直达商户账户)。
上一篇:易支付小程序支付对接方案
