优卡中控开放平台文档
V1.0

开放接口接入文档

连接您的业务系统,完成商品同步、卡密采购与账号直充。

经销商 / 合作方系统 V2026.10.22.3更新于 2026-10-22
HTTPSJSON / UTF-8HMAC-SHA256金额单位:分
接口网关地址(Base URL)
https://www.k8s2024.com/open/v1

系统接入域名:www.k8s2024.com。请使用上述网关地址调用接口。

完整请求示例:GET https://www.k8s2024.com/open/v1/account

下文接口路径已包含 /open/v1;按完整路径拼接域名时,不要重复添加这个前缀。

文档概述

本文用于合作方系统连接优卡中控,完成账户余额查询、商品同步、卡密采购、账号直充、订单查询和结果通知。调用使用经销商 API 身份,订单按该经销商的授权范围及结算价格从预存余额扣款。

接口一览

项目说明
文档版本V1.0 · 2026年10月22日
适用系统V2026.10.22.3
协议版本system-supply-v1
接口网关地址https://www.k8s2024.com/open/v1
接入资料平台提供正式或联调域名、经销商 API Key、签名密钥及商品授权
技术约定HTTPS · UTF-8 · JSON · HMAC-SHA256

先完成身份开通和签名验证,再读取账户及商品,最后联调下单、查单和回调。本文示例均使用演示商品、订单及密钥;接入时替换为平台发放的数据。域名占位符不是可调用地址。

本协议用于合作方调用本站。京东专用接单协议、供货商采购协议及管理后台接口各自独立,不能混用地址、签名或回执格式。

接入准备与请求签名

经销商须已审核通过、账号启用并开通 API,且取得可卖卡种授权。平台暂停经销商访问时,API 同时停止接入。登录密码不用于接口签名。

请求头填写规则
X-Api-Key32 位接口账号,由平台发放
X-Timestamp10 位 Unix 秒时间戳,与平台时间差不超过 300 秒
X-SignatureHMAC-SHA256 结果,64 位小写十六进制
Acceptapplication/json,所有请求均建议携带
Content-TypePOST 请求使用 application/json

签名原文

TEXT
canonical = timestamp + "\n" + METHOD + "\n" + path + "\n" + rawBody
signature = HMAC_SHA256(secret, canonical)  // 小写十六进制

METHOD 使用大写 GET 或 POST。path 包含 /open/v1 前缀,不含域名和查询字符串。换行是单个 LF 字节,不是字面量反斜杠加 n。GET 不发送正文,rawBody 为空;POST 按实际发送的 UTF-8 原始字节签名,签名后不得再次序列化或更改空格。

TEXT
GET 请求 URL:/open/v1/products?page=2
签名使用 path:/open/v1/products
POST 请求 path:/open/v1/orders

密钥轮换与网络限制

A、B 两组签名密钥均可验证入站请求。轮换时先切换到另一组可用密钥,再重置旧密钥;API Key 不变。密钥只保存在服务器端,不放入网页、客户端安装包或日志。

IP 白名单为空时允许通过验签的任意来源;配置后按明确的 IPv4 或 IPv6 地址匹配,不支持 CIDR。请提供实际调用服务器的公网出口 IP。

防重复规则

签名在时间窗口内可以重复验证,不属于一次性凭据。交易防重依靠 request_id:同一经销商、同一 UUID、相同业务参数返回原订单;修改参数后复用 UUID 会拒绝。每次重试可更新签名时间,但必须保留原 request_id 和原下单参数。

查询账户与商品目录

2.1 查询账户

TEXT
GET /open/v1/account

无业务请求参数。返回当前接口经销商的账户信息,不返回其他经销商账户。

字段类型说明
protocol_versionstring固定 system-supply-v1
balance_centsinteger可用余额,单位分
frozen_centsinteger冻结金额,单位分
JSON
{"protocol_version":"system-supply-v1",
 "balance_cents":100000,"frozen_cents":0}

