PayHook Docs & Sandbox Docs & Sandbox
GitHub
PayHook Developer

Alternatif Payment Gateway GratisFree Payment Gateway Alternative

Ubah notifikasi pembayaran dari e-wallet & mobile banking menjadi webhook otomatis ke server Anda. Tanpa biaya transaksi, tanpa registrasi payment gateway. Turn payment notifications from e-wallets & mobile banking into automatic webhooks to your server. No transaction fees, no payment gateway registration.

๐Ÿ’ธ 0% Biaya0% Fees ๐Ÿ”’ 100% LokalLocal โšก Real-time Webhook ๐Ÿ‡ฎ๐Ÿ‡ฉ 30+ AplikasiApps

Tentang PayHookAbout PayHook

PayHook adalah aplikasi Android yang membaca notifikasi pembayaran masuk dari aplikasi e-wallet dan mobile banking, lalu mengirimkannya sebagai webhook JSON ke endpoint server yang Anda tentukan. Cocok untuk UMKM, toko online, aplikasi SaaS, dan sistem pencatatan keuangan yang butuh notifikasi pembayaran otomatis tanpa biaya payment gateway. PayHook is an Android app that reads incoming payment notifications from e-wallet and mobile banking apps, then delivers them as JSON webhooks to your server endpoint. Perfect for small businesses, online stores, SaaS apps, and bookkeeping systems that need automatic payment notifications without payment gateway fees.

Real-timeReal-time

Notifikasi ditangkap & diteruskan seketika saat dana masuk.Notifications captured & forwarded the moment funds arrive.

Privasi PenuhFull Privacy

Semua data disimpan lokal (SQLite). Tidak ada server perantara.All data stored locally (SQLite). No intermediary server.

Gratis SelamanyaFree Forever

Tanpa biaya transaksi, tanpa langganan. Dana langsung ke rekening Anda.No transaction fees, no subscription. Funds go straight to your account.

Bagaimana dana diterima?How are funds received? Pembayaran tetap masuk langsung ke rekening bank / e-wallet Anda seperti biasa. PayHook hanya membaca notifikasi dari aplikasi tersebut dan meneruskan datanya โ€” PayHook tidak menyentuh atau menahan dana Anda. Payments still go directly into your bank / e-wallet account as usual. PayHook only reads the notification from those apps and forwards the data โ€” PayHook never touches or holds your funds.

Alur KerjaHow It Works

Empat langkah dari pembayaran pelanggan hingga notifikasi diterima aplikasi Anda. Four steps from a customer payment to a notification landing in your app.

1. Pelanggan bayar QRIS / transfer 2. App bank/e-wallet notifikasi "dana masuk" 3. PayHook baca + ekstrak nominal 4. Server Anda webhook JSON (POST)
  1. Pelanggan melakukan pembayaranCustomer makes a payment ke QRIS / nomor rekening / e-wallet Anda seperti biasa. to your QRIS / bank account / e-wallet as usual.
  2. Aplikasi bank / e-wallet menampilkan notifikasiThe bank / e-wallet app shows a notification seperti “Rp 300.000 sudah masuk ke rekening Anda”. such as “Rp 300,000 has been credited to your account”.
  3. PayHook membaca notifikasiPayHook reads the notification , mencocokkan kata kunci pembayaran masuk, dan mengekstrak nominal Rupiah. , matches incoming-payment keywords, and extracts the Rupiah amount.
  4. PayHook mengirim webhookPayHook sends a webhook berupa JSON via HTTP POST ke semua endpoint aktif secara paralel. as JSON via HTTP POST to all active endpoints in parallel.

Instalasi & PenggunaanInstall & Usage

