# 跨境无忧代理 API v2

基础域名：`https://fb.payfb.cn`

本文档描述跨境无忧自己的代理 API。请求路径、认证参数、主要字段和错误码遵循本平台开放 API v2 兼容规范；商品、价格、钱包和订单均为本平台数据。

## 认证与签名

每个请求在 Query 中携带：

- `api_key`：API 中心显示的 Client ID。
- `timestamp`：UNIX 秒，与服务器时间相差不超过 5 分钟。
- `nonce`：8–32 位随机字符串，同一密钥下不得重复。
- `signature`：小写十六进制 HMAC-SHA256。

签名原文：

```text
{HTTP_METHOD}\n{timestamp}\n{nonce}\n{request_path}\n{sorted_business_params}
```

Method 必须大写，Path 不包含 Query。业务参数按键名升序排列，用 PHP `http_build_query` 默认编码生成参数串（空格为 `+`）。不要把四个认证参数加入业务参数串。

## 响应结构

```json
{"code":1,"message":"Success","data":{},"timestamp":1784160000}
```

`code=1` 表示成功。请根据 JSON `code` 判断业务结果，不要仅依赖 HTTP 状态码。

## 接口

### 分类

`GET /api/v2/categories`

返回本地分类 `id`、`name`、`count` 和 `image`。

### 商品列表

`GET /api/v2/products`

可选业务参数：`lang`、`cate_id`、`keyword`。返回本地商品和 SKU ID、合作方售价与本地可售库存，不包含上游 ID、成本或上游余额。

### SKU 详情与库存

`GET /api/v2/products/sku?sku_id=1001&lang=zh-cn`

`sku_id` 必填。返回 `price_cny`、`stock`、`has_race`、`shared_min_price`、`shared_max_price` 和 `race_list`。当前本平台未开放多规格时，`has_race=0`且 `race_list=[]`。

### 钱包余额

`GET /api/v2/account/balance`

返回 `balance`、`freeze_balance`、`currency=CNY` 和 `total_consume`，均为本平台钱包数据。

### 创建订单

`POST /api/v2/order/create`

Content-Type 使用 `application/x-www-form-urlencoded`。

| 参数 | 必填 | 说明 |
|---|---:|---|
| `sku_id` | 是 | 本地 SKU ID |
| `quantity` | 是 | 1–1000，同时受密钥商品权限和数量限额约束 |
| `sku_race_id` | 条件 | `has_race=1` 时必填 |
| `remark` | 否 | 最长 500 字符 |
| `request_no` | 建议 | 唯一业务号，1–64 位，仅字母、数字、下划线和短横线 |
| `response_mode` | 否 | 可传 `strict` |
| `notify_url` | 否 | 必须与 API 中心已启用的公网 HTTPS Webhook 完全一致 |

示例业务参数：

```text
sku_id=1001&quantity=1&request_no=PARTNER-20260716-001&response_mode=strict
```

同一会员账户重复提交同一 `request_no` 只返回原订单，不重复冻结、采购或发卡。下单时先冻结本地钱包；发卡后转消费；上游明确失败时解冻；上游结果不确定时保留冻结并只查单，不重复创建。

### 查单与取卡

`GET /api/v2/order/query`

优先传 `request_no`；只有未使用 `request_no` 时才传本地 `order_no`。已发货返回 `status=shipped`、`delivery_content`、`secret` 和 `delivery_count`。完整卡密只在已发货查询响应中返回，请加密保存，禁止写入普通日志。

## Webhook

发货后异步发送 `order.shipped` 状态通知。请求头包含 `X-Event-Id`、`X-Timestamp` 和 `X-Signature`，签名原文为 `timestamp + "." + raw_body`。回调正文不包含卡密、上游订单号或成本；收到后用原 `request_no` 查单取卡。平台仅把 HTTP 200 视为成功，其他结果按退避策略重试。

## 错误码

- `1`：成功
- `1100`：参数错误
- `1200`：认证、Nonce、IP 或权限错误
- `1203`：签名验证失败
- `1300`：库存不足
- `1301`：本地钱包余额不足
- `1303/1304`：SKU 不存在或已下架
- `1305/1306/1307`：版本或规格错误
- `1400`：请求过于频繁
- `1500`：系统或依赖异常

排查时可提供时间、路径和 `request_no`，不要提供 API Secret、完整签名或卡密。