2.2 查询商品目录

TEXT
GET /open/v1/products?page=1
查询参数类型说明
pageinteger可选,默认第 1 页,每页固定 20 条
qstring可选,最多 150 字符,搜索名称、编码或 ID
search_bystring可选,all / title / sku / id,默认综合搜索
product_idinteger可选,精确查询平台商品 ID,最小为 1
categorystring可选,最多 80 字符,按分类名称筛选;一级包含其二级

仅返回当前经销商获准、平台已上架且经销商分销启用的商品。商品下架、分类隐藏或授权撤销后可能从目录消失;同步时必须处理已移除商品。

分页约定

响应直接是分页对象,无统一 code/data 成功包装。data 是本页商品数组;current_page、last_page、per_page、total 表示当前页、总页数、每页数量和总条数。读取至 last_page 为止,不要只同步第一页。查询结果为空时 data=[]。

商品字段与价格库存

商品字段类型含义
idinteger本站商品 ID,下单 product_id 使用此值
title / skustring商品名称 / 商品编码;不是上游商品 ID
categorystring所属分类名称
kindstringcard 为卡密;phone 为直充,包含非话费券包直充
business_typestring业务类型 card 或 recharge
price_centsinteger当前经销商每份结算单价,整数分
stockinteger当前销售库存额度;0 表示不能下单
min_quantity /
max_quantity
integer每单购买份数范围,还受供货及实发数量限制
delivery_multipleinteger每购买 1 份的实发数量,直充必须为 1
delivery_formstringnumber_password / password / link / upstream
recharge_fieldsobject / array附加直充字段,键为提交字段名,值为中文名称;无字段为 []
cover_url / descriptionstring / null商品封面 / 商品说明
aftersales_daysinteger售后展示周期,具体售后由平台处理

响应可能包含额外展示字段,接入方应忽略未知字段。商品目录不返回采购成本或供货账号。下单不要使用展示面值作为结算价格。

价格和数量

price_cents 已按卡种成本、发货倍数和该经销商比例计算,并向上取整到分。接入方直接使用该值,不必自行重算成本比例。订单总额为当前每份单价乘购买份数;实际扣款以订单 total_cents 为准。

quantity 是购买份数,delivery_quantity 是实发数量。例如倍数为 2、购买 3 份,实发 6 张;价格和库存按购买份数计算。实发总数不能超过 9999,还可能受该货源单次数量限制。

stock 是销售额度,不等于物理仓库卡密数,也不是上游库存保证。下单时再次校验额度和履约条件。当前版本返回非负整数;不能将缺失或异常库存解释为无限库存。

提交订单

TEXT
POST /open/v1/orders
Content-Type: application/json

一次提交购买一种商品。接口按当前经销商价格校验并扣减余额,安排履约后返回订单对象。HTTP 成功仅表示接口已返回,是否交付成功仍须检查 status。

字段类型及必填规则
product_idinteger 必填目录中的本站商品 ID
quantityinteger 必填1—9999,符合限购;直充仅为 1
request_idstring 必填合法 UUID,先持久保存再提交;一笔业务一个 UUID
max_total_centsinteger 可选建议必传,总额上限,单位分;范围 0—10000000000
recharge_numberstring 条件必填直充主账号,最多 500 字符;话费为合法手机号。卡密订单省略
recharge_paramsobject 可选按商品 recharge_fields 填写;最多 30 项,每值为非空字符串且最多 200 字符
pickupobject 可选交付方式;普通系统对接省略,使用默认自动交付
supply_chainarray 可选同系统转接专用;普通合作方省略,不自行生成链路标识

直充字段

不要把所有直充都当成手机号充值。根据商品说明填写主账号;附加字段必须使用 recharge_fields 返回的原始键名。列出的字段须填写完整,不得提交未配置的键;无附加字段时省略 recharge_params。数店等商品还会按模板检查账号类型。

金额上限与重试

