四海通 下游代理商 API 文档

最新版本静态文档 · 由 Markdown 自动生成

四海通 下游代理商 API 文档

版本:v1.4
更新:2026-03-18
Base URL:https://mama.best

一、接入说明

1.1 获取凭证

联系平台管理员开通代理商账户,获取:

1.2 Sandbox 测试环境

平台提供 Sandbox 测试账号,用于开发联调。测试账号的所有接口返回模拟数据,不调用真实上游、不消耗真实资金,接口格式与生产环境完全一致。

项目
Base URLhttps://mama.best/api/v1(与生产相同)
API Keysandbox_test_key_dd123456789abc
API Secretsandbox_test_secret_dd_abcdef1234567890abcdef1234567890abcdef123456
初始余额$1000(模拟)
文档地址https://doc.mama.best

Sandbox 特征:

⚠️ 联调完成后,将 API Key / Secret 替换为正式账号凭证即可上生产,代码无需其他改动。

Sandbox Webhook 测试

联调 Webhook 时,先告知平台配置你的 webhookUrl,然后调用以下接口,平台会主动向你的地址推送一条模拟事件:

POST /api/v1/sandbox/trigger-webhook

字段类型必填说明
notifyTypestring事件类型,默认 CARD_SETTLEMENT。可选:CARD_AUTH / CARD_AUTH_CANCEL / CARD_SETTLEMENT / CARD_REFUND / CARD_CANCEL / CARD_FREEZE / CARD_UNFREEZE
virtualCardIdstring卡片 ID,不传则用随机 mock ID
externalRefstring用户标识,默认 sandbox_user_001

响应中 data.httpStatus 是你的 Webhook 端点返回的 HTTP 状态码,data.response 是你端点的响应体,可用于确认接收是否正常。

此接口仅 Sandbox 账号可调用,生产账号调用返回 4003 错误。

1.3 请求规范

1.4 请求头(所有接口必填)

Header说明示例
X-Api-Key代理商 API Keya1b2c3d4e5f6...
X-Timestamp当前毫秒时间戳(13位)1710641234567
X-SignatureHMAC-SHA256 签名(见下方)3a7f9c2b...
Content-Type固定值application/json

二、签名规则

2.1 签名算法

signature = HMAC-SHA256(api_secret, METHOD + PATH + TIMESTAMP + BODY)
组件说明示例
METHOD大写 HTTP 方法GETPOST
PATHreq.path,即 pathname不含域名,不含 Query String/api/v1/balance
TIMESTAMPX-Timestamp 相同的毫秒时间戳字符串1710641234567
BODY原始请求体字符串(GET 请求时为空字符串 ""{"virtualCardId":"VC..."}

签名结果取 hex 编码(小写)

⚠️ 重要:签名使用 pathname,不含 query string。
例如请求 /api/v1/cards?page=1&externalRef=user_123,签名时使用 /api/v1/cards
⚠️ POST body 签名注意: 使用 JSON.stringify 的原始字符串,不要格式化(不能有多余空格/换行)。签名时的 body 字符串必须与实际发送的 HTTP body 完全一致

2.2 时间戳有效期

时间戳必须在当前时间 ±5 分钟 内,超时返回 401

2.3 签名验证方式

服务端使用 timing-safe 比较crypto.timingSafeEqual),防止时序攻击。

2.4 签名示例(Node.js)

const crypto = require('crypto');

function sign(secret, method, path, timestamp, body = '') {
  // ⚠️ 取 pathname,去掉 query string
  const pathname = path.split('?')[0];
  const payload = `${method.toUpperCase()}${pathname}${timestamp}${body}`;
  return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}

// GET 请求示例
const secret = 'your_api_secret';
const timestamp = Date.now().toString();
const signature = sign(secret, 'GET', '/api/v1/balance', timestamp, '');

const headers = {
  'X-Api-Key': 'your_api_key',
  'X-Timestamp': timestamp,
  'X-Signature': signature,
  'Content-Type': 'application/json',
};

