ZankethStore
Reseller API
Deliver game top-ups through our secure fulfilment platform. One POST — we handle queuing, balance checks, dispatch, and confirmation.
Overview
The ZankethStore Reseller API lets authorised developers trigger game top-ups via a single HTTP POST. Send a player ID and item code — the API validates your key, checks balance, serialises through the queue, dispatches to the fulfilment system, waits for confirmation, deducts your balance, and returns structured JSON.
ok (boolean), status (machine-readable), message (human-readable), and data (payload). Always check ok first.Request lifecycle
request
balance
lock
command
reply
balance
response
Authentication
Include your API key on every request as a POST field or HTTP header.
| Method | Where | Example |
|---|---|---|
| POST field | Request body | api_key=your-secret-key |
| HTTP Header | Request headers | X-API-Key: your-secret-key |
Key properties
| Property | Description |
|---|---|
| allowed_domains | Comma-separated whitelist. Requests from unlisted origins → 403. |
| expires_at | Expiry datetime. null = never expires. |
| rate_limit | Max calls per 60-second window. 0 = unlimited. |
| balance | Shell balance. Auto-deducted on each successful top-up. |
| is_active | Admin can disable instantly. Disabled → 403. |
| mode | product / test / disabled — see Test vs Product Mode below. |
Test vs Product Mode
Every reseller account has one of three statuses, set by the admin. It controls which Telegram fulfilment group a request is routed to, and is recorded on every request so it can be told apart from real orders later. The two fulfilment groups are universal — one Test group and one Product group shared by every reseller — not configured per account.
| Mode | Behaviour |
|---|---|
| product | Live requests. Dispatched to the universal Group ID — Product (or the global Group ID if unset). Stored with is_test = 0. Deducts SHELLS balance as normal. |
| test | Sandbox requests. Dispatched to the universal Group ID — Test (or the global Group ID if unset). Stored with is_test = 1. Never checks or deducts SHELLS balance — win or lose, your balance is untouched. Counts against the Test request limit instead. Use this to integrate and test your storefront without touching production fulfilment or spending SHELLS. |
| disabled | The key is rejected immediately with 403 disabled — identical to a fully deactivated account. |
is_test is decided entirely by your account's current mode — you never send it yourself. Every Top-Up response includes "is_test" and "mode" so your integration can confirm which one just ran.admin_resellers.php — every reseller in Test mode shares the same Test group, and every reseller in Product mode shares the same Product group. Only the per-reseller Mode itself is set individually. The single global Group ID on the Site Settings page still works as the fallback if either universal ID is unset — that page and behaviour are unchanged.low_balance can't happen in Test mode, and balance_before/balance_after in the response stay identical. What Test mode does reduce is your remaining daily Test request count (below) — every Test request counts against it regardless of whether it succeeds or fails.Test mode also has a dynamic, admin-configurable limit on how many Test requests a reseller can send — one number, set on admin_resellers.php, applied to every reseller in Test mode. It is always a real, positive number — there is no "unlimited" option. Usage is a persistent running counter per reseller that does not reset on any schedule; the admin can view, adjust, or reset it anytime for any reseller.
| Field | Where | Meaning |
|---|---|---|
| test_request_limit | admin table | Universal cap on Test-mode requests per reseller. Always ≥ 1 — the admin panel won't accept 0 or blank. Changing it takes effect immediately — no deploy needed. |
| test_requests_used | resellers table | Per-reseller running counter. Incremented by 1 on every Test-mode request (success or failure). Never auto-resets — admin adjusts/resets it per reseller via the pencil icon next to Test Usage on admin_resellers.php. |
Once a Test-mode reseller's usage reaches the limit, further Top-Up calls return 429 test_limit_reached until the admin raises the limit or resets that reseller's usage. Check current usage anytime with GET /api.php?whoami, which returns test_request_limit, test_requests_used, and test_requests_remaining. Every Test-mode Top-Up response — success or failure — also includes these three fields.
Check your own current mode anytime with GET /api.php?whoami, and review past requests — tagged Test or Product — with GET /api.php?history. Both are read-only and never change live parameters.
Test request limit
// Test-mode Top-Up once the limit is hit { "ok": false, "status": "test_limit_reached", "message": "Test request limit reached (50/50). Ask admin to raise or reset your usage on admin_resellers.php, or switch to Product mode.", "data": { "test_request_limit": 50, "test_requests_used": 50, "mode": "test" } }
Queue System
Only one top-up dispatches at a time, preventing fulfilment responses from being mixed up when multiple requests arrive simultaneously.
Validates balance, queues the request, dispatches to fulfilment, waits for confirmation, deducts balance, and returns the result.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | string | REQUIRED | Reseller API key. Or use X-API-Key header. |
| player_id | string | REQUIRED | In-game player ID to top up. |
| item_code | string | REQUIRED | Item to deliver (lowercase). See Item Codes. |
| quantity | integer | OPTIONAL | Units. Default: 1. Max: 99. |
balance_before equals balance_after — Test mode never touches SHELLS. test_requests_used is a running total that only changes when a Test request is sent or the admin resets it — it does not reset on a schedule.Success 200
{
"ok": true,
"status": "success",
"message": "✅ Top-up delivered successfully.",
"data": {
"queue_id": 1042,
"product": "Weekly Pass",
"shell_cost": 86,
"balance_before": 5000,
"balance_after": 4914,
"balance_deducted":true,
"shells_deducted": "86 SHELLS",
"new_balance": "4914.00 SHELLS",
"is_test": false,
"mode": "product"
}
}Same request, but in Test mode
{
"ok": true,
"status": "success",
"message": "✅ Top-up delivered successfully.",
"data": {
"queue_id": 1043,
"product": "Weekly Pass",
"shell_cost": 86,
"balance_before": 5000,
"balance_after": 5000,
"balance_deducted":false,
"is_test": true,
"mode": "test",
"test_request_limit": 50,
"test_requests_used": 13,
"test_requests_remaining": 37
}
}Insufficient balance 402
{
"ok": false,
"status": "low_balance",
"message": "Insufficient balance. Required: 86 SHELLS, Available: 40 SHELLS.",
"data": {
"queue_id": 1043,
"required_shells": 86,
"available_shells":40
}
}Failed / timeout 502
{
"ok": false,
"status": "failed",
"message": "⚠️ Top-up failed or timed out.",
"data": { "queue_id": 1044, "debug_msgs": ["..."] }
}Returns the live product catalogue. No authentication required. Fetch at startup to keep pricing in sync.
{
"ok": true,
"products": [
{ "item_code": "25", "name": "25 Shells Pack", "shell_cost": "13.00" },
{ "item_code": "weekly", "name": "Weekly Pass", "shell_cost": "86.00" }
]
}Health Check
Returns server health — PHP version, DB connectivity, table existence, reseller count. No auth required. Use this to diagnose integration issues before going live.
Looks up a player's profile before placing a top-up. Requires a valid API key — included free with all reseller accounts. The upstream verification service URL and credentials are never exposed.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| api_key | string | REQUIRED | Your reseller API key. |
| uid | string | REQUIRED | In-game player UID to look up. |
| region | string | OPTIONAL | Server region code (e.g. SG, IND). Default: IND. |
Key fields for checkout
| Field path | Type | Use |
|---|---|---|
| data.user_information.nickname | string | Display to customer for confirmation |
| data.user_information.open_id | string | Player's Open ID |
| data.user_information.region | string | Server region, e.g. SG |
Success 200
{
"ok": true,
"status": "found",
"data": {
"user_information": {
"nickname": "FB:ㅤ@GMRemyX",
"open_id": "12345678",
"region": "SG",
"session_key": "e8b5d..."
}
}
}cURL example
curl "https://zankethstore.com/tg/api.php?verify&api_key=YOUR_KEY&uid=12345678®ion=SG"
PHP example
$res = json_decode(file_get_contents(
'https://zankethstore.com/tg/api.php?verify'
.'&api_key=YOUR_KEY'
.'&uid=' . urlencode($playerId)
.'®ion=SG'
), true);
if ($res['ok']) {
$name = $res['data']['user_information']['nickname'];
$region = $res['data']['user_information']['region'];
echo "Player: $name (Region $region)";
} else {
echo "Error: " . $res['message'];
}Error responses
// Invalid or missing key { "ok": false, "status": "invalid_key", "message": "Invalid API key." } { "ok": false, "status": "missing_key", "message": "api_key is required." } { "ok": false, "status": "missing_uid", "message": "uid (player_id) is required." } { "ok": false, "status": "key_expired", "message": "API key expired." } { "ok": false, "status": "upstream_error", "message": "Verification service unavailable." }
Returns your account's current status — Product, Test, or Disabled — plus balance and rate limit. Read-only: it never changes any live top-up parameters, so it's safe to call before every session to confirm which mode you're about to dispatch into.
test_request_limit is always a real, positive number set by the admin — it can never be unlimited. test_requests_used is a running counter that only changes when you send a Test request or the admin manually adjusts/resets it on admin_resellers.php — it does not reset automatically.Success 200
{
"ok": true,
"status": "found",
"data": {
"reseller": "Shop Name",
"account_mode": "test",
"is_test": true,
"active": true,
"balance": 4914,
"rate_limit": 30,
"expires_at": "2026-12-31 23:59:59",
"key_expired": false,
"test_request_limit": 50,
"test_requests_used": 12,
"test_requests_remaining": 38
}
}Returns your own past requests — read-only, does not consume balance, touch the queue, or change any current top-up parameters. Use it to review or audit what's already been sent, filtered by Test or Product if needed.
Parameters
| Param | Type | Description | |
|---|---|---|---|
| api_key | string | REQUIRED | Your reseller API key. |
| limit | integer | OPTIONAL | Max rows to return. Default 20, max 100. |
| mode | string | OPTIONAL | test or product to filter. Omit for both. |
Success 200
{
"ok": true,
"status": "found",
"data": {
"count": 2,
"requests": [
{ "queue_id": 1042, "player_id": "123456789", "item_code": "weekly", "quantity": 1, "status": "success", "is_test": 0, "shell_cost": 86, "created_at": "2026-07-01 10:04:12" },
{ "queue_id": 1041, "player_id": "999999999", "item_code": "25", "quantity": 1, "status": "failed", "is_test": 1, "shell_cost": 13, "created_at": "2026-07-01 09:58:44" }
]
}
}🎮Item Codes
Pass item_code exactly as shown (lowercase). Total shell cost = shell_cost × quantity.
Shell packs
| item_code | Name | Shell cost / unit |
|---|---|---|
| 25 | 25 Shells Pack | 13 SHELLS |
| 100 | 100 Shells Pack | 50 SHELLS |
| 310 | 310 Shells Pack | 152 SHELLS |
| 520 | 520 Shells Pack | 254 SHELLS |
| 1060 | 1,060 Shells Pack | 500 SHELLS |
| 2180 | 2,180 Shells Pack | 1,010 SHELLS |
| 5600 | 5,600 Shells Pack | 2,500 SHELLS |
| 11500 | 11,500 Shells Pack | 5,150 SHELLS |
Passes & subscriptions
| item_code | Name | Shell cost / unit |
|---|---|---|
| lite | Lite Pass | 18 SHELLS |
| weekly | Weekly Pass | 86 SHELLS |
| monthly | Monthly Pass | 430 SHELLS |
Evo passes
| item_code | Name | Shell cost / unit |
|---|---|---|
| evo3 | Evo 3-Day Pass | 30 SHELLS |
| evo7 | Evo 7-Day Pass | 45 SHELLS |
| evo30 | Evo 30-Day Pass | 135 SHELLS |
GET /api.php?products at startup to get live pricing. Admin can update prices at any time.Error Codes
All errors return JSON with "ok": false. Use the status field for programmatic handling.
missing_playermissing_itemmissing_keyinvalid_key?checkkey=YOUR_KEY to diagnose.disabledkey_expireddomain_blockedlow_balancerequired_shells and available_shells.rate_limitedtest_limit_reachedadmin_resellers.php. Check ?whoami for current usage, or switch to Product mode. Doesn't apply to Product-mode requests.processing_errordb_errorqueue_id.faileddebug_msgs and retry.Security
Key Hashing
Raw keys never stored. Only SHA-256 hashes in DB.
Domain Whitelist
Each key locked to approved domains. Unknown origins → 403.
Balance Guard
Shell balance checked before dispatch. Orders that would overdraft → 402.
Queue Lock
Only one fulfilment runs at a time. Prevents mixed responses.
Key Expiry
Every key has an expiry date. Expired → 403. Renew via admin panel.
Audit Log
Every call logged to api_call_log with IP, domain, status, timestamp.
⏱Rate Limits
Sliding 60-second window per API key. Exceeding the limit → 429.
| Plan | Limit | Use case |
|---|---|---|
| Standard | 30 / min | Moderate traffic |
| High-volume | 60 / min | Busy storefronts |
| Unlimited | 0 (none) | Trusted internal systems |
🐘PHP
<?php $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://zankethstore.com/tg/api.php', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_TIMEOUT => 120, CURLOPT_POSTFIELDS => http_build_query([ 'api_key' => 'your-reseller-api-key', 'player_id' => '123456789', 'item_code' => 'weekly', 'quantity' => 1, ]), ]); $res = json_decode(curl_exec($ch), true); curl_close($ch); if ($res['ok']) { echo "✅ Cost: " . $res['data']['shell_cost'] . " | Balance: " . $res['data']['balance_after']; } elseif ($res['status'] === 'low_balance') { echo "❌ Need: ".$res['data']['required_shells']." | Have: ".$res['data']['available_shells']; } else { echo "❌ " . $res['message']; }
🟨JavaScript (Node.js)
const axios = require('axios'); async function topUp(playerId, itemCode, qty = 1) { const { data } = await axios.post( 'https://zankethstore.com/tg/api.php', new URLSearchParams({ api_key: 'your-key', player_id: playerId, item_code: itemCode, quantity: qty }).toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, timeout: 120000 } ); if (data.ok) console.log(`✅ Cost: ${data.data.shell_cost} | Balance: ${data.data.balance_after}`); else if (data.status === 'low_balance') console.error(`❌ Need ${data.data.required_shells}, have ${data.data.available_shells}`); else console.error(`❌ ${data.message}`); return data; }
🐍Python
import requests def top_up(player_id, item_code, quantity=1): data = requests.post( 'https://zankethstore.com/tg/api.php', data={ 'api_key': 'your-key', 'player_id': player_id, 'item_code': item_code, 'quantity': quantity }, timeout=120 ).json() if data['ok']: print(f"✅ Cost: {data['data']['shell_cost']} | Balance: {data['data']['balance_after']}") elif data['status'] == 'low_balance': print(f"❌ Need {data['data']['required_shells']}, have {data['data']['available_shells']}") else: print(f"❌ {data['message']}") return data
🖥cURL
# Top-up curl -X POST https://zankethstore.com/tg/api.php \ --max-time 120 \ -d "api_key=your-key" \ -d "player_id=123456789" \ -d "item_code=weekly" \ -d "quantity=1" # Verify player curl "https://zankethstore.com/tg/api.php?verify&api_key=your-key&uid=123456789®ion=SG" # Live product list curl https://zankethstore.com/tg/api.php?products # Health check curl https://zankethstore.com/tg/api.php?test # Diagnose key curl https://zankethstore.com/tg/api.php?checkkey=your-raw-key
Key Management
Create and manage keys from the Admin Panel at /admin.php or via the CLI script. Per-reseller Test/Product/Disable mode and the two Telegram Group IDs are managed separately on /admin_resellers.php — the global Site Settings page (admin_variables.php) is unaffected.
/admin_resellers.php to set its Mode (Product / Test / Disable). The Group ID — Test and Group ID — Product fields on that same page are universal and apply to every reseller; you only set them once, not per account.# List resellers php reseller_admin.php list # Create (prints key ONCE — save immediately) php reseller_admin.php add "Shop Name" "shop.com" 365 60 # Disable / enable php reseller_admin.php revoke 3 php reseller_admin.php enable 3 # History & stats php reseller_admin.php history 3 php reseller_admin.php stats
Database
Run api_schema.sql once to create all tables. Safe to re-run.
resellers
| Column | Type | Notes |
|---|---|---|
| api_key | VARCHAR(64) | SHA-256 hash of raw key |
| balance | DECIMAL(10,2) | Auto-deducted on success. Top up via admin. |
| allowed_domains | TEXT | Comma-separated whitelist |
| rate_limit | SMALLINT | Calls/min. 0 = unlimited |
| mode | ENUM('product','test','disabled') | 3-way status. See Test vs Product Mode. Managed on admin_resellers.php. |
| test_requests_used | INT | Per-reseller running counter checked against the universal test_request_limit. +1 on every Test-mode request. Never auto-resets — admin adjusts/resets per reseller on admin_resellers.php. |
admin
| Column | Type | Notes |
|---|---|---|
| group_id | BIGINT | Original single Group ID. Managed on admin_variables.php (unchanged). Used as the fallback below. |
| group_id_test | BIGINT NULL | Universal Telegram group used for every reseller when mode=test. Falls back to group_id if NULL. Managed on admin_resellers.php. |
| group_id_product | BIGINT NULL | Universal Telegram group used for every reseller when mode=product. Falls back to group_id if NULL. Managed on admin_resellers.php. |
| test_request_limit | INT | Universal cap on Test-mode requests per reseller, checked against each reseller's test_requests_used. Always ≥ 1 — never unlimited. Dynamic — takes effect immediately. Managed on admin_resellers.php. |
products
| Column | Type | Notes |
|---|---|---|
| item_code | VARCHAR(60) | Unique, lowercase. Matches API item_code |
| shell_cost | DECIMAL(10,2) | Cost per unit checked against reseller balance |
| is_active | TINYINT(1) | Inactive items → missing_item error |
api_queue
| Column | Notes |
|---|---|
| id | Returned as queue_id in all responses |
| reseller_id | NULL for internal orders (manual/wallet/instant) |
| status | pending → processing → success | failed | timeout |
| origin_domain | manual / instant / wallet / reseller domain |
| is_test | Set automatically from the reseller's mode at request time. 1 = Test, 0 = Product. |
api_call_log
| Column | Notes |
|---|---|
| is_test | Same flag as api_queue.is_test, mirrored onto every logged call. |
mode/test_requests_used on resellers; group_id_test/group_id_product/test_request_limit on admin; is_test on api_queue/api_call_log) are added automatically the first time api.php or admin_resellers.php runs — no manual migration needed.