Lompat ke konten utama
G GEMPAY

Akun Gempay

For resellers & integrators

Gempay H2H API Documentation

Connect your bot, website, or sales system directly to your Gempay balance. This guide covers the complete sandbox-to-production flow, the MLBB Region API, safe timeout handling, and callback verification.

Complete integration guide

Connect your system to the H2H API

Follow this guide from key creation to go-live. All examples use JSON, prices are integer Indonesian rupiah amounts, and sandbox and production use the same response contract. The same key can also access the MLBB Region API.

Base URL
https://www.genztopup.com/api/v1
Example key username
gmp_1234abcd

1. Recommended integration flow

  1. 01 Create a sandbox keyStore the username and one-time secret securely.
  2. 02 Test authenticationCall the balance endpoint with a valid signature.
  3. 03 Fetch the price listStore SKUs, role-based prices, availability, and destination field definitions.
  4. 04 Test all three statesUse sandbox ref_id values that produce success, failed, and pending results.
  5. 05 Configure callbacksVerify the raw-body HMAC and keep status polling as a fallback.
  6. 06 Create a production keyFund your balance, restrict server IPs, and run a small transaction.
Most important rule: if a transaction request times out or returns rc 03/rc 99, query status using the same ref_id. Never create a new ref_id because the first transaction may already be processing.

2. Connection, credentials, and authentication

Transport rules

  • • Every endpoint uses POST.
  • • Send Content-Type: application/json.
  • • Use HTTPS from your backend/server, never browser JavaScript.
  • • Prices and balances are integer rupiah amounts without decimals.
  • • Business outcomes usually use HTTP 200; inspect data.rc.

Required authentication fields

username
Your key prefix, for example gmp_1234abcd.
ref_id
Your unique reference, 1-64 characters, without leading or trailing spaces.
sign
Lowercase MD5 of username, secret, and ref_id concatenated without separators.
sign = md5(username + secret + ref_id)

// Do not add colons, pipes, spaces, or newlines between values.
// Example hash source:
gmp_1234abcdYOUR_SECRETorder-20260802-0001
Request signatures use MD5 for reseller-client compatibility. Callback signatures are different: they use HMAC-SHA256 over the raw body.

3. Client code examples

Keep credentials in server environment variables. The helper below works with every endpoint; add endpoint-specific fields through the final argument.

PHP 8+ with 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');
Node.js 18+ with 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();
}
cURL for quick diagnostics
curl --request POST 'https://www.genztopup.com/api/v1/balance' \
  --header 'Content-Type: application/json' \
  --data '{
    "username": "gmp_1234abcd",
    "ref_id": "balance-20260802-0001",
    "sign": "YOUR_MD5_RESULT"
  }'

Generate the signature in your system. Never paste secrets into shell history on a shared server.

4. Endpoint reference

EndpointPurposeAdditional fields
POST /price-listLists products and prices for the key owner's tier.None.
POST /transactionCreates a transaction or returns an idempotent replay.buyer_sku_code, tujuan.
POST /statusReturns the latest status for a ref_id.None.
POST /balanceReturns the current reseller balance.None.
POST /ml-regionChecks an MLBB nickname, region, and service type.user_id, zone_id; production requires an active plan.
POST

/api/v1/balance

Test this endpoint first to verify the key, signature, IP allowlist, and rate limit.

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

/api/v1/price-list

The price already reflects the key owner's pricing tier. Refresh the catalog regularly. Sell only when both product status fields are true; check stock when unlimited_stock is false, and obey cut-off times. Only use the public buyer_sku_code.

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

