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.

Persiapan

Setelah terdaftar sebagai mitra, Anda menerima:

DataKeterangan
usernameIdentitas akun mitra, dikirim di setiap request.
apikeyKunci rahasia untuk menghitung sign. Jangan pernah dikirim di payload.
memberIDKode 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:

EndpointRumus sign
/trxMD5(username + apikey + refID)
/balanceMD5(username + apikey + memberID) — tanpa pin/password
/saldoMD5(username + apikey + amount)
/GetTrxMD5(username + apikey + trxid)
/GetTrxHistoryMD5(username + apikey + kode_reseller)

Contoh: username mitra01, apikey abc123, refID TRX-001 → sign = MD5("mitra01abc123TRX-001").

Ketentuan IP

POST /trx — Transaksi

POST https://permatapay.com/access/api/v1/trx

FieldWajibKeterangan
usernameYaUsername mitra.
productYaKode produk, huruf/angka (mis. S10).
qtyYaJumlah, default 1 (1–99).
destYaNomor tujuan / ID pelanggan.
refIDYaID transaksi dari sisi Anda, unik selamanya per akun (huruf, angka, -, _; maks 40).
memberIDYaKode reseller milik Anda.
pinYaPIN transaksi.
passwordYaPassword akun reseller.
signYaMD5(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.

FieldWajibKeterangan
usernameYaUsername mitra.
memberIDYaKode reseller milik Anda (alias memberid / kode_reseller).
signYaMD5(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": …}.

FieldTipeKeterangan
statusangkaKode status (lihat Kode status)
status_descteksKeterangan status sesuai tabel kode status
trxidangka / nullID transaksi di sistem kami, dipakai untuk /GetTrx. null bila belum diketahui saat respons dikirim (mis. masih antre/timeout) — trxid tetap dikirim di webhook.
refidteksrefID Anda (untuk /trx). Pada /saldo berisi ID permintaan dari sistem kami (TKT-…), sama dengan refid di webhook. Kosong bila request ditolak sebelum refID terbaca.
messageteksPesan lengkap dari sistem
KondisiHTTPContoh body
Diterima & diproses200{"status":1,"status_desc":"Sedang diproses","trxid":82353212,"refid":"TRX-…","message":"… akan diproses …"}
Status final sudah diketahui200{"status":20,"status_desc":"Sukses",…}
refID sudah pernah dipakai200{"status":46,"status_desc":"Transaksi double","trxid":…,"refid":"…","message":"Transaksi double, …"}
Sign salah / username tidak dikenal401{"status":40,"status_desc":"Gagal","trxid":null,"refid":"","message":"Invalid sign"}
IP tidak terdaftar / memberID bukan milik Anda403{"status":40,…,"message":"Akses ditolak"}
Parameter tidak valid400{"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 Anda429{"status":40,…,"message":"Terlalu banyak percobaan gagal, coba lagi nanti"} — tunggu 15 menit
Terlalu banyak request429{"status":40,…,"message":"Terlalu banyak request"}
Layanan sementara tidak tersedia503{"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.

KodeKeteranganFinal?
20SuksesYa
40GagalYa
43Saldo tidak cukupYa
45Stok kosongYa
46Transaksi doubleYa
47Produk gangguanYa
50DibatalkanYa
52Tujuan salahYa
53Tujuan di luar wilayahYa
54Kode area tidak sesuaiYa
55TimeoutYa
56Nomor blacklistYa
58Nomor tidak aktifYa
61Qty tidak sesuaiYa
69CutoffYa
200Proses ulangTidak — 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"
}
FieldKeterangan
eventtrx.update untuk status transaksi; message untuk balasan non-transaksi (cek saldo/tiket)
refidrefID Anda
trxidID transaksi di sistem kami (untuk /GetTrx); null pada event message
memberIDKode reseller
statusKode status (lihat tabel)
status_descKeterangan status sesuai tabel Kode status (mis. 52 → Tujuan salah)
priceHarga transaksi
snSerial number (bila ada)
messagePesan lengkap
timestampWaktu 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:

Webhook dikirim dari IP 103.179.66.124 — Anda dapat menambahkannya ke whitelist server Anda.

Balasan & retry

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