快速开始

快速开始

约 15 分钟完成第一笔沙箱收款。

本教程使用 Node.js 18+,其他语言步骤完全相同 —— Python、Go、PHP、Java 示例见请求签名。

获取沙箱凭证

在开发者控制台中:

  1. 创建 App 并申请开通支付(App 未发布也可使用沙箱)。
  2. 支付状态变为已开通后,进入支付 → API Key,创建沙箱 Key。复制 key_id(ok_test_…)和 Secret —— Secret 只展示一次。
  3. 设置回调地址,复制回调签名密钥(同样只展示一次)。

保存为环境变量:

bash
export HTP_API_BASE=
export HTP_KEY_ID=ok_test_xxxxxxxx
export HTP_API_SECRET=xxxxxxxxxxxxxxxx
export HTP_CALLBACK_SECRET=xxxxxxxxxxxxxxxx

添加签名函数

每个请求都使用 HMAC-SHA256 签名。保存为 htp.js:

htp.js
import crypto from 'node:crypto';

const { HTP_API_BASE, HTP_KEY_ID, HTP_API_SECRET } = process.env;

export async function htp(method, path, body, idempotencyKey = '') {
  const raw = body === undefined ? '' : JSON.stringify(body);
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomBytes(18).toString('base64url'); // 24 位 URL 安全字符
  const bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
  const stringToSign = [method, path, timestamp, nonce, idempotencyKey, bodyHash].join('\n');
  const signature = crypto.createHmac('sha256', HTP_API_SECRET).update(stringToSign).digest('base64');

  const headers = {
    'Content-Type': 'application/json',
    'X-Key-Id': HTP_KEY_ID,
    'X-Timestamp': timestamp,
    'X-Nonce': nonce,
    'X-Signature': signature,
  };
  if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;

  const res = await fetch(HTP_API_BASE + path, { method, headers, body: raw || undefined });
  const json = await res.json();
  if (!res.ok) {
    throw Object.assign(new Error(json.message), { code: json.error_code, status: res.status, traceId: json.trace_id });
  }
  return json.data;
}

创建收款单

create-payment.js
import crypto from 'node:crypto';
import { htp } from './htp.js';

const orderNo = 'A' + Date.now();
const payment = await htp(
  'POST',
  '/open/v1/payments',
  { out_order_no: orderNo, amount: '1.00', currency: 'USDT', description: '测试订单' },
  crypto.randomUUID().replaceAll('-', ''), // Idempotency-Key —— 重试时复用
);

console.log(payment.id, payment.status); // …  CREATED
console.log(payment.checkout_url);

用测试用户付款

用 App 的测试用户(在控制台测试用户中添加)登录 HTP App,打开 checkout_url,输入支付 PIN 确认。

请使用测试用户,而不是你自己的账号

开发者永远不能向自己的 App 付款(SELF_PAYMENT_FORBIDDEN),沙箱和生产都一样。沙箱订单只能由 App 的测试用户支付(其他人返回 SANDBOX_USER_ONLY)。

接收回调

HTP 向你的回调地址 POST payment.paid。信任之前先校验签名 —— 代码见回调。

json
{
  "event_id": "9e8d7c6b-5a4f-4e3d-2c1b-0a9f8e7d6c5b",
  "event": "payment.paid",
  "created_at": "2026-11-07T09:05:12Z",
  "data": { "id": "0b7a9c62-…", "out_order_no": "A1730970000000", "status": "PAID", "amount": "1.00", "net_amount": "0.99", "user_sub": "2f6c0d1e8a9b4c7d", "…": "…" }
}

还没有公网地址?可以改为查询订单:

js
const order = await htp('GET', `/open/v1/orders/${payment.id}`);
console.log(order.status); // PAID

完成

接下来:处理退款,在沙箱与测试中测试各种失败场景,然后对照上线清单。