API to API

Callback

Webhook transaksi dari Miharogames ke endpoint mitra.

Callback

Terakhir diperbarui: 9 September 2026.

Callback dikirim ke URL mitra saat status transaksi berubah. Callback membantu mitra memperbarui transaksi tanpa polling terus-menerus.

Aturan komisi untuk transaksi API-to-API mengikuti kesepakatan bisnis API mitra yang sudah dikonfigurasi oleh tim Miharogames. Detail nominal komisi tidak dikirim lewat callback transaksi customer.

Mitra wajib menyiapkan endpoint callback sesuai kontrak Miharogames. Setelah endpoint siap, ajukan URL callback ke tim Miharogames untuk whitelist dan aktivasi. Struktur payload dan response harus mengikuti standar pada halaman ini agar delivery, retry, dan rekonsiliasi status berjalan konsisten.

Callback URL Registration

Miharogames hanya mengirim callback ke URL yang sudah diajukan dan diaktifkan. Informasikan URL callback per environment saat onboarding.

EnvironmentContoh URL mitra
Developmenthttps://sandbox-partner.example.com/miharogames/callback
Productionhttps://partner.example.com/miharogames/callback

Ketentuan endpoint callback mitra:

  • menggunakan HTTPS untuk production;
  • menerima request POST dengan body JSON;
  • mengembalikan response 2xx jika payload sudah diterima;
  • memproses payload secara idempotent menggunakan event_id atau partner_trx_id;
  • tidak mengubah nama field payload dari kontrak Miharogames.

Delivery Behavior

Callback dikirim dengan prinsip at-least-once. Artinya, mitra bisa menerima event yang sama lebih dari satu kali jika endpoint mitra timeout atau tidak mengembalikan response sukses.

Mitra wajib membuat receiver idempotent menggunakan event_id atau partner_trx_id.

Webhook Payload

{
  "event": "transaction.updated",
  "event_id": "callback_9381_20260814144652",
  "occurred_at": "2026-08-14 14:46:52",
  "partner_trx_id": "OMNI-TRX-001",
  "transaction_number": "PAY/KFI/8/2026/54",
  "payment_status": "PAID",
  "transaction_status": "SUCCESS",
  "serial_number": "0214073400005825489229",
  "reference_number": "2ISV21VS17470A09F1028C5E18996BAA"
}

Payload Field

FieldKeterangan
eventNama event. Untuk update transaksi bernilai transaction.updated.
event_idID unik callback. Simpan untuk idempotency.
occurred_atWaktu event dibuat, format YYYY-MM-DD HH:mm:ss.
partner_trx_idID transaksi dari sistem mitra.
transaction_numberNomor transaksi Miharogames.
payment_statusStatus pembayaran.
transaction_statusStatus transaksi.
serial_numberSN transaksi jika tersedia, misalnya token PLN prabayar atau SN produk digital. Bernilai null jika produk tidak memiliki SN.
reference_numberNomor referensi dari biller/provider jika tersedia. Bernilai null jika provider tidak mengirim referensi.

Webhook Signature

Miharogames mengirim signature pada header callback menggunakan metode HMAC-SHA256. Signature digunakan untuk memastikan callback berasal dari Miharogames dan body callback tidak berubah saat diterima oleh sistem mitra.

Tim Miharogames akan memberikan webhook_secret secara terpisah kepada mitra. Simpan nilai tersebut di server mitra dan jangan tampilkan di aplikasi frontend, log publik, atau repository.

Setiap request callback akan membawa header berikut:

X-Miharo-Timestamp: 1788850800
X-Miharo-Signature: v1=5f2d7c8a...

Keterangan header:

HeaderKeterangan
X-Miharo-TimestampUnix timestamp saat callback dikirim oleh Miharogames.
X-Miharo-SignatureSignature callback dengan format v1={hash}.

Signature dibuat dari gabungan timestamp dan raw JSON body callback:

