支付接入全链路:下单、拉起、回调验签、幂等与对账
把一次支付从点击到入账的完整技术时序讲透:为什么发货只认服务端回调、签名怎么防伪造、订单状态机怎么设计、如何用幂等防重复入账,并对比微信/支付宝、Stripe、苹果 IAP 与 Google Play 的差异
- 画出用户、客户端、商户后端、支付平台四方的完整支付时序
- 理解为什么支付结果必须以服务端异步通知为准、客户端回调只做展示
- 掌握验签原理、订单状态机、幂等入账与主动查询兜底
- 分清国内第三方支付、Stripe、苹果 IAP/Google Play 的技术与规则差异
支付是 App 里最不能出错的一条链路
别的功能出 bug 顶多体验不好,支付出 bug 是真金白银:重复扣款、付了没发货、没付却发货。所以支付系统的第一原则是「宁可保守、必须可追溯、绝对幂等」。这一课讲清一笔钱在技术上是怎么流的、每个环节为什么这么设计。各平台的具体费率、入驻资质属于易变商业规则(Stage14 从资质角度讲过),这里专注技术链路,数字以各平台官方文档为准。
四个角色与完整时序
一次支付涉及四方:用户、你的客户端、你的商户后端、第三方支付平台(背后再连银行/卡组织)。关键在于:涉及钱的决策全部在「服务器到服务器」之间完成,客户端只是发起和展示。
标准支付时序
① 用户点「购买」 -> 客户端请求【你的后端】创建订单
② 后端生成唯一订单号、记录「待支付」订单 -> 返回支付参数给客户端
③ 客户端用参数调起支付平台的收银台/SDK,用户在平台完成付款
④ 支付平台在它的服务器确认扣款
⑤ 支付平台【服务器到服务器】异步通知你的后端(Webhook)
⑥ 你的后端:验签 -> 核对金额与订单 -> 幂等地把订单改「已支付」并发货 -> 回 ACK
⑦ 客户端收到 SDK 的支付完成回调,只用于「跳转/提示」,不作为发货依据
⑧ 若通知丢失,后端用主动查询接口对账兜底(定时查「待支付」单状态)客户端的支付结果可以被断网丢失、被抓包篡改、被用户直接构造。如果看到客户端回调就发货,攻击者可以不付款直接拿到商品。唯一可信依据是第⑥步:支付平台服务器发来、且你验签通过、金额订单核对一致的异步通知。客户端回调最多用来「友好地等待并刷新订单状态」。
验签:怎么证明通知真的是支付公司发的
Webhook 是打在你公网接口上的,别人也能伪造一个「已支付」POST 过来。怎么防伪?靠非对称签名:支付平台用它的私钥对通知内容签名,你用平台给你的公钥(或对称密钥)重新计算签名比对,内容被改一个字、或根本不是平台签的,验签就失败,直接丢弃。验签通过后还要核对:通知里的订单号是不是你系统里的、金额是否和订单一致、币种是否正确——防止「用一张小额真实支付单去冒充大额订单」。
后端收到支付通知的处理顺序(任何一步不过都拒绝)
1. 验签:用平台公钥/密钥校验签名,失败直接丢弃
2. 找单:订单号存在且属于该用户、状态为「待支付」
3. 核额:通知金额/币种与订单完全一致
4. 幂等入账:若已是「已支付」,不重复发货,直接回 ACK
5. 改状态为已支付、发货(开通会员/加额度)、写流水
6. 按平台要求返回成功应答,否则平台会反复重发通知订单状态机与幂等
订单要有清晰的状态流转,且每次流转只允许沿合法方向走:待支付 → 已支付 → 已发货(或 待支付→已关闭/已退款)。幂等是核心:网络会重试、支付平台也会重复发通知,所以「处理这张订单」的操作必须执行一次和执行 N 次结果相同——常见做法是数据库层面给订单号加唯一约束、用「乐观更新」(只有当前状态是待支付才允许改成已支付),并发重复通知到达时只有一个能成功。
通知丢了怎么办:主动查询与对账兜底(选学)
异步通知可能因服务器重启、网络问题丢失,所以不能只等通知。两道兜底:主动查询——客户端轮询或后端定时扫描「待支付超过 N 分钟」的订单,调用支付平台「查单接口」获取真实状态;每日对账——每天拉平台的对账文件和自己的订单流水逐笔比对,找出「平台扣了款但本地没发货」「本地显示已付但平台无记录」等异常单,人工或脚本修复。资金链路一定要做到可逐笔追溯。
主流通道的技术差异
在 iOS 上售卖 App 内消费的数字内容/会员,按苹果规则必须使用 IAP 内购、不能跳外部支付,苹果会抽成(小开发者计划费率更低,具体比例与规则以苹果官方为准);国内安卓渠道政策各不相同,实物电商则不受 IAP 约束。这套是平台规则(Stage14 已展开资质与费率),技术上你要额外做的是:IAP/Play 支付完成后,把支付票据拿到你自己的服务器,再由服务器向苹果/谷歌校验真伪后发货——依然是「服务器校验后才发货」的同一原则。
退款不是把订单删掉,而是新增一条退款流水、把订单状态流转为「已退款」、回收对应权益。退款同样可能收到重复通知,也要幂等。保持「订单永不删除、只追加流水和状态」,你的账目才永远对得平。
用户在 App 内完成支付后,下列哪一步才是后端「发货」的正确依据?
本节小结
支付四方:用户、客户端、商户后端、支付平台,钱的决策只在服务器到服务器。时序:后端建唯一待支付订单→客户端拉起平台收银台→用户付款→平台服务器异步Webhook通知→后端验签+核对金额订单+幂等改已支付并发货+回ACK→客户端回调仅展示→通知丢失用主动查单和每日对账兜底。验签用平台公钥校验签名防伪,再核对订单号/金额/币种。订单用状态机(待支付→已支付→已发货/关闭/退款)、唯一约束+乐观更新保证幂等,重复通知只成功一次,退款追加流水不删单。通道:微信支付宝需商户号统一下单+SDK;Stripe/Paddle走Webhook(后者可代征税);iOS数字商品必须IAP、安卓海外Play Billing,二者都要服务器向平台校验票据后发货,费率规则以官方为准。
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
把会话、上传文件、定时任务状态从应用进程里挪到 Redis、对象存储或数据库后,任何一台应用实例都能处理任何请求,扩容就是多开几个实例挂到负载均衡后面。反过来,只要状态留在某台机器的内存里,它就既无法横向扩容,也无法滚动更新(一重启用户就掉线)。面试答「如何支撑更高并发」,先讲无状态化,再谈加机器和缓存。