Tài liệu API

Tích hợp cổng thanh toán

Base URL: . Mọi request/response dùng JSON.

Xác thực

Gửi API key (biến API_KEY trong .env) qua header. Chỉ gọi API từ máy chủ của bạn, không để lộ key ở trình duyệt.

Authorization: Bearer YOUR_API_KEY

Nhiều website

Mỗi website kết nối có API key, Webhook secret và Webhook URL riêng, tạo trong Quản trị → Website & API.

GET/api/v1/me

Kiểm tra key đang thuộc website nào.

Nút Gửi thử trong trang quản trị gửi một sự kiện ping có chữ ký tới Webhook URL. Website chỉ cần trả mã 2xx.

Tạo hoá đơn

POST/api/v1/invoices
TrườngMô tả
amount *Số tiền dạng chuỗi, ví dụ "19.99"
currencyUSD (mặc định), VND, EUR — tự quy đổi theo tỷ giá thời gian thực
order_idMã đơn của bạn. Gọi lại với cùng order_id sẽ trả về hoá đơn cũ (chống tạo trùng)
descriptionHiển thị cho khách trên trang thanh toán
customer_emailTuỳ chọn
methodsGiới hạn loại coin, ví dụ ["USDT_TRC20","BTC"]. Bỏ trống = tất cả đang bật
callback_urlURL nhận webhook cho hoá đơn này (mặc định DEFAULT_CALLBACK_URL)
success_urlChuyển khách về đây sau khi trả xong
cancel_urlNút "Quay lại cửa hàng"
metadataObject JSON bất kỳ (user_id, product_id…) — được gửi lại trong webhook
curl -X POST /api/v1/invoices \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "19.99",
    "currency": "USD",
    "order_id": "ORDER-1001",
    "description": "Gói Premium 1 tháng",
    "metadata": { "user_id": 42, "product_id": "premium" },
    "callback_url": "https://nen-tang-cua-ban.com/webhooks/crypto",
    "success_url": "https://nen-tang-cua-ban.com/thanh-cong"
  }'

Phản hồi 201:

{
  "id": "inv_Qm9X...",
  "status": "new",
  "amount": "19.99",
  "currency": "USD",
  "checkout_url": "/pay/inv_Qm9X...",
  "methods": [{ "code": "USDT_TRC20", "coin": "USDT", "network": "TRON (TRC20)" }, ...],
  "payment": null,
  "expires_at": "..."
}

Chuyển khách đến checkout_url (redirect hoặc mở popup/iframe).

Xem hoá đơn

GET/api/v1/invoices/:id
GET/api/v1/invoices?status=paid&order_id=ORDER-1001
POST/api/v1/invoices/:id/resend-webhook

Phương thức thanh toán

GET/api/v1/methods
MãCoin / Mạng
USDT_TRC20USDT trên TRON — phí rẻ nhất, khuyên dùng
USDT_BEP20USDT trên BNB Smart Chain
USDT_ERC20USDT trên Ethereum
USDC_ERC20USDC trên Ethereum
ETHEthereum
BTCBitcoin (mỗi hoá đơn một địa chỉ riêng nếu dùng xpub)

Trạng thái hoá đơn

newVừa tạo, khách chưa chọn coin
awaiting_paymentĐã chọn coin, đang chờ tiền
confirmingĐã thấy giao dịch, đang chờ đủ số xác nhận
paidHoàn tất — mở khoá sản phẩm
expiredHết hạn. Nếu tiền đến muộn (trong LATE_PAYMENT_GRACE_MINUTES) vẫn tự chuyển sang paid

Webhook

Mỗi khi trạng thái thay đổi, cổng gửi POST JSON đến callback_url. Sự kiện: invoice.confirming, invoice.paid, invoice.expired. Trả về HTTP 2xx để báo đã nhận; nếu không, cổng tự gửi lại (30s, 2m, 10m, 30m, 1h, 3h, 6h, 12h, 24h).

POST /webhooks/crypto
X-CryptoPay-Event: invoice.paid
X-CryptoPay-Signature: t=1762400000,v1=5f2b...e9

{
  "id": "evt_8c1f...",
  "event": "invoice.paid",
  "created_at": "2026-11-06T03:00:00.000Z",
  "data": {
    "id": "inv_Qm9X...",
    "status": "paid",
    "order_id": "ORDER-1001",
    "amount": "19.99",
    "currency": "USD",
    "metadata": { "user_id": 42, "product_id": "premium" },
    "payment": {
      "method": "USDT_TRC20", "coin": "USDT", "network": "TRON (TRC20)",
      "address": "TXyz...", "amount": "19.9901",
      "txid": "a1b2...", "confirmations": 1, "explorer_url": "https://tronscan.org/#/transaction/a1b2..."
    },
    "paid_at": "2026-11-06T03:00:00.000Z"
  }
}

Webhook có thể đến nhiều lần — hãy xử lý idempotent theo data.id (chỉ mở khoá một lần).

Xác minh chữ ký

Chữ ký = HMAC_SHA256(WEBHOOK_SECRET, t + "." + raw_body). Luôn dùng body gốc (chưa parse).

// Node.js / Express
import crypto from 'node:crypto';
app.post('/webhooks/crypto', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString();
  const { t, v1 } = Object.fromEntries(req.get('x-cryptopay-signature').split(',').map(p => p.split('=')));
  const expected = crypto.createHmac('sha256', process.env.WEBHOOK_SECRET).update(`${t}.${raw}`).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) || Math.abs(Date.now()/1000 - t) > 300)
    return res.status(401).end();
  const evt = JSON.parse(raw);
  if (evt.event === 'invoice.paid') unlockProduct(evt.data.order_id, evt.data.metadata);
  res.json({ ok: true });
});
<?php // PHP
$raw = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_CRYPTOPAY_SIGNATURE']), $sig);
$expected = hash_hmac('sha256', $sig['t'] . '.' . $raw, getenv('WEBHOOK_SECRET'));
if (!hash_equals($expected, $sig['v1']) || abs(time() - (int)$sig['t']) > 300) { http_response_code(401); exit; }
$evt = json_decode($raw, true);
if ($evt['event'] === 'invoice.paid') unlock_product($evt['data']['order_id']);
echo 'ok';

Luồng tích hợp

  1. Khách bấm "Mua" → máy chủ của bạn gọi POST /api/v1/invoices với order_id.
  2. Chuyển khách tới checkout_url.
  3. Nhận webhook invoice.paid → xác minh chữ ký → mở khoá sản phẩm cho order_id.
  4. (Dự phòng) Khi khách quay về success_url, gọi GET /api/v1/invoices/:id để kiểm tra lại trạng thái.