# 团购核销幂等怎么做，重试漏单和 requestId 别踩坑, 无限聚核

> 核销服务由无限聚核提供，美团、抖音团购核销一站式接入。

联调最怕两种结局：超时后换参数再打一枪，平台其实已经核掉，本地却没有首次成功明细；或者看到「已核销」就当成功，又给顾客发了一份权益。幂等不是多传一个字段就完事，而是「首次成功怎么存、未知结果怎么重试、已核销提示怎么核对」三件事一起做对。

**一句话回答：** 团购核销幂等要分三层落地。自接美团验券时，用 `requestId` 标识同一笔业务请求：第一次拿到完整成功响应必须整份落库；超时或结果未知时，只用**同一个** `requestId` 和同一组券码／门店／数量重试，不要换参、不要重跑准备。自接抖音时，验券准备与验券一一对应，失败后应重新 `certificate.prepare`，**禁止**拿旧的 `verify_token` 再试。本地业务再按「平台 + 门店 + 券码／券ID」做去重键。聚合侧命中已成功订单时常见 `code=10500`、`errorCode=ALREADY_CONSUMED`：这是核对信号，不是成功响应，不能据此补发权益或编造撤销凭证。以下整理自美团／抖音开放平台公开接口说明与无限聚核公开 FAQ／API 口径，查阅日期 2026-10-25，以官方后台与当期文档为准。

- **美团：`requestId` 管同一笔写请求。** 成功明细只认第一次完整响应；未知结果同键重试；
- **抖音：失败就重新准备。** 旧 `verify_token` 不符合「一次准备配一次验券」；
- **本地：`platform + shop + coupon` 去重。** 接口层防「核成多笔」≠ 业务层自动补明细；
- **10500／已核销：停重试、去核对。** 保存原单号，不要当成功入账。

![示意图：左栏美团 requestId——首次成功完整落库、超时未知用同一 requestId 重试、勿换参勿重预核销；中栏抖音 verify_token——失败后重新 prepare、禁止复用旧 token、一次准备配一次验券；右栏本地去重键 platform+shop+coupon，10500≠成功补发，ALREADY_CONSUMED 只核对](assets/hero.png)

*示意图：通用做法与字段名摘自平台公开文档及聚合 FAQ 口径（2026-10-25 查阅），不是真实后台界面。*

## 一、先分清：接口幂等 ≠ 业务幂等

很多人把「幂等」理解成「随便重试都安全」。更准确的拆法是：

| 层 | 解决什么 | 不解决什么 |
|---|---|---|
| 平台／聚合写接口 | 同一业务请求不要被核成多笔 | 不会每次重复调用都吐回「第一次那份完整成功明细」 |
| 本地业务库 | 同一张券只发一次权益、首次成功可追溯 | 不能代替平台侧的 `requestId`／准备—验券对应关系 |
| 对账任务 | 事后发现漏单、多发、状态不一致 | 不替代核销当下的重试与落库策略 |

聚合核销公开 FAQ 写明：调用方**无需**自传 `idempotencyKey`（旧客户端传入也会被忽略）。业务系统仍要按券与自身订单去重，并保存首次完整成功记录——不能指望「再打一次就能把明细补回来」。

## 二、美团：`requestId` 怎么用才不漏单

自接美团（含大众点评来源券）服务零售验券时，公开文档要求传用于标识幂等性的请求 ID `requestId`，并带上券码、验券数量等。聚合层对外时，会把平台侧的幂等语义收进「原请求重试」规则；无论自接还是聚合，业务侧都要遵守同一条纪律：

1. **发核销请求前**写入本地 `pending`（或等价状态），记下将要用的 `requestId`（或聚合侧完整原请求五元组：门店、平台、券码、数量、凭证）；
2. **第一次 `success=true`（或平台约定的成功码）**：把完整响应、逐张明细、撤销所需凭证原样落库，再发权益。权益发放与核销写成功要拆开：本地权益失败走补处理，不要再核一次已经成功的券；
3. **超时、连接中断、结果未知**：页面显示「结果待确认」，只用**同一个** `requestId`／同一组原参数有间隔、有上限地重试。不要换 `requestId`「再开一笔」，也不要为了重试重新跑验券准备或让顾客重输码；
4. **明确拒绝**：停止自动重试，展示原因；修配置后再人工发起。

`requestId` 的价值是：平台把「同一笔业务写请求」认成一笔。你一旦在未知结果时换了新的 `requestId`，就等于另开一笔写请求——若第一次其实已成功，第二次可能撞上已核销，本地却两头都没有干净的首次成功报文。

## 三、抖音：失败后重新 prepare，别抱着旧 token

抖音生活服务是 `certificate.prepare` → `certificate.verify`：准备返回 `verify_token` 与加密券码，验券确认才真正核销。公开文档要求准备与验券次数一一对应；验券成功后当前二维码失效。

和美团「同键重试」不同的关键点：

- **核销失败（明确失败）后，应重新走准备**，拿新的 `verify_token`／加密券码，再进入下一次验券；
- **不要复用旧的 `verify_token` 瞎重试**——不符合「一次准备配一次验券」；
- 通过聚合接口时：超时／结果未知的自动重试，仍按公开 FAQ 的白名单用**完全相同的原核销请求**重试，且**不要重新预核销**；这与「自接抖音明确失败后重新 prepare」是不同分支，按当期文档分支落地，不要混成「任何失败都重预核销」或「任何失败都抱旧 token」；
- **抖音次卡结果未知时不要自动重试**，先查平台券状态或进入对账待核对（预核销返回里可判断是否次卡）。

