API 接口文档

Card Consume API · v1 · HMAC

📘
基础信息
v1.0 · 2026-09-16

发卡宝 API v1 只有两个接口:幂等核销(consume)与对账查询(query)。

Base URLhttps://www.fakabao.net
统一入口POST /api/index.php
GET /api/index.php
独立入口(等价)POST /api/v1/consume.php
GET /api/v1/query.php
鉴权HMAC-SHA256 三签名头
频率限制60 次/分钟/Key · 120 次/分钟/IP
Key / Secret用户中心 → API 设置(重置 24h 一次)
老协议已下线(2026-09-16):body 传 apikey 或 method=check/setUsed 的请求一律返回 410 GONE,未升级的集成请尽快切换到本页协议。
🔐
HMAC 签名鉴权
每个请求必带三个 Header
Header说明
X-API-Key商户 API Key(身份标识,重置后同步变化)
X-TimestampUnix 秒级时间戳,与服务端偏差 ≤ ±300 秒;只取一次,签名与发送必须同值
X-Sign按下述规则计算的签名(小写 hex),服务端常量时间比较
签名规则
POST: Sign = HMAC_SHA256( Secret, Timestamp + "." + 请求体原文(raw body) )
GET : Sign = HMAC_SHA256( Secret, Timestamp + "." + URL查询串原文(QUERY_STRING) )

要点:
1. POST 参与签名的是"实际发出的字节"——先 json_encode 得到 $body,签名用 $body,发送也用 $body,中间不得重排/格式化;
2. GET 的查询串自行拼接(如 http_build_query 的结果),签什么就发什么,避免 curl/代理二次编码改变顺序;
3. 签名失败返回 401 INVALID_SIGN,可用示例 Demo 对照排查。
🧾
幂等与对账原则(必读)
规则说明
clientTxnId 每单唯一使用 SDK 的 FakabaoClient::newTxnId() 自动生成(格式 时间_随机),并先写入你自己的订单表再发请求;自行生成请保证随机且唯一(勿嵌卡号,单号会进日志)。
重试必须复用同一个 clientTxnId超时/断网属于“结果未知”:拿原单号重发 consume(命中重放语义)或调 query 确认。禁止换新单号重试同一张卡,否则失去幂等保护
重放返回同一结果同单号重复请求返回 replay:true 且 pid 与首次一致,不会二次扣卡;同单号换卡号请求被拒(INVALID_PARAM)
一卡一销平台唯一台账;卡被别的单烧掉后,你的新单收到 ALREADY_USED
接入方本地订单三态推荐 pending / success / failed:发放权益与订单翻 success 在同一个本地事务内原子完成(只允许翻转一次);“结果未知”保持 pending,由你的定时任务 query 收敛
补发唯一依据query 返回 usedTxn == 自己的单号 才可补发;不等 = 卡经其他渠道消耗,严禁发放,转人工
🚥
错误码总表
统一响应包 {code,data} / {code,err,msg}
errHTTP含义接入方动作
NOT_FOUND404卡密不存在终态失败,提示换卡
ALREADY_USED409卡已被核销(含被别的单烧掉)终态失败,提示换卡
REFUNDED409该核销单已被客服退款,拒绝重放终态失败
INVALID_PARAM400参数缺失/非法,或单号与卡号不符修正后重试(未烧卡,安全)
INVALID_AUTH / INVALID_KEY / INVALID_SIGN / INVALID_TIMESTAMP401鉴权头缺失 / Key不存在 / 签名错 / 时间偏差>10min检查 Key、Secret、服务器时钟(官方 SDK 内置自动对时自愈,无需人工干预);校时后用原单号重试
ACCOUNT_DISABLED / API_DISABLED / IP_DENIED403账号或 API 被禁用 / IP 不在白名单联系管理员
RATE_LIMITED429触发限流退避后原单号重试
BUSY / 5xx / 超时 / 非JSON响应500结果未知(不是失败!)订单保持 pending,返回用户“处理中”;稍后 query 对账收敛,不得判死
GONE410调用了已下线的老协议升级到 v1
POST
1. 卡密核销 consume(唯一写接口)

行锁原子操作:校验 + 登记台账 + 标记已用一次完成,是整条链路唯一需要同步等待的调用(建议连接 3s / 总 6s 超时)。幂等:同 clientTxnId 重放返回相同结果。

请求参数(JSON Body)
参数必填说明
method是*固定 "consume"(*独立入口 /api/v1/consume.php 无需 method 字段)
cardNo是卡号(本平台 17 位:ABCDE-FGHIJ-KLMNO)
clientTxnId是接入方订单号,字符集 A-Za-z0-9_.:-,长度 1–64,同一商户内唯一,重试复用;推荐用 SDK newTxnId() 生成
POST /api/index.php
Content-Type: application/json
X-API-Key: 9ffa60a9c4aa4eb512312d680e2974dc
X-Timestamp: 1789502400
X-Sign: hmac_sha256(secret, "1789502400." + 请求体原文)

{"method":"consume","cardNo":"BQ7WK-T9U4W-NGRTJ","clientTxnId":"20260916032156_3f9a27de01c8b4f5"}
响应示例 / 处理
首次核销成功{"code":1,"data":{"pid":38,"replay":false}}
同单号重放{"code":1,"data":{"pid":38,"replay":true}}(pid 与首次一致)
pid 为发卡宝商品ID(卡种),到账换算由接入方映射(例:38=30天、39=90天、40=永久)。收到映射外的未知 pid:卡已核销,绝不可判死,转人工核对发放。
GET
2. 对账查询 query(只读)

