一笔授权是有状态的。发卡行批准 0100 的那一刻,持卡人的可用额度就被扣掉一块(hold / 冻结),这块钱要一直占着,直到请款清算或者授权过期。这就带来一个根本问题:
冲正(Reversal)就是消除这种不一致的机制:由发起方主动告诉对方"刚才那笔不算,把状态退回去"。它不是退款、不是新交易,而是对同一笔原交易的撤销指令。
先把 MTI 的第二位(消息类)对齐:x1xx 是授权类、x2xx 是金融类、x4xx 就是冲正/退单类。第三位(功能码)区分"请求"还是"通知":
| MTI | 名称 | 语义 / 什么时候用 |
|---|---|---|
0400 | 冲正请求 | 收单方请求撤销原交易,等待对方回应。对方有权拒绝(虽然实践中极少)。 |
0410 | 冲正响应 | 对 0400 的应答,DE39 给结果。 |
0420 | 冲正通知(advice) | 既成事实的通知:"我这边已经撤了,你也必须撤"。对方不能拒绝,只能确认收到。 |
0430 | 冲正通知响应 | 确认收到 0420。收不到就要一直重发。 |
0400 和 0420 的选择不是随意的。判据是:状态变更是否已经发生。如果你只是"想撤",用 0400(请求);如果你本地已经把交易作废、款也没收、账已经这么记了,那就必须用 0420(通知)——因为你已经不可能因为对方回一个拒绝就把交易变回来。用错的典型后果是:发了 0400 被拒绝,你既不能撤也不能不撤,卡在中间。
这是新人最容易搞混的一对概念,也是生产事故的高发区。重发(repeat)是把同一笔再发一次,冲正(reversal)是发一笔新报文去撤销它。
ISO 8583 用 MTI 第四位(报文来源 / origin)标记重发:
| 第四位 | 含义 | 例 |
|---|---|---|
0 | 收单方发起 | 0400 |
1 | 收单方重发 | 0401 / 0421 |
2 | 发卡方发起 | 0422 |
3 | 发卡方重发 | 0423 |
4 | 其他方发起 | 0424 |
5 | 其他方重发 | 0425 |
什么时候重发、什么时候冲正?一句话:不确定对方有没有处理 → 先重发问;确定对方处理了但我方不要了 → 冲正。实践中常见的组合是"重发 N 次仍无响应 → 转冲正",而不是直接冲正,因为重发在对方幂等的前提下是无害的。
冲正报文本身不能只说"撤上一笔"——发卡行同一秒可能在处理上千笔。它必须带上足够定位原交易的身份信息,这就是 DE90 原始数据元(Original Data Elements),定长 42 位数字,由五段拼接:
| 位置 | 长度 | 内容 |
|---|---|---|
| 1–4 | 4 | 原报文 MTI(如 0100 / 0200) |
| 5–10 | 6 | 原 DE11 系统跟踪号 STAN |
| 11–20 | 10 | 原 DE7 传输日期时间 MMDDhhmmss |
| 21–31 | 11 | 原 DE32 受理机构标识码(不足补零) |
| 32–42 | 11 | 原 DE33 转发机构标识码(无则补零) |
把这 42 位丢进本站解析器就能自动切开对照。三个高频坑:
除 DE90 外,冲正报文通常还会原样带上 DE2(卡号)、DE3(处理码)、DE4(金额)、DE37(检索参考号 RRN)、DE41/42(终端/商户号),方便对端做多重校验。DE37 RRN 在很多卡组是实际的主匹配键,DE90 反而是兜底——具体以对方规范为准。
不是所有冲正都是"全撤"。典型场景:加油站预授权 150 元,实际加了 92.4 元;或者餐厅预授权含小费上浮、实际结账更低。这时要把多冻的那部分释放掉,而不是撤销整笔——这就是部分冲正。
承载它的是 DE95 替代金额(Replacement Amounts),定长 42 位,四段:
| 长度 | 内容 |
|---|---|
| 12 | 替代后的交易金额(对应 DE4) |
| 12 | 替代后的结算金额(对应 DE5) |
| 12 | 替代后的持卡人入账金额(对应 DE6) |
| 6 | 替代后的持卡人入账手续费(对应 DE8) |
000000009240(剩下的),不是 60.6(差额)。把差额填进去是经典事故:发卡行会把冻结额改成 60.6,持卡人凭空多出 90 块被占。另外,金额一律是最小货币单位的整数、左补零——日元没有小数、第纳尔是三位小数,别默认乘 100。详见金额为什么没有小数点。
"超时"在收单侧看只是一个现象:发出去了,没等到响应。但底层可能是三种完全不同的状态,而收单方无法区分:
| 情况 | 发卡侧实际状态 | 该怎么办 |
|---|---|---|
| A. 请求丢在路上 | 什么都没发生,没冻结 | 重发是安全的 |
| B. 响应丢在回程 | 已批准,已冻结 | 必须冲正,否则成孤儿冻结 |
| C. 发卡行慢,响应迟到 | 已批准,冲正窗口内到达 | 已回商户失败 → 也必须冲正 |
因为无法区分,行业的标准做法是宁可多冲一次:超时后先按重发试,仍无果就发冲正;即使原交易根本没到发卡行(情况 A),发卡行收到一个"找不到原交易"的冲正,照样应答成功并记录下来——这叫 unmatched reversal,是正常现象,不是错误。
DE39=00。就算找不到原交易,也应该记录一条"未匹配冲正"并回批准,而不是回 25(找不到原交易记录)。原因很实际:收单方看到失败会无限重发,把你的通道打满,而且状态还是不一致。找不到原交易是你要在对账时处理的事,不是拒绝对方的理由。另外要注意发卡行不可用(DE39=91)时的代授权(STIP / stand-in):卡组织替发卡行做了决定,事后用 0120 / 0220 通知类报文补告知发卡行。这类报文同样是"既成事实",发卡行只能接受并入账,不能拒绝。参见 DE39 响应码大全。
既然重发和冲正都可能收到多次,接收侧必须做幂等。问题是拿什么当唯一键。单用 DE11 STAN 是不够的:它只有 6 位,按规定当天内唯一,跨天必然复用,高峰期还可能被不同终端撞上。实践中的组合键:
去重窗口至少要覆盖对方的重发策略周期 + 日切,通常做 24~48 小时,并且把结果一起缓存——重复请求要返回与首次完全相同的响应(同一个 DE39、同一个授权码 DE38),而不是重新决策一次。重新决策会出现"第一次批准、重发时余额不足被拒"的诡异不一致。
这四个词在中文和英文里都经常被混着用,但它们在报文层面是四种不同的东西,资金路径和时效也完全不同:
| 动作 | 报文 | 发生时点 | 对持卡人账单 |
|---|---|---|---|
| 冲正 Reversal | 0400/0420 | 清算前,通常几秒~几分钟内 | 原交易不出现,只是冻结释放 |
| 撤销 Void | 多为 0400 或 0200 撤销处理码 | 当天、请款前 | 同上,通常也不上账单 |
| 退款 Refund | 0200,DE3 处理码 20xxxx | 清算后,任何时候 | 两笔:原消费 + 一笔贷记 |
| 拒付 Chargeback | 0442/0432 等争议报文 | 清算后,按卡组时限(常见 120 天) | 两笔,且伴随争议流程 |
对业务和客服来说最有用的一句话是:能冲正就别退款。冲正在清算前完成,持卡人账单上干干净净、额度立刻回来;退款是一笔新的贷记交易,要走完整清算,持卡人得等几天,还会在账单上看到两条记录、甚至在跨境场景下因两次换汇而损失汇差。
如果冲正最终没送达(收单方重试次数用尽、或者干脆没发),发卡行那边就留下一笔永远等不到清算的冻结——业内叫孤儿冻结 / 悬空授权。它的表现是:
兜底机制是授权过期自动释放:发卡行给每笔授权设生存期(常见 7 天,酒店 / 租车等场景 30 天),到期未清算就自动放掉冻结。但这是兜底,不是解决方案——持卡人要等一周。真正的解法是三条一起上:
手上有一笔"冲正没生效"的报文时,按顺序过这几条,九成能定位:
0400 还是 0420——你的场景是"请求"还是"既成事实"?第四位是不是被误设成重发?00 说明对方收下了(问题在你这边的状态机);25 说明匹配失败(回第 2、3 条);91 说明对方系统不可用(转 advice 重发)。00。