# INKZ OTP — Reseller API

เอกสารนี้สำหรับลูกค้า reseller ที่ต้องการเชื่อมระบบซื้อเบอร์ OTP เข้ากับเว็บหรือโปรแกรมของตัวเอง

- **Base URL:** `https://home.inkz.shop/api/reseller/`
- **รูปแบบข้อมูล:** JSON (UTF-8)
- **สกุลเงิน:** THB
- **API Key:** ขอได้จากหน้า **API** ในเว็บร้านค้า (ต้องตั้ง PIN ก่อน)

ไฟล์ที่เกี่ยวข้อง:

- เอกสารเวอร์ชันเว็บ: `https://home.inkz.shop/api/reseller/docs.html`
- OpenAPI 3.0: [`openapi.json`](./openapi.json)

---

## 1. การยืนยันตัวตน

แนะนำให้ส่ง API Key ผ่าน Header:

```http
Authorization: Bearer YOUR_API_KEY
```

หรือใช้ header ทางเลือก:

```http
X-API-Key: YOUR_API_KEY
```

ระบบยังรองรับ `?api_key=YOUR_API_KEY` และ `api_key` ใน JSON body เพื่อความเข้ากันได้กับระบบเดิม แต่ไม่แนะนำ เพราะ key อาจติดอยู่ใน log หรือ browser history

> **สำคัญ:** API Key ใช้แทนเงินในบัญชี ลูกค้าต้องเก็บไว้ฝั่งเซิร์ฟเวอร์เท่านั้น ห้ามฝังใน JavaScript ฝั่ง browser

---

## 2. รูปแบบ Response

ทุก endpoint ตอบกลับรูปแบบเดียวกัน:

```json
{
  "success": true,
  "message": "ข้อความอธิบาย",
  "data": {}
}
```

เมื่อผิดพลาด:

```json
{
  "success": false,
  "message": "ยอดเงินไม่เพียงพอ",
  "data": {
    "balance": 12.5,
    "required": 20,
    "unit_price": 10,
    "quantity": 2
  }
}
```

HTTP status ที่ใช้บ่อย:

| Status | ความหมาย |
|---|---|
| `200` | สำเร็จ |
| `400` | ค่าที่ส่งมาไม่ถูกต้อง |
| `401` | API Key ไม่ถูกต้องหรือไม่ได้ส่ง |
| `402` | ยอดเงินไม่เพียงพอ |
| `404` | ไม่พบบริการ/ออเดอร์ |
| `405` | HTTP method ไม่ถูกต้อง |
| `500` | ระบบภายในขัดข้อง |
| `502` | ติดต่อผู้ให้บริการเบอร์ไม่ได้ / ตรวจสต็อกไม่ได้ |

---

## 3. Endpoint ทั้งหมด

| Method | Endpoint | ใช้ทำอะไร |
|---|---|---|
| `GET` | `get_balance.php` | เช็คยอดเงิน |
| `GET` | `get_countries.php` | ดูรหัสประเทศที่เลือกได้ |
| `GET` | `get_services.php` | ดูแอพ/บริการที่เปิดขาย + ราคา + สต็อก |
| `POST` | `buy_otp.php` | ซื้อเบอร์ OTP |
| `POST` | `check_sms.php` | เช็ค SMS / รหัส OTP |
| `POST` | `activation_action.php` | ยกเลิก / ขอ SMS ซ้ำ / จบงาน |
| `GET` | `get_orders.php` | ดูประวัติออเดอร์ |

`server` ที่รองรับ:

- `server1` — SmsBower (ค่าเริ่มต้น)
- `server2` — GrizzlySMS

---

## 4. เช็คยอดเงิน

```http
GET /api/reseller/get_balance.php
Authorization: Bearer YOUR_API_KEY
```

ตัวอย่าง:

```bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://home.inkz.shop/api/reseller/get_balance.php"
```

```json
{
  "success": true,
  "message": "ดึงยอดเงินสำเร็จ",
  "data": {
    "user_id": 42,
    "balance": 1500.00,
    "currency": "THB"
  }
}
```

---

## 5. ดูรหัสประเทศ

```http
GET /api/reseller/get_countries.php?server=server1
Authorization: Bearer YOUR_API_KEY
```

```json
{
  "success": true,
  "message": "ดึงรายชื่อประเทศสำเร็จ",
  "data": {
    "server": "server1",
    "source": "provider",
    "count": 2,
    "countries": [
      { "id": "52", "name": "Thailand", "name_th": "ไทย" },
      { "id": "4", "name": "Philippines", "name_th": "ฟิลิปปินส์" }
    ]
  }
}
```

- `id` คือรหัสที่ต้องส่งเป็น `country` ใน `get_services.php` และ `buy_otp.php`
- ถ้าผู้ให้บริการล่ม ระบบจะส่งรายการประเทศยอดนิยมพร้อม `source: "fallback"` และ `warning`

