上周一个老客户半夜给我打电话,语气很急:他们的小程序上线三个月,售后问题堆了四十多条,客服在群里被用户追着骂,老板问技术能不能“顺手把售后也做了”。我听完只回了一句:售后不是顺手的事,它是一条独立的业务链路,做不好比没做还伤。
我做过十几个小程序项目,踩过最多的坑不是功能写不出来,而是上线之后没人管“出了问题怎么办”。今天聊聊小程序售后这件事,纯实战,不灌鸡汤。
先搞清楚:售后到底包含什么
很多人一提售后就想到退款,其实小程序里的售后至少有三层:
- 交易售后:退款、退货、换货、部分退款、优惠券退回。
- 服务售后:预约改期、服务取消、订单状态异常、履约失败。
- 技术售后:支付回调丢失、订单重复、消息推送失败、页面白屏。
这三层在代码里是三条不同的链路,但用户眼里只有一个词——“你们得给我解决”。所以设计阶段就得把入口统一,别让用户自己在页面里找。
退款流程:别自己造轮子,但要自己兜底
微信支付的退款接口本身不难,难的是状态管理。我见过最典型的 bug:用户点了退款,接口返回成功,但商家后台没同步,运营又手动退了一次,结果退了两笔。
我的做法是给每笔退款建一条独立记录,用业务单号做幂等键,状态机只允许单向流转:
// 退款状态:applying -> processing -> success / failed
async function applyRefund(orderId, amount) {
const key = `refund:${orderId}`;
const exist = await db.collection('refunds').where({ key }).get();
if (exist.data.length) return exist.data[0]; // 幂等,直接返回
const record = await db.collection('refunds').add({
data: { key, orderId, amount, status: 'applying', createdAt: Date.now() }
});
try {
const res = await cloud.callFunction({
name: 'wxpayRefund',
data: { orderId, amount, outRefundNo: key }
});
await db.collection('refunds').doc(record._id).update({
data: { status: res.result.success ? 'success' : 'failed', raw: res.result }
});
} catch (e) {
await db.collection('refunds').doc(record._id).update({ data: { status: 'failed' } });
}
return record;
}
关键点有两个:一是幂等键必须由业务生成,不能依赖微信返回的退款单号;二是失败要有重试入口,运营能在后台点一下重发,而不是让你去数据库里改状态。
售后入口:藏得越深,投诉越多
有个做本地生活的小程序,售后入口放在“我的-设置-帮助中心-联系客服”第四层,结果用户直接在评论区开骂。后来我们把入口提到订单详情页底部,做成一个固定按钮,投诉量直接降了一半。
我的建议是:
- 订单详情页必须有“申请售后”按钮,且状态可见。
- 消息通知里带上售后进度,别让用户反复进来查。
- 客服入口和售后入口分开,前者解决咨询,后者解决履约。
技术售后:日志比道歉有用
用户说“我付了钱没到账”,你第一反应不能是道歉,得先查日志。小程序端能拿到的信息有限,所以服务端要把关键节点都记下来:下单、支付回调、发货、核销、退款。每条日志带上用户 openid 和业务单号,出问题时按单号一搜就能定位。
我现在的习惯是:每个项目上线前,先写一个“售后排查手册”,列清楚常见问题和对应的查询语句。运营照着查,能自己解决八成问题,剩下两成再找我。
别忽略“售后成本”这件事
很多甲方在报价阶段只问开发多少钱,不问售后怎么算。我一般会在合同里写清楚:上线后免费维护期多久、超出范围怎么计费、紧急故障响应时间。不是斤斤计较,而是售后没有边界,最后一定是开发者吃亏、用户也吃亏。
说句实在话,小程序售后做得好不好,跟技术栈关系不大,跟有没有把它当正经业务设计关系很大。退款流程、入口位置、日志体系、维护约定,这四件事在开发阶段多花两天,上线后能省两个月。
如果你正在做小程序,别等用户来骂了才想起售后。把它当成一个功能模块,从一开始就画进流程图里。

评论 (0)
还没有评论,快来抢沙发吧~