摆摊AI 产品实战手册

支付接入

从创建订单、回调验签到幂等发放权益,跑通一条可补偿、可重复验证的状态机。含摆摊自己踩过的三个真实坑。

支付接入最容易被误解成"生成一个二维码"。

二维码只是入口。真正要建的系统回答五个问题:谁买了什么、应该付多少钱、平台是否确认到账、权益是否只发放了一次、漏掉回调之后怎么恢复。

最后一个问题决定了你要不要在半夜手工给用户开通权限。

快速版

  1. 画出订单状态机,只有服务端能推进到 paid。
  2. 金额和商品由服务端决定,前端只传商品 ID 和支付方式。
  3. 回调必须验签 + 核对商户身份 + 核对金额,任一不符就拒绝并记日志。
  4. 发放权益必须幂等:只有第一次状态迁移成功的请求才能发货。
  5. 一定要做主动查单兜底,回调丢了也能恢复。
  6. 查单和回调复用同一个结算函数,不要写第二条发权益的路径。
  7. 用小额真实支付跑完八项必测场景,沙盒过了不算过。

先画订单状态

MVP 阶段至少需要这三个状态:

pending(待支付) → paid(已支付)
                  ↘ failed / closed(失败或关闭)

只有服务端可以把订单推进到 paid。 用户跳回"支付成功页"、前端显示成功动画、URL 上带了 status=success,都不能作为到账依据——这些全都可以被伪造。

一条完整链路

1. 服务端创建订单

前端只提交商品 ID 和支付方式。金额、商品名称、有效期、是否可购买,全部由服务端读取。

服务端生成唯一的商户订单号,先落库本地订单,再去请求支付平台。顺序不能反:先请求平台后落库,一旦落库失败你就有一笔查不到的钱。

2. 展示支付入口

桌面端展示二维码,移动端提供唤起链接。页面轮询自己的订单状态接口,而不是让浏览器直接决定权益是否生效。

3. 接收并验证回调

回调至少检查五项:

检查项不检查的后果
签名是否合法任何人都能伪造一个"支付成功"给你
商户身份是否匹配别人的订单回调也会被你结算
商户订单是否存在凭空发货
实付金额是否与本地订单一致用户改前端参数,付 1 块拿 699 的权益
支付状态是否明确表示成功把"处理中"当成"已到账"

任何一项不一致,记录日志并拒绝结算。不要"先发货再说"。

4. 幂等发放权益

支付平台会重试回调,前端查单也可能和回调同时到达。所以结算逻辑必须保证:

只有把订单从 pending 改成 paid 这次状态迁移成功的请求,才有资格发放权益。

在一个事务里:
  UPDATE orders SET status='paid' WHERE id=? AND status='pending'
  如果影响行数 = 0 → 已经被别人结算过,直接返回成功,不发货
  如果影响行数 = 1 → 这次由我发货,延长会员有效期

条件更新加上影响行数判断,是这一整页最关键的几行代码。

5. 主动查单补偿

回调会延迟、会因为配置错误发不到、会被网络丢掉。所以待支付页面轮询时,服务端顺手向平台主动查一次单;确认成功后复用同一套结算函数。

不要另写一条"快捷发权益"的路径——两条路径迟早会出现行为不一致,而且第二条通常没做幂等。

摆摊的真实实现

摆摊的会员支付走简付,自建结账页,二维码在本站渲染:

  1. 登录用户创建会员订单
  2. 本地保存订单和支付信息
  3. 用户完成微信或支付宝付款
  4. 回调验签并核对订单、商户、金额
  5. 事务化地把订单置为已支付,并延长会员有效期
  6. 回调没到达时,由查单轮询完成补偿

用 ¥0.10 和 ¥0.20 各测一轮,发现了三个文档里读不出来的问题:

现象真实原因
回调一直报"订单不存在",连查 6 次回调里的商户单号字段是 merchantOrderNo,接口文档写的是 orderNo
查单接口返回了商户资料而不是订单查单必须传平台订单号 orderId,传商户单号不报错但返回错的东西
收银台下单报"支付方式不能为空"收银台模式必传 payMethod,文档里标的是可选参数

还有两个通道能力上的限制:微信不支持 native 扫码下单,jsapi 又需要 sub_appid,最后走收银台拿 payUrl 在自己页面上渲染成二维码;支付宝可以走 API 直接拿 qrCode。两个通道的代码路径因此不一样,别指望一套代码通吃。

结论不是"某个平台不好",而是:任何聚合支付的文档都和实际有偏差。类型检查和模拟数据发现不了这类问题,只能靠真实小额支付验收。

必测场景

  • 正常支付一次,权益即时生效
  • 同一回调发送两次,不重复增加权益
  • 回调与查单同时触发,只结算一次
  • 修改前端金额参数,服务端仍按商品真实价格创建订单
  • 回调金额与订单不一致,拒绝结算并留下日志
  • 用户关闭页面后重新打开,仍能查到自己的订单
  • 另一个账号不能查询或领取这笔订单
  • 支付完成但回调缺席,主动查单能恢复权益

第 2、3、8 条是最容易漏测、事故率最高的三条。

日志不要记录秘密

记录订单号、状态变化、平台响应码和字段名,足够排查绝大多数问题。

不要把签名密钥、完整令牌、密码或不必要的个人信息写进日志。 支付日志经常需要贴给别人看(同事、平台客服、AI 助手),带着密钥的日志一旦流出,损失比支付 bug 大得多。

常见卡点

卡在哪可能原因试试这样
回调报"订单不存在"回调字段名和文档不一致把整个回调体打出来,逐字段比对
回调收不到回调地址不可公网访问,或被 HTTPS/防火墙拦先用日志确认平台有没有请求进来
权益发了两次结算不幂等用条件更新 + 影响行数判断
沙盒通过,线上失败沙盒和生产的字段、签名规则不完全一致必须做小额真实支付验收
用户付了钱一直转圈前端只等回调,没有查单兜底加轮询 + 服务端主动查单
金额能被改前端传了 amount,服务端直接用金额只从服务端商品表读

验收标准

  • 订单状态机画出来了,只有服务端能推进到 paid
  • 回调五项校验都实现了,失败路径有日志
  • 发放权益是幂等的,重放回调验证过
  • 查单和回调复用同一个结算函数
  • 八项必测场景全部跑过,不是只跑了正常路径
  • 日志里没有密钥和完整令牌
  • 出现问题时,能只凭订单号定位到支付、发货、权益三处记录

本章动作

把会员或商品价格临时改成可承受的小额,完成一笔真实付款。然后做两件事:

  1. 手工重放一次同样的回调,确认权益没有被延长第二次。
  2. 把回调地址临时改错,再买一笔,确认主动查单能把权益补上。

三条路径(正常、重放、无回调)都正确,才算接入完成。

下一步

  1. 数字商品与会员交付——权益怎么存、访问怎么控
  2. 支付合规、退款与争议——退款和争议处理
  3. 发票、税务与交易台账——三方对账

来源与核查

  • 简付接口字段与通道能力限制:摆摊自身接入实测,2026-08

On this page