REST API gốc
API gốc là giao diện REST riêng của blockwallets: hóa đơn tính bằng cent Mỹ, một ref idempotent cho mỗi đơn hàng, tài khoản nạp tiền tĩnh bao phủ mọi mạng, và webhook ký bằng HMAC-SHA256. Nó tạo cùng loại hóa đơn trên cùng trang thanh toán như API tương thích Cryptomus, với cùng phí và số dư.
Bạn tự tạo khóa trong trang quản trị tại “Nhà phát triển › Tích hợp & khóa”: một khóa Live và một khóa Test cùng webhook signing secret của chúng. Mỗi khóa chỉ hiển thị một lần; danh sách chỉ hiện tiền tố. Nếu tài khoản bật xác minh hai bước, việc tạo hoặc xoay khóa Live sẽ yêu cầu nhập mã. Chưa có tài khoản người bán? Đăng ký dùng. Đã dùng Cryptomus? Xem API tương thích Cryptomus (cách chọn).
Khóa và yêu cầu
URL gốc https://blockwallets.io. Yêu cầu và phản hồi đều là JSON. Mọi yêu cầu đều mang:
Authorization: Bearer bw_live_…hoặcBearer bw_test_…: tiền tố quyết định chế độ, nên khóa Test chỉ thấy hóa đơn Test và chạy trên testnetContent-Type: application/jsonvới các yêu cầuPOST- Chỉ giữ khóa trên máy chủ của bạn, không bao giờ đặt trong trang web hay ứng dụng; chúng tôi chỉ lưu giá trị băm của khóa
Lỗi trả về mã HTTP và {"error": {"code": "…", "message": "…"}}: 400 invalid_request (thiếu hoặc sai định dạng một trường), 401 unauthorized (khóa thiếu, không tồn tại hoặc đã bị tắt), 404 not_found, 409 conflict (yêu cầu xung đột với trạng thái hiện tại của hóa đơn), 422 currency_unsupported.
Endpoint
| Endpoint | Chức năng |
|---|---|
GET /v1/networks | Các mạng bạn có thể nhận ngay lúc này trong chế độ của khóa này: mức tối thiểu, số xác nhận, thời gian đến thông thường và ước tính phí; coin có giá kèm tỷ giá hiện tại |
POST /v1/invoices | Tạo hóa đơn (201), hoặc trả về hóa đơn đã có với cùng ref (200) |
GET /v1/invoices?ref=… | Tra cứu hóa đơn theo ref của bạn |
GET /v1/invoices/:id | Tra cứu hóa đơn theo id |
POST /v1/invoices/:id/network | Tự chọn mạng cho hóa đơn (trang thanh toán white-label): trả về address và pay_amount để hiển thị |
POST /v1/invoices/:id/close | Đóng hóa đơn đang chờ chưa có giao dịch chuyển nào |
POST /v1/deposit-accounts | Mở (201) hoặc lấy (200) tài khoản nạp tiền của một khách: địa chỉ cố định trên mọi mạng |
GET /v1/deposit-accounts?customer_ref=… | Tra cứu tài khoản nạp tiền theo customer_ref |
GET /v1/deposit-accounts/:id | Tra cứu tài khoản nạp tiền theo id |
GET /v1/deposits | Liệt kê các khoản nạp của một tài khoản (account_id hoặc customer_ref, limit 1–200, mặc định 50) |
GET /v1/deposits/:id | Tra cứu một khoản nạp |
GET /v1/events | Các lần gửi webhook của một hóa đơn hoặc khoản nạp (invoice_id hoặc deposit_id) |
POST /v1/events/:id/redeliver | Gửi lại một webhook (202) |
Thêm ?probe=1 vào lệnh tra cứu hóa đơn hoặc tài khoản nạp tiền để kiểm tra chuỗi ngay lúc đó (tối đa 10 giây một lần); nếu không, bạn nhận trạng thái mới nhất từ trình quét của chúng tôi, quét mỗi phút một lần.
Tạo hóa đơn POST /v1/invoices
Gọi POST /v1/invoices khi khách thanh toán và chuyển hướng họ đến pay_url trong phản hồi.
curl -X POST https://blockwallets.io/v1/invoices \
-H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
-d '{"ref":"order_1042","amount":4200,"title":"Order #1042",
"return_url":"https://shop.example/cart","success_url":"https://shop.example/thanks?order=1042",
"meta":{"user_id":42}}'| Trường | Bắt buộc | Mô tả |
|---|---|---|
ref | Có | Mã đơn hàng của bạn: 1–64 ký tự gồm chữ cái, chữ số và _ . : -. Duy nhất trong mỗi chế độ; gửi lại cùng ref với cùng các trường sẽ trả về cùng hóa đơn, còn khác các trường thì trả về 409 |
amount | Có | Số tiền tính bằng cent Mỹ, là số nguyên: 4200 = $42.00. Không thấp hơn mức tối thiểu của mạng khi đã chọn mạng |
title | Có | Nội dung khách thấy trên trang thanh toán, tối đa 64 ký tự |
return_url | Có | Nơi nút “Quay lại” trên trang thanh toán dẫn đến; phải thuộc một trong các tên miền website của bạn |
success_url | Không | Nơi khách đến sau khi thanh toán; mặc định là return_url |
network | Không | Chọn trước id mạng trong bảng mạng; bỏ trống để khách tự chọn trên trang thanh toán |
customer_ref | Không | Id khách hàng của bạn, tối đa 64 ký tự, được trả về nguyên văn |
lifetime | Không | Thời hạn tính bằng giây, 600 – 43200, mặc định 3600 |
amount_rule | Không | "any" = ghi có bất cứ số tiền nào đến (nạp tiền): chuyển paid ngay khi được xác nhận. Bỏ trống để dùng “Quy tắc số tiền” trong “Cài đặt › Quy tắc thanh toán & phí” của trang quản trị |
accuracy_percent | Không | Mức thiếu tiền vẫn được tính là đã trả, 0 – 5 (phần trăm), mặc định 0 |
subtract_percent | Không | Phần phí nền tảng do khách trả, 0 – 100 (phần trăm), cộng vào pay_amount; bỏ trống để dùng mặc định trong trang quản trị |
payment_multiple | Không | Có cho phép nạp thêm phần còn thiếu vào cùng địa chỉ hay không, mặc định theo trang quản trị (bật nếu bạn chưa đổi); false: khoản chuyển thiếu đầu tiên kết thúc ở wrong_amount |
meta | Không | Bất kỳ đối tượng JSON nào tối đa 2 KB, được trả về nguyên văn trong tra cứu và webhook |
currency | Không | Chỉ USD (mặc định); giá trị khác trả về 422 |
Mọi URL phải thuộc một trong các tên miền website của bạn (trang quản trị → “Nhà phát triển › Tích hợp & khóa”). Phản hồi là đối tượng hóa đơn bên dưới; network, address và pay_amount được điền khi đã chọn mạng.
Đối tượng hóa đơn
Id bắt đầu bằng inv_. amount tính bằng cent Mỹ; pay_amount và paid_amount là số lượng coin dạng chuỗi thập phân trên mạng đã chọn.
{
"id": "inv_01J8…",
"ref": "order_1042",
"mode": "live",
"status": "pending",
"amount": 4200,
"currency": "USD",
"title": "Order #1042",
"meta": { "user_id": 42 },
"network": null,
"address": null,
"pay_amount": null,
"paid_amount": null,
"payments": [],
"pay_url": "https://blockwallets.io/pay/inv_01J8…",
"return_url": "https://shop.example/cart",
"success_url": "https://shop.example/thanks?order=1042",
"created_at": "2026-11-03T08:00:00.000Z",
"expires_at": "2026-11-03T09:00:00.000Z",
"paid_at": null,
"closed_at": null
}| status | Ý nghĩa | Bạn cần làm gì |
|---|---|---|
pending | Chờ thanh toán | — |
confirming | Đã thấy giao dịch chuyển và đang chờ xác nhận, hoặc đã chuyển thiếu và vẫn có thể nạp thêm | — |
paid | Đã thanh toán | Giao hàng / ghi có cho đơn |
paid_over | Đã thanh toán, và trả dư | Như trên; phần dư nằm trong số dư của bạn |
wrong_amount | Kết thúc với số tiền ít hơn đầy đủ (khi hết hạn, hoặc ở khoản chuyển thiếu đầu tiên khi payment_multiple là false) | Dịch vụ nạp tiền có thể ghi có phần đã đến (paid_amount; coin tính theo tỷ giá còn có paid_cents); cửa hàng thì yêu cầu khách trả nốt phần còn lại |
closed | Hết hạn mà chưa nhận được gì, hoặc bạn đã đóng | — (tiền đến muộn vẫn được ghi có và thông báo) |
Các trường khác: pay_url (trang thanh toán), payments (mọi khoản chuyển cùng txid, amount, confirmations và confirmed), expires_at, paid_at, closed_at; BTC, LTC, DOGE, BCH và các coin gốc còn có rate (đô la Mỹ trên mỗi coin, chốt trong 30 phút), rate_locked_until và paid_cents (phần đã đến, tính bằng cent Mỹ).
Mạng
Truyền id vào network. GET /v1/networks trả về các mạng đang mở ngay lúc này trong chế độ của bạn; một mạng có thể bị tạm dừng, nên hãy đọc từ đó thay vì mã hóa cứng.
| network | Coin | Chuỗi | Tối thiểu |
|---|---|---|---|
USDT_TRC20 | USDT | TRON | $1 |
USDT_BEP20 | USDT | BNB Chain | $1 |
USDC_BEP20 | USDC | BNB Chain | $1 |
USDT_ERC20 | USDT | Ethereum | $5 |
USDC_ERC20 | USDC | Ethereum | $5 |
USDC_BASE | USDC | Base | $1 |
USDT_POLYGON | USDT | Polygon | $1 |
USDC_POLYGON | USDC | Polygon | $1 |
USDT_ARBITRUM | USDT | Arbitrum | $1 |
USDC_ARBITRUM | USDC | Arbitrum | $1 |
USDT_OPTIMISM | USDT | Optimism | $1 |
USDC_OPTIMISM | USDC | Optimism | $1 |
USDT_AVALANCHE | USDT | Avalanche | $1 |
USDC_AVALANCHE | USDC | Avalanche | $1 |
USDT_XLAYER | USDT | X Layer | $1 |
USDC_XLAYER | USDC | X Layer | $1 |
USD1_BEP20 | USD1 | BNB Chain | $1 |
FDUSD_BEP20 | FDUSD | BNB Chain | $1 |
DAI_ERC20 | DAI | Ethereum | $5 |
DAI_ARBITRUM | DAI | Arbitrum | $1 |
DAI_BASE | DAI | Base | $1 |
BTC | BTC | Bitcoin | $5 |
LTC | LTC | Litecoin | $1 |
DOGE | DOGE | Dogecoin | $1 |
BCH | BCH | Bitcoin Cash | $1 |
TRX | TRX | TRON | $1 |
BNB | BNB | BNB Chain | $1 |
ETH | ETH | Ethereum | $5 |
ETH_BASE | ETH | Base | $1 |
ETH_ARBITRUM | ETH | Arbitrum | $1 |
ETH_OPTIMISM | ETH | Optimism | $1 |
POL | POL | Polygon | $1 |
AVAX | AVAX | Avalanche | $1 |
OKB | OKB | X Layer | $1 |
USDT_SOL | USDT | Solana | $1 |
USDC_SOL | USDC | Solana | $1 |
SOL | SOL | Solana | $1 |
USDT_TON | USDT | TON | $1 |
TON | TON | TON | $1 |
Tài khoản nạp tiền (địa chỉ tĩnh) POST /v1/deposit-accounts
POST /v1/deposit-accounts với {"customer_ref": "user_42"} cấp cho khách đó một địa chỉ cố định trên mọi mạng (tất cả chuỗi EVM dùng chung một địa chỉ). Mọi khoản họ gửi đều được ghi có theo đô la Mỹ, mỗi lần chuyển có một webhook deposit.credited.
curl -X POST https://blockwallets.io/v1/deposit-accounts \
-H "Authorization: Bearer $BW_KEY" -H "Content-Type: application/json" \
-d '{"customer_ref":"user_42"}'{
"id": "dac_01J8…",
"mode": "live",
"customer_ref": "user_42",
"deposit_url": "https://blockwallets.io/deposit/dac_01J8…",
"min_amount": 100,
"currency": "USD",
"networks": [
{ "id": "USDT_TRC20", "chain": "tron", "token": "USDT", "address": "TXy4…8kQ2", "min_amount": 100, "confirmations": 1, … },
{ "id": "USDC_BASE", "chain": "base", "token": "USDC", "address": "0x5aE1…", "min_amount": 100, "confirmations": 20, … }
]
}customer_ref: 1–128 ký tự gồm chữ cái, chữ số và_ . : @ -; cùng một giá trị luôn trả về cùng một tài khoản.return_urltùy chọn thuộc các tên miền của bạn- Phản hồi có
deposit_url(trang lưu trữ sẵn với các địa chỉ, mã QR và các khoản nạp gần đây) vànetworks(mỗi mạng cóid,address,min_amount,confirmations) - Id khoản nạp bắt đầu bằng
dep_;amounttính bằng cent Mỹ,amount_tokenlà số lượng coin, và coin có giá kèmrateđã dùng
Các khoản chuyển dưới mức tối thiểu (thường là $1, BTC $5) được ghi nhận là below_min và không được ghi có tự động; hãy liên hệ để chúng tôi ghi có.
Webhook
Chúng tôi POST JSON đến URL webhook trong trang quản trị (“Nhà phát triển › Tích hợp & khóa”; Live và Test mỗi bên có URL và secret riêng). Nội dung là sự kiện:
POST /your/webhook
X-BW-Event: ev_01J8…
X-BW-Delivery: 1
X-BW-Signature: t=1793693052,v1=5f0c9b…
{"id":"ev_01J8…","type":"invoice.paid","created_at":"2026-11-03T08:04:12.000Z","invoice":{"id":"inv_01J8…","ref":"order_1042","status":"paid","amount":4200,…}}X-BW-Signature:t=<unix seconds>,v1=<hex>, trong đóv1= HMAC-SHA256(webhook secret của chế độ đó,t+.+ nội dung thô)X-BW-Event: id sự kiện (ev_…); hãy loại trùng theo nóX-BW-Delivery: số lần gửi, bắt đầu từ 1
| Sự kiện | Khi nào |
|---|---|
invoice.paid | Hóa đơn chuyển sang paid hoặc paid_over (kể cả tiền đến sau khi hết hạn) |
invoice.wrong_amount | Hóa đơn kết thúc ở wrong_amount |
invoice.closed | Hóa đơn hết hạn mà chưa nhận được gì, hoặc bạn đã đóng |
deposit.credited | Một khoản chuyển vào tài khoản nạp tiền đã được ghi có |
Hãy xác minh trên nội dung thô trước khi phân tích, từ chối t lệch quá 300 giây so với đồng hồ của bạn, và so sánh trong thời gian không đổi. invoice / deposit trong nội dung là cùng đối tượng mà một lệnh tra cứu trả về.
import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), header: X-BW-Signature
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return typeof parts.v1 === "string" && parts.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}Phản hồi 2xx được tính là đã gửi; nếu không, chúng tôi thử lại sau 1 phút, 5 phút, 30 phút, 2 giờ, 12 giờ, 24 giờ và 24 giờ (tổng cộng 8 lần).
Events và gửi lại
GET /v1/events?invoice_id=inv_… (hoặc deposit_id=dep_…) liệt kê các webhook của một hóa đơn hoặc khoản nạp cùng state, attempts, last_status và last_error. POST /v1/events/ev_…/redeliver xếp hàng gửi lại ngay, ví dụ sau khi bạn đã sửa endpoint. “Nhà phát triển › Nhật ký webhook” trong trang quản trị hiển thị mọi lần thử kèm yêu cầu và phản hồi.
Chế độ thử nghiệm
Hóa đơn tạo bằng khóa bw_test_ chạy trên testnet và không bao giờ động đến tiền thật. Trang thanh toán hiển thị nút “Mô phỏng thanh toán” (đủ / một nửa / trả dư), để bạn xem trạng thái và webhook mà không cần có coin testnet.