# 美团核销失败怎么查，先看 errorCode 再决定能不能重试

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

联调美团、抖音团购核销时，最容易踩的坑不是「接口调不通」，而是失败后乱重试：页面已经提示失败，店员又让顾客重新输码；超时后换参数再打一枪；看到 `code=10001` 就一律重试。结果往往是：券其实已经核掉，本地却没有首次成功明细；或者平台明确拒绝的券被反复打，日志里全是同一类拒绝。

**一句话回答：** 美团核销失败先看顶层 `success`，再看稳定的 `errorCode`，不要只凭 HTTP 状态或数字 `code` 做分支。只有 `CONSUME_IN_PROGRESS`、`RESULT_UNKNOWN`、`SERVICE_UNAVAILABLE`、`PROVIDER_UNAVAILABLE`，或请求超时没拿到响应时，才用原请求原参数有间隔、有上限地重试；`PLATFORM_REJECTED`、`ALREADY_CONSUMED`（10500）等明确失败要停止自动重试。抖音次卡结果未知时不自动重试。依据无限聚核公开 FAQ 与 API 文档口径整理，查阅日期 2026-10-18，以当前公开文档与平台后台为准。

- **成功只认 `success=true`。** HTTP 200、`code=10000` 都不能单独当成功；
- **重试看 `errorCode` 白名单。** 白名单外一律停；超时没响应也按「可重试用原请求」处理；
- **已核销 10500 是核对，不是成功。** 保存 `wxdgOrderNo`，不要补发权益、不要编造撤销凭证；
- **预核销成功 ≠ 已核销。** 校验只读，权益只能在真实核销成功后发；
- **业务拒绝对照平台提示。** 券不存在、已使用、门店不适用等以平台与聚合返回文案为准，不要写死未公开数字码当分支条件。

![决策树示意：①是否拿到完整响应；②顶层 success 是否为 true；③失败时读稳定 errorCode，分成可重试白名单、停止重试展示原因、ALREADY_CONSUMED 用 wxdgOrderNo 核对三类](assets/hero.png)

*示意图：排查链路按无限聚核公开 FAQ／API 口径整理（2026-10-18），非某家后台截图。*

## 一、排查链路总览：先 success，再 errorCode

建议把「一次核销调用」的结果处理固定成四步，后端和前端共用同一套状态机：

1. **有没有拿到完整响应？** 连接中断、读超时、没 body，一律先记「结果未知」，保留原请求五元组（`shopId`、`platform`、`code`、`num`、`ticketInfo`），页面显示「正在确认，请勿重复提交」，不要立刻报失败、不要引导顾客重输码。
2. **顶层 `success` 是否为布尔值 `true`？** 是则进入成功分支：保存首次完整响应、`wxdgOrderNo`、按 `data[i]` 与 `consumeCredential[i]` 下标对应存明细与撤销凭证，再处理自己的预约／会员／出菜。
3. **`success=false` 时读 `errorCode`。** 用字符串枚举分支，不要用 `code` 数字猜：结果未知、参数错误、门店未授权、次数不足等，公开口径里可能共用同一类数字码（常见举例是 `10001`），「见 10001 就重试」会把明确拒绝也重试进去。
4. **落到三类动作：** 可重试（原样重发）、停止并展示原因、已核销转核对。任何一类都先落库，再决定 UI。

对照表（聚合侧稳定字段；平台内部业务码以官方后台为准）：

| 类别 | 典型信号 | 后端动作 | 不要做什么 |
|---|---|---|---|
| 明确成功 | `success=true` | 保存首次成功全量，再发权益 | 不要只看 HTTP／`code` |
| 可重试 | 白名单 `errorCode` 或超时无响应 | 原请求有限重试；未确定前不入账 | 不要重新预核销、换券码／门店／数量 |
| 业务／平台拒绝 | `PLATFORM_REJECTED` 等 | 停重试，展示返回原因 | 不要自动换平台再试 |
| 已核销提示 | `ALREADY_CONSUMED`，`code=10500` | 停重试，用 `wxdgOrderNo` 核对 | 不要当成功、不要补发、不要编造凭证 |
| 配置类 | 授权／门店／额度等 | 停重试，修配置后再人工发起 | 不要用同一错误请求死循环 |

## 二、预核销成功 ≠ 已核销：失败排查常从这里偏航

很多「核销失败」工单，根因其实是把两阶段混成一阶段：

- **输码验券校验（预核销）** 是只读确认：检查券是否可用、拿回不透明的 `ticketInfo`、展示套餐名与售价。它**不改变券状态**，也**不能发权益**。
- **验券／核销** 才是写操作：必须原样回传预核销保存的 `ticketInfo`，以及同一组 `shopId`、`platform`、`code`、`num`。成功依据仍是顶层 `success=true`。

