0. 接口兼容
本卡网框架兼容独角兽卡网(Dujiao-Next)Provider 协议,可按照独角兽卡网协议完成接口认证、商品同步、库存同步、采购下单、订单查询和状态轮询等对接。
同时,本卡网框架提供完备的自定义上下游同步协议,支持同步商品分类、商品信息、SKU、价格、库存、Logo、商品详情、详情图片、发货格式等字段。
未来将继续兼容更多卡网同步协议和 Provider 实现,具体以对应版本的 API 文档为准。
如需部署本卡网或进行上下游接口对接,可联系客服获取部署与技术支持。
1. 接入概览
Nexora Provider API 让本站可以作为上游货源站。下游系统配置 API URL、API Key、API Secret 后,可以同步分类、商品、SKU、库存、价格、图片、发货格式,并在用户付款后向本站创建采购订单。
基础地址示例:
https://your-domain.example.com
API 前缀:
/api/v1/provider
备用前缀:
/v1/provider金额字段统一使用字符串并保留两位小数,例如 "12.30"。当前币种为 CNY,采购余额复用 API Key 绑定用户的积分余额。
2. 认证与签名
所有 Provider API 请求都必须携带认证头:
Dujiao-Next-Api-Key: <api_key>
Dujiao-Next-Timestamp: <unix_timestamp_seconds>
Dujiao-Next-Signature: <signature>
Content-Type: application/json签名算法:
body_md5 = md5(raw_request_body)
sign_string = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + body_md5
signature = hex_lowercase(hmac_sha256(api_secret, sign_string))签名注意事项:
METHOD使用大写,例如 GET、POST。PATH只包含路径,不包含域名和查询字符串。- GET 请求或空请求体按空字节计算 MD5。
- 时间戳允许窗口为 60 秒,请保证服务器时间准确。
- 请求体最大 1MB,超过会返回 413;JSON 请求拒绝重复的顶层字段。
- 同一个签名请求的短期重放会被拦截;创建订单若已存在完全匹配的幂等记录,可只读返回原订单。
import crypto from "node:crypto"
function sign({ method, path, timestamp, body = "", secret }) {
const bodyMd5 = crypto.createHash("md5").update(body).digest("hex")
const signString = `${method.toUpperCase()}\n${path}\n${timestamp}\n${bodyMd5}`
return crypto.createHmac("sha256", secret).update(signString).digest("hex")
}3. 接口列表
| 方法 | 路径 | 说明 |
|---|---|---|
| GET/POST | /api/v1/provider/ping | 连接测试、账户信息、余额 |
| GET | /api/v1/provider/categories | 分类列表 |
| GET | /api/v1/provider/products | 商品列表 |
| GET | /api/v1/provider/products/{id} | 商品详情 |
| POST | /api/v1/provider/orders | 创建采购订单 |
| GET | /api/v1/provider/orders/{id} | 查询采购订单 |
| POST | /api/v1/provider/orders/{id}/cancel | 取消请求,当前处理中的订单返回 409 |
4. 商品同步接口
连接测试
GET /api/v1/provider/ping
{
"ok": true,
"site_name": "Nexora|耐索拉",
"provider_type": "ORION_KEY",
"protocol_version": "1.0",
"instance_id": "nexora",
"user_id": "00000000-0000-0000-0000-000000000000",
"balance": "100.00",
"currency": "CNY",
"member_level": {}
}分类列表
GET /api/v1/provider/categories
{
"ok": true,
"categories": [
{
"id": 1001,
"parent_id": 0,
"slug": "category-1001",
"name": { "zh-CN": "社交账号", "en": "社交账号" },
"sort_order": 0
}
]
}商品列表
GET /api/v1/provider/products?page=1&page_size=50&include_inactive=false
{
"ok": true,
"items": [
{
"id": 2001,
"slug": "product-2001",
"title": { "zh-CN": "示例商品", "en": "示例商品" },
"description": { "zh-CN": "商品简介", "en": "商品简介" },
"content": { "zh-CN": "商品详情 Markdown 或 HTML", "en": "商品详情 Markdown 或 HTML" },
"price_amount": "10.00",
"original_price": "10.00",
"fulfillment_type": "auto",
"manual_form_schema": { "fields": [] },
"is_active": true,
"category_id": 1001,
"delivery_format": "account_password",
"delivery_delimiter": "----",
"logo_url": "https://your-domain.example.com/api/uploads/logo.png",
"images": ["https://your-domain.example.com/api/uploads/logo.png"],
"has_sku": true,
"skus": [
{
"id": 3001,
"sku_code": "1-month",
"spec_values": { "name": "1个月" },
"price_amount": "10.00",
"stock_status": "available",
"stock_quantity": 100,
"is_active": true
}
]
}
],
"total": 1,
"page": 1,
"page_size": 50,
"includes_inactive": false
}下游映射商品时应使用 product.id 和 sku.id,不要依赖商品名。列表的 page_size 默认 50、最大 100。fulfillment_type 决定支付后的履约策略,manual_form_schema 是商品级真实表单定义,无表单时返回 {"fields":[]}。商品详情接口 GET /api/v1/provider/products/{id} 返回结构与列表中的单个商品一致。
5. 采购订单接口
创建订单会按商品的 fulfillment_type 执行:auto 本地自动发货,manual 等待人工履约,upstream 创建采购任务并由 Worker 调用上游。Provider API 当前使用绑定用户积分扣款,支付方式不等于履约方式。
POST /api/v1/provider/orders
Content-Type: application/json
{
"downstream_order_no": "D202608210001",
"sku_id": 3001,
"quantity": 1,
"trace_id": "trace-abc-001",
"manual_form_data": {}
}| 字段 | 必填 | 说明 |
|---|---|---|
| downstream_order_no | 是 | 下游订单号,同一个 API Key 下必须唯一,最长 128 |
| sku_id | 是 | Provider SKU ID |
| quantity | 是 | 购买数量,范围 1 到 1000000 |
| trace_id | 否 | 下游链路追踪 ID,最长 128 |
| manual_form_data | 否 | 按商品 schema 校验/规范化并参与幂等摘要;有 schema 时保存并透传上游,不能只保存 hash |
callback_url 以及其他未定义的顶层字段不被接受。请求体必须是 JSON 对象;重复顶层 key 会返回 invalid_json。
表单规则按商品类型执行:manual 会校验 schema、required、未知字段、类型、长度、正则、数值范围和 options;auto 以及无 schema 的 upstream 会将表单归一化为空对象;有 schema 的 upstream 会保存并透传规范化后的表单。文本值会 trim 并进行 HTML escape,checkbox 会 trim、去重并排序。
唯一键是 credential_id + downstream_order_no。相同下游订单号重复请求时,如果 SKU、数量、trace_id 及规范化后的 manual_form_data 完全一致,则返回原订单;如果参数不同,返回 409 idempotency_conflict,不重复扣款、不重复发货。表单值可能含账号、邮箱、UID 或授权码,日志和管理列表应脱敏。
{
"ok": true,
"order_id": 9001,
"order_no": "2026082112345678",
"downstream_order_no": "D202608210001",
"status": "processing",
"amount": "10.00",
"currency": "CNY",
"trace_id": "trace-abc-001",
"error_message": null,
"items": [
{
"product_id": 2001,
"sku_id": 3001,
"title": { "zh-CN": "示例商品", "en": "示例商品" },
"quantity": 1,
"unit_price": "10.00",
"total_price": "10.00"
}
]
}创建接口先保存本地订单、积分扣款和履约任务,再返回 processing;不会在请求内等待上游发货。订单状态包括 processing、delivered、failed。查询订单使用返回的数值型 order_id,只能查询当前 API Key 创建的订单。
已发货响应中的 fulfillment
"status": "delivered",
"fulfillment": {
"type": "card_key",
"status": "delivered",
"payload": "account@example.com----password",
"delivery_data": ["account@example.com----password"]
}fulfillment 只在已保存交付结果时返回;可能是 card_key、apple_account 或 provider_delivery。上游采购成本不会覆盖响应中的本地销售金额。
6. 轮询与状态查询
采购链路不接收或发送 Provider callback,也不接受或保存 callback_url。创建订单后返回 processing,下游应使用已返回的数值型订单 ID 定时调用查询接口,直到 delivered 或 failed。内部的 UNKNOWN_REVIEW 对外仍映射为 processing。
GET /api/v1/provider/orders/{id}
{
"ok": true,
"order_id": 9001,
"order_no": "2026082112345678",
"downstream_order_no": "D202608210001",
"status": "processing",
"amount": "10.00",
"currency": "CNY",
"trace_id": "trace-abc-001",
"error_message": null,
"items": [
{
"product_id": 2001,
"sku_id": 3001,
"quantity": 1,
"unit_price": "10.00",
"total_price": "10.00"
}
]
}轮询间隔应采用有限退避,不能重复创建订单或重新提交相同采购。若创建响应丢失,应使用相同的 downstream_order_no 和完全相同的参数重试创建请求;不要生成新的订单号。达到上游最大等待时间后,内部任务会进入人工复核,但 Provider 查询仍返回 processing。
7. 错误码
| HTTP | error_code | 说明 |
|---|---|---|
| 400 | bad_request | 请求参数错误 |
| 400 | invalid_json | JSON 重复顶层字段 |
| 401 | unauthorized | API Key、时间戳或签名错误 |
| 401/409 | replay_detected | 重复使用同一个已处理的签名请求 |
| 403 | points_disabled | 本站积分扣款能力未启用 |
| 404 | product_not_found | 商品详情不存在 |
| 400 | sku_unavailable | SKU 不存在或不可售 |
| 400 | insufficient_stock | 库存不足 |
| 400 | insufficient_balance | 余额不足 |
| 409 | idempotency_conflict | 同一下游订单号重复提交但参数不一致 |
| 413 | request_too_large | 请求体超过 1MB |
| 429 | rate_limited | Provider API 触发限流 |
| 500 | internal_error | 服务端内部错误 |
8. 下游同步建议
- 映射键使用
source_type=ORION_KEY、external_product_id=product.id、external_sku_id=sku.id。 - 上游价格上涨且高于本地售价时,应自动抬价到不亏损;上游价格下降时,不自动降低本地售价。
- 库存按
stock_quantity同步,最终能否发货以采购接口结果为准。 - 如果下游启用同步上下架,按上游
is_active复用本地上架/下架行为。 - 图片建议首次导入或管理员恢复默认图时下载到下游本地存储,普通定时同步不要重复下载未变化图片。
- 保存
delivery_format和delivery_delimiter,用于下游解析和展示卡密。
母站管理员关闭图片暴露后,Provider API 的 logo_url 返回空字符串,images 返回空数组,content 中的 Markdown 图片和 HTML img 会被移除。该开关不影响母站自己的前台展示。
9. 上线前检查清单
- API Key 已启用,Secret 未泄露
- 下游服务器时间误差小于 60 秒
- 创建订单必须传 downstream_order_no
- 同一下游订单号重试参数必须一致
- processing 状态不要换新订单号反复采购
- 不要发送 callback_url 或未定义字段
- 正确处理 processing、delivered、failed 三种公开状态
- 生产多实例使用共享 Redis,并配置明确的故障策略
- 下游售价不得低于上游成本
- 日志不打印 Secret、完整签名头和卡密明文
- 请求体控制在 1MB 内
当前实现边界:公共取消接口对已进入处理流程的订单返回 409,不提供独立 API 客户余额体系;不提供 IP 白名单,但会按 API Key 和 IP 维度限流;使用短期 replay 防护;金额币种为 CNY;商品详情内容按管理员填写内容透传,可能是 Markdown,也可能包含 HTML。