例如目录单价为 10200 分,购买 2 份,可设置 max_total_cents=20400。若提交时价格上涨至超过上限,接口拒绝该笔请求。不能在超时后修改金额上限继续复用同一 UUID;先查清原单,再决定后续业务。

平台可能存在余额门槛、风控或供货数量限制,满足通用参数范围并不保证一定受理。错误信息以实际响应 message / errors 为准。

下单与商品响应示例

5.1 卡密下单请求

JSON
{
  "product_id": 123,
  "quantity": 1,
  "request_id": "c2fbd360-750b-4e77-9041-9b3bc5344683",
  "max_total_cents": 10200
}

5.2 直充下单请求

以下演示商品要求 region 字段;实际字段名必须来自该商品目录。主账号和附加字段均以字符串发送。

JSON
{
  "product_id": 124,
  "quantity": 1,
  "request_id": "788be8a2-d884-4d1f-8f01-2b27c143b30b",
  "max_total_cents": 20,
  "recharge_number": "13800000000",
  "recharge_params": {"region": "上海"}
}

5.3 商品分页响应节选

JSON
{
  "current_page": 1,
  "data": [{
    "id": 123, "title": "演示卡券100元", "sku": "DEMO100",
    "category": "商超卡", "kind": "card", "business_type": "card",
    "price_cents": 10200, "stock": 9999,
    "min_quantity": 1, "max_quantity": 10,
    "delivery_multiple": 1, "delivery_form": "number_password",
    "recharge_fields": []
  }],
  "last_page": 1, "per_page": 20, "total": 1
}

示例为主要字段节选。金额 10200 表示人民币 102.00 元;20 表示人民币 0.20 元。所有订单号、卡号和密码均按字符串保存,避免丢失前导零或整数精度。

查询订单与状态处理

TEXT
GET /open/v1/orders/by-request/{request_id}
GET /open/v1/orders/{id}

推荐使用原下单 UUID 查单。id 是接口返回的内部订单编号;order_no 是展示订单号,接入系统应分别保存并优先使用内部 id 或 request_id。只能查询当前经销商本人的订单。

字段说明
id / order_no内部订单 ID / 展示订单号,均按字符串保存
request_id原下单 UUID;部分受控交付响应可能不提供,应在本地保留
status / reason履约状态 / 状态或失败说明
product_id / quantity商品 ID / 购买份数
delivery_quantity实发数量,可能与购买份数不同
kind / total_centscard 或 phone / 订单总额,整数分
created_at / completed_at创建 / 完成时间,未完成可为 null
cards / cards_masked卡密数组 / 是否掩码;不得交付掩码内容
aftersales_status / aftersales视响应分支提供售后状态或售后对象,须一并检查
status含义及处理
pending已创建待处理;继续查原单
processing处理中;继续查原单,不重复采购
unknown结果未知;保留原单,等待核实,不按失败重下
success履约成功;直充无需卡密,卡密按提取规则交付
failed履约失败;核对 reason 和资金流水

GET 查单读取当前状态,不等同于立即向上游发起新采购。售后状态与履约状态分别存在,不能仅凭历史 success 再次发出卡密。

网络超时、连接中断、5xx 或查单暂时 404 时,先继续查询原 request_id;404 不代表正在执行的提交必然失败。如需补交,必须复用原 UUID 和原参数。建议查单采用渐进间隔并控制总频率。

卡密结果与显式提取

7.1 普通成功响应节选

JSON
{
  "id": "b2a73d8d-72b6-4bcf-b4e3-321539ad0205",
  "order_no": "20261022100000123456",
  "request_id": "c2fbd360-750b-4e77-9041-9b3bc5344683",
  "product_id": 123, "quantity": 1, "delivery_quantity": 1,
  "kind": "card", "status": "success", "total_cents": 10200,
  "cards": [{"cardNumber":"DEMO0001", "password":"DEMO-PASS",
             "expiryDate":"2027-12-31"}]
}

