开发文档
接入第三方充值 / 发卡 API
最近更新:2026-10-08
一句话总结
默认走内置 Mock 供应商。接真实上游:填环境变量,重启,不改代码。
1. 上游只需要提供五种能力
| 能力 | 对应方法 | 用途 |
|---|---|---|
| 拉目录 | pullCatalog() | 同步价格、库存、缺货状态 |
| 查库存 | fetchStock(ids) | 下单前校验 |
| 下单 | createOrder(req) | 带幂等键,同步返回或异步发货 |
| 查单 | queryOrder(id) | Webhook 之外的兜底轮询 |
| 回调 | verifyWebhook() | 异步交付结果的主路径 |
2. 环境变量
UPSTREAM_ENABLED=true
UPSTREAM_ADAPTER=rest
UPSTREAM_BASE_URL=https://api.supplier.example
UPSTREAM_API_KEY=xxxxxxxx
UPSTREAM_API_SECRET=yyyyyyyy
UPSTREAM_SIGNATURE=hmac_sha256 # none|bearer|header_key|query_key|hmac_sha256|md5
UPSTREAM_PATH_CATALOG=/api/products
UPSTREAM_PATH_CREATE_ORDER=/api/order/create
UPSTREAM_PATH_QUERY_ORDER=/api/order/query
UPSTREAM_MAPPING_JSON='{"itemsPath":"data.list","fields":{"externalId":"id","title":"name","price":"price","stock":"stock","soldOutFlag":"sold_out"},"order":{"orderIdPath":"data.order_no","contentPath":"data.content"}}'
3. 签名做了什么
hmac_sha256 策略:把请求体按参数名排序拼成 k1=v1&k2=v2,尾部拼上 API Secret 再取摘要。这是国内绝大多数平台的通行做法,md5 是老平台的变体。
Webhook 验签采用 constant-time 比较,并校验时间戳容忍窗口,防止重放。
4. 字段映射怎么填
itemsPath:响应里数组所在的路径,例如data.list。fields.externalId:商品唯一标识,会与站内商品的upstream_ref对齐。soldOutFlag/soldOutWhen:不同平台表达缺货的方式五花八门(布尔、字符串、库存为 0),用这两个字段统一。order.contentPath:交付内容所在路径,字符串按行拆成多条凭证,数组按元素拆。
5. 换一家有多快
如果新平台的接口形态仍在这五个能力内:只改环境变量,代码零改动。
如果它比较另类(比如 SOAP、私有二进制协议、需要登录态会话):新增一个实现 UpstreamAdapter 的文件,在工厂里注册,其余层完全无感。
6. 自检
GET /api/upstream/health
返回当前生效的配置摘要、健康状态、目录条数。接新供应商后先看这个接口。
7. 稳定性策略
- 超时 + 退避重试:默认 10s 超时,最多重试 2 次。
- 下单不激进重试:避免重复发货,只重试 1 次。
- 目录缓存:默认 60s。上游抖动时返回上一次结果并标记
stale,页面不白屏。 - 幂等键:
订单号-行号,同一笔重复提交只产生一个上游单。
8. 代码位置
lib/server/upstream/types.ts # 接口契约(五种能力)
lib/server/upstream/mock-adapter.ts # 本地供应商
lib/server/upstream/rest-adapter.ts # 通用 REST 适配器
lib/server/upstream/index.ts # 门面:缓存、降级、错误处理
lib/server/env.ts # 全部配置项
lib/server/http.ts # 超时/重试/幂等
lib/server/signature.ts # 签名与 Webhook 验签
app/api/upstream/health/route.ts # 自检接口
app/api/upstream/webhook/route.ts # 异步回调入口