Siapkan PayHook dalam beberapa menit. Set up PayHook in a few minutes.

  1. Pasang aplikasi PayHookInstall the PayHook app di perangkat Android (min. Android 8.0 / API 26) yang menerima notifikasi pembayaran Anda. Unduh dari Google Play (PayHook - Payment Webhook). on the Android device (min. Android 8.0 / API 26) that receives your payment notifications. Download it from Google Play (PayHook - Payment Webhook).
  2. Beri izin Akses NotifikasiGrant Notification Access saat diminta. Izin ini wajib agar PayHook dapat membaca notifikasi. when prompted. This permission is required for PayHook to read notifications.
  3. Pilih Aplikasi yang DipantauChoose Monitored Apps โ€” aktifkan e-wallet / bank yang ingin dipantau dari daftar aplikasi terpasang. โ€” enable the e-wallets / banks you want to monitor from your installed apps.
  4. Tambahkan WebhookAdd a Webhook dengan URL endpoint, tipe autentikasi, dan token (lihat bagian Konfigurasi). with your endpoint URL, auth type, and token (see the Configuration section).
  5. Uji dengan tombol TestTest with the Test button , atau gunakan Simulator di halaman ini, lalu lakukan transaksi kecil untuk verifikasi. , or use the Simulator on this page, then run a small transaction to verify.
Tips agar layanan tetap berjalanTips to keep the service running Nonaktifkan optimisasi baterai untuk PayHook dan izinkan berjalan di latar belakang, agar notifikasi tidak terlewat saat layar mati. Disable battery optimization for PayHook and allow background activity, so notifications are not missed when the screen is off.

Konfigurasi WebhookWebhook Configuration

Setiap webhook memiliki pengaturan berikut. Anda dapat menambahkan lebih dari satu endpoint โ€” semuanya menerima data secara bersamaan. Each webhook has the settings below. You can add more than one endpoint โ€” all of them receive data simultaneously.

KolomField KeteranganDescription
name Label webhook, mis. “Server Produksi”.Webhook label, e.g. “Production Server”.
url URL endpoint HTTPS yang menerima POST.HTTPS endpoint URL that receives the POST.
authType bearer ยท api_key ยท basic ยท none
authHeaderName Nama header untuk mode api_key (default X-API-Key).Header name for api_key mode (default X-API-Key).
token Token / kunci rahasia. Tersedia generator token di aplikasi.Token / secret key. A token generator is built into the app.
secretKey Opsional. Jika diisi, aktifkan tanda tangan HMAC-SHA256 (header X-Payhook-Signature). Lihat bagian Signature.Optional. If set, enables HMAC-SHA256 signing (X-Payhook-Signature header). See the Signature section.
isEnabled Aktif / nonaktif tanpa menghapus.Enable / disable without deleting.
Endpoint harus menggunakan HTTPS. PayHook menolak lalu lintas cleartext (HTTP biasa). Endpoints must use HTTPS. PayHook rejects cleartext (plain HTTP) traffic.

Aplikasi DidukungSupported Apps

PayHook dapat memantau notifikasi aplikasi apa pun yang Anda pilih. Berikut daftar yang umum digunakan & teruji. PayHook can monitor notifications from any app you choose. Below are the commonly used & tested ones.

E-Wallet

AplikasiApp Package Default
DANA com.dana.id AktifActive
GoPay (Gojek) com.gojek.app AktifActive
ShopeePay com.shopee.id AktifActive
OVO id.co.ovo.app AktifActive
LinkAja com.linkaja AktifActive
i.saku id.co.isaku AktifActive

Bank / Mobile Banking

AplikasiApp Package Default
BCA Mobile com.bca AktifActive
myBCA com.bca.myBCA AktifActive
BRImo id.co.bri.brimo AktifActive
BNI Mobile com.bni.mobilebanking AktifActive
wondr by BNI id.co.bni.wondr AktifActive
Livin' by Mandiri id.bmri.livin AktifActive
BSI Mobile com.bsi.universalbanking AktifActive
Jenius (BTPN) com.btpn.dc AktifActive
CIMB Niaga id.co.cimbniaga.mobile.android AktifActive
Bank Jago id.co.bankjago.app AktifActive
SeaBank id.co.seabank.app AktifActive
Bank Sumut id.mbank.sumut NonaktifInactive

