# API tương thích Cryptomus của blockwallets

> API tương thích Cryptomus của blockwallets: hóa đơn và ví tĩnh, chữ ký, webhook và xác minh, chế độ thử nghiệm, mạng, lỗi và cách chuyển từ Cryptomus chỉ bằng đổi tên miền.
>
> HTML: https://blockwallets.io/docs/cryptomus?lang=vi · English: https://blockwallets.io/docs/cryptomus.md?lang=en

## API tương thích Cryptomus

blockwallets giúp bạn nhận USDT và USDC, cùng BTC, LTC, DOGE, BCH và các coin gốc (ghi có theo đô la Mỹ với tỷ giá đã chốt): khách thanh toán, chúng tôi xác nhận trên chuỗi, rồi báo cho bạn. API, chữ ký và webhook giống hệt **Cryptomus** — nếu bạn đã tích hợp Cryptomus, chỉ cần đổi tên miền là xong. Phần giải thích cách hoạt động đứng trước, sau đó mới đến API.

### Hai cách nhận tiền

Cả hai đều nhận **mọi số tiền**: đơn hàng giá cố định và nạp tiền do khách tự nhập số tiền. Khi đã thanh toán, bạn giao hàng hoặc cộng vào số dư của khách. Khác biệt duy nhất là **mỗi lần thanh toán có địa chỉ mới hay không**.

Khuyến nghị · hầu hết các tích hợp**Hóa đơn**
Khách chọn sản phẩm hoặc nhập số tiền trên trang của bạn, bạn tạo một hóa đơn, và khách chuyển đúng số tiền hiển thị trên trang thanh toán. Mỗi hóa đơn có một địa chỉ mới và đồng hồ đếm ngược; khi tiền đến, chúng tôi báo cho bạn số tiền thực nhận.

`POST /v1/payment` → webhook `type: payment`

Tùy chọn**Ví tĩnh**
Không cần số tiền trước: mỗi khách có một địa chỉ cố định trên mỗi mạng để lưu vào ví và chuyển bất cứ lúc nào. Mỗi lần chuyển sẽ kích hoạt một webhook riêng.

`POST /v1/wallet` → webhook `type: wallet`

