Dokumentasi API Revo.id
Ringkasan
Anda menjual produk di Revo.id — pembayaran QRIS / Virtual Account, invoice, saldo, dan penarikan dana kami urus. API ini menghubungkan transaksi tersebut ke sistem Anda sendiri:
| Anda menjual… | Yang dipakai |
|---|---|
| Plugin, tema, template, aplikasi desktop / mobile yang butuh kunci lisensi | Lisensi otomatis + endpoint /licenses/activate, /validate, /deactivate (tanpa API key) |
| Akses ke website / SaaS / kelas online Anda (sekali bayar atau langganan bulanan) | Webhook order.paid & subscription.expired + endpoint GET /access |
| Tombol beli di website Anda sendiri | POST /checkouts → arahkan pembeli ke checkout_url → kembali ke return_url |
Base URL semua endpoint:
https://revo.id/api/v1
Mulai dalam 5 menit
- Buka produk Anda di Dasbor → Produk, centang Lisensi software, dan tentukan batas aktivasi (mis. 1 domain). Catat Product ID-nya.
- Buka Dasbor → Developer, klik Buat API key, simpan key
rv_live_…di server Anda (variabel lingkungan, jangan di kode publik). - Isi URL webhook (mis.
https://aplikasianda.com/webhooks/revo), salin signing secretwhsec_…, lalu klik Kirim webhook tes. - Setiap pembelian lunas otomatis membuat kunci lisensi (format
XXXXX-XXXXX-XXXXX-XXXXX) yang tampil di halaman akses & email pembeli, dan server Anda menerima webhookorder.paid.
Autentikasi & format
API key (endpoint privat)
Kirim API key di header Authorization. Bila hosting Anda menghapus header tersebut, gunakan X-Revo-Key.
curl https://revo.id/api/v1/me \
-H "Authorization: Bearer rv_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - API key memberi akses penuh ke data toko Anda. Hanya pakai dari server — jangan masukkan ke plugin/aplikasi yang dipasang pembeli, aplikasi mobile, atau JavaScript browser.
- Ada dua jenis key:
rv_live_…untuk transaksi asli danrv_test_…untuk sandbox. Data keduanya terpisah: key uji hanya melihat dan membuat data uji. - Maksimal 10 key aktif per toko; key bisa dicabut kapan saja dari dasbor. Key hanya ditampilkan sekali saat dibuat.
- Batas: 300 permintaan/menit per API key, dan 60 permintaan/menit per IP untuk endpoint lisensi publik. Melewati batas → HTTP
429.
Format
- Request & response berupa JSON (
Content-Type: application/json). Parameter juga boleh dikirim sebagai form / query string. - Nominal uang dalam rupiah, bilangan bulat (
49000= Rp 49.000). - Waktu dalam ISO 8601 UTC, mis.
2026-10-26T03:15:00Z. - Objek tunggal dibungkus
{"data": {…}}; daftar dibungkus{"data": […], "meta": {"page", "per_page", "total", "total_pages"}}. Parameter paging:page,per_page(maks. 100). - Error berbentuk
{code, message, data.status}:
HTTP/1.1 404 Not Found
{
"code": "license_not_found",
"message": "Lisensi tidak ditemukan.",
"data": { "status": 404 }
}
A. Jual lisensi software
Cocok untuk plugin WordPress, tema, aplikasi desktop, bot, atau script. Aplikasi yang dipasang pembeli memanggil endpoint lisensi tanpa API key — kunci lisensi itu sendiri yang menjadi kredensial.
Siklus lisensi
| Status | Arti |
|---|---|
active | Sah dipakai. |
expired | Lisensi produk membership yang langganannya berakhir (otomatis aktif lagi bila diperpanjang). |
disabled | Dinonaktifkan oleh Anda (dasbor atau API). Bisa diaktifkan kembali. |
refunded | Pesanan direfund — permanen. |
Instance adalah identitas tempat lisensi dipakai: domain situs (tokoanda.com; https:// dan www. dihapus otomatis) atau ID perangkat. Jumlah instance dibatasi oleh batas aktivasi produk (0 = tanpa batas). Mengaktifkan ulang instance yang sama tidak menambah hitungan. Pembeli bisa melepas perangkat dari halaman aksesnya.
1. Aktivasi — saat pembeli memasukkan kunci
POST https://revo.id/api/v1/licenses/activate
Content-Type: application/json
{
"license_key": "RV7KD-Q2MXA-9HTPL-WZ3NC",
"product_id": 123,
"instance": "tokopembeli.com",
"label": "Situs utama"
} 200 OK
{
"activated": true,
"instance": "tokopembeli.com",
"license": {
"key": "RV7KD-Q2MXA-9HTPL-WZ3NC",
"status": "active",
"product_id": 123,
"product": "Plugin Ongkir Pro",
"store": "budi",
"max_activations": 1,
"activations": 1,
"expires_at": null
}
} Gagal: 404 license_not_found, 403 product_mismatch, 403 activation_limit, 403 license_expired / license_disabled / license_refunded, 400 instance_required.
Selalu kirim product_id — tanpa itu, kunci dari produk lain (bahkan dari toko lain) akan dianggap sah oleh aplikasi Anda.
2. Validasi — cek berkala
Panggil saat aplikasi dibuka atau maksimal sekali sehari (simpan hasilnya di cache). Response selalu 200 untuk kunci yang dikenal; lihat field valid.
POST https://revo.id/api/v1/licenses/validate
{ "license_key": "RV7KD-Q2MXA-9HTPL-WZ3NC", "product_id": 123, "instance": "tokopembeli.com" }
200 OK
{
"valid": false,
"reason": "instance_not_activated", // atau license_expired, license_disabled, license_refunded
"activated": false,
"license": { "key": "…", "status": "active", "product_id": 123, … }
} Tanpa instance, valid hanya memeriksa status lisensi.
3. Deaktivasi — saat plugin dinonaktifkan / pindah domain
POST https://revo.id/api/v1/licenses/deactivate
{ "license_key": "RV7KD-Q2MXA-9HTPL-WZ3NC", "product_id": 123, "instance": "tokopembeli.com" }
200 OK
{ "deactivated": true, "license": { … "activations": 0 } }
Contoh: plugin WordPress
<?php
// Di plugin yang Anda jual. Tidak memakai API key.
const MYPLUGIN_REVO_API = 'https://revo.id/api/v1';
const MYPLUGIN_PRODUCT_ID = 123;
function myplugin_license_call( $action, $key ) {
$res = wp_remote_post( MYPLUGIN_REVO_API . '/licenses/' . $action, array(
'timeout' => 15,
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'license_key' => trim( $key ),
'product_id' => MYPLUGIN_PRODUCT_ID,
'instance' => wp_parse_url( home_url(), PHP_URL_HOST ),
'label' => get_bloginfo( 'name' ),
) ),
) );
if ( is_wp_error( $res ) ) {
return $res; // Server tidak terjangkau: jangan langsung matikan fitur.
}
return json_decode( wp_remote_retrieve_body( $res ), true );
}
// Saat pengguna menyimpan kunci di halaman pengaturan plugin:
$result = myplugin_license_call( 'activate', $_POST['license_key'] );
if ( ! empty( $result['activated'] ) ) {
update_option( 'myplugin_license', sanitize_text_field( $_POST['license_key'] ) );
} else {
$message = $result['message'] ?? 'Lisensi tidak valid';
}
// Cek harian (WP-Cron), dengan toleransi offline 7 hari:
add_action( 'myplugin_daily', function () {
$r = myplugin_license_call( 'validate', get_option( 'myplugin_license' ) );
if ( is_wp_error( $r ) ) {
return; // Coba lagi besok; matikan fitur hanya bila gagal terus > 7 hari.
}
update_option( 'myplugin_license_ok', ! empty( $r['valid'] ) );
update_option( 'myplugin_license_checked', time() );
} );
// Saat plugin dinonaktifkan:
register_deactivation_hook( __FILE__, function () {
myplugin_license_call( 'deactivate', get_option( 'myplugin_license' ) );
} ); Contoh: aplikasi Node.js / Electron
const REVO = 'https://revo.id/api/v1';
async function activateLicense(key, deviceId) {
const res = await fetch(`${REVO}/licenses/activate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ license_key: key, product_id: 123, instance: deviceId, label: os.hostname() }),
});
const body = await res.json();
if (!res.ok) throw new Error(body.message); // mis. "Batas aktivasi tercapai (1)."
return body.license;
} Kelola lisensi dari server Anda (API key)
GET https://revo.id/api/v1/licenses?email=pembeli@mail.com&product_id=123
GET https://revo.id/api/v1/licenses/RV7KD-Q2MXA-9HTPL-WZ3NC // termasuk daftar instance
POST https://revo.id/api/v1/licenses/RV7KD-Q2MXA-9HTPL-WZ3NC
{ "status": "disabled", "note": "Chargeback" } // atau "active"
{ "max_activations": 3 } // ubah batas
{ "reset_activations": true } // hapus semua aktivasi
B. Jual akses website / membership
Pembeli membayar di Revo.id, website Anda yang memberi akses. Dua cara yang bisa digabung:
Cara 1 — Webhook (disarankan)
order.paid→ buat / aktifkan akun berdasarkandata.customer.email. Untuk produk membership, simpandata.subscription_id.subscription.renewed→ perpanjang akses sampaidata.current_period_end.subscription.expiredatauorder.refunded→ cabut akses.subscription.cancelled→ anggota berhenti; akses tetap sampaicurrent_period_end, lalu menyusulsubscription.expired.
Cara 2 — Cek akses saat login
GET https://revo.id/api/v1/access?email=pembeli@mail.com&product_id=456
Authorization: Bearer rv_live_…
200 OK
{
"data": {
"access": true,
"email": "pembeli@mail.com",
"product_id": 456,
"source": "subscription", // "order" untuk produk sekali bayar
"subscription_id": 88,
"status": "active",
"order_number": null,
"expires_at": "2026-11-26T03:15:00Z"
}
} Untuk membership, access tetap true selama masa tenggang perpanjangan. Untuk produk sekali bayar, access bernilai true bila ada pesanan lunas (termasuk sebagai order bump) yang tidak direfund. Simpan hasilnya di sesi beberapa menit — jangan panggil di setiap request halaman.
Contoh: middleware PHP
function revo_has_access( string $email, int $productId ): bool {
$ch = curl_init( 'https://revo.id/api/v1/access?' . http_build_query( [ 'email' => $email, 'product_id' => $productId ] ) );
curl_setopt_array( $ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv( 'REVO_API_KEY' ) ],
] );
$body = json_decode( (string) curl_exec( $ch ), true );
curl_close( $ch );
return ! empty( $body['data']['access'] );
}
if ( ! revo_has_access( $_SESSION['email'], 456 ) ) {
header( 'Location: https://revo.id/budi/kelas-premium' ); // halaman jual produk
exit;
}
C. Checkout dari website Anda
Buat pesanan dari server, lalu arahkan pembeli ke checkout_url untuk membayar dengan QRIS / Virtual Account. Setelah lunas, halaman sukses menampilkan tombol kembali ke return_url (ditambah ?revo_order=RV-…&status=paid).
POST https://revo.id/api/v1/checkouts
Authorization: Bearer rv_live_…
Content-Type: application/json
{
"product_id": 123,
"customer": { "name": "Siti Aminah", "email": "siti@mail.com", "phone": "081234567890" },
"external_id": "user_9812", // ID di sistem Anda (opsional, bisa difilter)
"metadata": { "plan": "pro", "team": 7 }, // bebas, maks. 2.000 karakter JSON
"return_url": "https://aplikasianda.com/billing/selesai",
"coupon": "HEMAT20", // opsional
"amount": 75000, // hanya untuk produk "bayar seikhlasnya"
"bump": false, // ikut ambil order bump produk ini
"affiliate": "username_afiliator" // opsional, komisi untuk afiliator
}
201 Created
{
"data": {
"id": 5012,
"number": "RV-261026-005012",
"status": "unpaid",
"amount": 149000,
"external_id": "user_9812",
"metadata": { "plan": "pro", "team": 7 },
"checkout_url": "https://revo.id/app/?iv=order&t=3f9c…",
…
}
} - Pesanan belum dibayar otomatis kedaluwarsa. Buat checkout baru setiap kali pembeli menekan tombol beli.
- Jangan anggap pesanan lunas dari
return_url— parameter di URL bisa diubah siapa saja. Konfirmasi lewat webhookorder.paidatauGET /orders/{number}. - Produk gratis (atau kupon 100%) langsung berstatus
paidpada response. - Validasi gagal →
422dengan kodecheckout_*, mis.checkout_email,checkout_soldout,checkout_member(email masih berlangganan).
Sandbox (mode uji)
Coba seluruh integrasi tanpa uang sungguhan. Buat API key mode Sandbox di Dasbor → Developer (rv_test_…), lalu pakai endpoint yang sama persis.
POST /checkoutsdengan key uji membuat pesanan uji dari produk asli Anda (tidak butuh gateway pembayaran).checkout_urlmenampilkan tombol Bayar berhasil (simulasi) dan Gagal / dibatalkan, tanpa QRIS/VA.- Pembayaran simulasi memicu semua efek nyata: lisensi diterbitkan, langganan dimulai, halaman akses terbuka, dan webhook terkirim ke URL webhook yang sama.
- Tidak ada uang, saldo, komisi afiliasi, email, maupun WhatsApp. Pesanan uji tidak muncul di statistik, daftar pesanan, pembeli, anggota, lisensi, atau ekspor CSV.
- Semua objek punya field
"livemode": false; payload webhook juga, ditambah headerRevo-Mode: test. Di server produksi, abaikan event denganlivemodefalse (atau pakai server terpisah untuk uji). - Kunci lisensi uji bisa diaktifkan lewat endpoint lisensi publik seperti biasa;
license.livemodebernilaifalsesehingga aplikasi Anda bisa menolaknya di build produksi. - Key live tidak bisa melihat data uji, dan sebaliknya (
404).
Simulasi lewat API (key uji saja)
POST https://revo.id/api/v1/sandbox/orders/{id atau nomor}/pay → order.paid (+ lisensi, langganan)
POST https://revo.id/api/v1/sandbox/orders/{id atau nomor}/fail → pesanan dibatalkan
POST https://revo.id/api/v1/sandbox/orders/{id atau nomor}/refund → order.refunded, license.updated
POST https://revo.id/api/v1/sandbox/subscriptions/{id}/renew → subscription.renewed (+ order.paid perpanjangan)
POST https://revo.id/api/v1/sandbox/subscriptions/{id}/cancel → subscription.cancelled
POST https://revo.id/api/v1/sandbox/subscriptions/{id}/expire → subscription.expired
POST https://revo.id/api/v1/sandbox/reset → hapus semua data uji toko Contoh alur uji otomatis:
KEY=rv_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ORDER=$(curl -s https://revo.id/api/v1/checkouts -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"product_id":123,"customer":{"name":"Uji","email":"uji@contoh.com"}}' | jq -r .data.number)
curl -s -X POST https://revo.id/api/v1/sandbox/orders/$ORDER/pay -H "Authorization: Bearer $KEY" | jq .data.licenses Endpoint /sandbox/… dengan key live dijawab 403 sandbox_only. Di Dasbor → Developer ada panel Sandbox untuk melihat pesanan uji, menjalankan simulasi, dan menghapus semua data uji.
Webhook
Kami mengirim POST JSON ke URL webhook toko Anda setiap ada kejadian berikut:
| Event | Kapan | data berisi |
|---|---|---|
order.paid | Pesanan lunas (termasuk perpanjangan membership & produk gratis) | Order |
order.refunded | Pesanan direfund — cabut akses pembeli | Order |
subscription.created | Langganan membership baru dimulai | Subscription |
subscription.renewed | Langganan diperpanjang satu periode | Subscription |
subscription.cancelled | Anggota berhenti (akses tetap sampai akhir periode) | Subscription |
subscription.expired | Langganan berakhir — cabut akses | Subscription |
license.updated | Status lisensi diubah (dinonaktifkan / diaktifkan / direfund) | License |
ping | Tes dari dasbor atau API | { "message" } |
Contoh payload
POST /webhooks/revo HTTP/1.1
Content-Type: application/json
Revo-Event: order.paid
Revo-Delivery: evt_5b0c9f2e4a1d7c3b8e6f0a12
Revo-Signature: t=1792984500,v1=6f1c0d…e93a
{
"id": "evt_5b0c9f2e4a1d7c3b8e6f0a12",
"type": "order.paid",
"created_at": "2026-10-26T03:15:00Z",
"store": "budi",
"data": {
"id": 5012,
"number": "RV-261026-005012",
"status": "paid",
"product_id": 123,
"customer": { "name": "Siti Aminah", "email": "siti@mail.com", "phone": "6281234567890" },
"paid_amount": 149000,
"external_id": "user_9812",
"metadata": { "plan": "pro", "team": 7 },
"subscription_id": null,
"licenses": [ { "key": "RV7KD-Q2MXA-9HTPL-WZ3NC", "status": "active", "max_activations": 1, … } ],
…
}
} Verifikasi tanda tangan
Header Revo-Signature berisi t (Unix time) dan v1 = HMAC-SHA256 (hex) dari string "{t}.{raw body}" dengan signing secret whsec_…. Tolak bila tanda tangan tidak cocok atau t lebih dari 5 menit dari sekarang. Gunakan body mentah sebelum di-parse.
PHP
$secret = getenv( 'REVO_WEBHOOK_SECRET' ); // whsec_…
$body = file_get_contents( 'php://input' );
$header = $_SERVER['HTTP_REVO_SIGNATURE'] ?? '';
parse_str( str_replace( ',', '&', $header ), $sig ); // ['t' => …, 'v1' => …]
$expected = hash_hmac( 'sha256', ( $sig['t'] ?? '' ) . '.' . $body, $secret );
if ( empty( $sig['v1'] ) || ! hash_equals( $expected, $sig['v1'] ) || abs( time() - (int) $sig['t'] ) > 300 ) {
http_response_code( 401 );
exit;
}
$event = json_decode( $body, true );
// Idempoten: abaikan bila $event['id'] sudah pernah diproses.
switch ( $event['type'] ) {
case 'order.paid':
grant_access( $event['data']['customer']['email'], $event['data']['product_id'] );
break;
case 'order.refunded':
case 'subscription.expired':
revoke_access( $event['data']['customer']['email'], $event['data']['product_id'] );
break;
}
http_response_code( 200 ); Node.js (Express)
const crypto = require('crypto');
app.post('/webhooks/revo', express.raw({ type: 'application/json' }), (req, res) => {
const sig = Object.fromEntries(req.get('Revo-Signature').split(',').map(p => p.split('=')));
const expected = crypto.createHmac('sha256', process.env.REVO_WEBHOOK_SECRET)
.update(`${sig.t}.${req.body}`).digest('hex');
const ok = sig.v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1))
&& Math.abs(Date.now() / 1000 - Number(sig.t)) < 300;
if (!ok) return res.sendStatus(401);
const event = JSON.parse(req.body);
// … proses event.type
res.sendStatus(200);
}); Pengiriman ulang
- URL webhook harus alamat publik (alamat internal/privat ditolak) di port 80, 443, atau 8080; sebaiknya
https://. - Balas dengan HTTP
2xxdalam 10 detik. Proses berat sebaiknya dijalankan di antrean Anda. - Selain
2xx(atau timeout), kami mencoba lagi setelah 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, dan 24 jam (7 percobaan). Redirect tidak diikuti. - Event yang sama bisa terkirim lebih dari sekali — gunakan
idevent (juga di headerRevo-Delivery) untuk mencegah proses ganda. Urutan event tidak dijamin. - Riwayat pengiriman, tombol Kirim ulang, dan Kirim webhook tes ada di Dasbor → Developer. Tes juga bisa lewat
POST /webhooks/test.
Referensi endpoint
| Metode & path | Auth | Kegunaan |
|---|---|---|
GET /me | API key | Toko pemilik key. |
GET /products | API key | Daftar produk (status: active, draft, all). |
GET /products/{id} | API key | Detail produk. |
POST /checkouts | API key | Buat pesanan + link pembayaran. |
GET /orders | API key | Filter: status (paid, unpaid, cancelled, refunded), email, product_id, external_id, page, per_page. |
GET /orders/{id atau nomor} | API key | Detail pesanan, termasuk lisensinya. |
GET /access | API key | Apakah email punya akses ke product_id. |
GET /subscriptions | API key | Filter: status, email, product_id. |
GET /subscriptions/{id} | API key | Detail langganan. |
GET /licenses | API key | Filter: email, product_id. |
GET /licenses/{key} | API key | Detail lisensi + instance aktif. |
POST /licenses/{key} | API key | status, max_activations, reset_activations, note. |
POST /webhooks/test | API key | Kirim event ping sekarang. |
POST /sandbox/… | Key uji | Simulasi bayar, gagal, refund, perpanjang, berhenti, berakhir, reset. |
POST /licenses/activate | Kunci lisensi | license_key, instance, product_id, label. |
POST /licenses/validate | Kunci lisensi | license_key, product_id, instance (opsional). |
POST /licenses/deactivate | Kunci lisensi | license_key, instance, product_id. |
Objek data
Order
id, number | ID angka dan nomor pesanan (RV-yymmdd-000123). |
status | unpaid, paid, cancelled, refunded. |
product_id, product_type, title | Produk yang dibeli (digital, link, service, event, membership, support). |
customer | {name, email, phone}. |
amount, paid_amount, discount, coupon, net_amount | Tagihan, jumlah dibayar, diskon, kode kupon, pendapatan bersih Anda (setelah biaya & komisi afiliasi). |
method, source | Cara bayar; checkout atau renewal (perpanjangan membership). |
bump | {product_id, amount} bila pembeli mengambil order bump. |
subscription_id | Langganan terkait (membership). |
external_id, metadata | Dari POST /checkouts. |
licenses | Daftar License pesanan ini. |
checkout_url | Halaman bayar / akses pembeli. |
created_at, paid_at, refunded_at | Waktu (UTC). |
License
key | XXXXX-XXXXX-XXXXX-XXXXX (huruf besar & angka, tanpa O/0/I/1). |
status | active, expired, disabled, refunded. |
product_id, order_id, order_number, subscription_id | Asal lisensi. |
customer | {name, email} — hanya di endpoint ber-API key & webhook. |
max_activations, activations | Batas (0 = tanpa batas) dan jumlah instance aktif. |
instances | [{id, instance, label, activated_at, last_seen_at}] — di GET /licenses/{key}. |
expires_at | Akhir periode langganan untuk lisensi membership; null = seumur hidup. |
Subscription
id, product_id | Langganan dan produk membership-nya. |
status | active, cancelled (berhenti di akhir periode), expired. |
access | true bila anggota boleh mengakses sekarang (termasuk masa tenggang). |
period, price, renewals | month/year, harga per periode, jumlah perpanjangan. |
current_period_end, cancelled_at, created_at | Waktu (UTC). |
Kode error
| HTTP | code | Penyebab |
|---|---|---|
| 400 | license_key_required, instance_required, invalid_email, invalid_status, invalid_return_url, metadata_too_large | Parameter kurang / salah. |
| 401 | missing_api_key, invalid_api_key | API key tidak dikirim, salah, atau sudah dicabut. |
| 403 | store_inactive, product_mismatch, activation_limit, license_expired, license_disabled, license_refunded | Ditolak. |
| 404 | not_found, product_not_found, license_not_found | Data tidak ada atau bukan milik toko Anda. |
| 403 | sandbox_only | Endpoint sandbox dipanggil dengan key live. |
| 409 | license_refunded, webhook_not_configured, sandbox_* | Konflik status. |
| 422 | checkout_* | Checkout ditolak; pesan siap ditampilkan ke pembeli. |
| 429 | rate_limited | Terlalu banyak permintaan; coba lagi setelah 1 menit. |
Checklist keamanan
- API key & signing secret hanya di server (variabel lingkungan), tidak di repo publik, aplikasi pembeli, atau browser. Cabut key yang bocor dari dasbor.
- Verifikasi
Revo-Signaturedi setiap webhook dan proses tiapidevent sekali saja. - Kirim
product_iddi setiap panggilan lisensi. - Jangan percaya parameter
return_url; konfirmasi pembayaran dari webhook atau API. - Toleransi gangguan jaringan: cache hasil validasi lisensi dan beri masa tenggang offline beberapa hari sebelum mematikan fitur.
Pertanyaan teknis? Hubungi info@revo.id.