Format PayloadPayload Format

PayHook mengirim HTTP POST dengan body JSON berikut ke endpoint Anda. PayHook sends an HTTP POST with the following JSON body to your endpoint.

Request Headers

POST /your-endpoint HTTP/1.1
Content-Type: application/json
Accept: application/json
User-Agent: PayHook-Android/2.0
Authorization: Bearer <token>          # tergantung tipe auth / depends on auth type
X-Payhook-Timestamp: 1752300000         # jika HMAC diaktifkan / if HMAC enabled
X-Payhook-Nonce: 9f2c...                 # jika HMAC diaktifkan / if HMAC enabled
X-Payhook-Signature: sha256=<hmac>   # jika HMAC diaktifkan / if HMAC enabled

Body (JSON)

{
  "event_id": "evt_1752300000000_a1b2c3",
  "event_type": "payment.incoming",
  "amount": 300000,
  "source": "BCA Mobile",
  "reference": "PH-1711425600000",
  "timestamp": "2026-03-26 10:26:00",
  "package_name": "com.bca",
  "notification_title": "Uang masuk dari John",
  "notification_text": "Rp 300.000,00 sudah masuk ke rekening Anda",
  "sent_by": "PayHook"
}
Field Type KeteranganDescription
event_id string ID unik & stabil per event. Pakai sebagai idempotency key (upsert) agar transaksi tidak dobel walau webhook dikirim ulang.Unique & stable per event. Use it as an idempotency key (upsert) so retries never create duplicates.
event_type string Jenis event, mis. payment.incoming.Event type, e.g. payment.incoming.
amount number (long) Nominal Rupiah, bilangan bulat tanpa desimal.Rupiah amount, integer without decimals.
source string Nama aplikasi sumber, mis. “BCA Mobile”.Source app name, e.g. “BCA Mobile”.
reference string ID unik format PH-{epoch_millis}.Unique ID in PH-{epoch_millis} format.
timestamp string Waktu deteksi, format yyyy-MM-dd HH:mm:ss (waktu perangkat).Detection time, yyyy-MM-dd HH:mm:ss (device local time).
package_name string Nama package Android aplikasi sumber.Android package name of the source app.
notification_title string Judul notifikasi asli.Original notification title.
notification_text string Isi teks notifikasi asli.Original notification text body.
sent_by string Selalu bernilai "PayHook".Always "PayHook".
Respons yang diharapkanExpected response Balas dengan status 2xx agar dianggap sukses. Status lain (4xx/5xx) atau timeout (>15 detik) dicatat sebagai gagal di log aktivitas aplikasi. Reply with a 2xx status to be counted as success. Other statuses (4xx/5xx) or a timeout (>15s) are logged as failed in the app's activity log.

Autentikasi & VerifikasiAuth & Verification

PayHook mendukung 4 tipe autentikasi yang dikirim sebagai header HTTP. PayHook supports 4 authentication types, sent as HTTP headers.

authType Header ContohExample
bearer Authorization Bearer <token>
api_key X-API-Key (atau kustom)(or custom) <token>
basic Authorization Basic base64(<token>)
none โ€” Tanpa autentikasiNo authentication
Verifikasi di sisi serverVerify on your server Selalu validasi header autentikasi pada setiap request masuk dan tolak jika token tidak cocok. Gunakan HTTPS dan jangan pernah menaruh token di URL. Always validate the auth header on every incoming request and reject mismatched tokens. Use HTTPS and never put the token in the URL.

Signature (HMAC)Signature (HMAC)

Untuk keamanan tingkat produksi, isi Secret Key pada webhook di aplikasi PayHook. Jika diisi, PayHook menandatangani setiap request dengan HMAC-SHA256 sehingga server Anda bisa memastikan request benar-benar dari PayHook (anti-spoofing) dan bukan kiriman ulang lama (anti-replay). For production-grade security, set a Secret Key on the webhook in the PayHook app. When set, PayHook signs every request with HMAC-SHA256 so your server can verify the request truly comes from PayHook (anti-spoofing) and is not an old replay (anti-replay).