// POST 请求示例
const body = JSON.stringify({ virtualCardId: 'VC2603172099197' });
const postSignature = sign(secret, 'POST', '/api/v1/cards/info', timestamp, body);

2.5 签名示例(Python)

import hmac, hashlib, time, json, requests

def sign(secret, method, path, timestamp, body=''):
    pathname = path.split('?')[0]
    payload = f"{method.upper()}{pathname}{timestamp}{body}"
    return hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()

API_KEY = 'your_api_key'
API_SECRET = 'your_api_secret'
BASE = 'https://mama.best'

# GET 示例
ts = str(int(time.time() * 1000))
sig = sign(API_SECRET, 'GET', '/api/v1/balance', ts)
resp = requests.get(f'{BASE}/api/v1/balance', headers={
    'X-Api-Key': API_KEY, 'X-Timestamp': ts, 'X-Signature': sig,
    'Content-Type': 'application/json',
})

# POST 示例(注意 separators 不能有多余空格)
body = json.dumps({"virtualCardId": "VC2603..."}, separators=(',', ':'))
ts = str(int(time.time() * 1000))
sig = sign(API_SECRET, 'POST', '/api/v1/cards/info', ts, body)
resp = requests.post(f'{BASE}/api/v1/cards/info', data=body, headers={
    'X-Api-Key': API_KEY, 'X-Timestamp': ts, 'X-Signature': sig,
    'Content-Type': 'application/json',
})

三、通用响应格式

{
  "code": 0,
  "data": { ... },
  "message": "success"
}
字段类型说明
codenumber0=成功,非0=失败
dataobject/null响应数据(失败时可能为空)
messagestring描述信息

错误码

code说明
0成功
401认证失败(签名无效 / API Key 无效 / 时间戳过期 / 账号已禁用)
4001余额不足 / 业务限制
4003参数错误 / 无权限
4004资源不存在
4009重复请求(幂等冲突)
5000服务器内部错误
5001上游渠道调用失败

四、接口总览

#方法路径说明
1GET/api/v1/balance查询余额
2POST/api/v1/cardholders创建持卡人
3GET/api/v1/cardholders持卡人列表
4POST/api/v1/cards/apply开卡
5GET/api/v1/cards卡片列表
6POST/api/v1/cards/info查卡详情(含完整卡号/CVV)
7POST/api/v1/cards/sensitive查卡号/CVV(有审计日志)
8POST/api/v1/cards/transactions卡片交易记录
9POST/api/v1/cards/increase提额(向卡充值)
10POST/api/v1/cards/decrease降额(从卡取回资金)
11POST/api/v1/withdraw/apply申请提现(USDT TRC20)
12GET/api/v1/withdraw/list提现记录
13GET/api/v1/fee-logs扣费流水

五、externalRef 用户标识

externalRef 是代理商自定义的用户标识,用于将平台资源(卡片/持卡人/交易)与你自己系统中的用户关联。

格式要求:

支持 externalRef 的接口:

接口参数位置说明
POST /cardholdersbody创建持卡人时绑定
GET /cardholders?externalRef=xxxquery按用户筛选持卡人
POST /cards/applybody开卡时绑定到卡片
GET /cards?externalRef=xxxquery按用户筛选卡片
POST /cards/transactionsbody查该用户所有卡的交易
Webhook 回调payload自动携带原始 externalRef
以上路径省略前缀 /api/api/v1

六、接口详情


6.1 查询余额

GET /api/v1/balance

无请求参数。

响应示例:

{
  "code": 0,
  "data": {
    "balance": "500.00",
    "frozenBalance": "10.00",
    "currency": "USD"
  }
}
字段类型说明
balancestring可用余额(已扣除冻结)
frozenBalancestring提现冻结中的金额
currencystring固定 USD

6.2 创建持卡人

POST /api/v1/cardholders

持卡人是虚拟卡的归属人,开卡时必须指定 cardholderId。一个持卡人可开多张卡。

请求参数:

字段类型必填说明
firstNamestring名(英文/拼音,如 San
lastNamestring姓(英文/拼音,如 Zhang
birthdaystring生日,格式 YYYY-MM-DD
genderstringM=男 / F=女
idTypestring证件类型,见下方枚举(推荐 ID_CARD
idNumberstring证件号码(中国身份证需 18 位真实号码)
phoneAreaCodestring手机区号,如 86(不含+号)
phonestring手机号
provincestring省份(中文,如 广东省
citystring城市(中文,如 深圳市
addressstring详细地址
emailstring邮箱(若提供,全局唯一,不同持卡人不可重复)
countrystring国家代码(默认 CN
postalCodestring邮政编码
remarksstring备注
externalRefstring自定义用户标识(见第五章),最长 128 字符

idType 枚举:

idType说明
ID_CARD中国身份证(推荐
PASSPORT护照
DRIVING_LICENSE驾照
⚠️ 重要: 平台会对持卡人信息进行真实性校验:
- 身份证号必须是真实存在的号码(区划码、生日与号码一致,有公安库比对)
- province / city 填中文(如 福建省 / 泉州市),填英文会导致审核失败
- birthday 必须与身份证中间 8 位一致
- email 若提供则全局唯一,同一邮箱不能注册两个持卡人

请求示例:

{
  "firstName": "San",
  "lastName": "Zhang",
  "birthday": "1990-01-15",
  "gender": "M",
  "idType": "ID_CARD",
  "idNumber": "350582199001150011",
  "phoneAreaCode": "86",
  "phone": "13800138000",
  "province": "福建省",
  "city": "泉州市",
  "address": "丰泽区东海街道东海大街1号",
  "country": "CN",
  "postalCode": "362000",
  "email": "[email protected]",
  "externalRef": "user_123456"
}

响应示例:

{
  "code": 0,
  "data": {
    "cardholderId": "2029856542649827330",
    "localId": "5",
    "firstName": "San",
    "lastName": "Zhang",
    "externalRef": "user_123456",
    "status": "1"
  }
}
字段说明
cardholderId持卡人 ID,开卡时必须传此值
localId平台内部 ID(仅供参考)
status持卡人状态

6.3 持卡人列表

GET /api/v1/cardholders

Query 参数:

字段类型必填说明
pagenumber页码,默认 1
pageSizenumber每页数量,默认 20,最大 100
externalRefstring按用户标识筛选

响应示例:

{
  "code": 0,
  "data": {
    "list": [
      {
        "cardholderId": "2029856542649827330",
        "localId": "5",
        "firstName": "San",
        "lastName": "Zhang",
        "country": "CN",
        "idType": "ID_CARD",
        "idNumber": "110***011",
        "externalRef": "user_123456",
        "createdAt": "2026-03-17T06:00:00.000Z"
      }
    ],
    "total": 1
  }
}
注:idNumber 返回脱敏值(前3位 + *** + 后3位)

6.4 查询可用卡 BIN

GET /api/v1/card-bins

无需请求参数。

响应示例:

{
  "code": 0,
  "data": [
    {
      "binCode": "48331700",
      "businessScene": "AD_DELIVERY",
      "area": "US",
      "currency": "USD",
      "cardType": "VISA",
      "productIntroduction": "适用于广告投放/线上订阅场景的虚拟卡产品",
      "cardOrganization": "VISA"
    },
    {
      "binCode": "55616701",
      "businessScene": "AD_DELIVERY",
      "area": "US",
      "currency": "USD",
      "cardType": "MASTERCARD",
      "productIntroduction": "适用于广告投放/线上订阅场景的虚拟卡产品",
      "cardOrganization": "MASTERCARD"
    }
  ]
}

响应字段说明:

字段类型说明
binCodestring卡 BIN 段(开卡时填入 cardBin
businessScenestring使用场景,如 AD_DELIVERY(广告投放)
areastring卡片归属地区,如 US
currencystring卡片结算币种,如 USD
cardTypestring卡类型,如 VISAMASTERCARD
productIntroductionstring产品介绍
cardOrganizationstring卡组织,枚举:VISAMASTERCARD
建议开卡前先调用此接口获取最新可用 BIN 列表,而非写死在代码中。

6.5 开卡

POST /api/v1/cards/apply

请求参数:

字段类型必填说明
outOrderIdstring自定义订单号(幂等键,建议 UUID,相同值不会重复开卡)
cardBinstring卡 BIN 段,如 48331700(可通过 GET /card-bins 接口获取可用列表)
cardAmtnumber开卡充值金额(USD),必须 > 0
cardholderIdstring持卡人 ID(创建持卡人返回的 cardholderId
externalRefstring自定义用户标识
⚠️ 不需要传 walletNo,平台自动使用主钱包。

请求示例:

{
  "outOrderId": "order-20260317-uuid-001",
  "cardBin": "556371",
  "cardAmt": 100,
  "cardholderId": "2029856542649827330",
  "externalRef": "user_123456"
}

响应示例(成功):

{
  "code": 0,
  "data": {
    "virtualCardId": "VC2603172099197",
    "status": "0",
    "outOrderId": "order-20260317-uuid-001",
    "cardNoMasked": "****"
  }
}
cardNoMasked 在刚开卡时为 *(占位),等平台回调后更新为真实脱敏卡号(如 5561*7969)。

响应示例(幂等重放,相同 outOrderId):

{
  "code": 0,
  "data": { "virtualCardId": "VC2603172099197", "..." : "..." },
  "message": "幂等重放"
}

响应示例(outOrderId 正在处理中):

{
  "code": 4009,
  "message": "相同 outOrderId 正在处理中,请勿重复提交"
}

费用说明:

开卡流程:

扣款(cardAmt + fee_apply)→ 校验 cardholderId 归属 → 调上游渠道 → 本地预建记录
      ↓ 失败自动退款              ↓ 失败退款                ↓ 失败退款
⚠️ 异步激活: virtualCardId 立即返回,但卡片激活(status=3)需等待平台回调(通常几秒~几分钟)。
激活后 Webhook 推送 CARD_APPLY 事件,届时可通过 cards/info 查询完整卡号和 CVV。

6.6 卡片列表

GET /api/v1/cards

Query 参数:

字段类型必填说明
pagenumber页码,默认 1
pageSizenumber每页数量,默认 20,最大 100
externalRefstring按用户标识筛选

响应示例:

{
  "code": 0,
  "data": {
    "list": [
      {
        "virtualCardId": "VC2603172099197",
        "cardNoMasked": "5561****7969",
        "cardBin": "55616701",
        "status": 3,
        "cardBalance": "50.00",
        "totalLimit": 100,
        "expiryDate": "03/27",
        "currency": "USD",
        "externalRef": "user_123456",
        "createdAt": "2026-03-17T08:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  }
}
字段类型说明
virtualCardIdstring卡片唯一 ID
cardNoMaskedstring脱敏卡号(5561*7969),刚开卡时为 *
cardBinstring卡 BIN
statusnumber卡片状态(见状态枚举)
cardBalancestring卡片余额(USD,2位小数)
totalLimitnumber消费总限额
expiryDatestring有效期(MM/YY),刚开卡时为 00/00
currencystring币种
externalRefstring/null自定义用户标识
createdAtstring创建时间(ISO 8601)

卡片状态枚举:

status说明
0申请中
1申请失败
2待激活
3正常使用
4已冻结
5已注销

6.7 查卡详情

POST /api/v1/cards/info

实时查询卡片数据,包含完整卡号、CVV、实时余额

请求参数:

字段类型必填说明
virtualCardIdstring卡片 ID(VC...

响应示例:

{
  "code": 0,
  "data": {
    "virtualCardId": "VC2603172099197",
    "cardNo": "5561670158047969",
    "cardBin": "55616701",
    "cvv": "446",
    "expiry": "03/27",
    "status": "3",
    "cardAmt": 100,
    "totalLimit": 100,
    "usedLimit": 20,
    "availableLimit": 80,
    "ccy": "USD"
  }
}
字段类型说明
cardNostring完整卡号(16位)
cvvstring安全码(3位)
expirystring有效期(MM/YY
cardAmtnumber开卡充值总额(不随消费变化)
totalLimitnumber消费总限额
usedLimitnumber已使用额度(含预授权)
availableLimitnumber可用余额 = totalLimit - usedLimit
⚠️ 判断卡片实际可用余额应使用 availableLimit,而非 cardAmt

6.8 查卡号/CVV(专用接口)

POST /api/v1/cards/sensitive

功能与 cards/info 类似,但此接口每次调用都会记录审计日志(含调用 IP 和时间),适合前端展示卡号/CVV 的场景。

请求参数:

字段类型必填说明
virtualCardIdstring卡片 ID

响应示例:

{
  "code": 0,
  "data": {
    "cardNo": "5561670158047969",
    "cvv": "446",
    "expiry": "03/27"
  }
}
⚠️ 安全提示: 请仅在你的服务端调用此接口,不要在前端 JS 中直接暴露 API Secret 和调用。

6.9 卡片交易记录

POST /api/v1/cards/transactions

支持两种查询模式:

模式一:按卡片查(单卡)

字段类型必填说明
virtualCardIdstring✅(二选一)查单张卡的交易记录
pagenumber页码,默认 1
pageSizenumber每页数量,默认 20,最大 100

模式二:按用户查(该用户所有卡)

字段类型必填说明
externalRefstring✅(二选一)查该用户下所有卡的交易
pagenumber页码,默认 1
pageSizenumber每页数量,默认 20,最大 100
virtualCardIdexternalRef 二选一,优先使用 externalRef(如果同时传了两个)。

响应示例:

{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1,
        "cardId": 5,
        "notifyType": "CARD_SETTLEMENT",
        "payOrderId": "CT2026...",
        "amount": "20.00",
        "currency": "USD",
        "merchantName": "OPENAI *CHATGPT SUBSCR",
        "transactionTime": "2026-03-17T10:00:00.000Z",
        "status": 1,
        "createdAt": "2026-03-17T10:00:05.000Z"
      }
    ],
    "total": 4,
    "page": 1,
    "pageSize": 20
  }
}

6.10 提额(向卡片充值)

POST /api/v1/cards/increase

请求参数:

字段类型必填说明
virtualCardIdstring卡片 ID
amountnumber提额金额(USD),必须 > 0

响应示例:

{
  "code": 0,
  "data": {
    "message": "提额成功",
    "virtualCardId": "VC2603172099197",
    "amount": 50
  }
}

说明:


6.11 降额(从卡片回收资金)

POST /api/v1/cards/decrease

请求参数:

字段类型必填说明
virtualCardIdstring卡片 ID
amountnumber降额金额(USD),必须 > 0

响应示例:

{
  "code": 0,
  "data": {
    "message": "降额成功",
    "virtualCardId": "VC2603172099197",
    "amount": 30
  }
}

说明:


6.12 申请提现

POST /api/v1/withdraw/apply

请求参数:

字段类型必填说明
amountnumber提现金额(USD),最小 $10
receiveAddressstringUSDT TRC20 收款地址(T 开头,34位 Base58 字符)

请求示例:

{
  "amount": 200,
  "receiveAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

响应示例:

{
  "code": 0,
  "data": {
    "id": 1,
    "requestNo": "AW1710641234567ABCD",
    "amount": "200.000000",
    "feeAmount": "2.000000",
    "actualAmount": "198.000000",
    "receiveAddress": "TXxxx...",
    "status": 0,
    "createdAt": "2026-03-17T05:30:00.000Z"
  }
}
字段说明
amount申请提现金额
feeAmount手续费 = amount × fee_withdraw_rate
actualAmount实际到账 = amount - feeAmount
requestNo平台提现单号
status提现状态(见下方枚举)

提现状态枚举:

status说明
0待审核
1已通过
2处理中(出金中)
3已完成
4已拒绝(余额已原路退回)

资金流转:


6.13 提现记录

GET /api/v1/withdraw/list

当前版本返回最近 20 条记录,按创建时间倒序。

响应示例:

{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1,
        "requestNo": "AW1710641234567ABCD",
        "amount": "200.000000",
        "feeAmount": "2.000000",
        "actualAmount": "198.000000",
        "receiveAddress": "TXxxx...",
        "status": 3,
        "txHash": "abc123...",
        "createdAt": "2026-03-17T05:30:00.000Z"
      }
    ],
    "total": 1
  }
}
字段说明
txHash链上交易哈希(完成出金后由管理员填写)

6.14 扣费流水

GET /api/v1/fee-logs

当前版本返回最近 20 条记录,按创建时间倒序。

响应示例:

{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1,
        "feeType": "APPLY_CARD",
        "feeAmount": "2.000000",
        "balanceBefore": "502.000000",
        "balanceAfter": "500.000000",
        "relatedCardId": "VC2603...",
        "remark": "开卡费 cardBin=556371",
        "createdAt": "2026-03-17T05:00:00.000Z"
      }
    ],
    "total": 21
  }
}

feeType 枚举:

feeType方向说明
APPLY_CARD开卡固定费
CARD_FUND开卡/提额充值到卡
CARD_RETURN退降额/销卡退款回余额
WITHDRAW提现手续费
REFUND退开卡失败退款
balanceBefore / balanceAfter 可用于对账,精度为 6 位小数。

七、资金流转说明

代理商平台余额(由管理员 USDT 充值)
    │
    ├── 开卡 ──→ 扣 cardAmt(→ 卡片 availableLimit)+ fee_apply(→ 平台收入)
    │            失败自动全额退款
    │
    ├── 提额 ──→ 扣 amount(→ 卡片 availableLimit)
    │            失败自动退款
    │
    ├── 降额 ←── 退 amount(← 卡片 availableLimit → 代理商余额)
    │
    ├── 销卡 ←── 退 cardBalance(← 卡片余额 → 代理商余额),然后卡注销
    │
    ├── 消费 ──→ 直接扣卡片额度(不经过代理商余额)
    │            平台通过 Webhook 通知
    │
    └── 提现 ──→ 冻结 amount(balance → frozenBalance)
                 管理员审核 → 出金 → 完成(frozenBalance 清除)
                 拒绝 → 退回(frozenBalance → balance)

八、Webhook 回调

8.1 概述

当卡片发生消费、退款、状态变更等事件时,平台自动向你配置的 webhookUrl 发送 POST 回调。

前提条件:

8.2 请求格式

请求头:

Header说明
X-SignatureHMAC-SHA256(apiSecret, requestBody) hex 编码
X-Timestamp毫秒时间戳
Content-Typeapplication/json
⚠️ Webhook 签名规则与 API 签名不同:Webhook 只对 body 做 HMAC(不拼接 method/path/timestamp)。

请求体示例:

{
  "notifyType": "CARD_SETTLEMENT",
  "data": {
    "virtualCardId": "VC2603172099197",
    "payOrderId": "CT2026...",
    "externalRef": "user_123456",
    "clientId": "4",
    "clientName": "你的代理商名称",
    "settlementAmount": 20.00,
    "settlementCurrency": "USD",
    "consumptionAmount": 20.00,
    "consumptionCurrency": "USD",
    "transactionObject": "OPENAI *CHATGPT SUBSCR",
    "transactionTime": "2026-03-17 10:00:00",
    "status": "2"
  }
}
安全说明: 平台内部信息(merchantId/merchantName)已自动移除,替换为 clientId/clientName
externalRef 字段从卡片记录自动读取并附加。

8.3 验签(必须实现)

const crypto = require('crypto');

function verifyWebhook(apiSecret, rawBody, signature) {
  // ⚠️ 注意:Webhook 签名只对 body 做 HMAC,不含 method/path/timestamp
  const expected = crypto
    .createHmac('sha256', apiSecret)
    .update(rawBody)  // 原始 body 字符串,不要 JSON.parse 后再 stringify
    .digest('hex');
  return expected === signature;
}

// Express 示例
// ⚠️ 重要:Webhook 路由必须在 express.json() 全局中间件之前注册!
// 如果全局注册了 app.use(express.json()),会提前消费 body stream,
// 导致 express.raw() 拿到空 body,签名验证永远失败。
// 正确写法:把 webhook 路由放在 express.json() 注册之前。
app.post('/webhook/dd', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-signature'];
  const raw = req.body.toString();
  
  if (!verifyWebhook('your_api_secret', raw, sig)) {
    return res.status(401).json({ code: 1, message: 'invalid signature' });
  }
  
  const payload = JSON.parse(raw);
  console.log('收到回调:', payload.notifyType, payload.data);
  
  // 处理业务...
  res.json({ code: 0, message: 'success' });
});

8.4 响应要求

10 秒内响应 HTTP 200:

{ "code": 0, "message": "success" }

8.5 notifyType 枚举

notifyType说明建议处理
CARD_APPLY开卡成功(卡片激活)更新卡片状态为可用,通知用户
CARD_AUTH消费预授权(冻结额度)记录,可展示"处理中"消费
CARD_AUTH_FAIL授权失败(余额不足/限额等)记录日志,通知用户
CARD_AUTH_CANCEL授权撤销释放之前冻结的额度
CARD_SETTLEMENT消费结算(最终扣款)核心事件:更新交易为已完成
CARD_SETTLE_SUPPLEMENT结算补扣(实际金额>预授权)额外扣差额
CARD_SETTLE_REFUND结算退款(实际金额<预授权)退回差额
CARD_REFUND商户退款退回用户可用余额
CARD_FREEZE卡被冻结(上游风控触发,非主动操作更新卡片状态,通知用户
CARD_UNFREEZE卡片解冻(上游风控解除)更新卡片状态,通知用户
CARD_CANCEL卡片注销(上游强制)更新状态,余额已自动退回代理商

九、完整接入流程

步骤1:获取凭证
  联系管理员创建代理商账号
  → 获得 api_key + api_secret(secret 只展示一次!)
  → 提供你的 Webhook URL 给管理员配置

步骤2:充值
  管理员通过后台给你充值平台余额(USDT → USD)
  → 调用 GET /balance 确认到账

步骤3:创建持卡人
  POST /cardholders
  → 保存返回的 cardholderId(后续开卡必须)

步骤4:开卡
  POST /cards/apply
  → 保存 virtualCardId

步骤5:等待激活
  收到 Webhook CARD_APPLY → 卡片 status=3
  → 调用 cards/info 或 cards/sensitive 获取完整卡号和 CVV

步骤6:使用
  用户拿到卡号+CVV+有效期,即可进行线上消费
  消费发生时你会收到 CARD_AUTH → CARD_SETTLEMENT 回调

步骤7:日常管理
  提额/降额/冻结/解冻/销卡/查交易

步骤8:提现
  POST /withdraw/apply → 管理员审核出金(1-2 工作日)

十、注意事项

  1. 幂等性: outOrderId 相同的开卡请求只执行一次,重复请求返回原始结果;正在处理中的返回 4009 错误
  2. 金额精度: 请求时可传 number 或 string;响应中金额为 string(6位小数精度),显示时建议保留 2 位
  3. 时间戳: 精确到毫秒(13位),与服务器时差不超过 5 分钟
  4. 签名 body: 使用 JSON.stringify 的原始字符串,不要格式化或添加多余空格
  5. 签名路径: 使用 pathname,不含 query string
  6. Webhook 验签: 使用 express.raw() 保留原始 body,不能先 parse 再 stringify;Webhook 签名只对 body 做 HMAC若项目中全局注册了 express.json(),必须将 Webhook 路由放在它之前,否则 body 被提前消费导致签名永远失败
  7. api_secret 安全: 仅在服务端使用,不要写入前端代码、Git 仓库或日志
  8. 卡号安全: cards/infocards/sensitive 返回明文卡号/CVV,仅在你的服务端调用
  9. 已注销卡片: status=5 的卡禁止所有写操作(提额/降额/冻结/解冻/销卡)
  10. 金额上限: 单次操作金额不超过 $1,000,000
  11. 提现地址: 仅支持 TRC20 地址(T 开头,34位 Base58 字符)