---

## 6. ดูแอพ/บริการที่เปิดขาย

```http
GET /api/reseller/get_services.php?server=server1&country=52&in_stock=1
Authorization: Bearer YOUR_API_KEY
```

Query parameters:

| พารามิเตอร์ | จำเป็น | คำอธิบาย |
|---|---|---|
| `server` | ไม่ | `server1` (default) หรือ `server2` |
| `country` | ไม่ | รหัสประเทศจาก `get_countries.php` เช่น `52` |
| `in_stock` | ไม่ | `1` = แสดงเฉพาะบริการที่มีสต็อกมากกว่า 0 |

- ระบบแสดงเฉพาะบริการที่ `is_active = 1` (เปิดขาย) เท่านั้น
- ถ้าส่ง `country` ระบบจะกรองเหลือเฉพาะแอพที่ผู้ให้บริการรองรับประเทศนั้น
- `stock_count` และ `available` ใช้บอกสต็อก ณ ตอนเรียก
- ถ้าดึงสต็อกไม่ได้ จะยังคืนรายการที่เปิดขาย พร้อม `warning`

```json
{
  "success": true,
  "message": "ดึงรายการบริการสำเร็จ",
  "data": {
    "server": "server1",
    "country": "52",
    "in_stock": true,
    "count": 1,
    "services": [
      {
        "id": 1,
        "code": "tg",
        "name": "Telegram",
        "original_price": 10.00,
        "price": 9.00,
        "discount": 1.00,
        "image_url": "https://...",
        "category": null,
        "server": "server1",
        "stock_count": 24,
        "available": true
      }
    ]
  }
}
```

> `price` คือราคาที่ลูกค้าต้องจ่ายจริงหลังหักส่วนลดรายบุคคลแล้ว

---

## 7. ซื้อเบอร์ OTP

```http
POST /api/reseller/buy_otp.php
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

Body:

```json
{
  "service_id": 1,
  "server": "server1",
  "quantity": 1,
  "country": "52"
}
```

| ฟิลด์ | จำเป็น | คำอธิบาย |
|---|---|---|
| `service_id` | ใช่ | `id` จาก `get_services.php` |
| `server` | ไม่ | `server1` (default) หรือ `server2` |
| `quantity` | ไม่ | จำนวนเบอร์ 1–15 (default 1) |
| `country` | ไม่ | รหัสประเทศจาก `get_countries.php` แนะนำให้ส่งทุกครั้ง |

ตัวอย่าง response เมื่อสำเร็จ:

```json
{
  "success": true,
  "message": "สั่งซื้อสำเร็จ 1 เบอร์",
  "data": {
    "quantity": 1,
    "unit_price": 9.00,
    "total_price": 9.00,
    "original_unit_price": 10.00,
    "discount_per_unit": 1.00,
    "remaining_balance": 1491.00,
    "server": "server1",
    "country": "52",
    "order_ids": [501],
    "orders": [
      {
        "order_id": 501,
        "activation_id": "123456789",
        "phone_number": "+66812345678",
        "price": 9.00
      }
    ]
  }
}
```

สิ่งที่ต้องเก็บไว้:

- `order_ids` / `orders[].order_id` — ใช้กับ `check_sms.php` และ `activation_action.php`
- `phone_number` — เบอร์ที่ได้
- `remaining_balance` — ยอดเงินคงเหลือหลังซื้อ

> ถ้าซื้อหลายเบอร์แต่ผู้ให้บริการมีของไม่ครบ ระบบจะคืน `quantity` เป็นจำนวนที่ซื้อสำเร็จจริง และคิดเงินตามจำนวนนั้น

---

## 8. เช็ค SMS / OTP

```http
POST /api/reseller/check_sms.php
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

```json
{ "order_id": 501 }
```

```json
{
  "success": true,
  "data": {
    "order_id": 501,
    "phone_number": "+66812345678",
    "sms_code": "123456",
    "status": "COMPLETED",
    "service_name": "Telegram"
  }
}
```

- `sms_code` เป็น `null` = ยังไม่ได้รับ SMS
- แนะนำให้ poll ทุก 5 วินาที ไม่เกิน 2 นาที
- `status` ที่พบบ่อย: `WAITING`, `COMPLETED`, `CANCELLED`

---

## 9. ยกเลิก / ขอ SMS ซ้ำ / จบงาน

