Dokumentasi API Syah Topup
Hubungkan website atau bot kamu ke Syah Topup: ambil daftar game & harga, cek ID game, buat pesanan top up otomatis (dibayar dari saldo akunmu), lalu terima hasilnya lewat webhook.
Base URL. Semua endpoint di bawah relatif terhadap alamat ini.
Mulai cepat
- Punya akun & saldo. Pesanan lewat API dibayar dari saldo akunmu. Daftar lalu isi saldo lewat menu Deposit.
- Buat API key di halaman API & Webhook. Key berawalan
syah-dan hanya tampil sekali, simpan baik-baik. - Panggil API dari server/bot kamu, kirim key lewat header
X-API-Key. - Isi URL webhook (opsional) supaya hasil pesanan dikirim otomatis ke server kamu, lengkap dengan tanda tangan
syah-secret.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/profile`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/profile',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/profile' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/profile');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
{
"success": true,
"data": {
"username": "budi",
"name": "Budi",
"balance": 150000,
"api_key": {
"name": "Bot Telegram",
"hint": "syah-3fa9••••••••c21d",
"created_at": "2026-11-03T08:00:00.000Z",
"webhook_enabled": true
},
"rate_limit_per_minute": 60
}
}
Autentikasi
Setiap request wajib membawa API key. Pilih salah satu:
X-API-Key: syah-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer syah-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx- Format key:
syah-diikuti 48 karakter heksadesimal. Secret webhook memakai awalan yang sama. - Panggil API hanya dari server atau bot. Jangan taruh key di JavaScript browser, aplikasi mobile, atau repository publik.
- Key disimpan dalam bentuk hash di server kami, jadi tidak bisa ditampilkan ulang. Kalau hilang atau bocor, ganti key atau cabut lewat halaman API & Webhook.
- Opsional: batasi key ke alamat IP server kamu (IP whitelist). Request dari IP lain ditolak dengan kode
ip_not_allowed.
Format respons & batas
Semua respons berbentuk JSON. Request POST mengirim body JSON dengan header Content-Type: application/json.
{
"success": true,
"data": { }
}
{
"success": false,
"error": {
"code": "insufficient_balance",
"message": "Saldo tidak cukup. Dibutuhkan Rp20.500."
}
}- Batas 60 request per menit per API key. Sisa kuota ada di header
X-RateLimit-Remaining; saat habis kamu menerima HTTP429dengan headerRetry-After. POST /check-iddibatasi 20 request per menit danPOST /orders30 request per menit per key.- Semua harga dalam Rupiah (angka bulat, tanpa titik). Semua waktu dalam format ISO 8601 UTC.
- Kode HTTP:
200/201berhasil,400/422input salah,401/403key atau akses bermasalah,402saldo kurang,404tidak ditemukan,429kena batas,5xxmasalah di server.
Akun & saldo
/profileInfo akun pemilik key, saldo saat ini, dan batas request.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/profile`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/profile',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/profile' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/profile');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": {
"username": "budi",
"name": "Budi",
"balance": 150000,
"api_key": {
"name": "Bot Telegram",
"hint": "syah-3fa9••••••••c21d",
"created_at": "2026-11-03T08:00:00.000Z",
"webhook_enabled": true
},
"rate_limit_per_minute": 60
}
}
/balanceHanya saldo akun.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/balance`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/balance',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/balance' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/balance');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": {
"balance": 150000
}
}
Game & produk
/gamesDaftar game yang tersedia beserta harga mulai dari.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/games`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/games',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/games' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/games');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": [
{
"slug": "mobile-legends",
"name": "Mobile Legends: Bang Bang",
"category": "MOBA",
"requires_server_id": true,
"id_label": "User ID",
"server_label": "Zone ID",
"is_voucher": false,
"product_count": 24,
"min_price": 3500
}
]
}
/games/{slug}Detail satu game, termasuk semua produk aktif dan harga jualnya. Pakai code produk saat membuat pesanan.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/games/mobile-legends`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/games/mobile-legends',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/games/mobile-legends' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/games/mobile-legends');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": {
"slug": "mobile-legends",
"name": "Mobile Legends: Bang Bang",
"requires_server_id": true,
"id_label": "User ID",
"server_label": "Zone ID",
"id_check": true,
"products": [
{
"code": "ML86",
"name": "86 Diamonds",
"price": 21000
},
{
"code": "ML172",
"name": "172 Diamonds",
"price": 41500
}
]
}
}
Harga bisa berubah mengikuti pembaruan katalog. Harga yang dipotong dari saldo adalah harga saat pesanan dibuat, dan selalu dihitung ulang di server.
Cek ID game
/check-idCek nickname pemain sebelum membuat pesanan. Gratis, tidak memotong saldo.
| Field | Wajib | Keterangan |
|---|---|---|
game | ya | Slug game, mis. mobile-legends |
user_id | ya | ID pemain |
server_id | jika game memakai server | Zone/Server ID |
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.post(`${BASE_URL}/check-id`, {
game: 'mobile-legends',
user_id: '12345678',
server_id: '2001'
}, {
headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.post(
BASE_URL + '/check-id',
headers={'X-API-Key': API_KEY, 'Content-Type': 'application/json'},
json={
'game': 'mobile-legends',
'user_id': '12345678',
'server_id': '2001',
},
timeout=30,
)
print(res.json())
curl -X POST 'https://syah-topup.my.id/api/v1/check-id' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"game":"mobile-legends","user_id":"12345678","server_id":"2001"}'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/check-id');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'game' => 'mobile-legends',
'user_id' => '12345678',
'server_id' => '2001',
]),
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": {
"status": "ok",
"nickname": "BudiGamer",
"message": null
}
}
status bernilai ok (ID ditemukan), notfound (ID jelas tidak ada), error (layanan cek bermasalah, pembelian tetap bisa dilanjutkan), atau unsupported (game tidak punya cek ID).
Pesanan
/ordersBuat pesanan top up. Harga langsung dipotong dari saldo akunmu, lalu pesanan diproses otomatis. Respons pertama bisa berstatus pending, processing, atau langsung success; hasil akhir dikirim lewat webhook atau bisa kamu cek lewat GET /orders/{id}.
| Field | Wajib | Keterangan |
|---|---|---|
product_code | ya | Kode produk dari GET /games/{slug} |
user_id | ya | ID pemain. Untuk game voucher: nomor HP atau email penerima |
server_id | jika game memakai server | Zone/Server ID |
ref_id | disarankan | ID unik dari sistem kamu (maks 64 karakter: huruf, angka, . - _). Mencegah pesanan ganda saat request diulang |
game | tidak | Slug game. Kalau diisi, harus cocok dengan game milik produk |
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.post(`${BASE_URL}/orders`, {
product_code: 'ML86',
user_id: '12345678',
server_id: '2001',
ref_id: 'INV-1001'
}, {
headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.post(
BASE_URL + '/orders',
headers={'X-API-Key': API_KEY, 'Content-Type': 'application/json'},
json={
'product_code': 'ML86',
'user_id': '12345678',
'server_id': '2001',
'ref_id': 'INV-1001',
},
timeout=30,
)
print(res.json())
curl -X POST 'https://syah-topup.my.id/api/v1/orders' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"product_code":"ML86","user_id":"12345678","server_id":"2001","ref_id":"INV-1001"}'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/orders');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'product_code' => 'ML86',
'user_id' => '12345678',
'server_id' => '2001',
'ref_id' => 'INV-1001',
]),
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"duplicate": false,
"data": {
"order_id": "SYMK2F9A1B7C",
"ref_id": "INV-1001",
"status": "processing",
"message": "Pesanan sedang diproses.",
"game": "mobile-legends",
"game_name": "Mobile Legends: Bang Bang",
"product_code": "ML86",
"product_name": "86 Diamonds",
"user_id": "12345678",
"server_id": "2001",
"nickname": "BudiGamer",
"price": 21000,
"sn": null,
"created_at": "2026-11-03T08:15:00.000Z",
"paid_at": "2026-11-03T08:15:00.000Z",
"updated_at": "2026-11-03T08:15:02.000Z"
}
}
Idempotensi: kalau ref_id yang sama dikirim lagi, pesanan lama dikembalikan dengan HTTP 200 dan "duplicate": true, saldo tidak dipotong lagi. Selalu kirim ref_id supaya aman saat koneksi putus dan request diulang.
/orders/{id}{id} boleh berupa order_id atau ref_id. Mengembalikan objek pesanan seperti di atas, dengan status terbaru dan nomor serial (sn) saat tersedia.
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/orders/INV-1001`, {
headers: { 'X-API-Key': API_KEY },
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/orders/INV-1001',
headers={'X-API-Key': API_KEY},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/orders/INV-1001' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/orders/INV-1001');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"data": {
"order_id": "SYMK2F9A1B7C",
"ref_id": "INV-1001",
"status": "success",
"message": "Top up berhasil dikirim.",
"game": "mobile-legends",
"game_name": "Mobile Legends: Bang Bang",
"product_code": "ML86",
"product_name": "86 Diamonds",
"user_id": "12345678",
"server_id": "2001",
"nickname": "BudiGamer",
"price": 21000,
"sn": "1234567890",
"created_at": "2026-11-03T08:15:00.000Z",
"paid_at": "2026-11-03T08:15:00.000Z",
"updated_at": "2026-11-03T08:15:09.000Z"
}
}
/ordersDaftar pesanan yang dibuat lewat API, terbaru dulu. Query: page (default 1), limit (default 20, maks 50), status (opsional).
Contoh request
const axios = require('axios');
const BASE_URL = 'https://syah-topup.my.id/api/v1';
const API_KEY = 'YOUR_API_KEY';
axios
.get(`${BASE_URL}/orders`, {
headers: { 'X-API-Key': API_KEY },
params: {
page: 1,
limit: 20,
status: 'success'
},
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.response ? err.response.data : err.message));
import requests
BASE_URL = 'https://syah-topup.my.id/api/v1'
API_KEY = 'YOUR_API_KEY'
res = requests.get(
BASE_URL + '/orders',
headers={'X-API-Key': API_KEY},
params={
'page': 1,
'limit': 20,
'status': 'success',
},
timeout=30,
)
print(res.json())
curl 'https://syah-topup.my.id/api/v1/orders?page=1&limit=20&status=success' \
-H 'X-API-Key: YOUR_API_KEY'
<?php
$baseUrl = 'https://syah-topup.my.id/api/v1';
$apiKey = 'YOUR_API_KEY';
$ch = curl_init($baseUrl . '/orders' . '?' . http_build_query([
'page' => 1,
'limit' => 20,
'status' => 'success',
]));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'X-API-Key: ' . $apiKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
print_r(json_decode($response, true));
Contoh response
{
"success": true,
"pagination": {
"page": 1,
"limit": 20,
"total": 42,
"pages": 3
},
"data": [
{
"order_id": "SYMK2F9A1B7C",
"ref_id": "INV-1001",
"status": "success",
"message": "Top up berhasil dikirim.",
"game": "mobile-legends",
"game_name": "Mobile Legends: Bang Bang",
"product_code": "ML86",
"product_name": "86 Diamonds",
"user_id": "12345678",
"server_id": "2001",
"nickname": "BudiGamer",
"price": 21000,
"sn": "1234567890",
"created_at": "2026-11-03T08:15:00.000Z",
"paid_at": "2026-11-03T08:15:00.000Z",
"updated_at": "2026-11-03T08:15:09.000Z"
}
]
}
Webhook
Isi URL webhook di halaman API & Webhook dan kami akan mengirim POST JSON ke server kamu setiap kali pesanan API mencapai status akhir. Tiap API key punya secret webhook sendiri (berawalan syah-) untuk memverifikasi bahwa request benar-benar dari kami.
- URL harus https://, dapat diakses dari internet, dan tidak boleh mengarah ke alamat lokal/privat.
- Balas dengan HTTP
2xxdalam 8 detik. Respons lain atau timeout dianggap gagal dan dicoba ulang. - Pengiriman diulang otomatis sampai 7 kali (percobaan pertama + 6 ulangan, berjarak 10 dtk, 1 mnt, 5 mnt, 30 mnt, 2 jam, 6 jam). Karena itu server kamu harus siap menerima event yang sama lebih dari sekali: cegah duplikat dengan field
id. - Redirect (3xx) tidak diikuti.
Event
| Event | Kapan dikirim |
|---|---|
order.success | Top up berhasil dikirim ke pemain |
order.failed | Top up gagal dan dana belum dikembalikan |
order.refunded | Top up gagal dan dana sudah dikembalikan ke saldo |
order.cancelled | Pesanan dibatalkan |
webhook.test | Tes dari tombol "Tes webhook" |
Header
| Header | Isi |
|---|---|
X-Syah-Event | Nama event, mis. order.success |
X-Syah-Delivery | ID pengiriman (sama dengan field id di body) |
X-Syah-Timestamp | Waktu kirim (detik Unix) |
X-Syah-Signature | sha256= + HMAC-SHA256 heksadesimal |
Body
{
"id": "whd_9c1d2e3f4a5b6c7d8e9f",
"event": "order.success",
"created_at": "2026-11-03T08:15:09.000Z",
"data": {
"order_id": "SYMK2F9A1B7C",
"ref_id": "INV-1001",
"status": "success",
"message": "Top up berhasil dikirim.",
"game": "mobile-legends",
"game_name": "Mobile Legends: Bang Bang",
"product_code": "ML86",
"product_name": "86 Diamonds",
"user_id": "12345678",
"server_id": "2001",
"nickname": "BudiGamer",
"price": 21000,
"sn": "1234567890",
"created_at": "2026-11-03T08:15:00.000Z",
"paid_at": "2026-11-03T08:15:00.000Z",
"updated_at": "2026-11-03T08:15:09.000Z"
}
}
Verifikasi tanda tangan
Hitung HMAC-SHA256 dengan secret webhook sebagai kunci atas teks timestamp + "." + body_mentah, lalu bandingkan dengan header X-Syah-Signature memakai perbandingan waktu-konstan. Pakai body mentah (belum di-parse), dan tolak request yang timestamp-nya lebih dari 5 menit.
const crypto = require('crypto');
const express = require('express');
const app = express();
const SECRET = 'YOUR_WEBHOOK_SECRET';
app.post('/syah-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-Syah-Timestamp') || '';
const signature = req.get('X-Syah-Signature') || '';
const body = req.body.toString('utf8');
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(timestamp + '.' + body).digest('hex');
const a = Buffer.from(signature);
const b = Buffer.from(expected);
const valid = a.length === b.length && crypto.timingSafeEqual(a, b);
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
if (!valid || !fresh) return res.status(401).json({ ok: false });
const evt = JSON.parse(body);
console.log(evt.event, evt.data.order_id, evt.data.status);
res.json({ ok: true });
});
app.listen(3000);
import hashlib
import hmac
import time
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET = b'YOUR_WEBHOOK_SECRET'
@app.post('/syah-webhook')
def syah_webhook():
timestamp = request.headers.get('X-Syah-Timestamp', '')
signature = request.headers.get('X-Syah-Signature', '')
body = request.get_data()
signed = timestamp.encode() + b'.' + body
expected = 'sha256=' + hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
fresh = timestamp.isdigit() and abs(time.time() - int(timestamp)) < 300
if not hmac.compare_digest(signature, expected) or not fresh:
return jsonify(ok=False), 401
evt = request.get_json()
print(evt['event'], evt['data']['order_id'], evt['data']['status'])
return jsonify(ok=True)
# Simulasi pengiriman webhook ke server kamu (untuk tes verifikasi tanda tangan)
SECRET='syah-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
TS=$(date +%s)
BODY='{"id":"whd_test","event":"webhook.test","created_at":"2026-11-03T08:15:09.000Z","data":{}}'
SIG="sha256=$(printf '%s' "$TS.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')"
curl -X POST 'https://server-kamu.com/syah-webhook' \
-H 'Content-Type: application/json' \
-H 'X-Syah-Event: webhook.test' \
-H 'X-Syah-Delivery: whd_test' \
-H "X-Syah-Timestamp: $TS" \
-H "X-Syah-Signature: $SIG" \
-d "$BODY"
<?php
$secret = 'YOUR_WEBHOOK_SECRET';
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_SYAH_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SYAH_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
$fresh = ctype_digit($timestamp) && abs(time() - (int) $timestamp) < 300;
if (!hash_equals($expected, $signature) || !$fresh) {
http_response_code(401);
exit;
}
$evt = json_decode($body, true);
error_log($evt['event'] . ' ' . $evt['data']['order_id'] . ' ' . $evt['data']['status']);
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
Secret webhook hanya tampil saat dibuat atau diputar ulang. Kalau lupa, putar ulang secret di halaman API & Webhook lalu perbarui di server kamu.
Status pesanan
| Status | Artinya | Final? |
|---|---|---|
pending | Pesanan diterima, menunggu giliran diproses | tidak |
processing | Sedang diproses ke penyedia produk | tidak |
success | Top up berhasil. Field sn berisi nomor serial bila tersedia | ya |
failed | Top up gagal. Dana belum kembali, hubungi admin bila perlu | ya |
refunded | Top up gagal dan dana sudah dikembalikan ke saldo akun | ya |
cancelled | Pesanan dibatalkan | ya |
Jangan menganggap pesanan berhasil sebelum status success. Kalau webhook tidak sampai, cek lewat GET /orders/{id} dengan jeda beberapa detik, jangan membuat pesanan baru.
Kode error
| HTTP | error.code | Penyebab |
|---|---|---|
| 400 | invalid_json | Body bukan JSON yang valid |
| 401 | missing_api_key, invalid_api_key | Key tidak dikirim, salah, atau sudah dicabut |
| 402 | insufficient_balance | Saldo kurang. Respons memuat required dan balance |
| 403 | ip_not_allowed, account_suspended | IP tidak ada di whitelist, atau akun ditangguhkan |
| 404 | game_not_found, product_not_found, order_not_found, not_found | Data tidak ada atau produk tidak dijual |
| 413 | payload_too_large | Body terlalu besar |
| 422 | invalid_request, invalid_user_id, invalid_server_id, invalid_ref_id, invalid_status, game_mismatch, id_not_found | Input tidak valid. id_not_found = ID game jelas tidak ada |
| 429 | rate_limited | Kena batas request. Tunggu sesuai header Retry-After |
| 500 | server_error | Kesalahan di server kami |
| 503 | api_disabled, maintenance | API dimatikan admin atau website sedang perawatan |
Alur untuk bot & website
- Ambil katalog dengan
GET /gamesdanGET /games/{slug}, simpan sebentar di cache sisi kamu. - Pembeli mengisi ID. Panggil
POST /check-iddan tampilkan nickname untuk konfirmasi. - Buat
ref_idunik (mis. nomor invoice sistemmu), laluPOST /orders. - Tunggu webhook
order.success/order.refunded. Sebagai cadangan, cekGET /orders/{ref_id}tiap 10-30 detik sampai status final. - Kalau
POST /orderstime out atau koneksi putus, ulangi denganref_idyang sama, jangan buat ref baru.