默认交付且实发不超过 30 张的成功卡密订单通常直接返回明文。直充不返回卡密。卡号、密码和有效期对应 cardNumber、password、expiryDate;无值可能为 "-"。密码或链接型商品需按商品形式和实际字段交付,不得擅自截断内容。

7.2 大批量卡密

实发超过 30 张时,普通查单不直接提供整批明文,返回 cards_masked=true、pickup_required=true,并在可提取时提供 pickup_url 和 machine_pickup_path。提卡链接有效期为 3 天,后续查单可能更新链接。链接属于交付凭据。

TEXT
POST /open/v1/orders/{id}/cards

{"confirmed":true}

按实际 POST 正文签名。仅限本人通过开放 API 创建、未被设为受控交付且当前允许提取的成功卡密订单。返回 id、request_id、order_no、quantity、delivery_quantity、cards、cards_masked=false 和 usage_instructions。此操作不会再次扣款或采购,会记录提取审计。

7.3 交付限制

pending、refunding、全额 refunded 或 refund_failed 等售后限制会阻止提取。部分卡券已退回时,应以提取接口实际返回的剩余可交付卡券为准,不得从旧缓存补回已退款内容。

普通对接建议省略 pickup。主动选择 link、sms、email、secure_archive 等受控交付后,必须走对应交付流程,不能用机器提卡接口绕过。短信和邮箱需平台配置可用发送服务;直充只允许默认自动方式。

订单结果通知

接入方提供独立的公网 HTTPS 接收地址,在经销商 API 权限中的“订单结果通知”保存并启用。地址不得含账号、密码、查询参数或片段,不接受内网及保留地址,不跟随重定向。

平台向该地址 POST JSON,仅通知启用期间完成的开放 API 订单。成功事件为 order.succeeded,失败事件为 order.failed。处理中和未知状态不产生终态通知;未启用期间的历史终态不补发。

通知头说明
X-Api-Key / X-Timestamp /
X-Signature
使用与请求相同的 HMAC-SHA256 签名规则
X-Event-Id事件 UUID,与正文 event_id 一致
X-Signature-Key-SlotA 或 B;优先用当前 A,仅有 B 时用 B
JSON
{
  "event_id": "c04a263f-128a-42b1-bdea-3595fc2a0a31",
  "event": "order.succeeded",
  "user_id": 123,
  "occurred_at": "2026-10-22 10:00:00",
  "order": {
    "id": "b2a73d8d-72b6-4bcf-b4e3-321539ad0205",
    "order_no": "20261022100000123456",
    "request_id": "c2fbd360-750b-4e77-9041-9b3bc5344683",
    "status": "success", "product_id": 123, "quantity": 1,
    "kind": "card", "total_cents": 10200,
    "completed_at": "2026-10-22 10:00:00"
  }
}

失败事件的 event 为 order.failed、order.status 为 failed,其余结构相同。通知不含卡密、提卡链接或采购成本;失败原因通过查单获取。日期时间使用平台配置时区,默认 Asia/Shanghai;签名时间使用 Unix 秒。

通知是订单完成时的历史快照。收到成功事件后,应签名查单并获取当前可交付结果,后续售后仍可能限制提取。

通知验签与确认

9.1 验签步骤

读取原始请求体,核对 X-Api-Key、时间差、签名密钥槽位及事件编号。签名 path 使用接入方所配置的回调路径,而不是 /open/v1/orders。不要先解析再重新编码 JSON 进行验签。

TEXT
例如接收地址:https://partner.example.com/callback/order
canonical = timestamp + "\nPOST\n/callback/order\n" + rawBody

计算 HMAC-SHA256 并使用恒定时间比较。校验正文 event_id 与 X-Event-Id 一致;用 event_id 建立唯一约束,在自己的事务中保存业务处理结果后,再向平台确认。

9.2 成功回执

返回 HTTP 2xx,同时 JSON 正文包含匹配的事件编号和 status=success。只返回 HTTP 200、空正文或纯文本 success / ok 均不算订单通知确认。

