> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 点卡充值 API 共通规范

> Waffo Point Topup 与供应商之间的接口协议、消息结构、SHA256WithRSA 签名验签、错误码与 RSA 密钥生成。

本页适用于 **Mode B** 与 **Mode C** 集成。Mode A 不涉及 API，无需关注本页。

## 共通接口信息

| 项        | 说明                                                                                                                       |
| -------- | ------------------------------------------------------------------------------------------------------------------------ |
| 接口协议     | RESTful                                                                                                                  |
| 接口格式     | 消息体为 JSON（`Content-Type: application/json`），UTF-8 编码                                                                     |
| 提交方式     | HTTP `POST`                                                                                                              |
| HTTPS 传输 | TLS 1.2 及以上                                                                                                              |
| 时间       | 表示某个时间点，遵循 ISO 8601 标准，精度到毫秒。类型 String，示例 `2023-04-01T03:00:00.000Z`                                                     |
| 币种       | 遵循 [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) 币种代码标准。类型 String，示例 `IDR` 印尼卢比、`PHP` 菲律宾比索、`MYR` 马来西亚林吉特           |
| 金额       | 法币精度为小数点后 2 位，单位为元。类型 String，示例 `2.27`                                                                                   |
| 国家代码     | 遵循 [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) 标准。类型 String，示例 `IDN` 印度尼西亚、`PHL` 菲律宾、`HKG` 香港 |

接口响应通过 HTTP 状态码表达请求是否被成功处理：

| 类别            | 状态码                                                                                           |
| ------------- | --------------------------------------------------------------------------------------------- |
| **2XX 成功**    | `200` OK                                                                                      |
| **4XX 客户端错误** | `400` Bad request、`401` Unauthorized、`403` Forbidden、`404` Not found、`405` Method not found   |
| **5XX 服务端错误** | `500` Internal server error、`502` Bad gateway、`503` Service unavailable、`504` Gateway timeout |