Header KeteranganDescription
X-Payhook-Timestamp Epoch detik saat request dibuat.Epoch seconds when the request was created.
X-Payhook-Nonce Nilai acak sekali pakai.Single-use random value.
X-Payhook-Signature sha256=<hex> โ€” HMAC-SHA256 dariof "{timestamp}.{raw_body}"
Nilai yang ditandatangani adalah "{timestamp}.{body_json_mentah}". Verifikasi memakai raw body (sebelum di-parse) agar hash sama persis. The signed value is "{timestamp}.{raw_json_body}". Verify using the raw body (before parsing) so the hash matches exactly.

Contoh VerifikasiVerification Example

<?php
$secret = 'YOUR_SECRET_KEY';
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_PAYHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_PAYHOOK_SIGNATURE'] ?? '';

// Tolak jika timestamp terlalu lama (anti-replay), mis. > 5 menit
if (abs(time() - (int)$ts) > 300) { http_response_code(408); exit; }

$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (!hash_equals($expected, $sig)) { http_response_code(401); exit; }

$data = json_decode($raw, true);
// upsert berdasarkan $data['event_id'] (idempoten)
http_response_code(200);
const crypto = require('crypto');
// Pakai raw body: app.use(express.raw({ type: 'application/json' }))
app.post('/payhook', (req, res) => {
  const secret = process.env.PAYHOOK_SECRET;
  const ts  = req.header('X-Payhook-Timestamp') || '';
  const sig = req.header('X-Payhook-Signature') || '';
  const raw = req.body.toString('utf8');

  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300)
    return res.sendStatus(408); // anti-replay

  const expected = 'sha256=' +
    crypto.createHmac('sha256', secret).update(ts + '.' + raw).digest('hex');
  const ok = sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  if (!ok) return res.sendStatus(401);

  const data = JSON.parse(raw);
  // upsert by data.event_id (idempotent)
  res.sendStatus(200);
});
Idempotency & RetryIdempotency & Retry Bila pengiriman gagal karena jaringan/timeout/5xx, PayHook menyimpannya di antrean lokal dan mengirim ulang otomatis (exponential backoff). Karena event_id tetap sama pada retry, gunakan sebagai kunci upsert agar tidak ada transaksi ganda. If delivery fails due to network/timeout/5xx, PayHook stores it in a local queue and retries automatically (exponential backoff). Since the event_id stays the same across retries, use it as an upsert key to avoid duplicate transactions.

Heartbeat MonitoringHeartbeat Monitoring Opsional

Deteksi otomatis saat HP PayHook mati / offline / kuota habis, sebelum ada komplain pelanggan. Automatically detect when the PayHook phone is off / offline / out of data, before customers complain.

PayHook dapat mengirim ping status berkala ke endpoint monitoring terpisah milik Anda. Jika backend tidak menerima heartbeat dalam batas waktu tertentu, tandai perangkat sebagai offline dan kirim peringatan (WhatsApp/Telegram/email). Fitur ini opsional โ€” atur di aplikasi: Pengaturan โ†’ Heartbeat Monitoring. Jika tidak diisi, alur webhook transaksi tetap berjalan seperti biasa (kompatibel mundur). PayHook can send a periodic status ping to a separate monitoring endpoint you own. If your backend does not receive a heartbeat within a threshold, mark the device offline and send an alert (WhatsApp/Telegram/email). This feature is optional โ€” configure it in the app under Settings โ†’ Heartbeat Monitoring. If left empty, the transaction webhook flow keeps working as before (backward compatible).

Contoh Payload HeartbeatHeartbeat Payload Example