JSON
{
  "event_id": "c04a263f-128a-42b1-bdea-3595fc2a0a31",
  "status": "success"
}

9.3 重试和幂等

平台至少投递一次,同一事件可能重复到达。重复已成功处理的 event_id,直接返回同一成功回执,不得重复发货、充值或重新向本站下单。

每轮最多 8 次投递,失败后依次约 1、2、5、10、30、60、180 分钟重试;实际时间受调度影响。每次重试使用当前密钥和新的签名时间,事件 ID 和正文不变。单次连接等待约 20 秒。

达到上限后,可在经销商通知记录中核对并手动重试原事件。已确认事件不能手动重发。密钥轮换后应同步更新接收端。

9.4 配置变更

关闭通知或停用账号/API 会暂停已有待发事件,恢复后继续;关闭期间完成的订单不新建通知。变更接收地址会影响尚未确认事件的后续投递,已发出的请求无法追回。

平台需保持队列与计划任务正常运行。接入方仍应保留查单和对账任务,不能把没有收到通知直接视为下单失败。

商品变更通知

商品通知与订单通知分别配置。平台通常每 5 分钟检查当前经销商可见目录,发生变化后向“商品变更通知地址”发送 POST JSON。通知只提示重新同步,不附带完整商品列表。

字段类型说明
eventstring固定 catalog.changed
user_idinteger当前经销商用户 ID
revisionstring64 位 SHA-256 目录版本
timestampinteger通知生成时间,Unix 秒
JSON
{
  "event": "catalog.changed",
  "user_id": 123,
  "revision": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "timestamp": 1792627200
}

签名及回执

请求头为 X-Api-Key、X-Timestamp、X-Signature,原文使用接收地址的路径和原始正文。商品通知当前只使用密钥 A,只有 B 密钥的账号不会发送商品通知。

接收方验签后,应按 user_id + revision 去重,可靠保存同步任务再确认;重新分页读取 /products 并处理新增、修改和移除。成功返回纯文本 success 或 ok,去除首尾空白后必须完全一致。未确认通常约 5 分钟后重试。

TEXT
HTTP 200
Content-Type: text/plain

success

两类回执不能混用

项目订单结果通知商品变更通知
去重键event_iduser_id + revision
签名密钥优先 A,仅有 B 时使用 B仅 A
成功回执JSON:event_id + status=success纯文本 success 或 ok
接收后的动作查原单并取得当前可交付结果重新分页同步目录

接收地址建议分开,例如 /callback/order 与 /callback/catalog。不要只凭 URL 或 JSON 中的 event 值信任通知,必须完成签名校验。

异常处理与联调验收

HTTP 状态含义接入方处理
401账号、时间或签名失败核对 API 状态、密钥和签名原文
403IP 或访问权限不允许核对出口 IP、白名单及访问开关
404记录不可见或原单暂未查到核对本人订单;超时场景继续查原 UUID
422参数或业务条件不满足读取 message / errors,不盲目重试
429触发限流降速、退避,读取 Retry-After 如有返回
5xx / 超时服务或网络结果不明确保留原交易,查单后再决策

网关配置每分钟 120 次请求限制,共享给该鉴权账户的相关调用。请为查单、目录和提卡统筹频率,避免并发轮询挤占下单额度。

JSON
{"message":"接口签名不正确"}
{"message":"请求数据校验失败",
 "errors":{"quantity":["下单数量不符合商品限购范围"]}}

以上为错误结构示意,具体文案可能变化,不应把中文 message 当固定错误码。协议未定义统一数值业务错误码,也没有公开取消或退款接口;售后联系平台处理。

接入验收清单

