余额原子预占
下单时原子冻结余额,重复请求不会重复扣款。
在用户中心 → API 接入 → 订单回调中设置公网 HTTPS 地址,生成独立签名密钥并保存到你的接收服务,然后启用回调。平台部署侧也需开启外部投递;未开启时只允许准备配置,不发送通知。旧订单不会补发。
仅通知所属 API 创建的订单,事件为 order.created 或 order.updated。正文包含 id、version=1、sequence、occurredAt,以及 data 中的 orderNo、clientOrderNo、productCode、amount、currency、paymentStatus、status;金额保持订单原始结算币种。不会包含 Session、密码、银行卡、发货卡密或渠道内部信息。
每次 POST 带 X-ABCNUM-Event-Id、X-ABCNUM-Timestamp(Unix 秒)和 X-ABCNUM-Signature(v1=十六进制 HMAC-SHA256)。签名内容为 timestamp + '.' + eventId + '.' + 原始 JSON 请求体,不要先解析再序列化。签名密钥不是下单 API Key。
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody: original UTF-8 Buffer, before JSON parsing.
export function verifyWebhook(rawBody, headers, secret) {
const id = headers['x-abcnum-event-id'];
const timestamp = headers['x-abcnum-timestamp'];
const signature = headers['x-abcnum-signature'];
if (!/^[a-f0-9-]{36}$/.test(id || '') ||
!/^\d{10,12}$/.test(timestamp || '') ||
!/^v1=[a-f0-9]{64}$/.test(signature || '') ||
Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
throw new Error('Invalid webhook');
}
const expected = createHmac('sha256', secret)
.update(timestamp + '.' + id + '.')
.update(rawBody).digest();
const received = Buffer.from(signature.slice(3), 'hex');
if (!timingSafeEqual(expected, received)) {
throw new Error('Invalid signature');
}
const event = JSON.parse(rawBody.toString('utf8'));
if (event.id !== id || event.version !== 1) {
throw new Error('Invalid event');
}
return event;
}
// Persist the event ID with a UNIQUE constraint before acknowledging.
// Apply only a newer sequence for each order, in the same transaction.
// Return 2xx for both an accepted event and an already stored duplicate.开放 API 账号须审核通过且保持启用。任务领取后、DNS 解析完成后都会复核账号、审批、回调配置和发送租约;失效时不发起 HTTP 请求。平台在请求发出前暂停时不消耗发送次数,恢复后沿用原事件,不改发新地址。已发出的在途请求无法撤回。
接收端需在 10 秒内返回任意 2xx,且先持久化再确认。首次失败后约 10 秒、30 秒、2 分钟、10 分钟、30 分钟、1 小时、3 小时重试,共最多 8 次。重启或网络中断可能重复投递;同一事件的 ID 和正文不变,时间戳和签名重新生成。可能乱序,按订单 sequence 拒绝旧状态覆盖新状态。请用唯一事件 ID 去重,不能只靠时间戳。
回调只支持 443 端口的公网 HTTPS 域名,目前要求公网 IPv4 A 记录,不跟随重定向。私网、回环、保留地址和混合不安全 DNS 记录会被拒绝。配置变更或密钥轮换会取消旧待投递事件;轮换后需重新启用。在途请求可能已发出。失败通知不会再次充值、退款或改变余额,最终业务状态可用订单查询接口核对。
下单时原子冻结余额,重复请求不会重复扣款。
支持充值卡密作为订单支付方式,并防止并发重复使用。
支持订单签名回调、投递记录和失败重试;邮件和 Telegram 通知继续接入。
独立权限范围,并支持后续配置 IP 白名单、轮换和禁用。
屏蔽不同上游差异,返回一致的业务订单状态。
使用稳定的 v1 路径,并保留向后兼容空间。