POST /payhook/heartbeat
Content-Type: application/json
X-Payhook-Timestamp: 1752300000        # jika secret diisi / if secret set
X-Payhook-Signature: sha256=<hmac>  # jika secret diisi / if secret set

{
  "type": "heartbeat",
  "device_id": "ph-a1b2c3d4e5f6",
  "app_version": "1.5.0",
  "listener_connected": true,
  "battery_optimized": false,
  "last_event_at": 1752299880000,
  "sent_at": 1752300000000
}
Field KeteranganDescription
device_id ID unik perangkat.Unique device id.
app_version Versi aplikasi PayHook.PayHook app version.
listener_connected Status listener notifikasi.Notification listener status.
battery_optimized true = optimisasi baterai masih aktif (berisiko).true = battery optimization still on (risky).
last_event_at Epoch ms pembayaran terakhir terdeteksi.Epoch ms of last detected payment.
sent_at Epoch ms saat heartbeat dikirim.Epoch ms when heartbeat was sent.
Aturan monitoring yang disarankanSuggested monitoring rules Interval kirim 60โ€“300 detik. Jika tidak ada heartbeat > 2โ€“3ร— interval, anggap offline. Gunakan grace period โ€” jangan alert hanya dari satu heartbeat yang meleset โ€” untuk menghindari false alarm. Send every 60โ€“300s. If no heartbeat arrives for > 2โ€“3ร— the interval, treat it as offline. Use a grace period โ€” do not alert on a single missed heartbeat โ€” to avoid false alarms.

Contoh IntegrasiIntegration Examples

Contoh menerima & memverifikasi webhook PayHook di berbagai bahasa. Examples of receiving & verifying a PayHook webhook in several languages.

<?php
// webhook.php โ€” endpoint penerima PayHook
$expected = 'YOUR_SECRET_TOKEN';
$auth = $_SERVER['HTTP_AUTHORIZATION'] ?? '';

if ($auth !== 'Bearer ' . $expected) {
    http_response_code(401);
    exit(json_encode(['error' => 'Unauthorized']));
}

$payload = json_decode(file_get_contents('php://input'), true);
if (!$payload) { http_response_code(400); exit; }

// Idempotent upsert by event_id
$eventId = $payload['event_id'] ?? '';
if ($eventId === '') { http_response_code(422); exit(json_encode(['error' => 'Missing event_id'])); }

// Simpan pembayaran
file_put_contents('payments.log',
    sprintf("[%s] %s Rp%s event_id=%s ref=%s\n",
        $payload['timestamp'], $payload['source'],
        number_format($payload['amount'], 0, ',', '.'),
        $eventId,
        $payload['reference']
    ), FILE_APPEND);