signed_payload = X-Miharo-Timestamp + "." + raw_json_body
signature = "v1=" + HMAC_SHA256(signed_payload, webhook_secret)

raw_json_body adalah body callback asli yang diterima oleh endpoint mitra. Gunakan body request sebelum diparse atau diubah formatnya. Perubahan spasi, urutan field, atau format JSON dapat membuat hasil signature berbeda.

Contoh callback yang diterima mitra:

POST /miharogames/callback HTTP/1.1
Content-Type: application/json
X-Miharo-Timestamp: 1788850800
X-Miharo-Signature: v1=5f2d7c8a...

{"event":"transaction.updated","event_id":"callback_9381_20260814144652","occurred_at":"2026-08-14 14:46:52","partner_trx_id":"OMNI-TRX-001","transaction_number":"PAY/KFI/8/2026/54","payment_status":"PAID","transaction_status":"SUCCESS","serial_number":"0214073400005825489229","reference_number":"2ISV21VS17470A09F1028C5E18996BAA"}

Langkah verifikasi di sisi mitra:

  1. Ambil header X-Miharo-Timestamp dan X-Miharo-Signature.
  2. Ambil raw body callback persis seperti request diterima.
  3. Buat signed payload dengan format {timestamp}.{raw_json_body}.
  4. Hitung HMAC-SHA256 menggunakan webhook_secret.
  5. Bandingkan hasil signature dengan X-Miharo-Signature menggunakan constant-time comparison.
  6. Tolak callback jika signature tidak valid.
  7. Tolak callback jika timestamp terlalu jauh dari waktu server mitra, misalnya lebih dari 5 menit.
  8. Jika valid, simpan payload dan proses transaksi secara idempotent.

Contoh verifikasi PHP:

$timestamp = $_SERVER['HTTP_X_MIHARO_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MIHARO_SIGNATURE'] ?? '';
$rawBody = file_get_contents('php://input');
$webhookSecret = getenv('MIHARO_WEBHOOK_SECRET');

if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit('Timestamp tidak valid');
}

$expectedSignature = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $webhookSecret);

if (!hash_equals($expectedSignature, $signature)) {
    http_response_code(401);
    exit('Signature tidak valid');
}

http_response_code(200);
echo json_encode(['status' => true, 'message' => 'Callback diterima.']);

Contoh verifikasi Node.js:

const crypto = require('crypto');

function verifyMiharoCallback(req, rawBody) {
  const timestamp = req.header('X-Miharo-Timestamp');
  const signature = req.header('X-Miharo-Signature');
  const webhookSecret = process.env.MIHARO_WEBHOOK_SECRET;

  if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return false;

  const expectedSignature = 'v1=' + crypto
    .createHmac('sha256', webhookSecret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(signature));
}

Catatan penting:

  • Gunakan raw body yang sama persis dengan request callback.
  • Jangan membuat ulang JSON dari object yang sudah diparse untuk proses verifikasi.
  • Pastikan jam server mitra sinkron agar validasi timestamp tidak salah.
  • Setiap retry callback dari Miharogames akan membawa timestamp dan signature baru.
  • Signature tidak menggantikan idempotency. Mitra tetap perlu mencegah proses ganda menggunakan event_id atau partner_trx_id.

Referensi konsep signature webhook dapat dilihat pada dokumentasi GitHub Webhook Validation, Stripe Webhook Signature, dan Standard Webhooks.

Response Mitra

Endpoint mitra sebaiknya mengembalikan response 2xx secepat mungkin. Proses berat seperti update banyak tabel atau notifikasi user sebaiknya diproses async di sisi mitra.

{
  "status": true,
  "message": "Callback diterima."
}

Retry Policy

Jika endpoint mitra timeout, mengalami kesalahan server (HTTP 5xx), atau tidak mengembalikan response sukses (HTTP 2xx), Miharogames akan melakukan percobaan pengiriman ulang (retry) otomatis menggunakan mekanisme Exponential Backoff hingga maksimal 6 kali retry setelah pengiriman awal.