查询核销台账,供超时收敛、定时对账、人工补单前核对。先按 clientTxnId 查,未命中自动回落按 cardNo 查(并可追溯卡表兜底),单参数也可查,两个都带最稳。

参数(querystring)必填说明
clientTxnId二选一核销时携带的接入方订单号
cardNo二选一卡号
GET /api/index.php?clientTxnId=20260916032156_3f9a27de01c8b4f5
(签名原文 = Timestamp + "." + "clientTxnId=20260916032156_3f9a27de01c8b4f5")

已核销:{"code":1,"data":{"used":true, "pid":38,"usedTxn":"20260916032156_3f9a27de01c8b4f5","usedAt":"2026-09-16 03:21:56"}}
未核销:{"code":1,"data":{"used":false,"pid":0, "usedTxn":"","usedAt":""}}
补发唯一依据:used==true && usedTxn==本单 → 允许执行本地发放(只发一次);usedTxn≠本单 → 他渠道消耗,禁止发放转人工;used==false 且订单已超时(如>30分钟)→ 可安全置失败,允许用户复用原单号重新提交。
🔁
推荐充值流程

平台不提供自动回滚(卡状态单调向前)。“本地没办成”的兜底不是退卡,而是本地重试发放。

1 本地落单 pending(生成 clientTxnId,落库成功后才发请求)
2 POST consume(唯一同步阻塞,1 次 HTTP)
   ├─ code=1 → 3
   ├─ NOT_FOUND / ALREADY_USED / REFUNDED → 订单 failed,提示换卡
   └─ 超时 / 429 / BUSY / 5xx / 鉴权错 → 保持 pending,返回"处理中"
3 本地事务:按 pid 发放权益 + 订单翻 success(同一事务,原子只翻转一次)
4 定时任务(每分钟):扫 pending 超时单 → GET query
   ├─ used=false 超30分钟 → failed(用户可复用原单号重提)
   ├─ used=true  usedTxn==本单 → 补执行第3步(自愈)
   └─ used=true  usedTxn!=本单 → 转人工,禁止发放
5 客服通道:误核销人工 refund,卡恢复可用;你的历史单号再重放会收到 REFUNDED
🐘
PHP 调用 Demo
零依赖单文件 · PHP ≥ 8.0
<?php
require 'FakabaoClient.php';   // 下载见下方「官方 PHP SDK」

$c    = new FakabaoClient('https://www.fakabao.net', '你的KEY', '你的SECRET');
$card = 'ABCDE-FGHIJ-KLMNO';

$txn = FakabaoClient::newTxnId();   // 自动生成,无需商户号
// TODO 先把 $txn 以 pending 写入你自己的订单表,再发起核销(重试/对账复用,禁止重新生成)

try {
    $r = $c->consume($card, $txn);   // ['pid'=>38,'replay'=>false]
    // TODO 本地事务:按 $r['pid'] 发放权益 + 订单翻 success(同一事务,只翻转一次)
} catch (FakabaoException $e) {
    if ($e->isRetryable()) {
        // TIMEOUT/BUSY/RATE_LIMITED/BAD_RESPONSE/5xx:保持 pending,
        // 前端文案"处理中,1分钟后自动到账",定时任务调 query 收敛
    } else {
        // NOT_FOUND / ALREADY_USED / REFUNDED:终态失败,订单置 failed,提示换卡
    }
}

// 定时任务:对账 pending 超时单
$q = $c->query($card, $txn);         // ['used','pid','usedTxn','usedAt']
if ($c->isUsedByMe($card, $txn)) { /* 唯一允许自动补发的情况 */ }
?>
📦
PHP SDK
零依赖单文件 · PHP ≥ 8.0

已封装签名、超时语义、错误分类与补发核对,含充值闭环 + reconcile 模板。

⬇ 下载 PHP SDK fakabao-sdk-php.zip

❓
常见问题
问题回答
一直 401 INVALID_SIGN?三点排查:① POST 签的是请求体原文(encode 后不许再改动);② GET 签的是 QUERY_STRING 原文(自行拼接,别让库改写);③ Secret 重置后未同步
INVALID_TIMESTAMP?时钟偏差超 10 分钟。官方 SDK 内置自动对时自愈,会自动对齐服务端重发;自研接入者校准 NTP 后用原单号重试即可,不会重复扣卡
consume 超时,卡到底烧没烧?未知,别猜。等1分钟拿原单号 query:used=false → 可安全重发;used=true 且 usedTxn=自己 → 直接补发放。也可用同一单号再 consume,命中重放返回相同结果
能回滚已核销的卡吗?API 不提供回滚(防“退款重烧”双花)。误核销走客服人工 refund:卡恢复可用,但旧单号重放会收到 REFUNDED
用户重复提交同一张卡?你侧落订单表并复用同一 clientTxnId;即便发了不同单号,平台一卡一销兜底,败者收 ALREADY_USED
还在用老 apikey/setUsed 代码?老协议已 410 下线,请尽快升级;怀疑泄漏的旧 Key 请在商户中心重置作废