```http
POST /api/reseller/activation_action.php
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

```json
{ "order_id": 501, "action": 8 }
```

| `action` | ความหมาย |
|---|---|
| `1` | แจ้งพร้อมรับ SMS |
| `3` | ขอ SMS ซ้ำ |
| `6` | จบการใช้งาน |
| `8` | ยกเลิกและขอคืนเงิน (ตามเงื่อนไขผู้ให้บริการ) |

> การยกเลิกจะคืนเงินเข้าบัญชีก็ต่อเมื่อผู้ให้บริการยืนยันการยกเลิกสำเร็จ และออเดอร์ยังไม่ถูกยกเลิกมาก่อน

---

## 10. ดูประวัติออเดอร์

```http
GET /api/reseller/get_orders.php?page=1&limit=20&status=WAITING
Authorization: Bearer YOUR_API_KEY
```

| พารามิเตอร์ | คำอธิบาย |
|---|---|
| `page` | หน้าเริ่มต้น 1 |
| `limit` | จำนวนต่อหน้า 1–100 (default 20) |
| `status` | `WAITING`, `COMPLETED`, `CANCELLED` |

```json
{
  "success": true,
  "message": "ดึงประวัติสำเร็จ",
  "data": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "total_pages": 1,
    "orders": [
      {
        "id": 501,
        "service_id": 1,
        "activation_id": "123456789",
        "phone_number": "+66812345678",
        "sms_code": "123456",
        "price": "9.00",
        "status": "COMPLETED",
        "server": "server1",
        "created_at": "2026-10-20 12:30:00",
        "service_name": "Telegram"
      }
    ]
  }
}
```

---

## 11. Flow ที่แนะนำ

```text
1. GET  get_balance.php          -> เช็คยอดเงิน
2. GET  get_countries.php        -> เลือกประเทศ
3. GET  get_services.php         -> เลือกแอพที่เปิดขาย (ส่ง country, in_stock=1)
4. POST buy_otp.php              -> ซื้อเบอร์ (ส่ง service_id, country, server)
5. POST check_sms.php            -> poll ทุก 5 วินาทีจนได้ sms_code
6. POST activation_action.php    -> action=6 จบงาน หรือ action=8 ยกเลิก
7. GET  get_orders.php           -> ดูประวัติย้อนหลัง
```

---

## 12. ตัวอย่างโค้ด

### JavaScript (ฝั่งเซิร์ฟเวอร์เท่านั้น)

```js
const BASE = 'https://home.inkz.shop/api/reseller';
const KEY = process.env.INKZ_API_KEY;

async function api(path, options = {}) {
  const res = await fetch(`${BASE}/${path}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${KEY}`,
      'Content-Type': 'application/json',
      ...(options.headers || {}),
    },
  });
  const json = await res.json();
  if (!json.success) throw new Error(json.message || 'API error');
  return json.data;
}

// เช็คยอด
const balance = await api('get_balance.php');

// ดูบริการประเทศไทยที่มีของ
const services = await api('get_services.php?server=server1&country=52&in_stock=1');

// ซื้อ
const order = await api('buy_otp.php', {
  method: 'POST',
  body: JSON.stringify({
    service_id: services.services[0].id,
    server: 'server1',
    quantity: 1,
    country: '52',
  }),
});

console.log(order.order_ids[0], order.orders[0].phone_number);
```

### PHP

```php
<?php
$base = 'https://home.inkz.shop/api/reseller';
$key  = getenv('INKZ_API_KEY');

function apiGet(string $url, string $key): array {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key],
        CURLOPT_TIMEOUT => 30,
    ]);
    $res = curl_exec($ch);
    curl_close($ch);
    return json_decode((string)$res, true) ?: [];
}

$services = apiGet($base . '/get_services.php?server=server1&country=52&in_stock=1', $key);
print_r($services);
```

> ตัวอย่าง PHP ด้านบนเป็นแค่ GET หากต้องการ POST ให้เพิ่ม `CURLOPT_POST` และ `CURLOPT_POSTFIELDS` เป็น JSON

---

## 13. ข้อควรระวัง

- เก็บ API Key ฝั่งเซิร์ฟเวอร์เท่านั้น
- อย่า retry `buy_otp.php` ซ้ำเมื่อ timeout โดยไม่เช็ค `get_orders.php` ก่อน เพราะอาจซื้อซ้ำ
- ควรบันทึก `order_id` ทุกครั้งหลังซื้อสำเร็จ
- ตรวจ `remaining_balance` หรือเรียก `get_balance.php` เป็นระยะ
- รหัสประเทศและรหัสบริการอาจเปลี่ยนได้ ควรดึงใหม่ทุกครั้งก่อนขาย
- หากสงสัยว่า API Key รั่วไหล ให้สร้าง Key ใหม่ในหน้า API ทันที

---

## 14. ติดต่อ / ขอ API Key

- เปิดเว็บร้านค้า แล้วไปที่เมนู **API**
- หรือเปิดจาก Telegram Bot: `https://t.me/` + ชื่อบอทของร้าน
- เอกสารเวอร์ชันเว็บ: `https://home.inkz.shop/api/reseller/docs.html`
