登录
开发指南 / 移动支付 / 交易结果和异常处理

交易结果和异常处理

约 9 分钟
以 Markdown 格式查看 |
## 响应数据结构

标准接口响应由通信层、业务执行层和交易数据层组成:

```json
{
  "result_code": "200",
  "error_code": "",
  "error_message": "",
  "biz_response": {
    "result_code": "PAY_SUCCESS",
    "error_code": "",
    "error_message": "",
    "data": {
      "sn": "收钱吧订单号",
      "client_sn": "商户订单号",
      "order_status": "PAID",
      "status": "SUCCESS"
    }
  }
}
```

| 层级       | 字段路径                                                     | 说明                                 |
| ---------- | ------------------------------------------------------------ | ------------------------------------ |
| 通信层     | `result_code`、`error_code`、`error_message`                 | 本次接口请求是否被正常受理           |
| 业务执行层 | `biz_response.result_code`、`biz_response.error_code`、`biz_response.error_message` | 本次支付、查询或退款动作的执行结果   |
| 交易数据层 | `biz_response.data`                                          | 订单号、金额、付款方式及交易状态     |
| 订单状态   | `biz_response.data.order_status`                             | 原订单当前所处的业务状态             |
| 流水状态   | `biz_response.data.status`                                   | 本次支付、查询或退款流水是否已经结束 |

响应字段可能因接口和业务结果不同而缺失。程序必须先判断上一级对象存在并且结果允许读取,再访问下一层。

## 响应对象兼容要求

> 所有接口响应均应按 JSON 对象(Object/Map,或配置为忽略未知字段的响应对象)接收和解析,不得依赖固定字段数量或完整字段集合。接口后续可能新增返回字段,客户端应自动兼容并忽略未识别字段,仅按业务需要读取已定义字段。

使用强类型对象时,应启用“忽略未知字段”能力。新增字段不得导致反序列化失败,字段顺序变化也不得影响结果读取。

## 读取交易结果

```text
收到HTTP响应
→ 判断顶层result_code
→ 判断biz_response是否存在
→ 判断biz_response.result_code
→ 读取biz_response.data
→ 组合判断order_status和status
→ 更新订单或继续查询
```

### 判断通信结果

先读取顶层`result_code`:

- 为`200`:请求已进入业务处理,继续读取`biz_response`。
- 不为`200`:本次通信或请求受理失败,读取顶层错误信息,不使用`biz_response.data`判断交易。
- HTTP超时、连接中断或响应无法解析:只能判断“本次客户端未取得结果”,不能判断交易失败。

通信失败后,如果请求可能已经到达收钱吧,应使用原商户订单号调用查询接口,不要立即生成新订单号重复提交。

### 判断业务执行结果

通信成功后,读取`biz_response.result_code`:

- 明确成功:继续读取`biz_response.data`并判断交易状态。
- 明确失败且文档说明不会产生交易:记录失败原因,结束本次接口动作。
- 处理中、未知或无法确定是否已经产生交易:查询原订单。

