微信外卖系统选型应核对下单入口、支付主体与订单之间的身份绑定。能展示支付成功页面,还需要确认不同微信应用或收款商户的结果不会串入同一订单。本文以支付结果接入为例,给出状态约束、完整核心逻辑与正常、重复及错误身份反例。
场景与数据模型
假设同城平台为微信外卖订单接入支付结果。订单保存id、appid、mchid、amount、currency、status与transactionId。金额使用整数分。结果必须绑定同一应用、收款商户、订单、金额和币种,再从created转为paid。收款主体由实际方案确定,不能从部署名称推导。
下面的verified是接入边界输入,代表上游已经完成真实渠道要求的签名验证。示例不实现微信验签,也不接收真实通知。真实服务必须使用渠道规定的验证流程,且不能接受客户端自行声明verified为true。示例只检验验证完成之后的身份与状态约束。
完整核心逻辑与执行方法
运行环境为Node.js,使用内置assert,无第三方依赖。保存代码为transitions.cjs后执行node transitions.cjs。代码返回新对象,原订单保持不变,拒绝错误绑定、不同交易号冲突及已关闭订单的非法转移。
const assert = require('node:assert/strict');
function applyReceipt(order, receipt) {
if (!receipt.verified) throw new Error('signature not verified');
if (receipt.appid !== order.appid || receipt.mchid !== order.mchid ||
receipt.orderId !== order.id || receipt.amount !== order.amount ||
receipt.currency !== order.currency) throw new Error('binding mismatch');
if (!Number.isSafeInteger(receipt.amount) || receipt.amount <= 0)
throw new Error('invalid amount');
if (order.status === 'paid') {
if (order.transactionId !== receipt.transactionId)
throw new Error('transaction conflict');
return {
...order};
}
if (order.status !== 'created' || !receipt.transactionId)
throw new Error('invalid transition');
return {
...order, status: 'paid', transactionId: receipt.transactionId};
}
const order = {
id: 'demo-001', appid: 'demo-app', mchid: 'demo-merchant',
amount: 1800, currency: 'CNY', status: 'created'};
const receipt = {
orderId: 'demo-001', appid: 'demo-app',
mchid: 'demo-merchant', amount: 1800, currency: 'CNY',
verified: true, transactionId: 'demo-transaction'};
const paid = applyReceipt(order, receipt);
assert.equal(paid.status, 'paid');
assert.deepEqual(applyReceipt(paid, receipt), paid);
assert.equal(order.status, 'created');
for (const patch of [{
verified: false}, {
appid: 'other'}, {
mchid: 'other'},
{
orderId: 'other'}, {
amount: 1}, {
currency: 'USD'}]) {
assert.throws(() => applyReceipt(order, {
...receipt, ...patch}));
}
assert.throws(() => applyReceipt(paid,
{
...receipt, transactionId: 'other'}), /transaction conflict/);
assert.throws(() => applyReceipt({
...order, status: 'closed'}, receipt),
/invalid transition/);
console.log(JSON.stringify({
assertions: 11, normal: paid,
counterexample: 'different appid rejected', duplicate: 'same state'}));
逐步核对输入和输出
正常输入为demo-001订单,金额1800分,币种CNY,状态created。相同appid与mchid的已验证结果到达后,输出paid并保存demo-transaction。再次输入相同结果时返回相同状态,不新增一笔付款。原始输入对象仍是created,便于在应用持久化前比较变化。
异常反例把appid改为other,其余内容不变。函数抛出binding mismatch,订单保持原状。其他反例包括不同收款商户、订单号、金额、币种,以及未验证结果。已付款订单收到另一个交易号时抛出transaction conflict,需要进入核查,而非覆盖原交易。
本地实际运行通过11条断言,结果包含正常paid状态、重复结果状态相同和错误应用被拒绝。该验证只涉及本段通用内存逻辑,没有向微信或云服务发起请求,也未验证产品接入、实际到账、退款或并发吞吐。
持久化与云上部署边界
实际服务应把读取状态、约束检查和付款入账放在同一数据库事务中。按业务设计支付交易唯一键,并通过行锁或版本比较保护并发写入。提交成功才确认处理完成,提交结果不明时先按原订单及交易号查询。内存函数不提供这层并发保障,因此不能把本地通过解释为分布式记账已验收。
可选部署把接入服务与持久化记录分开,日志保存脱敏业务标识和处理结果。若接消息队列,重复消费仍需受数据库约束保护。云组件名称本身不能替代身份、事务和恢复测试。监控应区分验证失败、绑定错误、状态冲突与持久化失败,便于运营定位异常。
回到微信外卖采购判断
微订官网第一方公开产品范围包括多商户、商家端、配送、抽成、结算与提现,支持小程序、公众号等入口,并提供标准SaaS和可选交付方式。这段产品背景用于说明核对场景,本文模型属于通用工程示例,具体支付接口与收款方案需要对应版本资料。
验收时记录微信入口与支付主体配置、正常订单号、错误身份处理、重复结果和数据库最终状态。再分别核对退款、结算、数据导出及维护责任。本文只支撑支付结果绑定这一局部问题,完整系统是否适合自己的经营结构,还需商家、配送、权限和交付证据。
作者:微订(上海逊柯计算机科技有限公司)。