{
  "data": {
    "price_list": [{
      "buyer_sku_code": "ml-86",
      "product_name": "Mobile Legends 86 Diamonds",
      "input_fields": [
        {"key":"user_id","type":"numeric","required":true,"min":5,"max":20,"options":[]},
        {"key":"zone_id","type":"numeric","required":true,"min":1,"max":10,"options":[]}
      ],
      "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"
  }
}
POST

/api/v1/transaction

Creates an order from the member balance. The server recalculates the price; do not send a price field.

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

/api/v1/status

Use the original transaction ref_id and a signature generated from that same value. Only the key that created a transaction can read it. A missing transaction returns rc 01.

POST

/api/v1/ml-region

Uses the same H2H authentication. Sandbox returns deterministic results without a plan. Production requires an active Region API plan plus both the key allowlist and plan IP whitelist.

{
  "username": "gmp_1234abcd",
  "ref_id": "ml-check-20260804-0001",
  "sign": "YOUR_MD5_RESULT",
  "user_id": "106101371",
  "zone_id": "2540"
}

5. Building destination fields correctly

Never guess or concatenate identifiers yourself. For each SKU, read input_fields from the price list and send a tujuan object with matching keys. The server validates and constructs the supplier destination.

Single destination

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

Mobile Legends

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

Genshin / Honkai: Star Rail

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

A select field lists valid values in options. Send numeric fields as strings to preserve leading zeroes. Never alter destination digits.

6. Responses and response codes

API payloads use a data envelope. Store at least ref_id, rc, status, price, and sn, plus a redacted raw response for reconciliation.

rcMeaningRequired action
00SuccessMark successful and store the SN when present.
01Failed / status not foundFinal for a transaction; create a new order only on explicit user intent.
02Supplier rejectedFinal; retry with a new ref_id only after fixing the cause.
03PendingPoll status using the same ref_id. Do not reorder.
40Missing parameter / invalid ref_idFix the payload and resend.
41Authentication failedCheck username, secret, signature source, key, and account status.
42IP not allowedAdd the correct public server IP or use the correct key.
43Rate limit exceededWait for the next minute and use queue backoff.
51Insufficient balanceFund the balance; the response may include balance and required.
52SKU not foundRefresh the price list and check buyer_sku_code.
53Product unavailable / cut-offDisable it temporarily and refresh the catalog.
54Invalid destinationValidate against input_fields; never alter digits automatically.
55Repeat transaction unsupportedDo not repeat the same product and destination that day.
56Amount outside limitsCorrect the amount according to product rules.
57Region API plan inactiveActivate or renew the plan on the production key.
58Region API quota exhaustedPurchase a new monthly period or wait for the next period.
99Uncertain due to internal errorPoll status using the same ref_id. Do not reorder.

Public status values remain Sukses, Pending, or Gagal. Use rc as the primary decision field.

7. Timeout, retries, and idempotency

Safe actions

  • • Retry a timeout with the same key and ref_id.
  • • Call /status with the original ref_id.
  • • Persist a unique ref_id before sending.
  • • Use one ref_id per purchase intent.

Never do this

  • • Create a new ref_id after a timeout.
  • • Treat HTTP 200 as automatic success.
  • • Change SKU or destination for an existing ref_id.
  • • Retry forever or without delays.

Idempotency is scoped per key. Replaying a transaction ref_id returns the first transaction without another order or debit. Poll pending transactions every 5 seconds initially, then slow to 10-30 seconds.

8. Testing with a sandbox key

Sandbox does not debit balance, create sales orders, or contact suppliers. Validation, response envelopes, idempotency, and callbacks follow the production contract.

ref_id endingResultExample
Odd digitrc 00 / Suksestest-order-1
Digit 0rc 03 / Pendingtest-order-0
Even digit except 0, or non-digitrc 01 / Gagaltest-order-2, test-order-x

A key's mode cannot change. After all branches pass, create a new production key and run a small smoke test.

9. Receiving and verifying callbacks

When a callback HTTPS URL is configured, Gempay sends POST application/json. Callbacks do not use the data envelope. Return HTTP 2xx quickly after verifying and storing the payload.

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",
  "rc": "00",
  "message": "Transaksi sukses"
}
PHP callback verification
$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 by ref_id, then return 200.
Compute HMAC from the exact raw body received. Never parse and re-encode JSON first. Retries occur after 1 minute, 5 minutes, 15 minutes, 1 hour, and 6 hours. Redirects are not followed.

10. Mandatory security practices

Backend-only secrets

Never put a secret in mobile apps, browser JavaScript, logs, screenshots, chat, or repositories.

One key per system

Separate bots, websites, staging, and production so each key can be revoked independently.

Restrict production IPs

Use stable public egress IPs. The allowlist matches exact IPv4/IPv6 addresses, not CIDR ranges.

Rotate without downtime

Create a new key, move traffic, verify success, then revoke the old key.

Redact observability data

Never log signatures, secrets, raw callbacks, voucher SNs, or complete destination values.

Validate callbacks

Reject invalid signatures and make handlers idempotent because callbacks may be repeated.

11. Troubleshooting

SymptomWhat to check
Always rc 41Check the prefix, active key/account, exact ref_id in the hash and body, username+secret+ref_id order, and lowercase hexadecimal MD5 output.
rc 42 in productionFind the server's public egress IP. Private/container addresses are often different.
rc 43 too soonThe limit is per key per calendar minute, not per IP. Use a global queue across all workers sharing the key.
rc 54 for destinationMatch keys, types, min/max, required, and options from the latest input_fields. Send numbers as strings.
Transaction timeoutDo not create a new ref_id. Replay transaction or query status with the same key and ref_id.
Callback 401Use the raw request body, matching key secret, SHA-256 hexadecimal output, and timing-safe comparison.
Callback keeps retryingReturn HTTP 2xx after storing a valid payload. Redirects are not followed and non-2xx is failure.
Price differs from public webThis can be correct: price-list uses the key owner's pricing tier. Treat API price as reseller cost.
Stale status after callback failureCall the status endpoint. Callback failure does not change or reprocess a transaction.

12. Go-live checklist

This browser-only checklist is not saved. Use it before moving real traffic.