http_response_code(200);
echo json_encode(['status' => 'ok']);
// routes/api.php
Route::post('/payhook', function (Illuminate\Http\Request $request) {
    if ($request->bearerToken() !== config('services.payhook.token')) {
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    $data = $request->validate([
        'event_id'  => 'required|string',
        'amount'    => 'required|integer',
        'source'    => 'required|string',
        'reference' => 'required|string',
        'timestamp' => 'required|string',
    ]);

    Payment::updateOrCreate(
        ['event_id' => $data['event_id']], // idempoten
        ['amount' => $data['amount'], 'source' => $data['source'], 'reference' => $data['reference']]
    );

    return response()->json(['status' => 'ok']);
});
// server.js โ€” Express
const express = require('express');
const app = express();
app.use(express.json());

const TOKEN = process.env.PAYHOOK_TOKEN;

app.post('/payhook', (req, res) => {
  if (req.headers.authorization !== `Bearer ${TOKEN}`) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

    const { event_id, amount, source, reference, timestamp } = req.body;
    console.log(`[${timestamp}] ${source} Rp${amount} event_id=${event_id} ref=${reference}`);

    // TODO: simpan ke database (idempoten berdasarkan event_id)
  res.json({ status: 'ok' });
});

app.listen(3000, () => console.log('PayHook listener on :3000'));
# app.py โ€” Flask
import os
from flask import Flask, request, jsonify

app = Flask(__name__)
TOKEN = os.environ["PAYHOOK_TOKEN"]

@app.post("/payhook")
def payhook():
    if request.headers.get("Authorization") != f"Bearer {TOKEN}":
        return jsonify(error="Unauthorized"), 401

        data = request.get_json(silent=True) or {}
    print(f"[{data.get('timestamp')}] {data.get('source')} "
            f"Rp{data.get('amount')} event_id={data.get('event_id')} "
            f"ref={data.get('reference')}")

    # TODO: simpan ke DB (idempoten berdasarkan event_id)
    return jsonify(status="ok")

if __name__ == "__main__":
    app.run(port=3000)

Simulator / SandboxSimulator / Sandbox

Susun payload PayHook (payment/heartbeat), salin sebagai cURL/kode, atau kirim uji coba langsung ke endpoint Anda โ€” tanpa perlu transaksi sungguhan. Build PayHook payloads (payment/heartbeat), copy them as cURL/code, or send a live test to your endpoint โ€” without any real transaction.

Otomatis terisi sesuai sumber & nominal. Bisa Anda ubah.Auto-filled from source & amount. You can edit it.

Jika diisi, simulator menambahkan header X-Payhook-Timestamp, X-Payhook-Nonce, dan X-Payhook-Signature.If set, simulator adds X-Payhook-Timestamp, X-Payhook-Nonce, and X-Payhook-Signature headers.
B
Uang masuk dari John
Rp 300.000,00 sudah masuk ke rekening Anda
BCA Mobile

Payload JSONJSON Payload

// klik "Buat Payload"
Tentang mode “Kirim Test (Live)”About “Send Test (Live)” mode Simulator akan mencoba kirim langsung dari browser terlebih dulu. Jika endpoint Anda memblokir CORS, gunakan relay serverless (isi PAYHOOK_RELAY_URL di bawah) atau tombol Salin cURL. The simulator first tries direct browser delivery. If your endpoint blocks CORS, use the serverless relay (set PAYHOOK_RELAY_URL below) or use Copy cURL.

FAQ & Pemecahan MasalahTroubleshooting

Webhook tidak terkirim, kenapa?Webhook not being sent, why?

Pastikan izin Akses Notifikasi aktif, aplikasi sumber ada di daftar dipantau, optimisasi baterai dinonaktifkan, dan URL endpoint menggunakan HTTPS yang valid. Cek log aktivitas di aplikasi untuk kode HTTP respons. Ensure Notification Access is granted, the source app is in the monitored list, battery optimization is disabled, and the endpoint URL is valid HTTPS. Check the in-app activity log for the response HTTP code.

Nominal terbaca salah / kosong?Amount parsed wrong / empty?

PayHook mengekstrak angka dari teks notifikasi (mis. Rp 300.000, IDR 300,000). Jika format bank Anda tidak dikenali, sesuaikan kata kunci di aplikasi. PayHook extracts numbers from the notification text (e.g. Rp 300.000, IDR 300,000). If your bank's format is not recognized, adjust the keywords in the app.

Bagaimana menghindari data ganda?How to avoid duplicate data?

Gunakan field event_id sebagai kunci idempoten saat menyimpan ke database. Nilai ini tetap sama saat retry, sehingga aman untuk mekanisme upsert dan mencegah transaksi ganda. Use the event_id field as your idempotency key when saving to the database. It stays stable across retries, making upsert safe and preventing duplicates.

Apakah aman untuk produksi?Is it production-safe?

PayHook cocok untuk volume kecilโ€“menengah. Untuk keandalan tinggi, gunakan perangkat khusus yang selalu online, dan verifikasi pembayaran penting secara manual atau via mutasi rekening. PayHook suits smallโ€“medium volume. For high reliability, use a dedicated always-online device, and verify important payments manually or via account statements.