检查项通过标准
身份与签名账户查询成功;错误密钥、过期时间及非白名单请求被拒绝
目录与价格完整读取分页;正确处理授权、上下架、售价和库存变化
卡密与直充各用平台指定测试商品验证;直充字段完整且数量为 1
重复提交同 UUID 同参数只产生一笔订单和一次扣款
超时恢复模拟丢失响应后按原 UUID 找回订单,不生成新采购
交付与通知能处理批量提卡、重复通知及两类不同回执
金额与对账订单总额、余额变化和最终失败退款结果一致

联调下单会使用该经销商余额,应由平台提供测试账号、测试商品和限额。上线前确认实际域名、账号、出口 IP、回调地址及密钥轮换联系人。

签名校验向量

以下固定数据仅用于离线核对不同语言的签名实现,不能直接用于在线请求,固定时间戳会过期。密钥为演示值,不对应任何账户。

参数值
secretdemo-secret-not-for-production
X-Timestamp1792627200
METHODPOST
path/open/v1/orders

原始 JSON 正文

下方为参与签名的单行原文,可直接复制。不要在正文末尾添加换行。

JSON
{"product_id":123,"quantity":1,"request_id":"c2fbd360-750b-4e77-9041-9b3bc5344683","max_total_cents":10200}

预期签名

TEXT
91b9876e54560e80c397325b8011f8e2f9f7ea85eb4464bf82a5f51f4c505064

PHP 离线校验

PHP
<?php
$secret = 'demo-secret-not-for-production';
$body = '{"product_id":123,"quantity":1,'
      . '"request_id":"c2fbd360-750b-4e77-9041-9b3bc5344683",'
      . '"max_total_cents":10200}';
$canonical = "1792627200\nPOST\n/open/v1/orders\n".$body;
$actual = hash_hmac('sha256', $canonical, $secret);
$expected = '91b9876e54560e80c397325b8011f8e2f9f7ea85eb4464bf82a5f51f4c505064';
if (!hash_equals($expected, $actual)) {
    throw new RuntimeException('签名校验失败');
}
echo "signature OK\n";

常见差异

时间戳使用毫秒、path 带域名或查询串、换行变成 CRLF、正文末尾多出换行、签名后重新格式化 JSON、把 HMAC 当普通 SHA256,都会导致验签失败。查询参数本身不进入当前签名原文。

PHP 服务器调用示例

示例使用 PHP 8 和 cURL。API_BASE 填 https://www.k8s2024.com,不带 /open/v1;API_KEY 与 API_SECRET 从安全配置读取。以下只调用账户查询,未自动采购商品。

PHP
<?php
function callApi(string $method, string $path, string $body = '',
                 string $query = ''): array {
    $base = rtrim((string)getenv('API_BASE'), '/');
    $key = (string)getenv('API_KEY');
    $secret = (string)getenv('API_SECRET');
    if (!str_starts_with($base, 'https://') || !$key || !$secret) {
        throw new RuntimeException('请配置 HTTPS 域名和接口凭据');
    }
    $ts = (string)time();
    $raw = $ts."\n".$method."\n".$path."\n".$body;
    $sign = hash_hmac('sha256', $raw, $secret);
    $ch = curl_init($base.$path.($query === '' ? '' : '?'.$query));
    curl_setopt_array($ch, [
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 60,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_HTTPHEADER => [
            'Accept: application/json',
            'Content-Type: application/json',
            'X-Api-Key: '.$key, 'X-Timestamp: '.$ts,
            'X-Signature: '.$sign,
        ],
    ]);
    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
    }
    $response = curl_exec($ch);
    $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $failed = $response === false;
    curl_close($ch);
    if ($failed) {
        throw new RuntimeException('连接未确认,交易须查原单');
    }
    return ['http_status' => $status,
            'body' => json_decode($response, true, 512,
                                  JSON_THROW_ON_ERROR)];
}
$result = callApi('GET', '/open/v1/account');
// 判断 http_status 后处理 body;不要输出密钥或卡密到日志。

POST 时先生成并保存 request_id,将参数编码一次得到 $body,再用同一字符串签名和发送。自行实现查单及重试策略,不要在网络异常时自动生成新 UUID。

↑