排查时先问三句：

1. 这次失败发生在校验接口还是核销接口？（路径、请求体、是否带 `ticketInfo`）
2. 若校验已成功，本地是否把 `ticketInfo` 当不透明字符串整段保存，而不是解析重组？
3. 页面是否在校验成功后就提示「已核销」？（这是产品文案问题，会导致顾客投诉「核了但没权益」）

超时或结果未知时，**不要为了重试再跑一遍预核销**，也不要让顾客重新输码：券若已在平台侧核掉，重新预核销会变成平台拒绝，日志更难对上。

## 三、常见业务拒绝：对照平台返回，以文案为准

聚合侧常见会把平台明确拒绝收成 `PLATFORM_REJECTED`（公开口径举例：外层 `code` 可能是 `10000`，或 `10000` 加平台业务码，如美团侧业务码 `1006` 映射成 `11006` 一类写法）。**具体数字以你账号当前返回与平台后台为准，本文不把未公开码表写成硬编码分支。**

现场高频现象（作「常见现象举例」，处理以平台与聚合返回文案为准）：

| 现象举例 | 店员／开发侧先查什么 | 系统侧建议 |
|---|---|---|
| 券不存在／券码无效 | 是否扫成网址、空格未去、手输漏位；平台是否选错 | 停自动重试；提示核对券码与平台 |
| 券已使用／已核销 | 是否他店／他系统已核；是否本系统上次超时其实已成功 | 查本地是否有成功或待确认；必要时按平台「已验券／券状态」核对 |
| 门店不适用／未授权本店 | 聚核 `shopId` 是否对应正确门店（不是商户 ID）；该店美团／抖音授权是否有效 | 停重试；修门店映射与授权后再人工发起 |
| 已过期／未到可用时间 | 券详情页可用时段；门店营业规则 | 停重试；展示平台原因 |
| 次数不足／额度类 | 控制台剩余次数、账号配额 | 停重试；处理额度后再发起（勿把额度错误当可重试网络错误） |

原则：**拒绝类错误展示原因即可，不要自动换平台轮询试。** 轮流试美团再试抖音，只会把错误提示打乱，也增加无效调用。

## 四、可重试集合：白名单 + 原请求 + 次卡例外

按无限聚核公开 API／FAQ 口径，**只有**下列情况才允许业务后端自动重试：

- `errorCode` 为 `CONSUME_IN_PROGRESS`
- `errorCode` 为 `RESULT_UNKNOWN`
- `errorCode` 为 `SERVICE_UNAVAILABLE`
- `errorCode` 为 `PROVIDER_UNAVAILABLE`
- **或**请求超时／连接中断，根本没拿到可用响应

重试规则必须同时满足：

1. **原样五参数：** `shopId`、`platform`、`code`、`num`、`ticketInfo` 与首次核销请求逐字一致；
2. **有间隔、有上限：** 上限与退避是你业务侧配置，不是「无限打」；超限仍未知则转待核对／查券状态／对账；
3. **页面态：** 未确定前不报成功也不报最终失败，避免顾客被引导重输码；
4. **抖音次卡例外：** 结果未知时**不自动重试**，先查平台券状态或完成对账（由预核销保存的 `ticketData` 判断是否次卡，例如公开示例里看 `certificates[].time_card` 是否有值）。

伪代码意图（字段名以当前公开文档为准）：

```text
RETRYABLE = {
  CONSUME_IN_PROGRESS,
  RESULT_UNKNOWN,
  SERVICE_UNAVAILABLE,
  PROVIDER_UNAVAILABLE
}

若 超时无响应 或 errorCode ∈ RETRYABLE:
  若 抖音次卡 → 标记待核对（不自动重试）
  否则 → 用首次请求 JSON 原样有限重试
否则若 errorCode == ALREADY_CONSUMED:
  停止；保存 wxdgOrderNo；转核对
否则:
  停止；展示原因；不自动重试
```

接口侧幂等的含义是：**同一业务请求不会被重复核销成多笔。** 它不等于「每次重复调用都返回同一份首次成功明细」。因此业务系统仍要按券码 + 自身业务订单去重，并保存首次完整成功记录；公开 FAQ 也写明聚合核销调用方**无需传** `idempotencyKey`（旧客户端传入也会被忽略），去重责任在业务侧。

## 五、ALREADY_CONSUMED（10500）：停重试，用 wxdgOrderNo 核对

命中已成功核销时，当前公开口径的典型形态是：

- `success=false`
- `code=10500`
- `errorCode=ALREADY_CONSUMED`
- `wxdgOrderNo` 为原核销单号
- `data` 为空／`null`，`consumeCredential` 为空数组
- `message` 里可能带核销时间与门店信息（展示前注意脱敏）