把美团的「同 `requestId`」抄到抖音的 `verify_token` 上，是漏单与错单的常见来源。

## 四、本地去重键：platform + shop + coupon

接口层防的是「同请求核成多笔」；业务层还要防「同券发两份权益」「同券两条成功订单」。建议每张券一条核销记录，唯一键取：

- **平台**（如美团／抖音枚举）；
- **门店**（聚核门店 `shopId` 或你们映射后的业务门店，不要混用商户 ID）；
- **券键**（美团存券码；抖音优先存券 ID／`certificate_id`，按字符串存大整数）。

请求发出前写 `pending`；成功改 `succeeded` 并挂上权益 ID；未知改 `unknown` 进入核对队列。一次核销多张券时，按响应逐项入库，不要「整请求一条糊过去」。对账任务（见「团购核销对账」专文）用同一唯一键比对平台记录与本地记录，只出差异单，不自动补发、不自动撤销。

## 五、10500／ALREADY_CONSUMED：核对，不是成功

聚合核销命中已成功订单时，公开口径常见为：顶层 `success=false`、`code=10500`、`errorCode=ALREADY_CONSUMED`，并带回原核销单号（如 `wxdgOrderNo`），但**不再**返回首次成功明细和撤销凭证。

正确动作：

1. **立刻停止**该次自动核销重试；
2. 保存完整响应、原请求与原单号；
3. 用原单号与平台「已验券／券状态」核对：是否本店本单、本地是否已有首次成功记录；
4. **本地没有首次成功时**：标记待核对；确认前不要仅凭 10500 补发权益，也不要编造 `consumeCredential`——没有首次成功响应就没有合法撤销凭证。

10500 最常见的故事是：第一次其实已经成功，客户端只看到超时／失败，店员又点了一次。提示语是在说「别再核了，去对原单」，不是「再试一次就能拿到成功报文」。

## 六、落地清单：发版前勾这五项

| 项 | 通过标准 |
|---|---|
| 首次成功落库 | 完整响应、逐张明细、撤销凭证、关联业务订单齐全后才算可发权益 |
| 美团／同键重试 | 未知结果只用同一 `requestId`（或聚合原请求）重试；有间隔、有上限 |
| 抖音准备—验券 | 明确失败后重新 prepare；不复用旧 `verify_token`；次卡未知不自动重试 |
| 本地唯一键 | `platform + shop + coupon` 唯一；权益只发一次 |
| 10500 分支 | 停重试 → 按原单号核对 → 不补发、不编造凭证 |

更细的失败码白名单、可重试集合，见错误码专文；每日三份数据怎么对，见对账专文。本文只钉「幂等与重试」这一截，避免把整份对账流程或完整 Java 示例再贴一遍。

## 常见问题

### 团购核销幂等只要传 idempotencyKey 就够了吗？

不够。聚合核销公开文档说明调用方无需传 `idempotencyKey`，旧客户端传入也会被忽略。业务系统仍须按券码（或券 ID）与自身业务订单去重，并保存首次完整成功记录；不能依赖重复请求补回成功明细。自接美团时另按平台规则使用 `requestId` 标识同一笔验券写请求。

### 超时或结果未知时，能不能换一个新的 requestId 再试？

不能。超时、结果未知时应用与首次相同的 `requestId`（或聚合侧完全相同的原请求参数）有间隔、有上限地重试。换新的 `requestId` 等于另开一笔写请求，容易在「其实已成功」时撞上已核销，本地却缺少第一份完整成功报文。也不要为了重试重新预核销或让顾客重输码。

### 抖音核销失败后，可以拿旧的 verify_token 继续重试吗？

不可以。抖音生活服务要求验券准备与验券一一对应；明确失败后应重新 `certificate.prepare`，使用新的 `verify_token` 与加密券码再验券。复用旧 token 不符合公开规则。聚合路径下「结果未知」的自动重试仍按当期 FAQ 白名单用原核销请求重试，且次卡结果未知禁止自动重试。

### 本地去重键应该怎么设计？

建议每张券一条记录，唯一键为平台 + 门店 + 券键：美团用券码，抖音用券 ID（大整数按字符串存），门店用聚核 `shopId` 或已映射的业务门店 ID，不要与商户 ID 混用。请求前写 pending，成功后再发权益；对账与差异处理沿用同一唯一键。

### 返回 10500 或 ALREADY_CONSUMED，可以直接给顾客补发权益吗？

不可以。该结果表示命中已成功订单：应停止自动重试，保存原单号与完整响应，再与平台券状态、本地首次成功记录核对。不要把已核销提示当作成功响应，不要仅凭提示补发权益，也不要编造撤销凭证；没有首次成功响应就没有合法撤销凭证。

### 接口幂等是否等于不用做每日对账？

不等于。接口幂等降低「同请求核成多笔」的概率，对账负责发现「平台已核本地没有」「本地成功平台没有」「已撤销权益还在」等差异。核销当下把唯一键与首次成功明细存全，次日对账才有可比数据；对账任务只出差异单，不自动补发、不自动撤销。
