Dokumentasi API
Versi API: v1 · Base URL: https://permatapay.com/access/api/v1
Pengantar
API ini menerima request JSON dari server mitra, meneruskannya untuk diproses, lalu mengembalikan hasil secara sinkron (respons HTTP) dan asinkron melalui webhook ke URL yang Anda daftarkan.
- Semua request memakai method POST dengan header
Content-Type: application/json(UTF-8). - Nama endpoint case-sensitive (mis.
/GetTrx, bukan/gettrx). - Wajib HTTPS. Request ke
http://ditolak (HTTP 403) karena memuat PIN/password. Request langsung ke IP server tidak dilayani. - Setelah 20 kali gagal autentikasi dalam 1 menit (sign/username salah), IP pengirim diblokir sementara selama 15 menit (HTTP 429).
Persiapan
Setelah terdaftar sebagai mitra, Anda menerima:
| Data | Keterangan |
|---|---|
username | Identitas akun mitra, dikirim di setiap request. |
apikey | Kunci rahasia untuk menghitung sign. Jangan pernah dikirim di payload. |
memberID | Kode reseller (ID agen) yang boleh Anda gunakan. Satu mitra bisa memiliki beberapa memberID. |
Anda perlu memberikan kepada kami: IP public IPv4 server Anda dan URL webhook untuk menerima hasil transaksi.
Autentikasi & sign
Setiap request wajib menyertakan parameter sign berupa MD5 (hex huruf kecil) dari gabungan string berikut:
| Endpoint | Rumus sign |
|---|---|
/trx | MD5(username + apikey + refID) |
/balance | MD5(username + apikey + memberID) — tanpa pin/password |
/saldo | MD5(username + apikey + amount) |
/GetTrx | MD5(username + apikey + trxid) |
/GetTrxHistory | MD5(username + apikey + kode_reseller) |
Contoh: username mitra01, apikey abc123, refID TRX-001 → sign = MD5("mitra01abc123TRX-001").
Ketentuan IP
- Request hanya diterima dari IP public IPv4 yang terdaftar untuk akun Anda. IPv6 tidak didukung.
- Pastikan server mengirim request dari IP yang terdaftar (perhatikan NAT/load balancer). IP asal setiap request dicatat untuk keperluan audit.
- Perubahan atau penambahan IP diajukan ke admin terlebih dahulu.
POST /trx — Transaksi
POST https://permatapay.com/access/api/v1/trx
| Field | Wajib | Keterangan |
|---|---|---|
username | Ya | Username mitra. |
product | Ya | Kode produk, huruf/angka (mis. S10). |
qty | Ya | Jumlah, default 1 (1–99). |
dest | Ya | Nomor tujuan / ID pelanggan. |
refID | Ya | ID transaksi dari sisi Anda, unik selamanya per akun (huruf, angka, -, _; maks 40). |
memberID | Ya | Kode reseller milik Anda. |
pin | Ya | PIN transaksi. |
password | Ya | Password akun reseller. |
sign | Ya | MD5(username + apikey + refID) |
{
"username": "mitra01",
"product": "S10",
"qty": "1",
"dest": "081234567890",
"refID": "TRX-20261012-001",
"memberID": "MBR12345",
"pin": "123456",
"password": "password123",
"sign": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
}
Respons (JSON):
{
"status": 1,
"status_desc": "Sedang diproses",
"trxid": 82353212,
"refid": "TRX-20261012-001",
"message": "S10.081234567890 akan diproses @19:02:02. Saldo 330.742.469"
}
Status 1 berarti transaksi sedang diproses; hasil akhir dikirim ke webhook Anda. Mengirim ulang refID yang sama tidak membuat transaksi baru — respons berisi "status": 46 beserta trxid dan status terakhir transaksi tersebut.
POST /balance — Cek saldo
POST https://permatapay.com/access/api/v1/balance — saldo dibaca real-time langsung dari sistem kami.
| Field | Wajib | Keterangan |
|---|---|---|
username | Ya | Username mitra. |
memberID | Ya | Kode reseller milik Anda (alias memberid / kode_reseller). |
sign | Ya | MD5(username + apikey + memberID) |
pin & password tidak diperlukan untuk cek saldo; bila dikirim akan diabaikan.
{ "username": "mitra01", "memberID": "MBR12345", "sign": "…" }
{ "status": 20, "status_desc": "Sukses", "memberID": "MBR12345", "saldo": 27849103 }
Bila gagal: "status": 40 tanpa field saldo, alasan di message (HTTP 401/403/400/503 sesuai tabel Format respons), mis. { "status": 40, "status_desc": "Gagal", "memberID": "", "message": "Invalid sign" }.
POST /saldo — Tiket deposit
POST https://permatapay.com/access/api/v1/saldo · field: username, memberid, amount (angka saja), pin, password, sign = MD5(username + apikey + amount).
{ "username": "mitra01", "memberid": "MBR12345", "amount": "500000", "pin": "123456", "password": "password123", "sign": "…" }
POST /GetTrx — Cek status transaksi
POST https://permatapay.com/access/api/v1/GetTrx · field: username, trxid, sign = MD5(username + apikey + trxid). Hanya transaksi milik memberID Anda yang dapat dibaca. Status selalu dicek ulang secara langsung ke sistem kami (real-time); field statusDesc berisi keterangan sesuai tabel Kode status.
{ "username": "mitra01", "trxid": 82353109, "sign": "…" }
{
"ok": true,
"result": {
"kode": 82353109, "kode_reseller": "MBR12345", "tgl_entri": "2026-09-13T12:25:35.707",
"kode_produk": "SN10", "tujuan": "082298769000", "qty": 1, "harga": 10175.0,
"status": 20, "ref_id": "DJW95VHJ", "sn": "2500XZ1YGBOAGXVNE3DGB8D", "statusDesc": "Sukses"
}
}
POST /GetTrxHistory — Riwayat transaksi
POST https://permatapay.com/access/api/v1/GetTrxHistory · field: username, kode_reseller (alias memberid), tgl_awal & tgl_akhir (YYYY-MM-DD), limit (opsional, 1–1000), sign = MD5(username + apikey + kode_reseller).
{ "username": "mitra01", "kode_reseller": "MBR12345", "tgl_awal": "2026-09-01", "tgl_akhir": "2026-09-23", "limit": 10, "sign": "…" }
{ "ok": true, "count": 1, "result": [ { "kode": 82353107, "kode_produk": "SN20", "tujuan": "081282706050", "harga": 19910.0, "status": 20, "sn": "…", "statusDesc": "Sukses" } ] }
Format respons
Semua endpoint merespons JSON (Content-Type: application/json). /trx dan /saldo memakai struktur berikut (nama field sama dengan webhook); /balance memakai struktur saldo (lihat POST /balance); /GetTrx dan /GetTrxHistory memakai {"ok": …, "result": …}.
| Field | Tipe | Keterangan |
|---|---|---|
status | angka | Kode status (lihat Kode status) |
status_desc | teks | Keterangan status sesuai tabel kode status |
trxid | angka / null | ID transaksi di sistem kami, dipakai untuk /GetTrx. null bila belum diketahui saat respons dikirim (mis. masih antre/timeout) — trxid tetap dikirim di webhook. |
refid | teks | refID Anda (untuk /trx). Pada /saldo berisi ID permintaan dari sistem kami (TKT-…), sama dengan refid di webhook. Kosong bila request ditolak sebelum refID terbaca. |
message | teks | Pesan lengkap dari sistem |
| Kondisi | HTTP | Contoh body |
|---|---|---|
| Diterima & diproses | 200 | {"status":1,"status_desc":"Sedang diproses","trxid":82353212,"refid":"TRX-…","message":"… akan diproses …"} |
| Status final sudah diketahui | 200 | {"status":20,"status_desc":"Sukses",…} |
| refID sudah pernah dipakai | 200 | {"status":46,"status_desc":"Transaksi double","trxid":…,"refid":"…","message":"Transaksi double, …"} |
| Sign salah / username tidak dikenal | 401 | {"status":40,"status_desc":"Gagal","trxid":null,"refid":"","message":"Invalid sign"} |
| IP tidak terdaftar / memberID bukan milik Anda | 403 | {"status":40,…,"message":"Akses ditolak"} |
| Parameter tidak valid | 400 | {"status":40,…,"message":"Parameter 'dest' tidak valid"} |
Request lewat http:// (bukan https) | 403 | {"status":40,…,"message":"Wajib menggunakan HTTPS: https://…"} |
| Terlalu banyak percobaan gagal dari IP Anda | 429 | {"status":40,…,"message":"Terlalu banyak percobaan gagal, coba lagi nanti"} — tunggu 15 menit |
| Terlalu banyak request | 429 | {"status":40,…,"message":"Terlalu banyak request"} |
| Layanan sementara tidak tersedia | 503 | {"status":40,…,"message":"Layanan sementara tidak tersedia"} — aman diulang dengan refID yang sama |
Jangan menyimpulkan transaksi gagal hanya karena timeout koneksi. Jika ragu, cek dengan /GetTrx atau tunggu webhook; mengulang refID yang sama aman karena tidak akan menimbulkan transaksi ganda.
Kode status
Kode status dipakai di respons sinkron, /GetTrx, /GetTrxHistory, dan webhook.
| Kode | Keterangan | Final? |
|---|---|---|
20 | Sukses | Ya |
40 | Gagal | Ya |
43 | Saldo tidak cukup | Ya |
45 | Stok kosong | Ya |
46 | Transaksi double | Ya |
47 | Produk gangguan | Ya |
50 | Dibatalkan | Ya |
52 | Tujuan salah | Ya |
53 | Tujuan di luar wilayah | Ya |
54 | Kode area tidak sesuai | Ya |
55 | Timeout | Ya |
56 | Nomor blacklist | Ya |
58 | Nomor tidak aktif | Ya |
61 | Qty tidak sesuai | Ya |
69 | Cutoff | Ya |
200 | Proses ulang | Tidak — tunggu status berikutnya |
Status antara (bukan final) yang juga dapat Anda terima: 0 baru/antre, 1 sedang diproses, 2 menunggu jawaban, 200 proses ulang.
Status final = 20 (sukses) atau 40–69 (gagal/ditolak). Setelah status final diterima, status transaksi tidak berubah lagi. Status selain itu berarti transaksi masih berjalan — tunggu webhook berikutnya atau cek dengan /GetTrx.
Webhook
Hasil transaksi dikirim ke URL webhook Anda sebagai HTTP POST dengan body JSON (Content-Type: application/json):
POST https://mitra.example.com/report
Content-Type: application/json
X-Gateway-Event: trx.update
X-Gateway-Timestamp: 2026-10-24T09:49:25+07:00
X-Gateway-Signature: ecca99da7e111b97…
{
"event": "trx.update",
"refid": "TRX-20261012-001",
"trxid": 82353211,
"memberID": "MBR12345",
"status": 20,
"status_desc": "Sukses",
"price": 10175,
"sn": "81373934225599591409",
"message": "TELKOMSEL 10.000 SN10 082298769000 BERHASIL SN 81373934225599591409 …",
"timestamp": "2026-10-24T09:49:25+07:00"
}
| Field | Keterangan |
|---|---|
event | trx.update untuk status transaksi; message untuk balasan non-transaksi (cek saldo/tiket) |
refid | refID Anda |
trxid | ID transaksi di sistem kami (untuk /GetTrx); null pada event message |
memberID | Kode reseller |
status | Kode status (lihat tabel) |
status_desc | Keterangan status sesuai tabel Kode status (mis. 52 → Tujuan salah) |
price | Harga transaksi |
sn | Serial number (bila ada) |
message | Pesan lengkap |
timestamp | Waktu kirim (ISO-8601, WIB) |
Untuk kompatibilitas sistem lama, webhook dapat diubah ke HTTP GET dengan parameter query (report?t=…&refid=…&status=…&price=…&memberID=…&sn=…&trxid=…&message=…) atas permintaan ke admin.
Verifikasi webhook
Setiap webhook membawa header:
X-Gateway-Signature=hex(HMAC-SHA256(apikey, data)), dengandata= body JSON mentah (atau query string mentah bila memakai format GET).X-Gateway-Timestamp= waktu kirim (ISO-8601).X-Gateway-Event= jenis event.
Webhook dikirim dari IP 103.179.66.124 — Anda dapat menambahkannya ke whitelist server Anda.
Balasan & retry
- Balas dengan HTTP 2xx dalam 10 detik. Proses berat sebaiknya dilakukan setelah membalas.
- Bila gagal, webhook dikirim ulang hingga 10 kali (30 dtk, 1 m, 2 m, 5 m, 10 m, 30 m, 1 j, 2 j, 6 j, 12 j).
- Gateway meneruskan semua respons/callback dari sistem kami, termasuk status antara (mis.
0,200). Satu transaksi bisa menghasilkan beberapa webhook; gunakan webhook dengan status final sebagai hasil akhir. - Webhook untuk status yang sama bisa diterima lebih dari sekali (mis. pengiriman ulang) — gunakan
refid+statusuntuk deduplikasi.
Contoh kode
PHP — kirim transaksi
$username = 'mitra01';
$apikey = 'APIKEY_ANDA';
$payload = [
'username' => $username, 'product' => 'S10', 'qty' => '1', 'dest' => '081234567890',
'refID' => 'TRX-' . date('YmdHis'), 'memberID' => 'MBR12345',
'pin' => '123456', 'password' => 'password123',
];
$payload['sign'] = md5($username . $apikey . $payload['refID']);
$ch = curl_init('https://permatapay.com/access/api/v1/trx');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$out = json_decode(curl_exec($ch), true);
// $out['status'], $out['status_desc'], $out['trxid'] (null bila belum ada), $out['refid'], $out['message']
PHP — terima & verifikasi webhook (JSON)
$raw = file_get_contents('php://input');
$expected = hash_hmac('sha256', $raw, 'APIKEY_ANDA');
if (!hash_equals($expected, $_SERVER['HTTP_X_GATEWAY_SIGNATURE'] ?? '')) {
http_response_code(401); exit;
}
$data = json_decode($raw, true);
// $data['refid'], $data['status'], $data['sn'], $data['message'] ...
http_response_code(200);
echo 'OK';
Node.js — sign & verifikasi
const crypto = require('crypto');
const sign = crypto.createHash('md5').update(username + apikey + refID).digest('hex');
// verifikasi webhook POST JSON (gunakan raw body)
const expected = crypto.createHmac('sha256', apikey).update(rawBody).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-gateway-signature'] || ''));
FAQ
Request saya ditolak "Akses ditolak". Pastikan IP server Anda (IPv4) sudah terdaftar, memberID milik akun Anda, dan akun aktif.
Respons "Invalid sign". Periksa urutan gabungan string (username + apikey + field kunci), gunakan nilai field persis seperti yang dikirim, dan hasil MD5 dalam hex.
Status masih 1 lama sekali. Transaksi sedang diproses oleh provider. Tunggu webhook atau cek dengan /GetTrx.
Apakah aman mengirim ulang? Ya, dengan refID yang sama. Transaksi tidak akan diproses dua kali.
Changelog
- v1 — rilis awal: trx, balance, saldo, GetTrx, GetTrxHistory, webhook POST JSON (default) / GET dengan HMAC.