Lompat ke konten utama
Gempay Topup

Akun Gempay

Untuk reseller & integrator

Dokumentasi API H2H Gempay

Hubungkan bot, website, atau sistem penjualan Anda langsung ke saldo Gempay. Panduan ini mencakup seluruh alur dari sandbox hingga produksi, API Cek Region MLBB, strategi aman saat timeout, dan verifikasi callback.

Masuk untuk Membuat Kunci

Panduan koneksi lengkap

Hubungkan sistem Anda ke API H2H

Ikuti panduan ini dari pembuatan key hingga go-live. Semua contoh memakai JSON, harga dalam integer rupiah, dan kontrak respons yang sama untuk sandbox maupun produksi. Key yang sama juga dapat digunakan untuk API Cek Region MLBB.

Base URL
https://gempaytopup.com/api/v1
Contoh username key
gmp_1234abcd

1. Alur integrasi yang disarankan

  1. 01 Buat key sandbox Simpan username dan secret yang hanya ditampilkan sekali.
  2. 02 Uji autentikasi Panggil endpoint balance dengan signature yang benar.
  3. 03 Ambil price-list Simpan SKU, harga role, status produk, dan definisi field tujuan.
  4. 04 Uji tiga status Gunakan ref_id sandbox untuk hasil sukses, gagal, dan pending.
  5. 05 Pasang callback Verifikasi HMAC dari raw body dan tetap sediakan polling status.
  6. 06 Buat key produksi Isi saldo, batasi IP server, lalu lakukan transaksi nominal kecil.
Aturan paling penting: jika request transaksi timeout, atau menerima rc 03/rc 99, gunakan endpoint status dengan ref_id yang sama. Jangan membuat ref_id baru karena transaksi pertama mungkin sudah diproses.

2. Koneksi, kredensial, dan autentikasi

Aturan transport

  • • Method seluruh endpoint: POST.
  • • Header: Content-Type: application/json.
  • • Gunakan HTTPS dan kirim dari backend/server, bukan JavaScript browser.
  • • Harga dan saldo selalu integer rupiah, tanpa desimal.
  • • Kondisi bisnis umumnya memakai HTTP 200; keputusan diambil dari data.rc.

Field autentikasi wajib

username
Prefix key, misalnya gmp_1234abcd.
ref_id
Referensi dari sistem Anda, 1–64 karakter, tanpa spasi di awal/akhir.
sign
MD5 huruf kecil dari gabungan username, secret, dan ref_id tanpa pemisah.
sign = md5(username + secret + ref_id)

// Tidak memakai titik dua, pipe, spasi, atau newline di antara nilai.
// Contoh sumber hash:
gmp_1234abcdSECRET_ANDAorder-20260802-0001
Signature request memakai MD5 untuk kompatibilitas klien reseller. Signature callback berbeda: callback memakai HMAC-SHA256 atas raw body. Jangan tertukar.

3. Contoh kode klien

Simpan kredensial di environment variable server. Fungsi berikut dapat dipakai untuk semua endpoint; payload khusus endpoint ditambahkan pada argumen terakhir.

PHP 8+ dengan cURL
<?php

function gempayRequest(string $endpoint, string $refId, array $payload = []): array
{
    $baseUrl = getenv('GEMPAY_API_BASE_URL');
    $username = getenv('GEMPAY_API_USERNAME');
    $secret = getenv('GEMPAY_API_SECRET');

    $body = array_merge($payload, [
        'username' => $username,
        'ref_id' => $refId,
        'sign' => md5($username.$secret.$refId),
    ]);

    $curl = curl_init($baseUrl.'/'.$endpoint);
    curl_setopt_array($curl, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 20,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode($body, JSON_THROW_ON_ERROR),
    ]);

    $raw = curl_exec($curl);
    if ($raw === false) {
        throw new RuntimeException(curl_error($curl));
    }

    return json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
}

$result = gempayRequest('balance', 'balance-20260802-0001');
$rc = $result['data']['rc'];
Node.js 18+ dengan fetch
import { createHash } from 'node:crypto';

