支付回调和订单对账怎么设计

📅 2026/6/30 6:32:13
支付回调和订单对账怎么设计
在涉及虚拟商品、游戏充值、会员订阅或平台订单的系统里支付回调和订单对账不能只看“支付成功后改状态”这一步。真正容易出问题的地方通常是重复回调、回调乱序、验签失败、订单金额不一致、补单不完整、对账口径不清和结算状态提前推进。本文只从后端工程角度拆解支付回调和订单对账的基础设计不讨论具体业务推广和产品选型。一、订单表先区分业务状态和支付状态订单表不要只放一个 status 字段。业务订单状态、支付状态、发货状态、结算状态最好分开。一个常见订单表可以包含这些字段order_no平台订单号。out_trade_no外部支付订单号。user_id用户 ID。product_id商品或业务对象 ID。channel_id支付渠道或来源渠道。amount订单金额建议使用整数分保存。pay_status待支付、支付成功、支付失败、已关闭。deliver_status未发货、发货中、已发货、发货失败。settle_status未结算、待结算、已结算、结算异常。callback_count回调次数。last_callback_at最近一次回调时间。created_at、paid_at、updated_at。这样做的好处是支付成功不等于业务处理完成发货成功也不等于可以结算。每个阶段都可以独立重试和排查。二、回调入口必须先验签再处理支付回调入口至少要做四层校验参数完整性校验。签名验签。订单号是否存在。金额和币种是否一致。伪代码可以这样组织receive_callback(request) raw_body request.body params parse(raw_body) save_callback_log(raw_body, params) if !verify_sign(params): mark_log_failed(invalid_sign) return fail order find_order(params.order_no) if order is null: mark_log_failed(order_not_found) return fail if order.amount ! params.amount: mark_log_failed(amount_mismatch) return fail process_paid_order(order, params) return success注意原始回调报文要先落日志再做业务处理。否则一旦验签或解析失败后面很难复盘。三、支付成功处理必须幂等第三方支付平台可能重复推送回调也可能因为网络抖动导致平台已经处理成功但对方继续重试。因此支付成功处理必须幂等。常见做法以 order_no 做唯一锁。只有 pay_status 从待支付变为支付成功时才执行业务动作。已支付订单再次收到成功回调时只记录日志并返回成功。发货或加余额动作要有独立流水号避免重复发货。数据库层可以使用条件更新UPDATEordersSETpay_statuspaid,paid_atNOW(),callback_countcallback_count1WHEREorder_no?ANDpay_statuspending;如果影响行数为 1说明本次是第一次支付成功可以进入后续业务处理。如果影响行数为 0需要查询当前订单状态再决定记录重复回调还是异常回调。四、回调日志要能支撑排查建议单独建 callback_logs 表不要只在订单表里留一个最近回调字段。日志表建议包含id。order_no。channel_order_no。callback_type。raw_body。parsed_body。sign_result。amount_check_result。process_result。error_code。error_message。created_at。这样出现“用户说已付款但订单未到账”时可以按平台订单号、外部订单号、用户 ID、时间范围快速定位。五、补偿任务不要直接覆盖状态补偿任务的作用是修复异常状态不应该绕过业务规则直接改成成功。补偿任务通常处理几类订单支付渠道显示成功但本地仍是待支付。本地支付成功但发货失败。回调验签失败但人工确认渠道订单有效。支付成功后结算状态没有推进。回调日志缺失或处理超时。补偿任务建议记录补偿来源、执行人、执行时间和补偿前后状态。自动补偿和人工补偿也要区分避免后期审计时说不清楚。六、对账不要只比订单总额对账至少要比较三类数据支付渠道账单。本地订单表。业务发货或权益流水。只比订单总额是不够的。更稳妥的方式是按订单号逐笔匹配并输出差异类型渠道有本地无。本地有渠道无。金额不一致。状态不一致。已支付但未发货。已发货但未结算。对账结果建议写入 reconcile_records 表保留每次对账批次号方便回放和复查。七、结算状态要晚于支付和发货结算状态不要在支付成功时立即完成。更合理的链路是订单创建。支付成功。业务发货或权益到账。对账确认。进入待结算。结算完成。如果支付成功后直接结算一旦后续出现退款、发货失败、回调重复或金额异常财务侧会很难处理。八、上线前建议准备的测试用例上线前至少要覆盖这些场景正常支付成功回调。重复成功回调。先失败后成功回调。金额不一致。签名错误。订单不存在。支付成功但发货失败。回调超时后补偿成功。渠道账单有单但本地无单。本地成功但渠道账单缺失。这些测试用例比单纯看页面是否能支付更重要。支付链路一旦上线问题往往发生在异常场景里。九、总结支付回调和订单对账的核心不是把订单状态改成成功而是保证每一步可校验、可重试、可追踪、可对账。订单状态拆分、回调验签、幂等处理、原始日志、补偿任务和逐笔对账是这类系统长期稳定运行的基础。配图说明支付回调、补偿任务和对账链路示意图