业务结果码的完整取值继续使用现有[业务结果码开发指南](https://open.shouqianba.com/docs/payment/103),本页只规定读取顺序和处理动作。

### 判断订单和流水状态

不能只判断一个字段。应组合读取:

- `biz_response.data.order_status`:整笔订单状态。
- `biz_response.data.status`:当前流水状态。

只有组合结果明确表示最终成功时,才能将商户订单更新为支付成功或退款成功。

## 最终状态与非最终状态

| 典型状态                                          | 类型             | 商户系统动作                         |
| ------------------------------------------------- | ---------------- | ------------------------------------ |
| `order_status=PAID`且`status=SUCCESS`             | 支付最终成功     | 原子更新订单,并触发一次履约         |
| `order_status=PAY_CANCELED`且流水明确结束         | 支付最终失败     | 更新失败原因,按业务规则允许重新支付 |
| `order_status=CREATED`                            | 非最终状态       | 保持支付中,查询原订单               |
| `order_status=PAY_ERROR`                          | 结果未知         | 禁止重复扣款,继续查询或人工核实     |
| `order_status=REFUNDED`且`status=SUCCESS`         | 全额退款最终成功 | 更新退款单和原订单                   |
| `order_status=PARTIAL_REFUNDED`且`status=SUCCESS` | 部分退款最终成功 | 更新累计退款金额                     |
| `order_status=REFUND_INPROGRESS`                  | 非最终状态       | 保持退款中并查询                     |
| `order_status=REFUND_ERROR`                       | 结果未知         | 禁止重复退款,查询或人工核实         |

状态值的完整定义以API文档和既有业务结果码附录为准。

## 主动查询

在以下情况调用**查询接口**:

- 支付或退款请求HTTP超时。
- 同步响应为处理中或结果未知。
- 前端页面、`wx.requestPayment`或插件返回后需要确认最终状态。
- 在预计时间内没有收到支付回调。
- 回调内容无法验签或无法处理。

查询必须针对原交易,优先使用已经保存的商户订单号或收钱吧订单号。查询结果仍按本页相同顺序解析。

不同场景的轮询起点、间隔和总时长不同,以各场景“注意事项”为准。达到场景查询上限仍未取得最终状态时,将订单标记为结果未知并进入人工核实,不能自动重新扣款或退款。

## 支付结果回调

接入**支付回调接口**时:

- 读取原始回调数据并按照回调接口规则验签。
- 验签失败时拒绝更新订单,并记录脱敏排查信息。
- 验签通过后,根据商户订单号找到原订单。
- 使用与查询相同的状态机完成原子更新。
- 已经处理过的结果直接返回成功,不重复履约。
- 数据成功落库后尽快返回纯文本`success`。

如果没有正确返回`success`,收钱吧可能重复发送回调。因此回调接口必须天然支持重复调用。

## 幂等更新和履约

建议把交易状态与履约状态分开保存:

```text
交易状态:支付中 → 支付成功
履约状态:未履约 → 履约中 → 履约成功
```

同步响应、查询和回调可能并发到达。更新订单时应使用商户订单号作为幂等键,并通过数据库唯一约束、条件更新或事务保证:

- 同一订单只从非最终状态进入一次最终状态。
- 同一支付成功事件只生成一个履约任务。
- 支付已经成功但履约失败时,只重试履约,不重新发起支付。
- 已经退款成功的退款流水不再次提交。

## 支付异常处理

| 异常                     | 正确处理                                 |
| ------------------------ | ---------------------------------------- |
| 支付请求超时             | 保持结果未知,立即查询原订单             |
| 同步结果为支付中         | 按场景节奏查询,不重新收款               |
| 顾客称已付款但系统无结果 | 查询原订单,仍未知时通过人工渠道核实     |
| 页面回跳或前端返回成功   | 仅展示结果确认中,由服务端查询和回调确认 |
| 回调重复                 | 验签后幂等返回`success`                  |
| 支付成功但业务处理失败   | 保留支付成功状态,只重试履约             |

只有查询得到明确最终失败,或者业务结果明确说明没有创建交易时,才允许按业务规则创建新的支付请求。

## 退款异常处理

- 每次退款使用唯一退款流水号,并关联原支付订单。
- 退款请求超时、处理中或结果未知时,查询原退款流水。
- 上一次退款结果未明确前,不得提交同一笔退款。
- 部分退款前检查累计退款金额,不能超过原订单允许退款金额。
- 退款成功后更新退款单和原订单,不根据前端提示直接改状态。

## 开发检查

- [ ] 响应使用Object/Map或忽略未知字段的对象接收。
- [ ] 先判断顶层`result_code`,再读取`biz_response`。
- [ ] `order_status`和`status`组合判断。
- [ ] HTTP超时不会直接更新为失败。
- [ ] 非最终状态会查询原订单。
- [ ] 回调先验签、再落库、最后返回`success`。
- [ ] 查询、回调和同步响应更新同一状态机。
- [ ] 支付成功只履约一次。
- [ ] 结果未知时禁止重复支付或重复退款。
目录
在线咨询