async function gempayRequest(endpoint, refId, payload = {}) {
  const baseUrl = process.env.GEMPAY_API_BASE_URL;
  const username = process.env.GEMPAY_API_USERNAME;
  const secret = process.env.GEMPAY_API_SECRET;
  const sign = createHash('md5')
    .update(username + secret + refId, 'utf8')
    .digest('hex');

  const response = await fetch(`${baseUrl}/${endpoint}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ ...payload, username, ref_id: refId, sign }),
    signal: AbortSignal.timeout(20_000),
  });

  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}

const result = await gempayRequest('balance', 'balance-20260802-0001');
console.log(result.data.rc, result.data.balance);
cURL untuk diagnosis cepat
curl --request POST 'https://gempaytopup.com/api/v1/balance' \
  --header 'Content-Type: application/json' \
  --data '{
    "username": "gmp_1234abcd",
    "ref_id": "balance-20260802-0001",
    "sign": "HASIL_MD5_ANDA"
  }'

Hitung sign di sistem Anda. Jangan menempel secret ke shell history pada server bersama.

4. Referensi endpoint

EndpointFungsiField tambahan
POST /price-listDaftar produk dan harga role pemilik key.Tidak ada.
POST /transactionMembuat transaksi atau mengembalikan replay idempoten.buyer_sku_code, tujuan.
POST /statusMengambil status terbaru berdasarkan ref_id.Tidak ada.
POST /balanceMengambil saldo reseller terbaru.Tidak ada.
POST /ml-regionCek nickname, region, dan type layanan MLBB.user_id, zone_id; paket produksi wajib aktif.
POST

/api/v1/balance

Endpoint pertama yang sebaiknya diuji untuk memastikan key, signature, allowlist IP, dan rate limit bekerja.

{
  "username": "gmp_1234abcd",
  "ref_id": "balance-20260802-0001",
  "sign": "HASIL_MD5"
}

{
  "data": {
    "username": "gmp_1234abcd",
    "balance": 500000,
    "rc": "00",
    "message": "Transaksi sukses"
  }
}
POST

/api/v1/price-list

Mengembalikan seluruh katalog. price sudah memakai role harga pemilik key. Jangan memakai harga yang disimpan permanen; sinkronkan ulang secara berkala dan sebelum menampilkan harga jual hilir.

{
  "username": "gmp_1234abcd",
  "ref_id": "pricelist-20260802-0001",
  "sign": "HASIL_MD5"
}

{
  "data": {
    "username": "gmp_1234abcd",
    "price_list": [{
      "buyer_sku_code": "ml-86",
      "product_name": "Mobile Legends 86 Diamonds",
      "category": "Mobile Legends",
      "supplier": "digiflazz",
      "input_fields": [
        {"key":"user_id","label":"User ID","type":"numeric","required":true,"min":5,"max":20,"options":[]},
        {"key":"zone_id","label":"Zone ID","type":"numeric","required":true,"min":1,"max":10,"options":[]}
      ],
      "customer_no_format": null,
      "price": 21000,
      "buyer_product_status": true,
      "seller_product_status": true,
      "unlimited_stock": true,
      "stock": 0,
      "multi": true,
      "start_cut_off": null,
      "end_cut_off": null
    }],
    "rc": "00",
    "message": "Transaksi sukses"
  }
}
  • • Tampilkan/jual produk hanya ketika buyer_product_status dan seller_product_status bernilai true.
  • • Jika unlimited_stock false, periksa stock.
  • multi menjelaskan apakah tujuan yang sama boleh ditransaksikan berulang.
  • supplier dapat bernilai digiflazz, foxy, atau manual. Tetap gunakan SKU publik pada buyer_sku_code; jangan mengirim SKU internal supplier.
  • start_cut_off/end_cut_off dapat menandai periode gangguan terjadwal.
POST

/api/v1/transaction

Membuat order dari saldo member. Server menghitung ulang harga berdasarkan key; jangan mengirim field harga dari sistem Anda.

{
  "username": "gmp_1234abcd",
  "ref_id": "order-20260802-0001",
  "sign": "HASIL_MD5",
  "buyer_sku_code": "ml-86",
  "tujuan": {
    "user_id": "123456789",
    "zone_id": "1234"
  }
}

{
  "data": {
    "ref_id": "order-20260802-0001",
    "status": "Pending",
    "buyer_sku_code": "ml-86",
    "customer_no": "1234567891234",
    "price": 21000,
    "balance": 479000,
    "sn": null,
    "created_at": "2026-08-02T13:30:00+07:00",
    "rc": "03",
    "message": "Transaksi pending"
  }
}
POST

/api/v1/status

Gunakan username, ref_id transaksi asli, dan sign yang dihitung dari ref_id itu. Status hanya dapat dibaca oleh key yang membuat transaksi tersebut.

{
  "username": "gmp_1234abcd",
  "ref_id": "order-20260802-0001",
  "sign": "HASIL_MD5_UNTUK_REF_ID_YANG_SAMA"
}

Jika transaksi tidak ditemukan untuk key tersebut, API mengembalikan rc 01 dan pesan Transaksi tidak ditemukan. Periksa key dan ref_id sebelum membuat order baru.

POST

/api/v1/ml-region

Memakai autentikasi H2H yang sama. Key sandbox dapat menguji respons deterministik tanpa paket; key produksi membutuhkan paket Region API aktif dan IP yang lolos allowlist key serta whitelist paket.

{
  "username": "gmp_1234abcd",
  "ref_id": "cek-ml-20260804-0001",
  "sign": "HASIL_MD5",
  "user_id": "106101371",
  "zone_id": "2540"
}
Dokumentasi Region API lengkap

5. Membentuk field tujuan dengan benar

Jangan menebak atau menggabungkan ID sendiri. Untuk setiap SKU, baca input_fields dari price-list lalu kirim object tujuan dengan key yang sama. Server yang akan memvalidasi dan menyusun nomor tujuan supplier.

Aturan ini juga berlaku untuk produk Foxygamestore. Produk Foxy memakai SKU publik berawalan FX-, tetapi bentuk tujuan tetap selalu mengikuti input_fields yang dikembalikan price-list.

Satu nomor tujuan

"tujuan": {
  "customer_no": "081234567890"
}

Mobile Legends

"tujuan": {
  "user_id": "123456789",
  "zone_id": "1234"
}

Genshin / Honkai: Star Rail

"tujuan": {
  "user_id": "123456789",
  "server": "os_asia"
}
Kode server Genshin/HSRRegion
os_asiaAsia
os_usaAmerica
os_euroEurope
os_chtTW, HK, MO

Field bertipe select mempunyai daftar nilai sah pada properti options. Field numeric tetap dikirim sebagai string agar nol di depan tidak hilang. Jangan mengubah digit tujuan.

6. Membaca respons dan response code

Semua payload API dibungkus oleh object data. Simpan minimal ref_id, rc, status, price, sn, dan respons mentah yang sudah Anda redaksi untuk kebutuhan rekonsiliasi.

rcArtiTindakan sistem Anda
00Transaksi suksesTandai sukses; simpan SN bila tersedia.
01Transaksi gagal / status tidak ditemukanFinal jika berasal dari transaksi. Order baru hanya atas keputusan pengguna.
02Ditolak supplierFinal; boleh order ulang dengan ref_id baru setelah penyebab diperbaiki.
03PendingPolling status dengan ref_id sama. Jangan order ulang.
40Parameter tidak lengkap/format ref_id salahPerbaiki payload atau ref_id, lalu kirim kembali.
41Autentikasi gagalPeriksa username, secret, ref_id sumber sign, status key, dan status akun.
42IP tidak diizinkanTambahkan IP publik server yang benar atau gunakan key yang sesuai.
43Rate limit terlampauiTunggu pergantian menit, gunakan antrean dan backoff.
51Saldo tidak cukupIsi saldo; respons dapat menyertakan balance dan required.
52SKU tidak ditemukanSinkronkan price-list dan periksa buyer_sku_code.
53Produk tidak tersedia/cut-offNonaktifkan sementara di sistem Anda dan sinkronkan ulang.
54Tujuan tidak validValidasi sesuai input_fields; jangan mengubah digit secara otomatis.
55Transaksi berulang tidak didukungJangan ulangi ke tujuan yang sama pada hari tersebut.
56Nominal di luar batasPerbaiki nominal berdasarkan aturan produk.
57Paket API Cek Region tidak aktifAktifkan atau perpanjang paket pada key produksi.
58Kuota API Cek Region habisBeli periode bulanan baru atau tunggu periode berikutnya.
99Hasil belum pasti karena kesalahan internalPolling status dengan ref_id sama. Jangan order ulang.

Nilai status publik adalah Sukses, Pending, atau Gagal. Gunakan rc sebagai keputusan utama karena pesannya dapat berubah untuk memberi konteks supplier.

7. Timeout, retry, dan idempotensi

Aman dilakukan

  • • Retry request yang timeout dengan key dan ref_id yang sama.
  • • Panggil /status memakai ref_id transaksi asli.
  • • Simpan ref_id unik di database Anda sebelum mengirim request.
  • • Gunakan satu ref_id untuk satu niat pembelian.

Jangan dilakukan

  • • Membuat ref_id baru hanya karena koneksi timeout.
  • • Menganggap HTTP 200 selalu berarti transaksi sukses.
  • • Mengirim SKU/tujuan berbeda dengan ref_id yang sudah tercatat.
  • • Menjalankan retry tanpa batas atau tanpa jeda.

Idempotensi berlaku per key: request transaction dengan ref_id yang sama akan mengembalikan transaksi pertama dan tidak membuat order atau debit kedua. Untuk status pending, polling awal setiap 5 detik lalu perlambat menjadi 10–30 detik. Callback mempercepat pembaruan, tetapi endpoint status tetap menjadi sumber pemulihan.

8. Menguji dengan key sandbox

Sandbox tidak mendebit saldo, tidak membuat order penjualan, dan tidak menghubungi supplier. Produk, harga role, validasi tujuan, response envelope, idempotensi, dan callback tetap mengikuti bentuk produksi.

Akhiran ref_idHasil sandboxContoh
Digit ganjilrc 00 / Suksesuji-order-1
Digit 0rc 03 / Pendinguji-order-0
Digit genap selain 0 atau karakter non-digitrc 01 / Gagaluji-order-2, uji-order-x

Mode melekat pada key dan tidak dapat diubah. Setelah semua cabang lolos, buat key produksi baru dan ulangi smoke test dengan nominal kecil.

9. Menerima dan memverifikasi callback

Jika callback HTTPS diatur pada key, server mengirim POST application/json ketika hasil tersedia. Callback tidak memakai envelope data. Balas status HTTP 2xx secepat mungkin setelah signature valid dan payload tersimpan.

X-Gempay-Signature: HMAC_SHA256_HEX
Content-Type: application/json

{
  "ref_id": "order-20260802-0001",
  "status": "Sukses",
  "buyer_sku_code": "ml-86",
  "customer_no": "1234567891234",
  "price": 21000,
  "balance": 479000,
  "sn": "SN-123456789",
  "created_at": "2026-08-02T13:30:00+07:00",
  "rc": "00",
  "message": "Transaksi sukses"
}
Verifikasi callback di PHP
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_GEMPAY_SIGNATURE'] ?? '';
$expected = hash_hmac(
    'sha256',
    $rawBody,
    getenv('GEMPAY_API_SECRET')
);

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$payload = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// Upsert berdasarkan ref_id, lalu balas 200.
Verifikasi callback di Node.js/Express
import crypto from 'node:crypto';

app.post('/gempay/callback',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const received = req.get('X-Gempay-Signature') ?? '';
    const expected = crypto
      .createHmac('sha256', process.env.GEMPAY_API_SECRET)
      .update(req.body)
      .digest('hex');

    const valid = received.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
    if (!valid) return res.sendStatus(401);

    const payload = JSON.parse(req.body.toString('utf8'));
    // Upsert berdasarkan ref_id.
    return res.sendStatus(200);
  }
);
Hitung HMAC dari raw body persis seperti diterima. Jangan parse lalu JSON encode ulang sebelum verifikasi. Server mencoba pengiriman awal lalu retry setelah 1 menit, 5 menit, 15 menit, 1 jam, dan 6 jam. Redirect callback tidak diikuti.

10. Praktik keamanan wajib

Secret hanya di backend

Jangan masukkan secret ke aplikasi mobile, JavaScript browser, log, screenshot, chat, atau repository.

Satu key per sistem

Pisahkan bot, website, staging, dan produksi agar satu key dapat dicabut tanpa mematikan semuanya.

Batasi IP produksi

Gunakan IP publik egress server yang stabil. Allowlist memakai pencocokan exact IPv4/IPv6, bukan CIDR.

Rotasi tanpa downtime

Buat key baru, pindahkan traffic, pastikan sukses, lalu nonaktifkan key lama. Secret lama tidak dapat dipulihkan.

Redaksi observability

Jangan catat sign, secret, raw callback, SN voucher, atau nomor tujuan lengkap pada log yang dibagikan.

Validasi callback

Tolak signature tidak valid, upsert berdasarkan ref_id, dan buat handler idempoten karena callback dapat diulang.

11. Troubleshooting

GejalaPemeriksaan
Selalu rc 41Pastikan prefix benar, key aktif, akun tidak diblokir, ref_id untuk hash sama persis dengan body, urutan hash username+secret+ref_id, dan output MD5 berupa hex lowercase.
rc 42 di server produksiCari IP egress publik server. IP private/container sering berbeda dari IP yang terlihat server Gempay.
rc 43 terlalu cepatLimit dihitung per key per menit kalender, bukan per IP. Gunakan antrean global untuk semua worker yang memakai key tersebut.
rc 54 pada tujuanCocokkan nama key, tipe, min/max, required, dan options dari input_fields SKU terbaru. Kirim angka sebagai string.
Transaksi timeoutJangan buat ref_id baru. Retry transaction atau panggil status dengan key dan ref_id yang sama.
Callback 401Pastikan HMAC memakai raw request body, secret key yang sama, SHA-256, output hex, dan perbandingan timing-safe.
Callback terus diulangBalas HTTP 2xx setelah payload valid tersimpan. Respons 3xx tidak diikuti dan non-2xx dianggap gagal.
Harga berbeda dari web publikItu dapat benar: price-list memakai role harga pemilik key. Gunakan nilai price dari API sebagai biaya reseller.
Status lama setelah callback gagalPanggil endpoint status. Kegagalan callback tidak mengubah status transaksi dan tidak memicu pemrosesan ulang.

12. Checklist sebelum go-live

Checklist ini berjalan di browser dan tidak disimpan. Gunakan sebagai pemeriksaan sebelum memindahkan traffic nyata.