# Thanh toán cho RPC bằng USDC / USDT / USDG: nạp tiền theo chương trình cho AI Agent

> Source: https://docs.blockvectra.com/vi/guides/agent-topup/

Nhà phát triển và AI Agent có thể nạp tiền vào tài khoản RPC và Data API qua HTTP: kiểm tra các mạng và token đang mở, sử dụng một API key hiện có để lấy địa chỉ nạp tiền EVM của tài khoản, sau đó polling trạng thái ghi có sau khi chuyển tiền. Trước khi nạp tiền, hãy xem [trang bảng giá](https://blockvectra.com/en/pricing/) và [ước tính chi phí RPC và Data API từ trọng số CU](https://docs.blockvectra.com/en/guides/reading-cu-pricing/).

## Lấy địa chỉ nạp tiền của bạn trong phần Thanh toán

Đăng nhập, [mở phần Thanh toán để lấy địa chỉ nạp tiền của bạn](https://console.blockvectra.com/login/?next=%2Fbilling%2F), và sử dụng địa chỉ nạp tiền cùng chi tiết token hiển thị cho tài khoản của bạn. Kiểm tra các mạng, token hiện tại và mức nạp tối thiểu tại [GET /v1/topup/status](https://api.blockvectra.com/v1/topup/status) trước khi chuyển tiền.

> **Bảo mật API key và yêu cầu phía máy chủ**
>
> Header `x-api-key` **chỉ có thể được gọi từ các môi trường phía máy chủ**. Không bao giờ gọi các endpoint nạp tiền từ mã trình duyệt phía client, và không bao giờ để lộ API key của bạn trong các bundle frontend, kho lưu trữ công khai hoặc các cuộc trò chuyện AI.


## Điều kiện tiên quyết

* **API key hiện có**: Việc gọi các endpoint nạp tiền đã xác thực yêu cầu một API key BlockVectra RPC đang hoạt động. Nếu bạn chưa có API key, hãy làm theo [Hướng dẫn đăng ký theo chương trình](https://docs.blockvectra.com/en/guides/programmatic-signup/) để đăng ký và tạo key bằng chữ ký ví Ethereum, hoặc tạo một key trong [Console](https://console.blockvectra.com/login/?next=%2Fkeys%2F).
* **Tài sản on-chain**: Môi trường agent hoặc ví nạp tiền của bạn phải nắm giữ USDC / USDT / USDG được liệt kê bởi `GET /v1/topup/status` trên một mạng được hỗ trợ, cùng với đủ token gas gốc để phát sóng các giao dịch.
* **Biến môi trường**: Lưu trữ key của bạn trong biến môi trường `BLOCKVECTRA_API_KEY`.

Các endpoint nạp tiền đã xác thực chấp nhận header `x-api-key` trực tiếp bằng cách sử dụng cùng một API key dùng cho các lệnh gọi RPC. Không yêu cầu phiên trình duyệt.

## Quy trình nạp tiền bốn bước

Sau khi khoản nạp tiền đầu tiên được ghi có, các đợt nạp lại chu kỳ miễn phí sẽ dừng lại, các khoản tín dụng miễn phí chưa sử dụng vẫn khả dụng, và giới hạn tốc độ gọi ở cấp tài khoản sẽ được gỡ bỏ; giới hạn tốc độ cho mỗi key vẫn không đổi. Xem [quy tắc định giá](https://blockvectra.com/en/pricing/) và [quy tắc gói miễn phí](https://blockvectra.com/en/free/#rules); đọc các giới hạn hiện tại và mức nạp tối thiểu từ [GET /v1/plans](https://console-api.blockvectra.com/v1/plans) (`free`, `key_defaults`, và `pricing.min_topup_usd`).

Các endpoint nạp tiền (trạng thái, địa chỉ nạp tiền và các khoản nạp) sử dụng host API production:

```
https://api.blockvectra.com
```

Các giới hạn gói và thông số định giá được phục vụ bởi Console API tại `https://console-api.blockvectra.com` (chẳng hạn như [GET https://console-api.blockvectra.com/v1/plans](https://console-api.blockvectra.com/v1/plans)).

### 1. Kiểm tra tính khả dụng (GET /v1/topup/status)

Trước khi khởi tạo giao dịch chuyển tiền, hãy xác minh trạng thái nạp tiền toàn cầu, kiểm tra mạng và token nào hiện đang mở, đồng thời đọc ngưỡng nạp tiền tối thiểu đang hoạt động. Endpoint này là công khai và không yêu cầu thông tin xác thực.

```bash
curl -s https://api.blockvectra.com/v1/topup/status
```

Phản hồi mẫu (các mạng và token được chọn):

```json
{
  "enabled": true,
  "networks": [
    {
      "network": "base_mainnet",
      "chain_id": 8453,
      "token": "USDC",
      "enabled": true
    },
    {
      "network": "bsc_mainnet",
      "chain_id": 56,
      "token": "USDT",
      "enabled": true
    },
    {
      "network": "bsc_mainnet",
      "chain_id": 56,
      "token": "USDC",
      "enabled": true
    }
  ]
}
```

* `enabled`: Công tắc toàn cầu. Nếu là `false`, việc nạp tiền sẽ bị đóng trên tất cả các mạng.
* `networks`: Trạng thái mở của từng mạng và token. Khi `enabled` là `false` đối với một mạng hoặc token, **không chuyển tiền trên mạng đó**.
* `min_deposit_usd`: Số tiền nạp tối thiểu toàn cầu tính bằng USD được định dạng đến 6 chữ số thập phân. Ngưỡng nạp tiền tối thiểu là động: luôn tham khảo `min_deposit_usd` được trả về theo thời gian thực bởi `GET https://api.blockvectra.com/v1/topup/status`.

Để đọc trực tiếp `min_deposit_usd` đang hoạt động:

```bash
curl -s https://api.blockvectra.com/v1/topup/status | jq -r .min_deposit_usd
```

### 2. Lấy địa chỉ nạp tiền và các tham số (GET /v1/topup/deposit-address)

Lấy hoặc phân bổ địa chỉ nạp tiền EVM của khách hàng và kiểm tra các mạng cùng hợp đồng token được hỗ trợ. Endpoint này yêu cầu xác thực bằng `x-api-key` và chỉ được gọi từ các môi trường phía máy chủ.

```bash
curl -s \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  https://api.blockvectra.com/v1/topup/deposit-address
```

Phản hồi mẫu (các mạng và token được chọn):

```json
{
  "address": "0x<your-dedicated-deposit-address>",
  "deposits_url": "https://api.blockvectra.com/v1/topup/deposits",
  "networks": [
    {
      "chain": "base_mainnet",
      "chain_id": 8453,
      "name": "Base",
      "typical_credit_seconds": 30,
      "explorer_tx_url": "https://basescan.org/tx/{tx_hash}",
      "tokens": [
        {
          "symbol": "USDC",
          "contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
          "decimals": 6,
          "min_amount_raw": "1000000"
        }
      ]
    },
    {
      "chain": "bsc_mainnet",
      "chain_id": 56,
      "name": "BNB Smart Chain",
      "typical_credit_seconds": 60,
      "explorer_tx_url": "https://bscscan.com/tx/{tx_hash}",
      "tokens": [
        {
          "symbol": "USDT",
          "contract": "0x55d398326f99059fF775485246999027B3197955",
          "decimals": 18,
          "min_amount_raw": "1000000000000000000"
        },
        {
          "symbol": "USDC",
          "contract": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
          "decimals": 18,
          "min_amount_raw": "1000000000000000000"
        }
      ]
    }
  ]
}
```

* `address`: Địa chỉ nạp tiền EVM có checksum EIP-55 dành riêng cho tài khoản của bạn.
* `deposits_url`: URL để truy vấn các bản ghi nạp tiền của khách hàng.
* `networks`: Danh sách các mạng EVM đang mở. Các mạng đã đóng sẽ bị bỏ qua. Bao gồm slug chuỗi `chain`, Chain ID EVM `chain_id`, tên hiển thị `name`, độ trễ ghi có thông thường tính bằng giây sau khi đưa vào khối `typical_credit_seconds`, và mẫu URL giao dịch trên trình khám phá khối `explorer_tx_url`.
* `tokens`: Các token trên mạng này, bao gồm ký hiệu token `symbol` (USDC / USDT / USDG), địa chỉ hợp đồng `contract`, số chữ số thập phân của token `decimals`, và số tiền nạp tối thiểu theo đơn vị nguyên tử thô `min_amount_raw` (tham khảo giá trị thực tế do endpoint trả về; không giả định một số tiền đã chia tỷ lệ).

> **Số thập phân của token và quy đổi số tiền**
>
> Cùng một token có thể có số chữ số thập phân khác nhau trên các chuỗi khác nhau (ví dụ: USDT và USDC trên BSC có 18 chữ số thập phân, trong khi USDC trên Base có 6 chữ số thập phân). Việc tính toán số tiền phải sử dụng `decimals` trả về cho mạng cụ thể đó thay vì hardcode một giá trị thập phân token duy nhất.


#### Phản hồi lỗi

Các endpoint nạp tiền đã xác thực (`/v1/topup/deposit-address` và `/v1/topup/deposits`) trả về cấu trúc lỗi JSON tiêu chuẩn:

* **HTTP 401 (Lỗi xác thực)**: Được trả về khi thiếu header `x-api-key` (`missing_api_key`) hoặc key không hợp lệ, đã bị thu hồi hoặc bị vô hiệu hóa (`invalid_api_key`):

```json
{
  "error": {
    "code": "missing_api_key",
    "message": "missing API key: send it in the x-api-key header",
    "data": {
      "reason": "missing_api_key",
      "docs_url": "https://docs.blockvectra.com/en/errors/#missing_api_key",
      "retryable": false
    }
  }
}
```

* **HTTP 409 (Nạp tiền bị vô hiệu hóa)**: Được trả về khi việc nạp tiền bị đóng trên toàn cầu hoặc trên tất cả các mạng (`topup_disabled`):

```json
{
  "error": {
    "code": "topup_disabled",
    "data": {
      "reason": "topup_disabled",
      "docs_url": "https://docs.blockvectra.com/en/errors/#topup_disabled",
      "retryable": false
    }
  }
}
```

Để biết danh sách đầy đủ các mã lỗi, hãy xem [Tài liệu tham khảo lỗi](https://docs.blockvectra.com/en/errors/).

### 3. Phát sóng giao dịch chuyển khoản on-chain

Sử dụng ví hoặc script của agent, gửi một giao dịch `transfer` ERC-20 đến `address` nạp tiền đã lấy ở Bước 2.

Yêu cầu chuyển tiền:

* Chỉ gửi các token và hợp đồng được liệt kê trong mảng `tokens` cho mạng đó.
* Đảm bảo số tiền chuyển lớn hơn hoặc bằng `min_amount_raw` (phụ thuộc vào giá trị thực tế do `GET /v1/topup/deposit-address` trả về, hoặc `min_deposit_usd` do `GET /v1/topup/status` trả về), được định dạng theo `decimals` của token trên mạng đó.
* Các khoản chuyển tiền được gửi đến các chuỗi không được hỗ trợ hoặc gửi sai token sẽ không thể được ghi có tự động; hãy xác minh mạng và hợp đồng token trước khi phát sóng.
* Ghi lại mã băm giao dịch on-chain (`tx_hash`) sau khi gửi.

### 4. Polling bản ghi nạp tiền và xác minh ghi có (GET /v1/topup/deposits)

Sau khi giao dịch được đưa vào một khối, hãy truy vấn lịch sử chuyển nạp tiền để theo dõi trạng thái ghi có. Endpoint này yêu cầu `x-api-key` và chỉ dành cho phía máy chủ.

#### Tham số truy vấn

* `limit`: Số lượng bản ghi nạp tiền trả về trên mỗi trang. Mặc định là `20`, phạm vi hợp lệ là `1`–`100`.
* `before`: Tham số phân trang cursor dựa trên `deposit_id`. Truyền giá trị `next_before` từ phản hồi của trang trước đó để lấy trang tiếp theo của các bản ghi cũ hơn.
* `tx_hash`: Mã băm giao dịch dạng thập lục phân 64 ký tự tùy chọn có tiền tố 0x để lọc cho một giao dịch chuyển tiền cụ thể.

Lọc theo hash giao dịch (`tx_hash`) để kiểm tra giao dịch chuyển tiền cụ thể của bạn:

```bash
curl -s \
  -H "x-api-key: $BLOCKVECTRA_API_KEY" \
  "https://api.blockvectra.com/v1/topup/deposits?tx_hash=0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
```

Phản hồi mẫu:

```json
{
  "items": [
    {
      "deposit_id": 42,
      "chain": "base_mainnet",
      "chain_id": 8453,
      "token": "USDC",
      "contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "25.000000",
      "tx_hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
      "tx_log_ordinal": 0,
      "block_number": 123456789,
      "external_ref": "eip155:8453:0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890:0",
      "status": "credited",
      "reason": null,
      "credited_units": 250000,
      "credited_cu": 250000000,
      "detected_at": "2026-10-02T10:00:00Z"
    }
  ],
  "next_before": null
}
```

* `items`: Mảng các bản ghi nạp tiền khớp với các tham số truy vấn.
* `next_before`: ID cursor cho trang tiếp theo khi còn nhiều bản ghi hơn, hoặc `null` nếu không còn bản ghi cũ hơn. Kết hợp với tham số truy vấn `before` để phân trang dựa trên cursor.

Các giá trị `status` của khoản nạp tiền:

* `processing`: Giao dịch chuyển tiền được phát hiện on-chain, đang tiến hành ghi có.
* `credited`: Đã được ghi có vào số dư tài khoản. `credited_units` và `credited_cu` biểu thị số lượng đã ghi có.
* `not_credited`: Giao dịch chuyển tiền không thể được ghi có. Trường `reason` cho biết nguyên nhân:
  * `below_minimum`: Số tiền nạp thấp hơn ngưỡng tối thiểu.
  * `large_amount`: Số tiền nạp vượt quá ngưỡng và yêu cầu xét duyệt thủ công.
  * `other`: Ngoại lệ ghi có khác.

Hướng dẫn về độ trễ ghi có và polling:

* **Thời gian đến và ghi có**: Thời gian ghi có được quản lý bởi `typical_credit_seconds` được trả về ở Bước 2.
* **Khoảng thời gian polling**: Polling ở khoảng thời gian khuyến nghị là **mỗi 20–60 giây**, không thường xuyên hơn, để tránh kích hoạt giới hạn tốc độ.

## Ví dụ mã nguồn

Các ví dụ sau minh họa cách đọc `BLOCKVECTRA_API_KEY` từ môi trường và truy vấn các endpoint nạp tiền trong Node.js và Python.

### Node.js (fetch)

```javascript
import process from "node:process";

const apiKey = process.env.BLOCKVECTRA_API_KEY;
if (!apiKey) {
  throw new Error("Missing BLOCKVECTRA_API_KEY environment variable");
}

const BASE_URL = "https://api.blockvectra.com";

// 1. Check availability and read minimum deposit threshold
const statusRes = await fetch(`${BASE_URL}/v1/topup/status`);
const status = await statusRes.json();
if (!status.enabled) {
  throw new Error("Top-up is currently disabled");
}
const minDepositUsd = status.min_deposit_usd;
console.log("Minimum deposit (USD):", minDepositUsd);

// 2. Retrieve deposit address and open networks
const addressRes = await fetch(`${BASE_URL}/v1/topup/deposit-address`, {
  headers: { "x-api-key": apiKey },
});

if (addressRes.status === 401) {
  throw new Error("Missing or invalid API key (HTTP 401)");
}
if (addressRes.status === 409) {
  throw new Error("Top-up is disabled (topup_disabled)");
}
if (!addressRes.ok) {
  throw new Error(`Failed to retrieve deposit address: ${addressRes.status}`);
}

const depositData = await addressRes.json();
console.log("Deposit address:", depositData.address);
console.log("Open networks count:", depositData.networks.length);

// 3. Poll deposit status
async function checkDepositStatus(txHash) {
  const url = new URL(`${BASE_URL}/v1/topup/deposits`);
  url.searchParams.set("tx_hash", txHash);

  const res = await fetch(url, {
    headers: { "x-api-key": apiKey },
  });
  if (res.status === 401) {
    throw new Error("Missing or invalid API key (HTTP 401)");
  }
  if (!res.ok) {
    throw new Error(`Failed to query deposits: ${res.status}`);
  }
  return res.json();
}
```

### Python (requests)

```python
# pip install requests
import os
import requests

api_key = os.environ.get("BLOCKVECTRA_API_KEY")
if not api_key:
    raise ValueError("Missing BLOCKVECTRA_API_KEY environment variable")

base_url = "https://api.blockvectra.com"

# 1. Check availability and read minimum deposit threshold
resp = requests.get(f"{base_url}/v1/topup/status", timeout=10)
resp.raise_for_status()
status_data = resp.json()
if not status_data.get("enabled"):
    raise RuntimeError("Top-up is currently disabled")
min_deposit_usd = status_data.get("min_deposit_usd")
print("Minimum deposit (USD):", min_deposit_usd)

# 2. Retrieve deposit address
resp = requests.get(
    f"{base_url}/v1/topup/deposit-address",
    headers={"x-api-key": api_key},
    timeout=10,
)
if resp.status_code == 401:
    raise RuntimeError("Missing or invalid API key (HTTP 401)")
if resp.status_code == 409:
    raise RuntimeError("Top-up is disabled (topup_disabled)")
resp.raise_for_status()
deposit_data = resp.json()
print("Deposit address:", deposit_data["address"])

# 3. Poll deposit status
def check_deposit_status(tx_hash: str):
    resp = requests.get(
        f"{base_url}/v1/topup/deposits",
        headers={"x-api-key": api_key},
        params={"tx_hash": tx_hash},
        timeout=10,
    )
    if resp.status_code == 401:
        raise RuntimeError("Missing or invalid API key (HTTP 401)")
    resp.raise_for_status()
    return resp.json()
```

## Các bước tiếp theo

* [Truy vấn số dư (`GET /v1/account`)](https://docs.blockvectra.com/en/guides/billing-rules/#query-balance-get-v1account) để xác minh số dư tài khoản và Compute Unit (CU) còn lại của bạn.
* [Quy tắc thanh toán](https://docs.blockvectra.com/en/guides/billing-rules/) để xem lại cách đo lường Compute Unit (CU), giới hạn tốc độ và các lỗi không bị tính phí.
* [Hướng dẫn gói miễn phí](https://docs.blockvectra.com/en/guides/free-plan/) để xem lại các giới hạn của gói miễn phí và quy tắc nâng cấp.
* [Hướng dẫn đăng ký theo chương trình](https://docs.blockvectra.com/en/guides/programmatic-signup/) để tạo tài khoản và cung cấp API key bằng chữ ký ví.
