# Ví tĩnh

> Các địa chỉ nạp tiền vĩnh viễn gắn với một đơn hàng hoặc người dùng cụ thể, hoàn hảo cho các thanh toán định kỳ và dài hạn.

Ví tĩnh là các địa chỉ vĩnh viễn để nhận thanh toán tiền điện tử. Chúng được liên kết với một `order_id` cụ thể và là duy nhất theo tổ hợp `project_id + order_id + currency + network`.

Sử dụng ví tĩnh cho:

- Các khoản nạp định kỳ từ cùng một người dùng
- Địa chỉ thanh toán dài hạn hiển thị trên hồ sơ người dùng
- Các luồng nạp tiền có khối lượng lớn khi bạn muốn có địa chỉ ổn định cho mỗi người dùng

## Tạo ví tĩnh

`POST /v1/static-wallet`

### Tham số yêu cầu

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `currency` | string | yes | Tiền điện tử (USDT, BTC, ETH, v.v.) |
| `network` | string | yes | Mã mạng |
| `order_id` | string | yes | ID đơn hàng / người dùng của bạn (tối đa 255 ký tự) |
| `label` | string | no | Nhãn ví (tối đa 255 ký tự) |
| `url_callback` | string | yes | URL nhận thông báo webhook |
| `invite_code` | string | no | Mã người giới thiệu |

### Ví dụ yêu cầu

```json
{
  "currency": "USDT",
  "network": "TRX-TRC20",
  "order_id": "USER-123",
  "label": "User deposit #123",
  "url_callback": "https://your-site.com/webhook/static"
}
```

### Ví dụ phản hồi

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "address": "TXYZabc123...",
    "currency": "USDT",
    "network": "TRX-TRC20",
    "label": "User deposit #123",
    "order_id": "USER-123",
    "status": "active",
    "url": "https://go.2328.io/static/019b2265-34d8-7001-a230-8f97de90d481",
    "created_at": "2026-01-20T12:00:00Z",
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

## Thông tin ví

Lấy thông tin ví tĩnh theo `uuid` hoặc `address`.

`POST /v1/static-wallet/info`

### Tham số yêu cầu

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `uuid` | string | yes* | UUID ví tĩnh |
| `address` | string | yes* | Địa chỉ ví blockchain |

> **INFO:** Phải cung cấp ít nhất một trong `uuid` hoặc `address`.