Phân vân thì cứ dùng hóa đơn. Nếu không viết code, [liên kết thanh toán](https://blockwallets.io/docs/cryptomus?lang=vi#links) trong trang quản trị chính là hóa đơn được tạo sẵn cho bạn. Chưa là người bán? Đăng ký dùng ở [trang chủ](https://blockwallets.io/?lang=vi#join); sau khi được duyệt, đăng nhập vào [trang quản trị](https://blockwallets.io/dashboard) và bắt đầu bằng “Thử thanh toán” ở trang chủ của trang quản trị, trả một số tiền nhỏ trước (tiền vào chính số dư của bạn).

### Một hóa đơn đi thế nào

Một khoản thanh toán có bốn bên: khách, website của bạn, blockwallets và blockchain. Sơ đồ chạy từ trên xuống theo thời gian; cột bên phải là trạng thái của hóa đơn ở từng bước.

Tóm lại, năm bước:

- Bạn tạo hóa đơn (liên kết thanh toán làm việc này giúp bạn, hoặc máy chủ của bạn gọi `/v1/payment`), nhận `url` của trang thanh toán và chuyển hướng khách đến đó.

- Trên trang thanh toán, khách chọn coin và mạng, nhận một địa chỉ và số tiền riêng, rồi trả từ ví hoặc sàn.

- Chúng tôi thấy giao dịch trên chuỗi, chờ đủ số xác nhận, rồi đối chiếu với một nhà cung cấp node thứ hai (để một node không thể làm giả).

- Hóa đơn chuyển sang `paid` và chúng tôi lập tức POST một webhook có chữ ký đến `url_callback` của bạn; vài giây sau, trang thanh toán đưa khách về `url_success` của bạn.

- Tiền được ghi có vào số dư của bạn; rút về ví của bạn từ trang quản trị.

#### Trạng thái hóa đơn

| status | Ý nghĩa | Bạn cần làm gì |
| --- | --- | --- |
| `check` | Chờ thanh toán | — |
| `confirm_check` | Đã thấy giao dịch, đang chờ xác nhận | — |
| `wrong_amount_waiting` | Đã nhận nhưng còn thiếu; khách có thể nạp thêm vào cùng địa chỉ (khi `is_payment_multiple` là true; chỉ thông báo một lần) | — (bạn sẽ nhận `paid` khi khách nạp đủ) |
| `paid` | Đã thanh toán | Giao hàng / ghi có theo `payment_amount_usd` |
| `paid_over` | Đã thanh toán, và trả dư | Như trên (`payment_amount_usd` là số tiền đã nhận) |
| `wrong_amount` | Hết hạn mà chưa đủ số tiền (khi `is_payment_multiple` là false: ngay từ khoản chuyển thiếu đầu tiên) | Dịch vụ nạp tiền có thể ghi có `payment_amount_usd` đã nhận; cửa hàng thì yêu cầu khách trả nốt phần còn lại |
| `cancel` | Hết hạn mà chưa nhận được gì | — (tiền đến muộn vẫn được ghi có và thông báo) |

Khi `is_final` là `true`, trạng thái sẽ không đổi nữa (trừ trường hợp thanh toán muộn).

### Liên kết thanh toán (không cần code)

Trang quản trị → “Thanh toán › Liên kết thanh toán” → nhập mô tả và số tiền (cố định mạng hoặc để khách chọn) → tạo. Mỗi liên kết có một URL và một mã QR.

- Mỗi lần có người mở liên kết, một hóa đơn mới được tạo, nên một liên kết dùng lại được (ví dụ “Hội viên $99”).

- Mọi khoản thanh toán đều hiện trong “Thanh toán › Giao dịch”; rút về ví của bạn từ “Tiền › Số dư & rút tiền”.

- Nếu bạn đã tạo thông tin xác thực và URL webhook trong “Nhà phát triển › Tích hợp & khóa”, các khoản thanh toán qua liên kết cũng được thông báo theo định dạng bên dưới.

- Muốn ngừng nhận, bấm “Tắt” và liên kết ngừng hoạt động ngay lập tức.

### Tích hợp API

#### 1. Thông tin xác thực và chữ ký

Trang quản trị → “Nhà phát triển › Tích hợp & khóa” → tạo một **merchant** (UUID) và một **API key**. Có một bộ cho Live và một bộ cho Test: Live nhận tiền thật, Test chạy trên testnet. Hãy thêm cả **tên miền website** của bạn: `url_callback`, `url_return` và `url_success` phải thuộc các tên miền đó. API key chỉ hiển thị một lần — hãy cất cẩn thận và chỉ để trên máy chủ của bạn.

URL gốc `https://blockwallets.io`. Mọi endpoint đều là `POST` + JSON, kèm các header sau:

- `merchant`: UUID merchant của bạn (giá trị này quyết định Live hay Test)

- `sign`: `md5(base64(request body) + API key)`, trong đó nội dung gửi đi là chính xác chuỗi JSON bạn gửi

Thành công trả về `{"state": 0, "result": {…}}`; thất bại trả về `{"state": 1, "message": "…"}` (xem [Lỗi](https://blockwallets.io/docs/cryptomus?lang=vi#errors)).

Các endpoint chính bên dưới có bảng “Thử ngay”: nhập merchant và API key của chế độ Test, trang sẽ ký yêu cầu ngay trong trình duyệt của bạn, gọi endpoint và hiển thị đầy đủ yêu cầu lẫn phản hồi (thông tin xác thực chỉ nằm trong bộ nhớ của trang). Khi chữ ký không khớp, hãy so từng bước với [công cụ kiểm tra chữ ký](https://blockwallets.io/docs/sign?lang=vi).

```nodejs
import crypto from "node:crypto";

async function call(path, data) {
  const body = JSON.stringify(data);
  const sign = crypto.createHash("md5").update(Buffer.from(body).toString("base64") + process.env.BW_API_KEY).digest("hex");
  const res = await fetch("https://blockwallets.io" + path, {
    method: "POST",
    headers: { merchant: process.env.BW_MERCHANT, sign, "Content-Type": "application/json" },
    body,
  });
  return res.json();
}
```

```php
function bw_call(string $path, array $data): array {
    $body = json_encode($data, JSON_UNESCAPED_UNICODE);
    $sign = md5(base64_encode($body) . getenv('BW_API_KEY'));
    $ch = curl_init('https://blockwallets.io' . $path);
    curl_setopt_array($ch, [
        CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['merchant: ' . getenv('BW_MERCHANT'), 'sign: ' . $sign, 'Content-Type: application/json'],
    ]);
    return json_decode(curl_exec($ch), true);
}
```

```python
import base64, hashlib, json, os, requests

def bw_call(path: str, data: dict) -> dict:
    body = json.dumps(data, separators=(",", ":"), ensure_ascii=False)
    sign = hashlib.md5((base64.b64encode(body.encode()).decode() + os.environ["BW_API_KEY"]).encode()).hexdigest()
    headers = {"merchant": os.environ["BW_MERCHANT"], "sign": sign, "Content-Type": "application/json"}
    return requests.post("https://blockwallets.io" + path, data=body.encode(), headers=headers).json()
```

#### 2. Tạo hóa đơn POST /v1/payment

Khi khách đặt hàng, hoặc nhập số tiền trên trang nạp tiền của bạn, hãy gọi `https://blockwallets.io/v1/payment` rồi chuyển hướng khách đến `result.url` trả về.

```json
{
  "amount": "42.00",
  "currency": "USD",
  "order_id": "order_1042",
  "url_callback": "https://shop.example/webhooks/crypto",
  "url_return": "https://shop.example/cart",
  "url_success": "https://shop.example/thanks?order=1042"
}
```

| Tham số | Bắt buộc | Mô tả |
| --- | --- | --- |
| `amount` | Có | Số tiền dạng chuỗi, tối đa hai chữ số thập phân: `"42.00"`. Là giá của bạn hoặc số tiền khách nhập; không thấp hơn mức tối thiểu của mạng đã chọn (xem [mạng](https://blockwallets.io/docs/cryptomus?lang=vi#networks)) |
| `currency` | Có | `USD`, `USDT` hoặc `USDC`, đều quy đổi 1:1 |
| `order_id` | Có | Mã đơn hàng của bạn: chữ cái, chữ số, `-`, `_`, ≤ 100 ký tự. Gọi lại với cùng order_id sẽ trả về hóa đơn đã có (không tạo đơn trùng) |
| `url_callback` | Không | URL webhook. Bỏ trống thì không gửi webhook |
| `url_return` | Không | Nơi “Quay lại” dẫn đến trước khi thanh toán |
| `url_success` | Không | Nơi khách được chuyển đến sau khi thanh toán |
| `network` | Không | Chọn trước một mạng: `tron` / `bsc` / `eth` / `base` / `polygon` / `arbitrum` / `optimism` / `avalanche` / `xlayer` / `btc` / `ltc` / `doge` / `bch` / `sol` / `ton`; bỏ trống để khách tự chọn trên trang thanh toán |
| `to_currency` | Không | Coin trên mạng đó: `USDT` / `USDC`; với `btc` / `ltc` / `doge` / `bch` là `BTC` / `LTC` / `DOGE` / `BCH` (tùy chọn) |
| `lifetime` | Không | Thời hạn của hóa đơn tính bằng giây, 300 – 43200, mặc định 3600; giá trị dưới 600 được tính là 600 |
| `accuracy_payment_percent` | Không | Mức trả thiếu vẫn được coi là đã thanh toán, 0 – 5, mặc định 0. `100` = nhận bất cứ số nào đến: `paid` ngay khi được xác nhận (`paid_over` nếu nhiều hơn), ghi có theo `payment_amount_usd` trong webhook — phù hợp cho nạp tiền. Nếu bỏ trống, áp 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ị: “ghi có số tiền đã đến” nghĩa là 100 |
| `subtract` | Không | Tỷ lệ phí nền tảng do khách trả, 0 – 100, được cộng vào `payer_amount` (phí nền tảng là 1%, thấp hơn khi doanh số cao hơn; ở mức 1%, với 100 thì hóa đơn $100 khách phải trả $101.00; xem [bảng giá](https://blockwallets.io/pricing?lang=vi)). Nếu bỏ trống, áp dụng cài đặt “Phí và trả thiếu” trong “Cài đặt › Quy tắc thanh toán & phí” của trang quản trị (mặc định 0) |
| `is_payment_multiple` | Không | Khoản trả thiếu có được nạp thêm hay không, mặc định true: trạng thái `wrong_amount_waiting`, trả nốt phần còn lại vào cùng địa chỉ thì chuyển thành `paid` (nếu lúc nhận khoản thiếu đầu tiên còn dưới 30 phút, hạn chót được dời đến 30 phút sau đó). false: khoản chuyển thiếu đầu tiên chuyển hóa đơn thành `wrong_amount`. Nếu bỏ trống, áp dụng cài đặt trong trang quản trị |
| `additional_data` | Không | Chuỗi bất kỳ (≤ 255), được trả lại nguyên vẹn trong truy vấn và webhook |

```json
{
  "state": 0,
  "result": {
    "uuid": "6f1c2a7e-…",
    "order_id": "order_1042",
    "amount": "42.00",
    "currency": "USD",
    "network": null,
    "address": null,
    "payer_amount": null,
    "payment_amount": null,
    "url": "https://blockwallets.io/pay/inv_01J8…",
    "expired_at": 1760965200,
    "status": "check",
    "is_final": false
  }
}
```

`network`, `address` và `payer_amount` được điền khi khách chọn mạng.

#### 3. Truy vấn hóa đơn

- `POST /v1/payment/info` `{"order_id": "…"}` hoặc `{"uuid": "…"}`: lấy một hóa đơn và kiểm tra chuỗi ngay lúc đó (tối đa một lần mỗi 10 giây).

- `POST /v1/payment/list`: liệt kê, 15 mục mỗi trang, tùy chọn `date_from` / `date_to`; trang tiếp theo dùng `paginate.nextCursor` trả về (`?cursor=…`).

- `POST /v1/payment/services`: các mạng khả dụng và số tiền tối thiểu của từng mạng.

- `POST /v1/payment/resend` `{"order_id": "…"}`: gửi lại webhook của hóa đơn đã thanh toán / trả thiếu.

Webhook là kênh chính; truy vấn là phương án dự phòng (ví dụ khi máy chủ của bạn ngừng hoạt động và lỡ mất một webhook).

#### 4. Ví tĩnh POST /v1/wallet

Mở một địa chỉ cố định cho một khách trên một mạng. Cùng một `order_id` (thường là id người dùng của bạn) + mạng luôn cho cùng một địa chỉ; mọi chuỗi EVM (BNB Chain, Ethereum, Base, Polygon, Arbitrum, Optimism, Avalanche, X Layer) dùng chung một địa chỉ, còn TRON, BTC, LTC, DOGE, BCH, Solana và TON mỗi mạng một địa chỉ riêng. Khách gửi bao nhiêu thì ghi có bấy nhiêu, mỗi lần chuyển một webhook; khoản chuyển BTC / LTC / DOGE / BCH / TRX / BNB / ETH / POL / AVAX / OKB / SOL / TON được quy đổi sang đô la Mỹ theo tỷ giá lúc thấy giao dịch (`payment_amount_usd`).

```json
{
  "order_id": "user_42",
  "currency": "USDT",
  "network": "tron",
  "url_callback": "https://shop.example/webhooks/crypto"
}
```

```json
{
  "state": 0,
  "result": {
    "wallet_uuid": "0b9d…",
    "uuid": "5e2f…",
    "order_id": "user_42",
    "currency": "USDT",
    "network": "tron",
    "address": "TXy4…8kQ2",
    "url": "https://blockwallets.io/deposit/dac_01J8…"
  }
}
```

`result.url` là trang nạp tiền được lưu trữ sẵn (địa chỉ + mã QR + các khoản nạp gần đây) mà bạn có thể đưa cho khách. Khoản chuyển dưới mức tối thiểu của mạng (thường là $1, BTC $5) không được ghi có tự động; hãy liên hệ để chúng tôi ghi có. Chặn địa chỉ (`/v1/wallet/block-address`) hiện chưa được hỗ trợ.

### Webhook

Khi trạng thái hóa đơn thay đổi (`paid`, `paid_over`, `wrong_amount_waiting`, `wrong_amount`, `cancel`) và với mỗi khoản chuyển vào ví tĩnh, chúng tôi POST JSON đến `url_callback`. Phản hồi 2xx được coi là đã nhận; 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ờ. Bạn cũng có thể gửi lại từ trang quản trị hoặc bằng `/v1/payment/resend`.

```json
{
  "type": "payment",
  "uuid": "6f1c2a7e-…",
  "order_id": "order_1042",
  "amount": "42.00",
  "payment_amount": "42",
  "payment_amount_usd": "42.00",
  "merchant_amount": "41.58",
  "commission": "0.42",
  "is_final": true,
  "status": "paid",
  "from": "TQ8m…",
  "wallet_address_uuid": null,
  "network": "tron",
  "currency": "USD",
  "payer_currency": "USDT",
  "payer_amount": "42",
  "payer_amount_exchange_rate": "1",
  "additional_data": null,
  "txid": "9c1e…",
  "sign": "5f0c9b…"
}
```

| type | Khi nào | Ghi có |
| --- | --- | --- |
| `payment` | Trạng thái hóa đơn thay đổi — xem `status` | Ghi có `payment_amount_usd` khi `paid` / `paid_over`; loại trùng theo `uuid` |
| `wallet` | Một khoản chuyển đến ví tĩnh (`status` luôn là `paid`; `order_id` là mã bạn dùng khi mở ví) | Ghi có `payment_amount_usd`; mỗi khoản chuyển có `uuid` riêng — loại trùng theo nó |

#### Xác minh chữ ký (bắt buộc)

Chữ ký là trường `sign` trong nội dung webhook: bỏ `sign`, mã hóa lại phần còn lại theo cách `json_encode` của PHP (gọn, không khoảng trắng, `/` được thoát thành `\/`, ký tự không phải ASCII không thoát), và `md5(base64(encoded) + API key)` phải bằng `sign`. Dùng API key của cùng chế độ. Cùng một webhook có thể đến nhiều lần — hãy loại trùng theo `uuid` + `status`.

Nếu xác minh thất bại, hãy dán nguyên nội dung webhook thô vào [công cụ kiểm tra chữ ký](https://blockwallets.io/docs/sign?lang=vi): công cụ hiển thị kết quả từng bước và đánh dấu chỗ khác biệt của bạn (mọi thứ chạy trong trình duyệt của bạn; API key không bao giờ được tải lên). Mọi webhook trong “Nhà phát triển › Nhật ký webhook” của trang quản trị đều mở được trong công cụ này chỉ với một cú nhấp.

```nodejs
import crypto from "node:crypto";

function verify(rawBody, apiKey) {
  const { sign, ...data } = JSON.parse(rawBody);
  const json = JSON.stringify(data).replaceAll("/", "\\/");
  const expected = crypto.createHash("md5").update(Buffer.from(json).toString("base64") + apiKey).digest("hex");
  return typeof sign === "string" && sign.length === expected.length && crypto.timingSafeEqual(Buffer.from(sign), Buffer.from(expected));
}
```

```php
$data = json_decode(file_get_contents('php://input'), true);
$sign = $data['sign'] ?? '';
unset($data['sign']);
$expected = md5(base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE)) . $apiKey);
if (!hash_equals($expected, $sign)) {
    http_response_code(401); exit;
}
```

```python
import base64, hashlib, hmac, json

def verify(raw_body: bytes, api_key: str) -> bool:
    data = json.loads(raw_body)
    sign = data.pop("sign", "")
    encoded = json.dumps(data, separators=(",", ":"), ensure_ascii=False).replace("/", "\\/")
    expected = hashlib.md5((base64.b64encode(encoded.encode()).decode() + api_key).encode()).hexdigest()
    return hmac.compare_digest(expected, sign)
```

### Chế độ thử nghiệm

Hóa đơn tạo bằng merchant và API key của Test chạy trên testnet, không có 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 thay đổi và webhook mà không cần có coin testnet.

- `POST /v1/test-webhook/payment`, `/v1/test-webhook/wallet`: gửi một webhook mẫu có chữ ký đến `url_callback` (truyền `url_callback` — bắt buộc, thuộc tên miền website đã đăng ký — cùng `currency`, `network`, tùy chọn `status`, `order_id`) để gỡ lỗi code xác minh của bạn.

- Trang quản trị “Nhà phát triển › Tích hợp & khóa” → “Gửi thông báo thử”: gửi một thông báo mẫu đến URL webhook của bạn.

- Trang quản trị “Nhà phát triển › Nhật ký webhook”: mọi lần gửi kèm thời gian, URL đích, trạng thái HTTP, thời lượng, nội dung yêu cầu và phản hồi của máy chủ bạn (4 KB đầu); lọc theo đơn hoặc trạng thái và gửi lại chỉ với một cú nhấp. Lưu trong 30 ngày.

### Chuyển từ Cryptomus

- Trang quản trị “Nhà phát triển › Tích hợp & khóa” → tạo merchant và API key.

- Thay `https://api.cryptomus.com` trong code của bạn bằng `https://blockwallets.io`, và dùng merchant và API key mới.

- Thêm tên miền của bạn trong “Tên miền website” (`url_callback` / `url_return` / `url_success` chỉ chấp nhận các tên miền này).

Được hỗ trợ: `/v1/payment`, `/v1/payment/info`, `/v1/payment/list`, `/v1/payment/services`, `/v1/payment/resend`, `/v1/wallet`, `/v1/test-webhook/payment`, `/v1/test-webhook/wallet`. Khác biệt: hóa đơn chỉ định giá bằng USD / USDT / USDC (đều 1:1, không chênh lệch tỷ giá); `commission` là phí nền tảng của chúng tôi (số tiền đã nhận × mức phí của người bán lúc tạo hóa đơn — 1% tiêu chuẩn, giảm tới 0.5% theo doanh số; phần khách đã chịu không bị tính hai lần) và `merchant_amount` = đã nhận − `commission`; khi khách trả bằng BTC / LTC / DOGE / BCH / TRX / BNB / ETH / POL / AVAX / OKB / SOL / TON, chúng tôi báo giá theo trung vị của nhiều sàn, chốt trong 30 phút — `payer_amount` là số coin cần gửi, `payer_amount_exchange_rate` là giá trị USD của một coin, và khoản thanh toán được quy đổi sang `payment_amount_usd` theo tỷ giá đã chốt (coin vẫn nằm ở chúng tôi, không đổi sang coin khác); các mạng `tron` / `bsc` / `eth` / `base` / `polygon` / `arbitrum` / `optimism` / `avalanche` / `xlayer` / `btc` / `ltc` / `doge` / `bch` / `sol` / `ton`; thời hạn hóa đơn tối thiểu 10 phút; không có `is_refresh` và không chặn được ví tĩnh; mặc định webhook đến từ các IP thay đổi; nếu bạn dùng danh sách cho phép theo IP, hãy bật “Webhook từ IP cố định” trong trang quản trị tại “Nhà phát triển › Tích hợp & khóa” và webhook sẽ chỉ đến từ `18.163.145.41` — danh sách cho phép chỉ là một lớp bổ sung, hãy tiếp tục xác minh `sign`.

Đến từ **NOWPayments** hoặc **Coinbase Commerce**? Chúng tôi có API tương thích cho cả hai — chỉ cần đổi URL gốc và khóa; xem [hướng dẫn chuyển đổi](https://blockwallets.io/docs/migrate?lang=vi).

### Mạng và số tiền

| Coin | Mạng | Tham số network | Tối thiểu | Thời gian đến |
| --- | --- | --- | --- | --- |
| USDT | TRON | `tron` | $1 | ~1 phút |
| USDT | BNB Chain | `bsc` | $1 | ~1 phút |
| USDC | BNB Chain | `bsc` | $1 | ~1 phút |
| USDT | Ethereum | `eth` | $5 | ~1 phút |
| USDC | Ethereum | `eth` | $5 | ~1 phút |
| USDC | Base | `base` | $1 | ~1 phút |
| USDT | Polygon | `polygon` | $1 | ~1 phút |
| USDC | Polygon | `polygon` | $1 | ~1 phút |
| USDT | Arbitrum | `arbitrum` | $1 | ~1 phút |
| USDC | Arbitrum | `arbitrum` | $1 | ~1 phút |
| USDT | Optimism | `optimism` | $1 | ~1 phút |
| USDC | Optimism | `optimism` | $1 | ~1 phút |
| USDT | Avalanche | `avalanche` | $1 | ~1 phút |
| USDC | Avalanche | `avalanche` | $1 | ~1 phút |
| USDT | X Layer | `xlayer` | $1 | ~1 phút |
| USDC | X Layer | `xlayer` | $1 | ~1 phút |
| USD1 | BNB Chain | `bsc` | $1 | ~1 phút |
| FDUSD | BNB Chain | `bsc` | $1 | ~1 phút |
| DAI | Ethereum | `eth` | $5 | ~1 phút |
| DAI | Arbitrum | `arbitrum` | $1 | ~1 phút |
| DAI | Base | `base` | $1 | ~1 phút |
| BTC | Bitcoin | `btc` | $5 | ~10 phút |
| LTC | Litecoin | `ltc` | $1 | ~3 phút |
| DOGE | Dogecoin | `doge` | $1 | ~1 phút |
| BCH | Bitcoin Cash | `bch` | $1 | ~10 phút |
| TRX | TRON | `tron` | $1 | ~1 phút |
| BNB | BNB Chain | `bsc` | $1 | ~1 phút |
| ETH | Ethereum | `eth` | $5 | ~1 phút |
| ETH | Base | `base` | $1 | ~1 phút |
| ETH | Arbitrum | `arbitrum` | $1 | ~1 phút |
| ETH | Optimism | `optimism` | $1 | ~1 phút |
| POL | Polygon | `polygon` | $1 | ~1 phút |
| AVAX | Avalanche | `avalanche` | $1 | ~1 phút |
| OKB | X Layer | `xlayer` | $1 | ~1 phút |
| USDT | Solana | `sol` | $1 | ~1 phút |
| USDC | Solana | `sol` | $1 | ~1 phút |
| SOL | Solana | `sol` | $1 | ~1 phút |
| USDT | TON | `ton` | $1 | ~1 phút |
| TON | TON | `ton` | $1 | ~1 phút |

Phí ERC-20 cao nên mức tối thiểu là $5; phí Bitcoin cũng không thấp, tối thiểu $5; còn lại là $1. Khách phải gửi trên đúng mạng hiển thị ở trang thanh toán — tiền gửi trên mạng khác sẽ không được ghi có tự động. BTC / LTC / DOGE / BCH hiện “đã phát hiện” ngay khi vào mempool và chỉ được tính khi đủ số xác nhận; giao dịch bị thay thế (RBF) hoặc bị loại bỏ thì không được tính. Coin gốc (TRX, BNB, ETH, POL, AVAX, OKB, SOL, TON) được báo giá theo tỷ giá đã chốt và ghi có theo đô la Mỹ như BTC / LTC / DOGE / BCH; hãy chỉ rõ cả mạng và coin: hóa đơn `network: "bsc", to_currency: "BNB"`, ví tĩnh `network: "bsc", currency: "BNB"`. Chỉ ghi mạng thì mặc định là USDT trên chuỗi đó (USDC trên Base). Khoản chuyển do hợp đồng thông minh thực hiện (multisig, chi trả hàng loạt) cũng được nhận; trên một số chuỗi không có mã băm giao dịch của chúng, và `txid` trong webhook là `null`.

### Lỗi

```json
{ "state": 1, "message": "amount must be a number like 10.28" }
```

| HTTP | Nguyên nhân thường gặp |
| --- | --- |
| 422 | Thiếu hoặc sai định dạng tham số; số tiền dưới mức tối thiểu của mạng; URL callback / chuyển hướng không thuộc các tên miền đã đăng ký; coin / mạng không được hỗ trợ ở `/v1/payment` |
| 401 | Sai merchant, sai `sign` (`Invalid Sign.`) hoặc API key đã được đổi |
| 404 | Hóa đơn không tồn tại hoặc không phải của bạn (`Payment not found`); ở `/v1/wallet`, tổ hợp coin / mạng không được hỗ trợ (`The service was not found`) |
| 503 | Mạng tạm thời không khả dụng — hãy chọn mạng khác hoặc đừng chọn trước |

Khi chữ ký sai (`Invalid Sign.`), phản hồi kèm một đối tượng `debug`: `body_base64` là base64 của nội dung yêu cầu chúng tôi nhận được (là nội dung của chính bạn, không bao giờ chứa API key) và `body_bytes` là độ dài của nó. Hãy so với base64 bạn đã ký: nếu khác nhau, nội dung bạn gửi không phải chuỗi bạn đã ký (framework của bạn tuần tự hóa lại, proxy đổi mã hóa, thừa ký tự xuống dòng ở cuối); nếu giống nhau thì API key sai. Dán vào [công cụ kiểm tra chữ ký](https://blockwallets.io/docs/sign?lang=vi) để thấy điểm khác biệt đầu tiên.

```json
{
  "state": 1,
  "message": "Invalid Sign.",
  "debug": {
    "body_base64": "eyJhbW91bnQiOiI0Mi4wMCIsImN1cnJlbmN5IjoiVVNEIiwi…",
    "body_bytes": 158,
    "sign_formula": "md5(body_base64 + API key)"
  }
}
```

### Hỏi đáp

#### Khách nạp số tiền tùy ý — dùng chế độ nào?

Hóa đơn. Để khách nhập số tiền trên trang nạp tiền của bạn, tạo hóa đơn cho số đó, và ghi có `payment_amount_usd` khi tiền đến. Chỉ dùng ví tĩnh nếu khách nên lưu một địa chỉ và chuyển vào đó nhiều lần.

#### Nếu khách trả thiếu thì sao?

Trước khi hết hạn, khách có thể chuyển phần chênh lệch ngay trên trang thanh toán (trang hiển thị còn thiếu bao nhiêu; trạng thái `wrong_amount_waiting`, và nếu lúc nhận khoản thiếu đầu tiên còn dưới 30 phút thì hạn chót được dời đến 30 phút sau đó). Nếu đến hạn vẫn thiếu, hóa đơn chuyển thành `wrong_amount` và `payment_amount_usd` trong webhook là số tiền đã nhận; dịch vụ nạp tiền có thể ghi có đúng số đó.

#### Nếu khách trả muộn, hoặc trả hai lần thì sao?

Tiền đến sau khi hết hạn vẫn được ghi có và thông báo; tiền đến sau khi hóa đơn đã thanh toán đủ cũng được ghi lại và xem được trong trang quản trị.

#### Nếu khách gửi nhầm mạng thì sao?

Ví dụ hóa đơn trên TRON mà họ gửi từ BNB Chain: khoản đó không được ghi có tự động — hãy liên hệ chúng tôi để xử lý thủ công.

#### Chi phí bao nhiêu?

1% trên mỗi khoản thanh toán nhận được, tự giảm theo doanh số tháng trước (0.9% từ $50,000, 0.8% từ $100,000, 0.7% từ $250,000, 0.6% từ $500,000 và 0.5% từ $1,000,000), không phí hằng tháng hay phí mở tài khoản. Khách trả phí mạng khi chuyển; muốn chuyển phí này sang khách, hãy đặt `subtract` khi tạo hóa đơn hoặc đặt mặc định trong trang quản trị. Rút tiền không mất thêm phí. Xem [bảng giá](https://blockwallets.io/pricing?lang=vi).

#### Khi nào tiền về tay tôi?

Tiền được ghi có vào số dư của bạn ngay khi thanh toán được xác nhận. Hãy yêu cầu rút về ví của bạn trong “Tiền › Số dư & rút tiền”; khi chúng tôi duyệt thì chi trả, và giao dịch trên chuỗi sẽ hiện trong lịch sử rút tiền của bạn.

Bạn cũng có thể bật rút tự động: khi số dư khả dụng đạt mức bạn đặt, hoặc mỗi ngày một lần lúc 00:00 UTC, toàn bộ số dư khả dụng sẽ được rút. Rút tự động cũng được kiểm duyệt như vậy, và mỗi lần đổi cài đặt đều có thông báo gửi đến email của bạn.