<Warning>
  **HTTP `200` 不代表业务成功。** 它只说明通讯层面正常——请求送达、被受理、响应回来了。真正的业务结果必须再看两处：

  1. 响应体的 `code`——`0` 才是受理成功，其余按[错误码](#错误码)处理；
  2. `data` 里的业务状态字段，例如 `status`、`fulfillmentStatus`。

  举例：一笔履行失败的订单同样会以 HTTP `200` + `code` 为 `0` 返回，失败信息在 `fulfillmentStatus` 为 `PAY_SUCCESS_SUPPLY_FAILED`、以及 `failureCode` / `failureReason` 里。只看 HTTP 状态码会把它误判成成功。
</Warning>

## API 消息结构

请求与响应消息均为 JSON、UTF-8 编码，由 header 与 body 两部分组成。

分销方（Waffo Point Topup）发给供应商的请求示例：

```json theme={null}
{
    "header": {
        "Content-Type": "application/json",
        "X-SIGNATURE": "..."
     },
     "body": {
         ...
     }
}
```

其中 `X-SIGNATURE` 是 Waffo Point Topup 用自己的私钥对整个 body 消息的签名。

供应商的响应示例：

```json theme={null}
{
    "header": {
        "Content-Type": "application/json",
        "X-SIGNATURE": "..."
     },
     "body": {
         "code": "xxx",
         "msg": "xxx",
         "data": {
              ...
         }
     }
}
```

这里的 `X-SIGNATURE` 是供应商用自己的私钥对整个 body 消息的签名。请求失败时 `data` 为空。

## API 安全

Waffo Point Topup 与供应商之间通过交易签名与验签，保证消息的不可否认性。签名算法为 **SHA256WithRSA**。

<Steps>
  <Step title="供应商入网阶段">
    Waffo 与供应商先交换 RSA 公钥。双方各自保管自己的 RSA 私钥，只把公钥给对方。
  </Step>

  <Step title="API 请求阶段">
    Waffo 用自己的 RSA 私钥对消息签名并发送给供应商。供应商用 Waffo 的公钥验签：验签通过则处理该请求；验签失败则向 Waffo 返回 `Invalid Signature` 错误。
  </Step>

  <Step title="API 响应阶段">
    供应商用自己的 RSA 私钥对响应消息签名并返回给 Waffo。Waffo 用供应商的公钥验签：验签通过则处理该响应。

    **验签失败时，Waffo 会排查并联系供应商，在问题解决前暂停向该供应商发送新交易。** 因为供应商可能已经处理了这笔请求，只是 Waffo 因验签失败没能处理响应。
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/waffo-docs/hUiobY-hbNbq3QYe/images/developer-docs/point-topup/api-security-flow.png?fit=max&auto=format&n=hUiobY-hbNbq3QYe&q=85&s=9eecf22d333d73e82a38dd99093ce1cb" alt="Waffo Point Topup 与供应商之间的 SHA256WithRSA 签名与验签流程" width="3269" height="2902" data-path="images/developer-docs/point-topup/api-security-flow.png" />
</Frame>

## 错误码

错误码前缀分类：

| 前缀      | 分类                                               |
| ------- | ------------------------------------------------ |
| `A`xxxx | 供应商相关错误                                          |
| `B`xxxx | 用户相关错误                                           |
| `C`xxxx | 系统相关错误                                           |
| `D`xxxx | 风控拒绝                                             |
| `E`xxxx | 未知错误。由供应商侧或第三方系统的未知状态导致，需要持续重试该交易，直到收到最终的成功/失败状态 |

| 错误码     | 说明                                                | HTTP 状态码 |
| ------- | ------------------------------------------------- | -------- |
| `0`     | Success                                           | 200      |
| `A0001` | Invalid Api secret key                            | 401      |
| `A0002` | Invalid signature                                 | 401      |
| `A0003` | Parameter validation failed                       | 400      |
| `A0004` | Permission denied                                 | 401      |
| `A0005` | Purchase face value exceeds maximum value allowed | 400      |
| `A0006` | Order does not exist                              | 400      |
| `A0007` | Idempotent param mismatch error                   | 400      |
| `A0008` | Too many requests, please try again later         | 400      |
| `B0001` | Buyer info does not exist                         | 400      |
| `B0002` | Buyer info does not match                         | 400      |
| `C0001` | System error                                      | 500      |
| `C0002` | Order info mismatch error                         | 500      |
| `D0001` | Risk rejection                                    | 406      |
| `E0001` | Unknown status                                    | 500      |

<Warning>
  收到 `E0001` 未知状态时，不要把交易直接判为失败。持续重试查询，直到拿到明确的成功或失败终态。
</Warning>

## 生成 RSA 密钥

供应商可以用 openssl 生成一对 RSA 密钥。

<Steps>
  <Step title="安装 openssl">
    从 [openssl.org/source](https://www.openssl.org/source) 下载并安装 openssl。
  </Step>

  <Step title="生成密钥对">
    ```bash theme={null}
    # 生成私钥
    openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -out supplier_private_key.pem

    # 生成公钥
    openssl rsa -in supplier_private_key.pem -pubout > supplier_public_key.pem
    ```
  </Step>

  <Step title="交换公钥">
    把 `supplier_public_key.pem` 交给 Waffo，并从 Waffo 取得 Waffo 侧的公钥。
  </Step>
</Steps>

<Warning>
  **私钥必须妥善保管，绝不能向任何第三方泄露。**
</Warning>

## 下一步

<CardGroup cols={2}>
  <Card title="Mode B：点卡获取 API" icon="package-search" href="/docs/zh/developer-docs/point-topup/mode-b-point-code-api">
    实时点卡获取接口的请求、响应与幂等要求。
  </Card>

  <Card title="Mode C：即时履行 API" icon="zap" href="/docs/zh/developer-docs/point-topup/mode-c-direct-fulfillment">
    URL 签名跳转、即时履行与履行结果查询。
  </Card>
</CardGroup>