正确处理：

1. **立刻停止**该次自动核销重试；
2. **保存** `wxdgOrderNo`、`message`、完整响应与原请求；
3. **用** `wxdgOrderNo` 和平台侧「已验券信息／券状态」核对：是否本门店、是否本笔业务订单、本地是否已有首次成功记录；
4. **若本地已有成功记录：** 复用本地权益状态，不要再发一遍；
5. **若本地没有首次成功：** 标记待核对；确认前不要仅凭 10500 补发权益，也不要编造 `consumeCredential`（没有首次成功响应就没有合法撤销凭证）。

这类工单最常见的场景，是「第一次其实已经成功，但客户端超时只看到失败／未知，又点了一次」。10500 是在告诉你「别再核了，去对原单」，不是「再试一次就能拿到成功报文」。

## 六、自检清单（联调／线上各走一遍）

| 检查项 | 预期 |
|---|---|
| 成功判断 | 仅 `success=true` 进入成功；HTTP／`code` 不作唯一条件 |
| 首次成功落库 | 完整响应、`wxdgOrderNo`、明细与凭证按下标保存后再发权益 |
| 预核销与核销 | 校验成功只进确认页；权益只在核销成功后发 |
| `ticketInfo` | 不透明整段回传，不解析重组 |
| 可重试白名单 | 仅四类 `errorCode` + 超时；原请求原参数；有上限 |
| 抖音次卡 | 结果未知不自动重试 |
| `PLATFORM_REJECTED` | 停重试，展示平台／聚合原因 |
| `ALREADY_CONSUMED`／10500 | 停重试；按 `wxdgOrderNo` 核对；不补发、不编造凭证 |
| 顾客侧文案 | 结果未知显示「正在确认，请勿重复提交」，不引导重输码 |
| 业务去重 | 按券码 + 本地订单去重；不依赖调方传 `idempotencyKey` |
| 配置类错误 | 授权、门店、额度等问题先修环境，再人工发起 |

## 七、小结

美团核销失败怎么查：把「网络结果」和「业务结果」拆开——先确认有没有完整响应，再用 `success` 判定成败，用 `errorCode` 白名单决定能不能重试。可重试就原样重发；拒绝就停；10500 就核对。预核销成功不要当已核销，次卡结果未知不要自动重试。按这套决策树落地，日志里的「失败」才能变成可解释的状态，而不是一串盲目重试。

## 常见问题

### 美团核销失败时，为什么不能只看 HTTP 状态或 code？

成功只认顶层布尔值 `success=true`。HTTP 200 或数字 `code`（例如 `10000`、`10001`）都不能单独当成功或失败依据：公开口径里不同业务结果可能共用同一类数字码，用 `code` 分支容易把明确拒绝也当成可重试。失败时读稳定字符串 `errorCode`，再决定重试、停重试还是核对。

### 哪些 errorCode 允许自动重试？原请求要怎么保留？

只有 `CONSUME_IN_PROGRESS`、`RESULT_UNKNOWN`、`SERVICE_UNAVAILABLE`、`PROVIDER_UNAVAILABLE`，或请求超时／连接中断没拿到响应时，才允许业务后端自动重试。重试必须与首次核销请求的 `shopId`、`platform`、`code`、`num`、`ticketInfo` 逐字一致，并设置间隔与上限；不要重新预核销、换参数或让顾客重输码。抖音次卡结果未知时不自动重试，先查券状态或对账。

### 返回 ALREADY_CONSUMED 或 code=10500 该怎么办？

立刻停止该次自动核销重试，保存返回的 `wxdgOrderNo`、完整响应与原请求，再用 `wxdgOrderNo` 与平台「已验券／券状态」核对是否本店本单、本地是否已有首次成功记录。不要把 10500 当成功、不要仅凭提示补发权益，也不要编造 `consumeCredential`；没有首次成功响应就没有合法撤销凭证。

### 预核销已经成功，为什么还说没核销？

预核销（输码验券校验）是只读确认：检查券是否可用并返回不透明的 `ticketInfo`，不改变券状态，也不能发权益。真正核销必须另调核销接口，原样回传 `ticketInfo` 与同一组门店、平台、券码、数量，且成功依据仍是顶层 `success=true`。超时或结果未知时不要为了重试再跑预核销。

### PLATFORM_REJECTED 要不要自动换平台再试一遍？

不要。`PLATFORM_REJECTED` 表示平台或聚合侧已给出明确拒绝，应停止自动重试并展示返回原因；券码无效、已过期、门店不适用、额度不足等先按文案排查。自动轮流试美团再试抖音会打乱错误提示并增加无效调用，修门店映射、授权或额度后再人工发起。
