2452 字
约 8 分钟
1
订单拍了价,回呼只入一次账

订单拍了价,回呼只入一次账

面向 VOZEB PRO。真钱入账要练的不是「接一下 Stripe 回调」,而是:下单那一刻把应付拍死;用户去供应商付钱;回呼验签后按事件 id 只完整入账一次。 15 是生成花积分。这篇是人民币变成永久积分或套餐。两本账不要写在一个函数里。

先用小王买套餐钉住四步,后面才不会把「回呼来了三次」写成「加了三次分」。

小王看见什么 账上拍死什么 再来一次会怎样
下单 确认页 69 元 原价 − 促销 − 券 = 应付,写进订单行 同一业务幂等则同一张单,不新开价
开收银台 跳转微信 / Stripe checkout 身份(metadata.checkout 刷新别开第二摊收银
付钱 在供应商 App 里付 钱在供应商侧 与本站无关
回呼入账 回到站点「已到账」 订单 paid:授信、绑套餐、核销券、邀请 同一事件 id / 已 paid → 不加第二笔

页面上的 69 只是展示。权威是订单行里的快照,禁止前端再算一遍当应付。


0. 什么时候用这套思路

先问三句,有一句对上就该掏出这篇,而不是先在 Route 里 creditPermanentPoints(100)

  1. 钱是供应商收的,还是站内积分划转? 前者走订单;后者走 15。
  2. 回呼再来一遍,会不会再发一套餐、再核一张券? 会,说明事件去重或订单状态没锁住。
  3. 小王还在收银台,你改了后台价,这张单收多少? 应收下单时的快照,不是新价。

适合:

  • ✅ 买套餐、买积分包、任何真钱 → 站内权益
  • ✅ 支付供应商会重试 webhook、用户会刷新收银台
  • ✅ 促销、优惠券要减应付
  • ✅ 邀请人要在被邀请人付成功后才拿奖励
  • ✅ 换支付渠道(Stripe / 微信 / 别的),怕页面重算价
  • ✅ 读 billing-service.tspayment-webhook-service.tspayment-checkout-service.ts

不要硬套:

  • ⚠️ 生成扣 8 分、规划退款(15)
  • ⚠️ 后台改逻辑模型价(影响下次生成,不影响已拍死的订单)
  • ⚠️ 未支付就发积分、或前端说「我付过了」就入账
  • ⚠️ Worker 脚本里处理支付(回呼进的是本站 payment Route,不是 maintenance 催单)

在 VOZEB PRO 中的定位: createBillingOrder 快照计价;createPaymentCheckoutForOrder 开收银台;processPaymentWebhook 验签 + 事件去重 + claim 订单;completeBillingOrderPayment 一次性授信/绑套餐/核销/邀请。适配在 payment-*,页面不得当计价器。


1. 核心公式

真钱入账 = 快照订单 + 可幂等的收银台 + 验签回呼 + 只从 pending 完整走到 paid 一次。

calculateOrderPricing(纯函数,原价 − 促销 − 券)
        │
        ▼
createBillingOrder → 行上写下应付快照,状态 pending
        │
        ▼
createPaymentCheckoutForOrder → 供应商收银(checkout 幂等)
        │
        ▼
用户在供应商付款
        │
        ▼
webhook:验签 → 事件 id 去重 → claim 这张单
        │
        ▼
verify → completeBillingOrderPayment
        授信永久积分 / 绑套餐 / 核销券 / 播种邀请
        状态 paid

之后只有 closed / canceled。canceled 要释放未核销的券,不能把已 paid 的权益收回又当没付过(退款是另一条、更严的流程)。

饭店对照:菜单印「今日 69」写进预订单(快照)。客人去前台扫码付。支付公司电话来说「69 到了」三次,后厨只上一次菜、只撕一张券。


2. 什么场景用,什么场景不用

该走订单

场景 落点
选套餐、选加量包、用券 createBillingOrder,再跳转收银
供应商 webhook / 同步回跳 只信验签后的事件,不信 querystring 里 paid=1
改促销、改目录价 新单用新公式;旧 pending 单看快照(产品若允许「未付改价」要另开明确规则)
邀请奖励 paid 成功之后再记账,钥匙挂邀请事件,不是挂「注册了就送」

不该走订单

场景 走哪
点生成 15 钱包 consume
管理员手工补分 15 creditPermanentPoints + 管理员钥匙,或后台专用入口
生成失败退 8 分 15 refund,不是把订单改 canceled

3. 下单:价拍在行上,页面不许重算

calculateOrderPricing 是纯函数:原价、促销、券 → 应付。
createBillingOrder 把结果写成订单字段。之后所有入账、对账、展示应付,读行,不读「现在后台配的价」。

券在 pending 占用,paid 才核销。取消 pending 要释放占用,否则小王取消后券永远锁死。

例子:改价

下单时 99 − 促销 20 − 券 10 = 69。小王在支付宝里待了十分钟,你把套餐改成 199。这张单仍应收 69。新点「立即购买」才是 199 的新单。若前端用 JS 再算一次当 amount 传给支付,供应商和订单会对不上,回呼要么拒要么入错账。

例子:同一加购连点

开单也应有业务幂等(用户 + 套餐 + 进行中的 pending 等,按实现)。不要每次连点都 insert 一张 69 的新单,否则收银台和回呼会对上「哪一张」。


4. 收银台:刷新不新开摊

createPaymentCheckoutForOrder 向供应商要支付页。metadata.checkout(或同类键)保证:小王 F5、返回再点「去支付」,仍是这一摊收银,不是第二笔 69 的支付意图。

换支付供应商:只换 payment-provider / checkout / webhook 适配。订单状态机和 completeBillingOrderPayment 不要复制一份。


5. 回呼:验签、事件去重、再入账

processPaymentWebhook 顺序不能倒:

  1. 验签 — 不是供应商的请求,当没有
  2. 事件 id 去重 — 同一条 webhook 体来三遍,只进一次后续
  3. claim 订单 — 两笔不同事件别同时把同一张 pending 打成 paid
  4. 核对金额 / 支付身份 与订单快照一致
  5. completeBillingOrderPayment — 授信、绑套餐、核销、邀请,放在同一段「只成功一次」的逻辑里

complete 必须假设自己会被重试。钱包授信用 15 的 credit 钥匙(挂订单 id / 支付记录 id)。邀请奖励同样。核销券按券占用记录,核过了再来是 no-op。

例子:回呼三次

第一次:验签过、事件 evt_1 未处理、订单 pending → paid、+100 永久分、券核销、邀请人 +10。
第二次、第三次:同一 evt_1 → 去重返回,钱包不动。
若供应商换了一个事件 id 表示「同一笔支付的重复通知」,还要靠订单已是 paid / 支付记录唯一约束挡住。两道门都要。

例子:伪造回跳

小王改 URL ?status=paid。没有供应商签名,不能 complete。展示「支付结果页」可以先查订单状态,状态仍 pending 就显示等待,不要信查询参数。


6. 和钱包、后台、生成的接缝

后台改套餐目录价、促销、券规则     → 只影响下一张 createBillingOrder
        │
        ▼
本篇 16  快照 → 收银 → 回呼 → paid
        │
        ▼
15 creditPermanentPoints / 绑套餐(以后每日发日券)
        │
        ▼
小王再去生成 → 15 consume 日券+永券

生成路径看不见支付供应商。支付路径 runImageTask
退款 lane 催的是生成失败退积分,不是支付退款;支付退款是单独、更严的产品流程,不要和 15 的 refundPoints 混成一个按钮。

接口外壳仍是 { code, data, msg }。积分不足、未登录、验签失败都走这套,不要 webhook 返回一大段 HTML 当成功。


7. 写代码时的五问

  1. 应付金额读的是订单行,还是此刻再跑一遍促销公式?
  2. webhook 没验签就入账了吗?querystring paid=1 能改状态吗?
  3. 同一 event id 第二次进来,还会 applied: true 地 credit 吗?
  4. 券是 paid 才核销、取消才释放吗?邀请是 paid 之后才播种吗?
  5. 换支付渠道时,有没有复制一份 completeBillingOrderPayment

8. 读完能指挥自己(或 AI)做什么

  • createBillingOrder 写入价格快照。入账和展示应付只读快照,禁止页面重算价。」
  • 「收银台按 checkout 幂等。刷新不得开第二笔支付意图。」
  • 「webhook:先验签,再按事件 id 去重,再 claim 订单,最后 completeBillingOrderPayment。」
  • complete 里授信/核销/邀请全部走带幂等键的服务;订单已 paid 则整段 no-op。」
  • 「未验签的回跳不能改订单。生成扣分走 15,不要在支付模块 consume。」
  • 「换渠道只加 payment-* 适配,不改状态机。」

相关文档

订单拍了价,回呼只入一次账
http://www.clxhxhhr.top/posts/537/
作者
clxstart
发布于
2026-09-08
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。