Percobaan Ke-Jeda Pengiriman UlangStatus
1 (Awal)Langsung saat transaksi diperbaruiPENDING (jika gagal)
2+1 menit setelah percobaan ke-1 gagalPENDING
3+2 menit setelah percobaan ke-2 gagalPENDING
4+5 menit setelah percobaan ke-3 gagalPENDING
5+15 menit setelah percobaan ke-4 gagalPENDING
6+30 menit setelah percobaan ke-5 gagalPENDING
7 (Terakhir)+60 menit setelah percobaan ke-6 gagalFAILED (berhenti)

Jika setelah 6 kali retry (total 7 percobaan termasuk pengiriman awal) endpoint mitra tetap gagal atau timeout, status pengiriman callback akan berubah permanen menjadi FAILED dan sistem tidak akan mencoba mengirim ulang. Mitra disarankan mengecek status transaksi manual melalui endpoint Payment Status.

Note: Skema kegagalan webhook Miharogames mengadopsi konsep Exponential Backoff dari standar industri penyedia API besar seperti Stripe, Adyen, dan Shopify. Skema ini bertujuan memberi waktu bagi server mitra untuk pulih (downtime/restart) dan mencegah efek thundering herd akibat koneksi yang bertubi-tubi.

Manual Redelivery

Manual redelivery digunakan jika transaksi sudah final, tetapi callback belum diterima oleh sistem mitra setelah beberapa saat. Kondisi ini jarang terjadi karena Miharogames sudah memiliki retry otomatis, tetapi endpoint ini dapat digunakan sebagai jalur recovery manual.

POST /client/v2/callback/redelivery

Header mengikuti standar autentikasi API mitra.

X-SECRET-KEY: {{secret_key}}
Authorization: Bearer {{token}}
Content-Type: application/json

Request body dapat menggunakan salah satu identifier transaksi:

{
  "partner_trx_id": "OMNI-TRX-043"
}

atau:

{
  "transaction_number": "PAY/KFI/9/2026/91"
}

Ketentuan redelivery:

  • hanya dapat digunakan untuk transaksi milik mitra yang sedang mengakses API;
  • hanya memproses transaksi yang sudah final, yaitu SUCCESS atau FAILED;
  • tidak mengirim ulang callback jika callback sebelumnya sudah berstatus SENT;
  • jika callback sebelumnya PENDING atau FAILED, Miharogames akan mencoba mengirim ulang callback satu kali;
  • jika redelivery gagal, callback tetap mengikuti retry policy yang berlaku selama batas retry masih tersedia.

Contoh response jika callback berhasil dikirim ulang:

{
  "status": true,
  "message": "Callback berhasil dikirim ulang.",
  "data": {
    "callback": {
      "id": 46,
      "delivery_status": "SENT",
      "attempt_count": 4,
      "last_attempt_at": "2026-09-09 12:38:01",
      "next_retry_at": null,
      "http_status_code": 200,
      "last_error": null,
      "sent_at": "2026-09-09 12:38:01",
      "redelivered": true
    }
  }
}

Contoh response jika callback sudah pernah terkirim:

{
  "status": true,
  "message": "Callback sudah terkirim.",
  "data": {
    "callback": {
      "id": 46,
      "delivery_status": "SENT",
      "attempt_count": 4,
      "last_attempt_at": "2026-09-09 12:38:01",
      "next_retry_at": null,
      "http_status_code": 200,
      "last_error": null,
      "sent_at": "2026-09-09 12:38:01",
      "redelivered": false
    }
  }
}

Best Practice Receiver

  • Validasi payload wajib ada sebelum update transaksi.
  • Simpan event_id untuk mencegah proses ganda.
  • Cocokkan partner_trx_id dengan transaksi di sistem mitra.
  • Balas 2xx setelah payload diterima dan disimpan.
  • Gunakan payment status endpoint sebagai fallback jika callback belum diterima.