### Ví dụ phản hồi

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "address": "TXYZabc123...",
    "currency": "USDT",
    "network": "TRX-TRC20",
    "status": "active",
    "total_received": "1250.50",
    "transactions_count": 3,
    "created_at": "2026-01-20T12:00:00Z",
    "qr": "data:image/png;base64,iVBORw0..."
  }
}
```

- `total_received` — tổng tất cả các khoản nạp ví này đã nhận, theo `currency`.
- `transactions_count` — số lần nạp đã nhận tới thời điểm hiện tại.
- `qr` — data URI QR mã hóa Base64 của địa chỉ nạp tiền (luôn có sẵn cho ví tĩnh, vì địa chỉ được gán ngay khi tạo).

## Danh sách ví

`POST /v1/static-wallet/list`

### Tham số yêu cầu

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `status` | string | no | Lọc theo trạng thái (`active`, `inactive`) |
| `currency` | string | no | Lọc theo đồng tiền |
| `network` | string | no | Lọc theo mạng |
| `order_id` | string | no | Lọc theo order_id |
| `page` | int | no | Số trang (mặc định: 1) |
| `per_page` | int | no | Số mục mỗi trang (mặc định: 20, tối đa: 100) |

### Ví dụ phản hồi

```json
{
  "state": 0,
  "result": {
    "items": [
      {
        "uuid": "019b2265-...",
        "address": "TXYZabc123...",
        "currency": "USDT",
        "network": "TRX-TRC20",
        "status": "active",
        "total_received": "1250.50",
        "transactions_count": 3
      }
    ],
    "paginate": {
      "count": 1,
      "current_page": 1,
      "per_page": 20,
      "total": 1,
      "total_pages": 1,
      "has_more": false
    }
  }
}
```

## Bật / tắt ví

Bật/tắt việc một ví tĩnh có chấp nhận thanh toán mới hay không.

`POST /v1/static-wallet/disable`

`POST /v1/static-wallet/enable`

### Yêu cầu

Cả hai endpoint đều nhận một tham số duy nhất:

```json
{
  "uuid": "019b2265-34d8-7001-a230-8f97de90d481"
}
```

### Ví dụ phản hồi

```json
{
  "state": 0,
  "result": {
    "uuid": "019b2265-34d8-7001-a230-8f97de90d481",
    "status": "inactive",
    "message": "Static wallet disabled successfully"
  }
}
```

Đối với `enable`, `status` là `"active"` và `message` là `"Static wallet enabled successfully"`.

## Giao dịch của ví

Lấy danh sách tất cả các khoản nạp đã nhận của một ví tĩnh.

`POST /v1/static-wallet/transactions`

### Tham số yêu cầu

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `uuid` | string | yes | UUID ví tĩnh |
| `date_from` | date | no | Ngày bắt đầu (YYYY-MM-DD) |
| `date_to` | date | no | Ngày kết thúc (YYYY-MM-DD) |
| `page` | int | no | Số trang (mặc định: 1) |
| `per_page` | int | no | Số mục mỗi trang (mặc định: 15, tối đa: 5000) |

### Ví dụ phản hồi

```json
{
  "state": 0,
  "result": {
    "items": [
      {
        "uuid": "abc123-def456-...",
        "order_id": "USER-123",
        "amount": "100.00",
        "currency": "USDT",
        "payment_status": "paid",
        "txid": "0xabc123def456...",
        "fee_amount": "3.00",
        "net_amount": "97.00",
        "created_at": "2026-01-20T15:30:00Z"
      }
    ],
    "paginate": {
      "count": 1,
      "hasPages": true,
      "perPage": 15,
      "page": 1
    }
  }
}
```

- `fee_amount` — phí nền tảng được trừ từ khoản nạp này, theo `currency`.
- `net_amount` — số tiền được ghi có vào số dư merchant sau khi trừ phí.

## Webhook ví tĩnh

Khi một thanh toán được nhận trên ví tĩnh, hệ thống sẽ gửi webhook đến `url_callback`.

> **WARNING:** Định dạng webhook cho ví tĩnh khác với webhook thanh toán thông thường. Đáng chú ý, webhook ví tĩnh có trường `merchant_amount` mà bạn nên dùng để ghi có.

### Payload webhook

```json
{
  "uuid": "a28b293f-5c76-4053-8062-ae9ca4ab784b",
  "order_id": "USER-7666308594",
  "amount": "10.00000000",
  "currency": "USDT",
  "amount_usd": "10.00000000",
  "exchange_rate": "1.00000000",
  "payer_currency": "USDT",
  "payer_amount": "10.00000000",
  "network": "TRX-TRC20",
  "address": "TMU9Tgpchvgbywkbj5SdC8KJS73t5m3M7G",
  "payment_status": "paid",
  "txid": "8369ede26a0da05b1bae154b4bb4072eb2453db30ba86b21831902670929454f",
  "tx_explorer_url": "https://tronscan.org/#/transaction/8369ede26a0da05b1bae154b4bb4072eb2453db30ba86b21831902670929454f",
  "payment_amount": "10.00000000",
  "merchant_amount": "9.920000000000000000",
  "created_at": "2026-05-09T16:13:04+03:00",
  "sign": "dd958d1405febce670a9a196e9141784b9f2a5f39cd6d1832d6f3f68d0de1e10"
}
```

> **INFO:** Webhook ví tĩnh **không** bao gồm `url` hoặc `expires_at` (vì địa chỉ là vĩnh viễn, không phải phiên). Chúng **có** bao gồm `exchange_rate` và `created_at`.

### Tham chiếu trường

| Field | Type | Description |
|-------|------|-------------|
| `uuid` | string | UUID giao dịch (hóa đơn) cho khoản nạp này |
| `order_id` | string | `order_id` của ví tĩnh của bạn |
| `amount` | decimal (8 dp) | Số crypto đã nhận |
| `currency` | string | Crypto đã nhận (khớp với `currency` của ví) |
| `amount_usd` | decimal (8 dp) | Giá trị USD tại thời điểm nhận |
| `exchange_rate` | decimal | Tỷ giá Crypto / USD đã sử dụng |
| `payer_currency` | string | Giống `currency` đối với ví tĩnh |
| `payer_amount` | decimal (8 dp) | Giống `amount` đối với ví tĩnh |
| `network` | string | Mạng blockchain |
| `address` | string | Địa chỉ ví tĩnh |
| `payment_status` | string | Tr?ng th?i deposit hi?n t?i; th??ng l? `paid`, nh?ng AML c? th? t?o `aml_lock` v? kh?ng ???c t? ??ng ghi c? |
| `txid` | string | Hash giao dịch blockchain |
| `tx_explorer_url` | string \| null | URL giao dịch trên trình khám phá blockchain. Là `null` khi không có `txid` hoặc giao dịch là chuyển P2P nội bộ. |
| `payment_amount` | decimal (8 dp) | Giống `amount` |
| `merchant_amount` | decimal (18 dp) | **Số tiền sau khi trừ phí** — dùng giá trị này để ghi có |
| `created_at` | string (ISO 8601) | Thời điểm khoản nạp được nhận |
| `sign` | string (hex) | Chữ ký HMAC-SHA256 của payload |

## Best practices

- **`order_id` duy nhất** — Sử dụng `order_id` duy nhất cho mỗi người dùng hoặc đơn hàng
- **Idempotency** — Kiểm tra `txid` trước khi xử lý để tránh ghi có trùng lặp
- **Xác minh chữ ký** — LUÔN xác minh chữ ký `sign` trước khi ghi có tiền
- **Sử dụng `merchant_amount`** — Ghi có cho người dùng dựa trên `merchant_amount`, không phải `payment_amount`

## Vòng đời và tính không thay đổi

Ví tĩnh là một định danh gửi tiền có thể tái sử dụng, không phải là hóa đơn. Nó không có số tiền dự kiến và không có hạn sử dụng. Một địa chỉ có thể tạo ra bất kỳ số lượng giao dịch gửi tiền nào trong suốt vòng đời của nó.

Việc tạo lập là không thay đổi đối với cùng một dự án thương nhân, `order_id`, `currency`, và `network`: ví hiện có sẽ được trả về. Giữ bộ ba đó ổn định và lưu giữ ví được trả về `uuid`; không sử dụng một `order_id` mới mỗi lần cùng một khách hàng mở màn hình gửi tiền.

Tính không thay đổi của giao dịch gửi tiền khác với tính không thay đổi của ví:

- `order_id` xác định ánh xạ ví/khách hàng có thể tái sử dụng;
- ví `uuid` xác định bản ghi ví vĩnh viễn;
- webhook `uuid` xác định một giao dịch nạp tiền được phát hiện;
- `txid` xác định chuyển khoản trên chuỗi và là khóa loại trùng chính để ghi có.

Sử dụng ràng buộc duy nhất trong cơ sở dữ liệu cho nhận dạng chuỗi/mạng/txid đã xử lý và xác nhận nó trong cùng giao dịch ghi có vào số dư nội bộ của khách hàng.

## Kích hoạt và vô hiệu hóa ngữ nghĩa

Vô hiệu hóa ví ngăn ứng dụng xử lý nó như một mục tiêu nạp tiền đang hoạt động; điều này không xóa địa chỉ hoặc lịch sử của nó và không thể ngăn chặn chuyển khoản trên blockchain đã được người dùng gửi.

> **DANGER:** Không bao giờ nói với người dùng rằng các quỹ gửi đến một địa chỉ không hoạt động sẽ tự động được hoàn trả. Các giao dịch blockchain là không thể đảo ngược. Chỉ vô hiệu hóa sau khi đã xóa địa chỉ khỏi giao diện người dùng của bạn, và giữ một quy trình phục hồi hoạt động cho các khoản gửi muộn.

Việc bật lại giữ nguyên danh tính ví và địa chỉ. Không tạo ví thay thế chỉ để thay đổi nhãn; nhãn không phải là định danh thanh toán.

## Các trường hợp đặc biệt của ví tĩnh

| Tình huống | Xử lý đúng |
|-----------|------------------|
| Yêu cầu tạo trùng lặp | Chấp nhận ví hiện có được trả về và xác minh bộ dữ liệu đã lưu của nó thay vì mong đợi một địa chỉ mới. |
| Nhiều khoản gửi đến một địa chỉ | Tạo một hàng tiền gửi địa phương riêng cho mỗi giao dịch `uuid`/`txid`; không bao giờ đánh dấu ví là “đã thanh toán.” |
| Webhook trùng lặp | Trả về HTTP 200 sau khi tìm thấy txid đã được cam kết; không bao giờ ghi có lại. |
| Trễ xác nhận hoặc quan sát lại chuỗi | Giữ cho việc xử lý không thay đổi trạng thái và đối chiếu từ `/v1/static-wallet/transactions`. |
| Tiền gửi dưới mức tự động chuyển đổi tối thiểu | Đợi ghi có tiền tệ nguồn mà không có khối `convert` hoàn tất. |
| Tự động chuyển đổi thành công | Lưu trữ giá trị thanh toán nguồn và kết quả `convert` đích riêng biệt. |
| Token sai hoặc mạng sai | Không tạo tín dụng giả. Ghi lại bằng chứng và chuyển lên bộ phận hỗ trợ/khôi phục vì khả năng khôi phục phụ thuộc vào từng chuỗi. |
| Chuỗi dựa trên memo/nhãn | Hiển thị và xác minh mọi trường đích được nền tảng trả về; chỉ một địa chỉ có thể là không đủ khi cần memo. |
| Khóa AML | Không ghi có cho người dùng cuối cho đến khi trạng thái chính thức được phát hành thông qua quy trình tuân thủ. |
| Ví bị vô hiệu hóa sau khi hiển thị địa chỉ | Xóa nó khỏi giao diện người dùng ngay lập tức, nhưng tiếp tục theo dõi cảnh báo vận hành đối với các chuyển khoản bị trễ. |

## Mô hình đối chiếu

Chạy một công việc định kỳ mà duyệt qua `/v1/static-wallet/transactions`, cập nhật hoặc chèn các khoản tiền gửi theo txid, và so sánh `merchant_amount`, trạng thái, và kết quả chuyển đổi tùy chọn của chúng với sổ cái nội bộ của bạn. Việc gửi webhook nên làm cho việc đối chiếu nhanh chóng, nhưng đối chiếu phải đảm bảo đầy đủ.