一次在线支付背后发生了什么:下单、支付、回调与对账全流程

用户在网站上点了"付款",几秒钟内其实发生了一连串动作:跳转、支付、回调、对账、退款、争议。如果只懂"用户付了钱"这一层,遇到掉单、重复扣款、对不上账时就会一头雾水。本文把在线支付的标准流程拆开讲。

一、完整链路一览

以"用户在你网站用第三方支付(如 Stripe/支付宝)付款"为例,完整链路是:

用户下单 → 服务端创建支付订单 → 跳转/唤起支付 → 用户完成支付 → 支付平台回调你的服务器 → 服务器验签并更新订单 → 对账 →(如有)退款/争议处理

二、步骤拆解

1. 用户下单:用户在购物车点击"去支付",你的服务端在数据库生成一笔订单,状态为"待支付"。

2. 服务端创建支付会话:服务端调用支付平台的创建订单 API,传入金额、货币、商品、回调地址等参数,拿到一个支付 URL 或 Token。

3. 跳转支付:前端跳转到支付页面(或拉起钱包),用户输入卡号或扫码完成支付。这一步在支付平台的环境里进行,你的网站看不到卡号。

4. 支付结果返回:用户支付成功后,支付平台做两件事:把用户带回你网站的"返回地址";同时异步调用你的"回调地址"(Webhook)。

三、回调(Webhook)与验签

这是最容易出错、也最关键的一步:

  • 回调:支付平台通过 Webhook 通知你的服务器"这笔订单支付成功"。它发给你的请求带有签名;
  • 验签:你的服务器必须用约定密钥验证签名,确认这条回调真的是支付平台发来的,而不是攻击者伪造的。

为什么必须验签:如果攻击者伪造一条"支付成功"回调,你的系统可能在没有收到钱的情况下发货。验签是防伪造的第一道门。

关键规则:

  1. 幂等处理:同一笔订单多次回调,只能执行一次发货/更新;
  2. 金额双重校验:回调里的金额必须与订单金额一致,防止"改金额"攻击;
  3. 失败重试:回调处理失败要返回非 2xx,让平台稍后重试。

技术实现参考Webhook 集成指南Webhook 签名验证

四、对账

对账是"账目和真实交易对上"的过程,一般每日进行:

  1. 从支付平台导出结算明细(每笔交易的状态、金额、手续费);
  2. 与本地订单数据库比对:是否存在"本地已支付但平台没有"或反过来;
  3. 找出差异(掉单、金额不符、重复扣款)并处理。

掉单:用户付了钱但回调丢失(网络原因),本地订单还是"待支付"。对策:定时任务主动向支付平台查询订单状态,把本地状态修正为"已支付"。这在正式上线前必须做。

对账的自动化:订单量大时,建议把对账写成每日定时任务,自动比对并生成差异报表,只把异常项推送给人工处理。日均几百单时人工还能应付,超过千单后基本不可行,必须靠脚本兜底。

五、退款

退款流程相对简单:服务端调用支付平台的退款 API,传入原订单号或交易号。注意:

  • 退款有手续费(部分平台不退还百分比手续费);
  • 退款不是立刻到账,通常要几个工作日;
  • 用户可能只退部分(部分退款)。

退款与争议处理的操作,见退款与争议处理指南

六、争议(拒付 / Chargeback)

当用户向发卡行发起"我没付过这笔钱"的申诉,就叫争议(拒付)。流程:

  1. 发卡行发起争议,资金被冻结/扣回;
  2. 网关通知你,通常给 7-21 天响应窗口;
  3. 你提交证据(订单、物流、用户确认记录);
  4. 发卡行判定:你赢则资金退回,输则扣回并可能收拒付费。

降低争议的方法:清晰的商品描述、明确的退换政策、留存发货证据、用 3DS 验证降低"未授权"类争议。

七、FAQ

Q1:用户付了钱但页面显示失败,怎么办? 以服务端回调/对账结果为准,不要以用户看到的前端页面为准。页面可以提示"支付结果确认中"。

Q2:回调重复收到会重复发货吗? 会,除非你做幂等处理。用"订单状态检查 + 唯一约束"保证每笔订单只发货一次。

Q3:对账发现多扣了用户钱,怎么处理? 走退款流程退差额,并给用户发送退款通知,避免升级为争议。