年初帮朋友做一个小众食品的微信小程序,时间紧又不想折腾服务器。之前就听过uniapp的uniCloud,但一直没真正用过。这次硬着头皮上,结果出乎意料地顺滑——数据库、云函数、用户认证、支付全都内置了,而且前端调用就像本地API一样方便。唯一需要自己兜底的就是支付和短信这两块逻辑,在官方文档和社区里翻了几晚,总算串成了一条完整的交易链路。
下面我就把这个电商小程序的搭建过程完整还原出来,从用户短信登录、商品展示、下单到微信支付回调全流程都有。跟着走一遍,基本就能上手uniCloud开发了。
环境准备与uniCloud服务空间创建
在HBuilderX里新建一个uniapp项目,选择“hello uni-app”模板或者空模板,然后在项目根目录上右键点击“创建uniCloud云开发环境”。选择阿里云或腾讯云均可(本例选阿里云),然后会自动在项目里生成uniCloud文件夹,里面有cloudfunctions和database等目录。
接下来需要关联服务空间:在浏览器中登录uniCloud控制台,新建一个服务空间,然后回到HBuilderX里右键云函数目录选择“关联服务空间”,选中刚才创建的空间。
第一步:用户认证——短信验证码登录
很多电商小程序都要求手机号,我们直接用uniCloud的短信验证码能力。首先在云函数目录新建一个名为send-sms的云函数,文件内容:
// cloudfunctions/send-sms/index.js
'use strict';
exports.main = async (event, context) => {
const { phone } = event;
if (!/^1d{10}$/.test(phone)) {
return { code: 1, msg: '手机号格式不正确' };
}
const db = uniCloud.database();
// 生成6位随机码
const code = Math.floor(100000 + Math.random() * 900000).toString();
try {
// 调用uniCloud短信服务(需在服务空间开通短信功能)
const res = await uniCloud.sendSms({
app: false, // 小程序端用true可能会涉及收费模板,这里用false走云函数调用
phone: phone,
templateId: '你的短信模板ID', // 在uniCloud控制台短信管理里申请
data: {
code: code,
minute: '5'
}
});
// 将验证码存入数据库,5分钟有效
await db.collection('sms-codes').add({
phone: phone,
code: code,
createTime: Date.now(),
expireTime: Date.now() + 5 * 60 * 1000
});
return { code: 0, msg: '发送成功' };
} catch (e) {
return { code: 1, msg: '发送失败:' + e.message };
}
}
前端调用:
// 页面中
uniCloud.callFunction({
name: 'send-sms',
data: { phone: '13812345678' }
}).then(res => {
if (res.result.code === 0) {
uni.showToast({ title: '验证码已发送' });
}
});
用户输入验证码后,我们再调用一个云函数login-by-sms来验证并生成uni-id token:
// cloudfunctions/login-by-sms/index.js
'use strict';
const uniID = require('uni-id'); // 需要在云函数目录安装uni-id
exports.main = async (event, context) => {
const { phone, code } = event;
const db = uniCloud.database();
const res = await db.collection('sms-codes')
.where({ phone, code })
.orderBy('createTime', 'desc')
.limit(1)
.get();
if (res.data.length === 0) {
return { code: 1, msg: '验证码错误' };
}
const record = res.data[0];
if (Date.now() > record.expireTime) {
return { code: 1, msg: '验证码已过期' };
}
// 查找或创建用户
const uniIdIns = uniID.createInstance({ context });
const user = await uniIdIns.getUserByUid({ uid: phone }) ||
await uniIdIns.register({ username: phone, password: phone });
// 登录获取token
const loginRes = await uniIdIns.login({ username: phone, password: phone });
if (loginRes.code === 0) {
// 删除验证码记录
await db.collection('sms-codes').doc(record._id).remove();
return { code: 0, token: loginRes.token, uid: loginRes.uid };
}
return { code: 1, msg: '登录异常' };
}
前端拿到token后存入uni.setStorageSync('uni_id_token', token),后续请求带上。
第二步:商品列表与详情——云数据库操作
在uniCloud的web控制台新建数据表goods,添加几条示例商品。然后前端直接使用uniCloud.database()查询:
// 商品列表页
const db = uniCloud.database();
db.collection('goods')
.where({ status: 'on' })
.orderBy('sellCount', 'desc')
.limit(20)
.get()
.then(res => {
this.goodsList = res.result.data;
});
因为数据库的访问权限可以在云函数里设定,如果是公开数据,可以直接在前端查询。但如果要保护字段(如成本价),就需要在云函数里查询后过滤。这里我们对公开的商品信息直接用前端查询,可以免去一次网络调用。
商品详情页:
db.collection('goods').doc(goodsId).get().then(res => {
this.goodsInfo = res.result.data[0];
});
第三步:下单与微信支付核心流程
这是最复杂的部分。微信支付的流程是:前端调用云函数创建订单、云函数调用支付接口返回支付参数、前端调起支付、后端接收支付回调并更新订单状态。
新建云函数pay-order:
// cloudfunctions/pay-order/index.js
'use strict';
const uniPay = require('uni-pay'); // 需要安装uni-pay依赖
exports.main = async (event, context) => {
const { goodsId, quantity, addressId } = event;
const db = uniCloud.database();
// 1. 获取商品信息
const goodsRes = await db.collection('goods').doc(goodsId).get();
if (goodsRes.data.length === 0) return { code: 1, msg: '商品不存在' };
const goods = goodsRes.data[0];
// 2. 生成订单号
const orderNo = 'OD' + Date.now() + Math.floor(Math.random() * 1000);
const orderData = {
orderNo,
userId: context.UNIID_USERID, // 从uni-id token解析
goodsId,
goodsName: goods.name,
price: goods.price,
quantity,
totalFee: goods.price * quantity,
status: 'unpaid',
createTime: Date.now(),
addressId
};
await db.collection('orders').add(orderData);
// 3. 调用uni-pay发起微信支付
const payIns = uniPay.initWeixin({ ... }); // 需要微信支付商户相关配置
const payParams = await payIns.getOrderInfo({
openid: context.UNIID_OPENID, // 需要前端传入code换取
orderNo: orderNo,
totalFee: orderData.totalFee,
body: goods.name,
// ...其他参数
});
return { code: 0, payParams, orderNo };
}
前端下单后调起支付:
uniCloud.callFunction({
name: 'pay-order',
data: { goodsId: 'xxx', quantity: 2, addressId: 'addr1' }
}).then(res => {
if (res.result.code === 0) {
const payParams = res.result.payParams;
// 微信小程序支付
uni.requestPayment({
provider: 'wxpay',
...payParams,
success: () => {
uni.showToast({ title: '支付成功' });
},
fail: (e) => {
console.error('支付失败', e);
}
});
}
});
支付回调云函数pay-callback(需要在微信支付后台配置回调URL指向这个云函数的URL化地址):
// cloudfunctions/pay-callback/index.js
exports.main = async (event, context) => {
const db = uniCloud.database();
const xml2js = require('xml2js');
const parser = new xml2js.Parser({ explicitArray: false });
const result = await parser.parseStringPromise(event.body);
const { return_code, out_trade_no } = result.xml;
if (return_code === 'SUCCESS') {
await db.collection('orders').where({ orderNo: out_trade_no }).update({ status: 'paid' });
// 可以在这里发模板消息等
}
return { code: 'SUCCESS', message: 'OK' };
}
第四步:订单列表与状态同步
用户在下单后需要查看订单状态,订单列表可以直接在前端查询:
const db = uniCloud.database();
db.collection('orders')
.where({ userId: this.uid })
.orderBy('createTime', 'desc')
.get()
.then(res => {
this.orderList = res.result.data;
});
如果希望支付成功后实时更新状态,可以在支付回调里通过uniCloud.push发送推送,或者使用uni-cloud的数据库实时监听。这里我们用简单轮询:在订单详情页设定定时器查询订单状态,当状态变为paid时停止轮询并展示成功界面。
第五步:添加购物车与本地状态管理
购物车数据可以存在本地,用我们在上一篇文章讲过的Pinia或直接uni.setStorageSync。但考虑到电商习惯,购物车最好和云端同步,免得换设备丢失。可以在云数据库新建carts表,通过云函数操作:
// 添加购物车云函数
exports.main = async (event, context) => {
const { goodsId, count } = event;
const db = uniCloud.database();
const userId = context.UNIID_USERID;
await db.collection('carts').add({
userId, goodsId, count,
createTime: Date.now()
});
return { code: 0 };
}
前端调用时传入token,云函数自动获取用户ID。
容易踩到的坑和应对
- 短信模板审批:uniCloud的短信签名和模板必须审核通过才能用,而且小程序内使用有严格限制。可以使用测试模板先调试,但上线前一定要改成企业认证的模板。
- 支付配置:微信支付商户需要与小程序绑定,并配置API密钥和证书。uni-pay封装了大部分操作,但证书路径等必须正确配置在云函数的
config或环境变量中。 - token过期:uni-id的token有有效期,前端需要处理
TOKEN_EXPIRED错误,自动调用刷新token接口或重新登录。 - 云函数冷启动:首次调用可能较慢,可以配置定时触发器保活,或者使用付费版提升性能。
总结
用uniapp + uniCloud做完这个电商小程序,最大的感受是后端工作量被大幅压缩了。以前写支付接口、短信服务、订单管理,都得自己搭服务器、开端口、调SDK,现在一通云函数调下来,后台管理页面都省了(uniCloud web控制台可直接管理数据)。更重要的是,前端和云函数用同一种JavaScript语言,端到端的类型一致性让人心情舒畅。
如果你也在计划快速试水一个带支付能力的小程序,真心推荐先把uniCloud跑通,它带来的速度感,会让你把更多心思花在产品本身,而不是运维和联调上。

