# Tài Liệu Đấu Nối API Chung Sức Liên Quân (v2.0)

Tài liệu kỹ thuật đầy đủ dành cho Đối tác / Shop tích hợp dịch vụ bơm vé **Sự Kiện Chung Sức Liên Quân** tự động 24/7. Hệ thống hỗ trợ song song **2 giao thức kết nối chuẩn**, tự động áp dụng **Chiết khấu giảm giá (%)** theo tài khoản/cấp bậc, chống tạo đơn trùng, tự động hoàn tiền vé lỗi/vé thiếu và bắn **Webhook Callback** thời gian thực.

---

## Mục Lục

1. [Tổng Quan Kiến Trúc & Luồng Xử Lý](#1-tổng-quan-kiến-trúc--luồng-xử-lý)
2. [Bảng Gói Dịch Vụ Chuẩn (`package_code`)](#2-bảng-gói-dịch-vụ-chuẩn-package_code)
3. [Cổng 1 — Merchant API Rechargews (`/api/rechargews` - Ký MD5)](#3-cổng-1--merchant-api-rechargews-apirechargews---ký-md5)
   - [3.1 Quy tắc tạo chữ ký MD5 (`sign`)](#31-quy-tắc-tạo-chữ-ký-md5-sign)
   - [3.2 `getbalance` — Kiểm tra số dư & Chiết khấu](#32-lệnh-getbalance--kiểm-tra-số-dư--chiết-khấu)
   - [3.3 `productlist` — Lấy danh sách gói & giá chiết khấu](#33-lệnh-productlist--lấy-danh-sách-gói--giá-chiết-khấu)
   - [3.4 `topup` / `topup_v2` — Tạo đơn bơm vé Chung Sức](#34-lệnh-topup-hoặc-topup_v2--tạo-đơn-bơm-vé-chung-sức)
   - [3.5 `getstatus` — Tra cứu tiến độ đơn nạp](#35-lệnh-getstatus--tra-cứu-tiến-độ-đơn-nạp)
   - [3.6 `topup_card` & `topup_charge_pool` — Gạch thẻ cào tự động](#36-lệnh-topup_card--topup_charge_pool--gạch-thẻ-cào)
   - [3.7 Bảng mã lỗi chuẩn Merchant API](#37-bảng-mã-lỗi-chuẩn-merchant-api)
4. [Cổng 2 — RESTful Bearer API (`/api/v1/chungsuc/*` - Siêu Gọn Nhẹ)](#4-cổng-2--restful-bearer-api-apiv1chungsuc---siêu-gọn-nhệ)
   - [4.1 Xác thực `USER_TOKEN` (3 cách linh hoạt)](#41-xác-thực-user_token-3-cách-linh-hoạt)
   - [4.2 `GET /api/v1/me` — Thông tin ví & Chiết khấu](#42-get-apiv1me--thông-tin-ví--chiết-khấu)
   - [4.3 `GET /api/v1/chungsuc/packages` — Bảng giá gói realtime](#43-get-apiv1chungsucpackages--bảng-giá-gói-realtime)
   - [4.4 `POST /api/v1/chungsuc/orders` — Tạo 1 hoặc nhiều đơn](#44-post-apiv1chungsucorders--tạo-1-hoặc-nhiều-đơn)
   - [4.5 `GET /api/v1/chungsuc/orders/:orderCode` — Tra cứu tiến độ & Danh sách đơn](#45-get-apiv1chungsucordersordercode--tra-cứu-tiến-độ--danh-sách-đơn)
   - [4.6 `POST /api/v1/chungsuc/orders/:orderCode/cancel` — Hủy đơn chờ & Hoàn tiền](#46-post-apiv1chungsucordersordercodecancel--hủy-đơn-chờ--hoàn-tiền)
   - [4.7 `POST /api/v1/token/reset` — Đổi `USER_TOKEN` mới](#47-post-apiv1tokenreset--đổi-user_token-mới)
5. [Đặc Tả Chi Tiết Webhook Callback (Dùng Chung Cả 2 Cổng)](#5-đặc-tả-chi-tiết-webhook-callback-dùng-chung-cả-2-cổng)
   - [5.1 Cơ chế kích hoạt, Retry & Chữ ký `callback_sign`](#51-cơ-chế-kích-hoạt-retry--chữ-ký-callback_sign)
   - [5.2 Bảng từ điển đầy đủ các trường dữ liệu Callback](#52-bảng-từ-điển-đầy-đủ-các-trường-dữ-liệu-callback)
   - [5.3 Payload JSON mẫu (Hoàn thành & Hoàn tiền)](#53-payload-json-mẫu-hoàn-thành--hoàn-tiền)
6. [Code Mẫu Đấu Nối Hoàn Chỉnh (PHP, Node.js, Python)](#6-code-mẫu-đấu-nối-hoàn-chỉnh-php-nodejs-python)

---

## 1. Tổng Quan Kiến Trúc & Luồng Xử Lý

- **Base URL hệ thống**: `https://chungsuclq.top` *(thay bằng tên miền thực tế của hệ thống khi đấu nối)*.
- **Thông tin xác thực** (lấy tại trang `/api-docs` hoặc `/customer/profile` sau khi đăng nhập):
  - `partner_id` & `partner_key`: Dùng cho cổng **Merchant API (`/api/rechargews`)** và dùng để **kiểm tra chữ ký MD5 của Webhook Callback**.
  - `USER_TOKEN`: Dùng cho cổng **RESTful API (`/api/v1/chungsuc/*`)**.

```mermaid
sequenceDiagram
    participant Shop as Server Đối Tác (Shop)
    participant Gateway as WebAPI Gateway
    participant Engine as Cụm Bơm Vé Đa Luồng

    Shop->>Gateway: 1. Tạo đơn (POST /api/rechargews hoặc POST /api/v1/chungsuc/orders)
    Gateway->>Gateway: Xác thực + Tính chiết khấu (%) + Trừ ví nguyên tử (Atomic)
    Gateway->>Engine: Đẩy mã mời Garena vào hàng đợi bơm vé
    Gateway-->>Shop: Trả ngay order_code, status="pending", pay_amount
    Engine-->>Gateway: Đồng bộ tiến độ runtime mỗi 5 giây (done_tickets / target_tickets)
    Note over Gateway: Tự động hoàn tiền vào ví nếu mã lỗi hoặc thiếu vé
    Gateway->>Shop: 2. Bắn Webhook Callback (completed / failed / canceled + callback_sign)
    Shop-->>Gateway: HTTP 200 OK (Xác nhận đã nhận Callback)
```

> [!TIP]
> **Chọn cổng nào nhanh nhất?**
> - Nếu bạn đang dùng mã nguồn shop game có sẵn module **Rechargews / NapVip**: chọn **Cổng 1 (`POST /api/rechargews`)**.
> - Nếu bạn tự code web/tool/bot Telegram/Discord mới: chọn **Cổng 2 (`POST /api/v1/chungsuc/orders`)** — chỉ cần truyền `USER_TOKEN` và 2 dòng JSON là chạy ngay.

---

## 2. Bảng Gói Dịch Vụ Chuẩn (`package_code`)

- **Mã dịch vụ (`service_code` / `game_key`)**: `chungsuc` (tương thích đồng thời `chungsuc_lq`, `chungsuc-lq`, `lq_chungsuc`).
- **Chiết khấu giảm giá (%)**: Hệ thống tự động lấy mức `%` cao nhất giữa chiết khấu riêng của tài khoản và chiết khấu cấp bậc (`0%` – `100%`).
  `pay_amount = round(price * (100 - discount_percent) / 100)`

| `package_code` | Tên gói | Số lượt giúp | Số vé mục tiêu (`tickets`) | Giá niêm yết gốc (`amount` / `price`) |
| :--- | :--- | :---: | :---: | ---: |
| `goi-38` | 1 Lượt | 1 | 38 vé | 2.000đ |
| `goi-152` | Rương SS | 4 | 152 vé | 5.000đ |
| `goi-320` | Full 3 Rương | 9 | 320 vé | 10.000đ |
| `goi-500` | 500 Vé | 14 | 500 vé | 15.000đ |
| `goi-1000` | 1000 Vé | 27 | 1.000 vé | 25.000đ |
| `goi-5000` | 5000 Vé | 132 | 5.000 vé | 75.000đ |
| `goi-10000` | 10.000 Vé | 264 | 10.000 vé | 150.000đ |
| `custom` | Vé lẻ tùy chỉnh | Tự tính | `38` – `20.000` vé | Theo đơn giá vé lẻ cấu hình trên hệ thống |

> [!NOTE]
> Khách hàng chỉ cần cung cấp **mã mời sự kiện (6–16 ký tự)** hoặc dán **nguyên văn tin nhắn chia sẻ từ trong game Liên Quân** (ví dụ: `Hãy giúp tôi ... --AbCdEf1234-- ...`), hệ thống tự động bóc sạch mã mời `AbCdEf1234` và tuyệt đối không cần tài khoản/mật khẩu game.

---

## 3. Cổng 1 — Merchant API Rechargews (`/api/rechargews` - Ký MD5)

- **Endpoint duy nhất**: `POST https://chungsuclq.top/api/rechargews` *(alias tương thích: `POST /api/v1/rechargews`)*
- **Content-Type hỗ trợ**: `application/json` hoặc `application/x-www-form-urlencoded`

### 3.1 Quy tắc tạo chữ ký MD5 (`sign`)

Nối chuỗi trực tiếp không dấu cách, mã hóa UTF-8 rồi băm `md5` (32 ký tự chữ thường):

- **Nhóm lệnh không có `request_id`** (`getbalance`, `productlist`):
  ```text
  sign = md5(partner_key + partner_id + command)
  ```
- **Nhóm lệnh gắn với đơn giao dịch** (`topup`, `topup_v2`, `getstatus`, `topup_card`, `topup_charge_pool`):
  ```text
  sign = md5(partner_key + partner_id + command + request_id)
  ```

---

### 3.2 Lệnh `getbalance` — Kiểm tra số dư & Chiết khấu

**Request JSON:**
```json
{
  "partner_id": "100001",
  "command": "getbalance",
  "sign": "e10adc3949ba59abbe56e057f20f883e"
}
```

**Response JSON (200 OK):**
```json
{
  "status": "success",
  "message": "success",
  "data": {
    "balance": 500000,
    "discount": 10,
    "currency": "VND"
  }
}
```

---

### 3.3 Lệnh `productlist` — Lấy danh sách gói & giá chiết khấu

**Request JSON:**
```json
{
  "partner_id": "100001",
  "command": "productlist",
  "game_key": "chungsuc",
  "sign": "827ccb0eea8a706c4c34a16891f84e7b"
}
```

**Response JSON (200 OK):**
```json
{
  "status": "success",
  "message": "Thành công",
  "data": [
    {
      "name": "Chung Sức Liên Quân",
      "key": "chungsuc",
      "service_code": "chungsuc",
      "cat_id": 1,
      "type": "game",
      "platform": "garena",
      "discount": 10,
      "items": [
        {
          "name": "1 Lượt (38 vé)",
          "package_code": "goi-38",
          "tickets": 38,
          "turns": 1,
          "value": 2000,
          "price": "2000",
          "pay_amount": 1800,
          "currency_code": "VND",
          "discount": 10
        },
        {
          "name": "Rương SS (152 vé)",
          "package_code": "goi-152",
          "tickets": 152,
          "turns": 4,
          "value": 5000,
          "price": "5000",
          "pay_amount": 4500,
          "currency_code": "VND",
          "discount": 10
        }
      ],
      "options": [
        {
          "key": "invite_code",
          "name": "Mã mời sự kiện Chung Sức Liên Quân",
          "type": "input",
          "format_input": "text"
        }
      ]
    }
  ]
}
```

---

### 3.4 Lệnh `topup` (hoặc `topup_v2`) — Tạo đơn bơm vé Chung Sức

#### Bảng tham số Request:

| Tham số | Kiểu | Bắt buộc | Mô tả chi tiết |
| :--- | :--- | :---: | :--- |
| `partner_id` | `string` | Có | Mã đối tác Merchant của bạn (VD: `"100001"`). |
| `command` | `string` | Có | Giá trị `"topup"` hoặc `"topup_v2"`. |
| `request_id` | `string` | Có | Mã giao dịch duy nhất phía đối tác (chữ, số, `_`, `-`, `:`, `.` tối đa 80 ký tự). Gửi lại cùng `request_id` sẽ trả lại đơn cũ (chống trừ tiền 2 lần). |
| `service_code` | `string` | Không | Mặc định `"chungsuc"` (chấp nhận `"chungsuc_lq"`). |
| `account` | `string \| object` | Có | Mã mời sự kiện hoặc nguyên câu chia sẻ Garena. Chấp nhận chuỗi `"AbCdEf1234"`, hoặc object `{"game_id": "AbCdEf1234"}` / `{"invite_code": "AbCdEf1234"}`, hoặc truyền ở trường `invite_code` / `event_code` / `uid`. |
| `package_code` | `string` | 1 trong 3 | **Cách 1 (Khuyên dùng):** Mã gói (`goi-38` .. `goi-10000`). |
| `amount` | `integer` | 1 trong 3 | **Cách 2:** Mệnh giá gốc của gói (`2000`, `5000`, `10000`, `15000`, `25000`, `75000`, `150000`). |
| `tickets` | `integer` | 1 trong 3 | **Cách 3:** Đặt số vé lẻ tùy chỉnh (VD: `200`). |
| `qty` | `integer` | Không | Số lượng nhân bội gói trên mã mời đó (mặc định `1`, tối đa `50`). |
| `callback_url` | `string` | Không | URL nhận Webhook Callback riêng cho đơn này (bỏ trống sẽ dùng URL mặc định của tài khoản). |
| `sign` | `string` | Có | `md5(partner_key + partner_id + command + request_id)` |

**Request JSON mẫu:**
```json
{
  "partner_id": "100001",
  "command": "topup",
  "request_id": "REQ_20261030_0001",
  "service_code": "chungsuc",
  "package_code": "goi-152",
  "amount": 5000,
  "qty": 1,
  "account": {
    "game_id": "AbCdEf1234"
  },
  "callback_url": "https://shopcuaban.vn/api/callback/rechargews",
  "sign": "d41d8cd98f00b204e9800998ecf8427e"
}
```

**Response JSON (200 OK):**
```json
{
  "status": "success",
  "message": "success",
  "data": {
    "time": "2026-10-30 19:05:10",
    "account": {
      "game_id": "AbCdEf1234",
      "invite_code": "AbCdEf1234"
    },
    "request_id": "REQ_20261030_0001",
    "order_code": "CS-261030-A1B2C3",
    "package_code": "goi-152",
    "package_name": "Rương SS",
    "pay_amount": 4500,
    "discount": 10,
    "service_code": "chungsuc",
    "status": "pending",
    "request_value": 5000,
    "complete_value": 0,
    "remain_value": 5000,
    "target_tickets": 152,
    "done_tickets": 0,
    "remain_tickets": 152,
    "refunded": false,
    "refund_amount": 0
  }
}
```

---

### 3.5 Lệnh `getstatus` — Tra cứu tiến độ đơn nạp

Tra cứu theo `request_id` của đối tác (hoặc truyền mã `order_code` vào `request_id` / `order_code`).

**Request JSON:**
```json
{
  "partner_id": "100001",
  "command": "getstatus",
  "request_id": "REQ_20261030_0001",
  "sign": "098f6bcd4621d373cade4e832627b4f6"
}
```

**Response JSON (200 OK):**
```json
{
  "status": "success",
  "message": "success",
  "data": {
    "time": "2026-10-30 19:05:10",
    "account": {
      "game_id": "AbCdEf1234",
      "invite_code": "AbCdEf1234"
    },
    "request_id": "REQ_20261030_0001",
    "order_code": "CS-261030-A1B2C3",
    "package_code": "goi-152",
    "package_name": "Rương SS",
    "pay_amount": 4500,
    "discount": 10,
    "service_code": "chungsuc",
    "status": "completed",
    "request_value": 5000,
    "complete_value": 5000,
    "remain_value": 0,
    "target_tickets": 152,
    "done_tickets": 152,
    "remain_tickets": 0,
    "refunded": false,
    "refund_amount": 0
  }
}
```

**6 trạng thái chuẩn (`data.status`):**
- `pending`: Chờ nạp / mới khởi tạo.
- `processing`: Đang bơm vé trên hệ thống.
- `completed`: Đã bơm đủ 100% số vé mục tiêu.
- `failed`: Dừng giữa chừng / thiếu vé (phần vé thiếu được hoàn tiền vào `refund_amount`).
- `canceled`: Đã hủy hoặc hoàn tiền 100% do mã mời lỗi.
- `none`: Nháp.

---

### 3.6 Lệnh `topup_card` & `topup_charge_pool` — Gạch thẻ cào

- **`topup_card`**: Gửi thẻ cào gạch tự động (`request_id`, `amount`, và bộ `telco`, `serial`, `pin` hoặc chuỗi `card: "VIETTEL|SERIAL|PIN"`). Trả về `status: 99` khi thẻ đã được tiếp nhận xử lý.
- **`topup_charge_pool`**: Tạo pool chờ nạp thẻ (`request_id`, `game_key`, `target_amount`).

---

### 3.7 Bảng mã lỗi chuẩn Merchant API

Khi xảy ra lỗi, API trả về JSON dạng `{"status": "error", "error_code": 102, "code": 102, "message": "..."}`:

| Mã lỗi (`error_code`) | HTTP Status | Ý nghĩa |
| :---: | :---: | :--- |
| `100` | `404` | Đơn hàng không tồn tại (sai `request_id` hoặc `order_code`). |
| `101` | `400` | `partner_id` không tồn tại hoặc chưa kích hoạt. |
| `102` | `400` | Số dư ví Merchant không đủ thanh toán đơn. |
| `103` | `400` | Sai chữ ký MD5 `sign` hoặc định dạng JSON không hợp lệ. |
| `104` | `400` | Thiếu tham số bắt buộc hoặc mã mời / mã gói / `callback_url` không hợp lệ. |
| `105` | `403` | Tài khoản bị khóa, tắt quyền API hoặc IP máy chủ gọi không nằm trong IP Whitelist. |
| `106` | `400` | Dịch vụ hoặc gói nạp đang tạm bảo trì. |
| `107` | `500` | Lỗi khởi tạo đơn tại máy chủ xử lý (số dư đã được hoàn trả nguyên vẹn 100%). |
| `117` | `400` | Lệnh `command` không được hỗ trợ. |
| `120` | `500` | Lỗi ngoại lệ hệ thống (Exception). |
| `126` | `400` | Số lượng `qty` hoặc số lượng mã vượt giới hạn cho phép. |

---

## 4. Cổng 2 — RESTful Bearer API (`/api/v1/chungsuc/*` - Siêu Gọn Nhẹ)

### 4.1 Xác thực `USER_TOKEN` (3 cách linh hoạt)

Bạn có thể truyền `USER_TOKEN` theo **bất kỳ cách nào** tiện nhất cho ngôn ngữ lập trình của bạn:

1. **Cách 1 (Khuyên dùng):** Header `Authorization: Bearer YOUR_USER_TOKEN`
2. **Cách 2:** Header `X-API-Key: YOUR_USER_TOKEN`
3. **Cách 3 (Tiện cho PHP / Script ngắn):** Truyền trực tiếp `"api_key": "YOUR_USER_TOKEN"` trong JSON Body hoặc Query `?api_key=YOUR_USER_TOKEN`.

---

### 4.2 `GET /api/v1/me` — Thông tin ví & Chiết khấu

- **Alias**: `GET /api/v1/chungsuc/balance`

```bash
curl -X GET "https://chungsuclq.top/api/v1/me" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"
```

**Response JSON:**
```json
{
  "status": "success",
  "ok": true,
  "data": {
    "username": "shopgame247",
    "name": "Shop Game 247",
    "partner_id": "100001",
    "balance": 500000,
    "total_balance": 2000000,
    "ranks": "daily",
    "discount_percent": 10,
    "user_chietkhau": 10
  }
}
```

---

### 4.3 `GET /api/v1/chungsuc/packages` — Bảng giá gói realtime

Lấy toàn bộ gói đang mở bán và cấu hình vé lẻ. Khi truyền kèm `USER_TOKEN`, trường `effective_price` trả về đúng số tiền thực trừ sau chiết khấu của tài khoản bạn.

```bash
curl -X GET "https://chungsuclq.top/api/v1/chungsuc/packages" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"
```

**Response JSON:**
```json
{
  "status": "success",
  "ok": true,
  "data": {
    "service_status": 1,
    "discount_percent": 10,
    "packages": [
      {
        "code": "goi-38",
        "name": "1 Lượt",
        "sub_label": "38 vé",
        "turns": 1,
        "tickets": 38,
        "price": 2000,
        "discount_percent": 10,
        "effective_price": 1800,
        "max_qty": 20
      },
      {
        "code": "goi-152",
        "name": "Rương SS",
        "sub_label": "152 vé",
        "turns": 4,
        "tickets": 152,
        "price": 5000,
        "discount_percent": 10,
        "effective_price": 4500,
        "max_qty": 20
      }
    ],
    "custom_ticket_config": {
      "enabled": true,
      "price_per_ticket": 50,
      "effective_price_per_ticket": 45,
      "vip_price_per_ticket": 60,
      "effective_vip_price_per_ticket": 54,
      "discount_percent": 10,
      "min_tickets": 38,
      "max_tickets": 20000,
      "max_codes_per_request": 20
    }
  }
}
```

---

### 4.4 `POST /api/v1/chungsuc/orders` — Tạo 1 hoặc nhiều đơn

- **Alias**: `POST /api/v1/chungsuc/order` hoặc `POST /api/v1/chungsuc/create`
- **Chống gửi trùng**: Truyền `"request_id"` trong JSON body hoặc Header `X-Idempotency-Key`.

#### Bảng tham số Body JSON:

| Tham số | Kiểu | Bắt buộc | Mô tả & Cách dùng |
| :--- | :--- | :---: | :--- |
| `code` / `codes` | `string \| string[]` | **Có** | Truyền `"code": "AbCdEf1234"` cho 1 đơn lẻ (chấp nhận cả văn bản chia sẻ Garena `--AbCdEf1234--`, hoặc các tên trường `invite_code`, `event_code`, `account`, `uid`). Muốn tạo nhiều đơn cùng lúc truyền `"codes": ["Ma1", "Ma2"]`. |
| `package_code` | `string` | 1 trong 3 | **Cách 1 (Khuyên dùng):** Mã gói chuẩn (`goi-38` .. `goi-10000`). |
| `amount` | `integer` | 1 trong 3 | **Cách 2:** Mệnh giá gốc của gói (`2000`, `5000`, `10000`, `15000`, `25000`, `75000`, `150000`). |
| `tickets` | `integer` | 1 trong 3 | **Cách 3:** Số vé lẻ tùy chỉnh (VD: `200`), tùy chọn kèm `"use_vip_pool": true`. |
| `qty` | `integer` | Không | Hệ số nhân số lượng gói cho mỗi mã mời (mặc định `1`, tối đa `50`). |
| `request_id` | `string` | Không | Mã đơn bên Shop đối tác — chống tạo trùng đơn, dùng để tra cứu đơn trực tiếp và nhận lại trong Webhook Callback. |
| `callback_url` | `string` | Không | URL nhận Webhook Callback khi đơn kết thúc (bỏ trống sẽ tự động dùng URL mặc định trong Hồ sơ). |
| `callback_type` | `string` | Không | `"POST"` (mặc định) hoặc `"GET"`. |
| `callback_post_type` | `string` | Không | `"Json"` (mặc định) hoặc `"Form"` (`application/x-www-form-urlencoded`). |
| `note` | `string` | Không | Ghi chú đơn hàng (tối đa 120 ký tự). |

**Mẫu 1 — Gọi tối giản nhất (1 mã mời):**
```bash
curl -X POST "https://chungsuclq.top/api/v1/chungsuc/orders" \
  -H "Authorization: Bearer YOUR_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "package_code": "goi-152",
    "code": "AbCdEf1234"
  }'
```

**Mẫu 2 — Đầy đủ cho Shop tự động (kèm `request_id` & `callback_url`):**
```json
{
  "package_code": "goi-152",
  "code": "AbCdEf1234",
  "request_id": "SHOP_ORDER_1001",
  "callback_url": "https://shopcuaban.vn/api/callback/chungsuc",
  "note": "Khach hang #1001"
}
```

**Response JSON (200 OK):**
```json
{
  "status": "success",
  "ok": true,
  "msg": "Tạo thành công 1 đơn Chung Sức Liên Quân!",
  "data": {
    "idempotent_replay": false,
    "created_count": 1,
    "invalid_lines": [],
    "discount_percent": 10,
    "total_charged": 4500,
    "balance_remaining": 495500,
    "order": {
      "order_code": "CS-261030-A1B2C3",
      "request_id": "SHOP_ORDER_1001",
      "package_code": "goi-152",
      "package_name": "Rương SS",
      "event_code": "AbCdEf1234",
      "invite_code": "AbCdEf1234",
      "qty": 1,
      "target_tickets": 152,
      "done_tickets": 0,
      "remain_tickets": 152,
      "progress_percent": 0,
      "use_vip_pool": false,
      "accounts_used": 0,
      "unit_price": 5000,
      "discount_percent": 10,
      "total_price": 4500,
      "pay_amount": 4500,
      "refunded": false,
      "refund_amount": 0,
      "refund_reason": "",
      "status": "pending",
      "callback_url": "https://shopcuaban.vn/api/callback/chungsuc",
      "callback_status": 0,
      "callback_attempts": 0,
      "error_message": "",
      "note": "Khach hang #1001",
      "logs": [],
      "source": "api",
      "created_at": "2026-10-30T12:05:10.000Z"
    },
    "orders": [
      {
        "order_code": "CS-261030-A1B2C3",
        "request_id": "SHOP_ORDER_1001",
        "status": "pending"
      }
    ]
  }
}
```

---

### 4.5 `GET /api/v1/chungsuc/orders/:orderCode` — Tra cứu tiến độ & Danh sách đơn

Hỗ trợ truyền **`order_code`** (`CS-261030-A1B2C3`) **hoặc trực tiếp `request_id` của shop bạn** (`SHOP_ORDER_1001`):

```bash
# Cách 1: Tra cứu qua URL path (bằng order_code hoặc request_id)
curl -X GET "https://chungsuclq.top/api/v1/chungsuc/orders/SHOP_ORDER_1001" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"

# Cách 2: Tra cứu qua query string
curl -X GET "https://chungsuclq.top/api/v1/chungsuc/status?request_id=SHOP_ORDER_1001" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"

# Cách 3: Lấy danh sách nhiều đơn có phân trang & bộ lọc
curl -X GET "https://chungsuclq.top/api/v1/chungsuc/orders?page=1&limit=20&status=running&search=AbCdEf1234" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"
```

#### Bảng trạng thái đơn (`data.status`):

| `status` (REST API) | Trạng thái Callback bắn về | Ý nghĩa |
| :--- | :---: | :--- |
| `pending` | — | Đơn mới tạo, đang xếp hàng chuẩn bị bơm vé. |
| `running` | — | Hệ thống đang bơm vé đa luồng (cập nhật `done_tickets` mỗi 5 giây). |
| `paused` | — | Đang tạm điều phối luồng xử lý. |
| `completed` | `completed` | Đã bơm đủ 100% số vé mục tiêu — **bắn Callback thành công**. |
| `failed` | `failed` | Đơn dừng giữa chừng / thiếu vé (phần vé thiếu đã hoàn tiền vào `refund_amount`) — **bắn Callback**. |
| `refunded` / `cancelled` | `canceled` | Mã mời lỗi / hết lượt nhận hoặc khách hủy đơn chờ, hoàn tiền 100% — **bắn Callback**. |

---

### 4.6 `POST /api/v1/chungsuc/orders/:orderCode/cancel` — Hủy đơn chờ & Hoàn tiền

- **Alias**: `POST /api/v1/chungsuc/cancel` (truyền `{"order_code": "..."}` hoặc `{"request_id": "..."}` trong body).
- Chỉ áp dụng cho đơn đang chờ và **chưa phát sinh vé nào (`done_tickets = 0`)**. Hoàn tiền 100% vào ví tức thì.

```bash
curl -X POST "https://chungsuclq.top/api/v1/chungsuc/orders/SHOP_ORDER_1001/cancel" \
  -H "Authorization: Bearer YOUR_USER_TOKEN"
```

**Response JSON:**
```json
{
  "status": "success",
  "ok": true,
  "msg": "Hủy đơn và hoàn tiền thành công!",
  "data": {
    "order": {
      "order_code": "CS-261030-A1B2C3",
      "request_id": "SHOP_ORDER_1001",
      "status": "cancelled",
      "refunded": true,
      "refund_amount": 4500
    },
    "balance_remaining": 500000
  }
}
```

---

### 4.7 `POST /api/v1/token/reset` — Đổi `USER_TOKEN` mới

Thu hồi `USER_TOKEN` cũ và sinh khóa mới 48 ký tự hex ngay lập tức.

```bash
curl -X POST "https://chungsuclq.top/api/v1/token/reset" \
  -H "Authorization: Bearer YOUR_OLD_USER_TOKEN"
```

---

## 5. Đặc Tả Chi Tiết Webhook Callback (Dùng Chung Cả 2 Cổng)

Dù bạn tạo đơn qua **Merchant API (`/api/rechargews`)** hay **REST API (`/api/v1/chungsuc/orders`)**, hệ thống đều gửi **cùng một chuẩn Webhook Callback** về server của bạn ngay khi đơn hàng kết thúc.

### 5.1 Cơ chế kích hoạt, Retry & Chữ ký `callback_sign`

1. **Nơi cấu hình URL nhận Callback**:
   - **Ưu tiên 1:** Trường `callback_url` truyền trực tiếp trong từng request tạo đơn.
   - **Ưu tiên 2:** URL Webhook mặc định cài đặt tại trang `/customer/profile` (tab **Kết nối Merchant API**).
   - *(Vì lý do an toàn chống SSRF, `callback_url` phải là tên miền/IP công khai `http://` hoặc `https://`, không trỏ về `localhost` / IP LAN nội bộ `127.0.0.1`, `192.168.x.x`, `10.x.x.x`).*
2. **Thời điểm bắn Callback**: Tự động kích hoạt khi đơn chuyển sang 1 trong 3 trạng thái kết thúc: `completed`, `failed`, hoặc `canceled`.
3. **HTTP Headers gửi kèm**:
   - `User-Agent: Rechargews-Webhook/2.0 (Topup Gateway)`
   - `X-Rechargews-Event: order_callback`
   - `X-Rechargews-Sign: <callback_sign>`
4. **Công thức kiểm tra chữ ký chống giả mạo (`callback_sign`)**:
   ```text
   callback_sign = md5(partner_key + request_id + order_code + status)
   ```
   - `partner_key`: Khóa bí mật MD5 lấy tại trang `/api-docs` hoặc `/customer/profile`.
   - `request_id`: Mã đơn đối tác gửi lên lúc tạo đơn *(nếu lúc tạo đơn REST API không truyền `request_id`, hệ thống tự gán `request_id = order_code`)*.
   - `order_code`: Mã đơn hệ thống (`CS-...`).
   - `status`: Trạng thái chuẩn trong Callback (`completed`, `failed`, hoặc `canceled`).
5. **Quy tắc phản hồi & Retry tự động**:
   - Endpoint của đối tác cần phản hồi HTTP Status **`200`** (hoặc `201`, `204`) trong vòng `10 giây`.
   - Nếu timeout hoặc trả HTTP khác (`4xx`, `5xx`), hệ thống tự động **thử gửi lại tối đa 3 lần** (backoff `1.5s`, `3.0s`) và chống gửi trùng khi đã nhận `200 OK`. Admin cũng có thể bấm **Gửi lại Webhook Callback** thủ công nếu server đối tác bảo trì.

---

### 5.2 Bảng từ điển đầy đủ các trường dữ liệu Callback

| Tên trường | Kiểu dữ liệu | Mô tả chi tiết |
| :--- | :--- | :--- |
| `status` | `string` | Trạng thái kết thúc chuẩn: `"completed"` (hoàn thành đủ vé), `"failed"` (thiếu vé / lỗi một phần), hoặc `"canceled"` (đã hủy / hoàn tiền 100%). |
| `message` | `string` | Thông điệp tiếng Việt mô tả kết quả (VD: `"Hoàn thành 152/152 vé Chung Sức"` hoặc lý do hoàn tiền). |
| `time` | `string` | Thời gian tạo đơn theo múi giờ Việt Nam (`YYYY-MM-DD HH:mm:ss`). |
| `request_id` | `string` | Mã đơn tham chiếu phía Shop của đối tác (bằng `order_code` nếu lúc tạo đơn không truyền `request_id`). |
| `order_code` | `string` | Mã đơn duy nhất trên hệ thống (VD: `"CS-261030-A1B2C3"`). |
| `service_code` | `string` | Mã dịch vụ (`"chungsuc"`). |
| `account` | `object` | Thông tin mã mời Garena: `{"game_id": "AbCdEf1234", "invite_code": "AbCdEf1234"}`. *(Ở chế độ `POST Form` hoặc `GET`, hệ thống truyền phẳng thành `account[game_id]` và `game_id` / `account_game_id`)*. |
| `package_code` | `string` | Mã gói đã chạy (`"goi-38"` .. `"goi-10000"` hoặc `"custom"`). |
| `package_name` | `string` | Tên gói hiển thị (VD: `"Rương SS"`). |
| `target_tickets` | `integer` | Tổng số vé mục tiêu của đơn (VD: `152`). |
| `done_tickets` | `integer` | Số vé thực tế đã bơm thành công cho khách (VD: `152`). |
| `remain_tickets` | `integer` | Số vé còn thiếu (`0` nếu đã `completed`). |
| `amount` | `integer` | Mệnh giá gốc niêm yết của đơn trước chiết khấu (VD: `5000`). |
| `request_value` | `integer` | Tương đương `amount` — mệnh giá yêu cầu chuẩn Rechargews (VD: `5000`). |
| `complete_value` | `integer` | Giá trị mệnh giá tương ứng tỷ lệ vé đã hoàn thành (VD: `5000` khi đủ vé). |
| `remain_value` | `integer` | Giá trị mệnh giá phần vé chưa hoàn thành (`0` khi đủ vé). |
| `discount` | `number` | Phần trăm chiết khấu giảm giá đã áp dụng cho đơn (`0` – `100`). |
| `pay_amount` | `integer` | Số tiền thực tế đã trừ ví của đối tác sau chiết khấu (VD: `4500`). |
| `refunded` | `boolean` | `true` nếu đơn đã được hệ thống hoàn tiền tự động (toàn phần hoặc phần vé thiếu), ngược lại `false`. |
| `refund_amount` | `integer` | Số tiền (VNĐ) thực tế đã cộng hoàn lại vào ví của đối tác. |
| `callback_sign` | `string` | Chữ ký bảo mật MD5: `md5(partner_key + request_id + order_code + status)`. |

---

### 5.3 Payload JSON mẫu (Hoàn thành & Hoàn tiền)

#### Trường hợp A — Đơn Hoàn Thành 100% (`status = "completed"`):
```json
{
  "time": "2026-10-30 19:05:10",
  "account": {
    "game_id": "AbCdEf1234",
    "invite_code": "AbCdEf1234"
  },
  "request_id": "SHOP_ORDER_1001",
  "order_code": "CS-261030-A1B2C3",
  "package_code": "goi-152",
  "package_name": "Rương SS",
  "pay_amount": 4500,
  "discount": 10,
  "service_code": "chungsuc",
  "status": "completed",
  "request_value": 5000,
  "complete_value": 5000,
  "remain_value": 0,
  "target_tickets": 152,
  "done_tickets": 152,
  "remain_tickets": 0,
  "refunded": false,
  "refund_amount": 0,
  "amount": 5000,
  "message": "Hoàn thành 152/152 vé Chung Sức",
  "callback_sign": "8f14e45fceea167a5a36dedd4bea2543"
}
```

#### Trường hợp B — Đơn Lỗi Mã Mời / Tự Động Hoàn Tiền 100% (`status = "canceled"`):
```json
{
  "time": "2026-10-30 19:06:02",
  "account": {
    "game_id": "SaiMa12345",
    "invite_code": "SaiMa12345"
  },
  "request_id": "SHOP_ORDER_1002",
  "order_code": "CS-261030-D4E5F6",
  "package_code": "goi-152",
  "package_name": "Rương SS",
  "pay_amount": 4500,
  "discount": 10,
  "service_code": "chungsuc",
  "status": "canceled",
  "request_value": 5000,
  "complete_value": 0,
  "remain_value": 5000,
  "target_tickets": 152,
  "done_tickets": 0,
  "remain_tickets": 152,
  "refunded": true,
  "refund_amount": 4500,
  "amount": 5000,
  "message": "Mã mời sự kiện không tồn tại hoặc đã đủ lượt",
  "callback_sign": "c9f0f895fb98ab9159f51fd0297e236d"
}
```

---

## 6. Code Mẫu Đấu Nối Hoàn Chỉnh (PHP, Node.js, Python)

### 6.1 PHP — Tạo đơn & Nhận Webhook Callback

```php
<?php
// ==================== 1. TẠO ĐƠN QUA REST API (/api/v1/chungsuc/orders) ====================
function createChungSucOrderRest(string $inviteCode, string $packageCode, string $shopOrderId): array {
    $apiUrl    = 'https://chungsuclq.top/api/v1/chungsuc/orders';
    $userToken = 'YOUR_USER_TOKEN';

    $payload = [
        'package_code' => $packageCode,      // VD: 'goi-152'
        'code'         => $inviteCode,       // Mã mời hoặc nguyên văn tin nhắn chia sẻ Garena
        'request_id'   => $shopOrderId,      // Mã đơn trên shop của bạn
        'callback_url' => 'https://shopcuaban.vn/callback_chungsuc.php',
    ];

    $ch = curl_init($apiUrl);
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 25,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . $userToken,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS     => json_encode($payload),
    ]);

    $raw = curl_exec($ch);
    curl_close($ch);
    return json_decode($raw ?: '{}', true) ?: [];
}

// ==================== 2. FILE NHẬN WEBHOOK CALLBACK (callback_chungsuc.php) ====================
$partnerKey = 'YOUR_PARTNER_KEY'; // Lấy tại trang /api-docs
$rawInput   = file_get_contents('php://input');
$data       = json_decode($rawInput, true) ?: $_POST;

$requestId = (string)($data['request_id'] ?? '');
$orderCode = (string)($data['order_code'] ?? '');
$status    = (string)($data['status'] ?? '');
$sign      = strtolower((string)($data['callback_sign'] ?? ''));

$expectedSign = md5($partnerKey . $requestId . $orderCode . $status);
if (!$sign || !hash_equals($expectedSign, $sign)) {
    http_response_code(403);
    exit(json_encode(['status' => 'error', 'msg' => 'Invalid signature']));
}

// Lưu ý: Khóa dòng đơn hàng theo $requestId trong DB trước khi cộng tiền hoàn để chống xử lý trùng!
if ($status === 'completed') {
    // Cập nhật đơn $requestId -> Hoàn thành ($data['done_tickets'] / $data['target_tickets'] vé)
} elseif ($status === 'canceled') {
    // Đơn lỗi / hủy 100% -> Hoàn lại toàn bộ tiền đơn cho khách của bạn
} elseif ($status === 'failed') {
    // Đơn hoàn thành một phần ($data['done_tickets'] vé) -> Đối soát hoàn phần vé thiếu ($data['remain_tickets'] vé)
}

http_response_code(200);
header('Content-Type: application/json');
echo json_encode(['status' => 'ok']);
```

---

### 6.2 Node.js (Axios + Express) — Tạo đơn & Nhận Webhook Callback

```javascript
const crypto = require("crypto");
const axios = require("axios");
const express = require("express");

const BASE_URL = "https://chungsuclq.top";
const USER_TOKEN = process.env.CHUNGSUC_USER_TOKEN;
const PARTNER_KEY = process.env.CHUNGSUC_PARTNER_KEY;

// 1. Hàm tạo đơn Chung Sức
async function createOrder(inviteCode, packageCode = "goi-152", shopOrderId) {
  const { data } = await axios.post(
    `${BASE_URL}/api/v1/chungsuc/orders`,
    {
      package_code: packageCode,
      code: inviteCode,
      request_id: shopOrderId,
      callback_url: "https://shopcuaban.vn/api/callback/chungsuc",
    },
    {
      headers: { Authorization: `Bearer ${USER_TOKEN}` },
      timeout: 25000,
    }
  );
  return data;
}

// 2. Endpoint nhận Webhook Callback
const app = express();
app.post("/api/callback/chungsuc", express.json(), async (req, res) => {
  const { request_id = "", order_code = "", status = "", callback_sign = "" } = req.body || {};

  const expectedSign = crypto
    .createHash("md5")
    .update(`${PARTNER_KEY}${request_id}${order_code}${status}`, "utf8")
    .digest("hex");

  const a = Buffer.from(expectedSign, "utf8");
  const b = Buffer.from(String(callback_sign).toLowerCase(), "utf8");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(403).json({ status: "error", msg: "Invalid callback_sign" });
  }

  if (status === "completed") {
    // Đánh dấu đơn request_id hoàn tất
  } else if (status === "canceled" || status === "failed") {
    // Xử lý hoàn tiền cho khách dựa trên req.body.done_tickets / req.body.remain_tickets
  }

  return res.status(200).json({ status: "ok" });
});
```

---

### 6.3 Python — Đấu nối cổng Merchant Rechargews (`/api/rechargews`) & Callback

```python
import hashlib
import hmac
import requests

BASE_URL = "https://chungsuclq.top/api/rechargews"
PARTNER_ID = "100001"
PARTNER_KEY = "YOUR_PARTNER_KEY"


def md5_hex(raw: str) -> str:
    return hashlib.md5(raw.encode("utf-8")).hexdigest()


def topup_chungsuc(request_id: str, invite_code: str, package_code: str = "goi-152") -> dict:
    command = "topup"
    sign = md5_hex(f"{PARTNER_KEY}{PARTNER_ID}{command}{request_id}")
    payload = {
        "partner_id": PARTNER_ID,
        "command": command,
        "request_id": request_id,
        "service_code": "chungsuc",
        "package_code": package_code,
        "account": {"game_id": invite_code},
        "callback_url": "https://shopcuaban.vn/api/callback/rechargews",
        "sign": sign,
    }
    resp = requests.post(BASE_URL, json=payload, timeout=25)
    return resp.json()


def verify_callback(payload: dict) -> bool:
    request_id = str(payload.get("request_id") or "")
    order_code = str(payload.get("order_code") or "")
    status = str(payload.get("status") or "")
    given_sign = str(payload.get("callback_sign") or "").lower()
    expected_sign = md5_hex(f"{PARTNER_KEY}{request_id}{order_code}{status}")
    return hmac.compare_digest(expected_sign, given_sign)
```
