Dokumentasi API
DaftarMasuk

Dokumentasi API Pembayaran

Terima pembayaran lewat Virtual Account dan QRIS dengan satu antarmuka. Seluruh contoh pada halaman ini dapat langsung dicoba memakai kunci sandbox tanpa melibatkan dana sungguhan.

Mulai cepat

Tiga langkah sampai pembayaran pertama Anda terbentuk.

  1. Daftar dan terbitkan kunci sandbox dari halaman ringkasan.
  2. Kirim permintaan pembuatan pembayaran seperti contoh di bawah.
  3. Daftarkan alamat notifikasi agar sistem Anda tahu ketika pelanggan sudah membayar.
curl -X POST http://localhost:3040/api/v1/payments \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Idempotency-Key: PESANAN-0001" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "PESANAN-0001",
    "amount": 150000,
    "method": { "type": "VIRTUAL_ACCOUNT", "channel": "BNI" }
  }'
Lingkungan sandbox
Kunci berawalan lg_sandbox_ memakai lingkungan uji penyedia pembayaran. Nomor Virtual Account yang terbentuk sungguhan, tetapi tidak ada dana yang berpindah.

Pendaftaran lewat API

POST/api/v1/merchants

Bila Anda memasang layanan ini di dalam produk Anda sendiri, merchant dapat didaftarkan langsung dari peladen Anda tanpa membuka halaman web. Kunci API sandbox diterbitkan pada jawaban yang sama, sehingga integrasi dapat berjalan seketika.

curl -X POST https://api.langsunggas.shop/api/v1/merchants \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@usaha-anda.com",
    "password": "kata-sandi-minimal-12",
    "legal_name": "PT Usaha Anda Sejahtera",
    "display_name": "Usaha Anda",
    "scopes": ["payments:write", "payments:read", "payouts:read"]
  }'
KolomWajibKeterangan
emailYaSurel pemilik akun, dipakai untuk masuk
passwordYaMinimal 12 karakter
legal_nameYaNama usaha resmi, minimal 3 karakter
display_nameYaNama yang dilihat pelanggan, minimal 2 karakter
country_codeTidakDua huruf, bawaannya ID
scopesTidakIzin kunci pertama. Bawaannya payments:write dan payments:read. Izin di luar daftar yang diizinkan diabaikan, bukan menggagalkan pendaftaran

Jawaban berstatus 201.

{
  "merchant": {
    "id": "mch_xxxxxxxxxxxx",
    "legal_name": "PT Usaha Anda Sejahtera",
    "display_name": "Usaha Anda",
    "status": "ACTIVE_SANDBOX"
  },
  "api_key": {
    "id": "key_xxxxxxxxxxxx",
    "prefix": "a1b2c3d4e5f6",
    "secret": "lg_sandbox_a1b2c3d4e5f6_xxxxxxxxxxxxxxxxxxxx",
    "environment": "SANDBOX",
    "scopes": ["payments:write", "payments:read"],
    "grantable_scopes": [
      "payments:write",
      "payments:read",
      "payouts:read",
      "crypto-withdrawals:read"
    ]
  },
  "next_steps": [
    "Simpan nilai api_key.secret sekarang. Nilai itu tidak dapat dibaca kembali.",
    "Panggil GET /api/v1/channels untuk melihat kanal pembayaran yang tersedia.",
    "Kirim berkas kelayakan usaha lewat POST /api/v1/kyb sebelum memakai mode live.",
    "Izin menulis pencairan dan penarikan kripto dibuka operator bersamaan dengan kenaikan ke mode live."
  ],
  "request_id": "req_xxxxxxxx"
}
Rahasia kunci hanya muncul sekali
Nilai api_key.secret tidak pernah dapat dibaca kembali, termasuk oleh kami. Simpan pada penyimpanan rahasia Anda saat jawaban diterima. Bila hilang, cabut kunci itu dan terbitkan yang baru dari halaman ringkasan.
Pendaftaran berhenti di sandbox
Kunci yang lahir dari endpoint ini selalu berawalanlg_sandbox_ dengan izin payments:write dan payments:read. Mode langsung dan izin pencairan menuntut pemeriksaan kelayakan usaha oleh tim kami, karena keduanya menyentuh dana sungguhan.

Bila operator memasang kunci penjaga, sertakan tajuk X-Signup-Secret. Tanpa tajuk itu jawabannya 401 UNAUTHORIZED.

KodeArti
409 EMAIL_TAKENSurel sudah terdaftar
400 WEAK_PASSWORDKata sandi kurang dari 12 karakter
400 VALIDATION_ERRORKolom tidak lengkap atau tidak sesuai
429 RATE_LIMITEDLebih dari 5 pendaftaran per jam dari satu alamat IP
500 API_KEY_ISSUE_FAILEDMerchant terbentuk tetapi kunci gagal diterbitkan. Jangan mendaftar ulang, hubungi operator

Kunci pertama membawa izin pembayaran saja bila kolom scopes dikosongkan. Sertakan kolom itu untuk meminta izin membaca pencairan atau penarikan kripto, sehingga integrasi Anda dapat disiapkan lebih dahulu.

IzinDapat diminta saat mendaftar
payments:writeYa. Membuat tagihan
payments:readYa. Membaca tagihan, kanal, riwayat notifikasi
payouts:readYa. Membaca saldo, mutasi, dan riwayat pencairan
crypto-withdrawals:readYa. Membaca alamat dan riwayat penarikan USDT
payouts:writeTidak. Dibuka operator bersamaan dengan kenaikan ke mode langsung
crypto-withdrawals:writeTidak. Dibuka operator bersamaan dengan kenaikan ke mode langsung
Izin yang tidak diizinkan diabaikan, bukan membatalkan
Meminta payouts:write tidak menggagalkan pendaftaran; izin itu hanya tidak diberikan. Membatalkan seluruh proses karena satu izin akan memaksa Anda mengulang semuanya, padahal maksudnya sudah jelas. Bacalah api_key.scopes pada jawaban untuk mengetahui izin yang benar-benar menempel, dan api_key.grantable_scopes untuk daftar yang dapat diminta.
Mengapa izin menulis ditahan
Mencairkan dana mengeluarkan uang, dan transfer rantai tidak dapat dibatalkan. Keduanya tidak layak menempel pada kunci yang lahir dari satu permintaan tanpa pemeriksaan siapa pun.

Autentikasi

Setiap permintaan menyertakan kunci pada tajuk Authorization.

Authorization: Bearer lg_sandbox_a1b2c3d4_xxxxxxxxxxxxxxxx
Idempotency-Key: PESANAN-0001
Content-Type: application/json
AwalanLingkunganKeterangan
lg_sandbox_SandboxUntuk mencoba integrasi, tanpa dana sungguhan
lg_live_ProduksiMemproses dana sungguhan

Setiap kunci membawa daftar izin. Permintaan di luar izin kunci dijawab 403 FORBIDDEN.

IzinMemberi akses ke
payments:writeMembuat pembayaran
payments:readMembaca pembayaran
payouts:writeMengajukan pencairan dana
payouts:readMembaca pencairan dana
crypto-withdrawals:writeMendaftarkan alamat dan mengajukan penarikan USDT
crypto-withdrawals:readMembaca penarikan USDT dan jejaknya
Jaga kerahasiaan kunci
Simpan kunci di sisi peladen Anda. Jangan pernah menaruhnya pada kode yang berjalan di peramban atau aplikasi telepon, karena siapa pun dapat membacanya dari sana. Bila kunci bocor, cabut segera dari halaman ringkasan dan terbitkan yang baru.

Mengelola kunci lewat API

Kunci dapat didaftar, diterbitkan, dan dicabut tanpa membuka halaman web. Wewenangnya berasal dari kunci yang Anda pakai memanggil, sehingga seluruh daur hidup kunci dapat dijalankan dari peladen Anda sendiri.

GET/api/v1/api-keys

Mengembalikan kunci pada lingkungan yang sama dengan kunci pemanggil. Hanya metadata; rahasia tidak pernah dapat dibaca ulang. Baris dengan current: true adalah kunci yang sedang Anda pakai.

curl https://api.langsunggas.shop/api/v1/api-keys \
  -H "Authorization: Bearer lg_sandbox_kunci_anda"
{
  "environment": "SANDBOX",
  "active_limit": 10,
  "active_count": 2,
  "data": [
    {
      "id": "key_xxxxxxxxxxxx",
      "prefix": "1ff77c9739cc",
      "environment": "SANDBOX",
      "scopes": ["payments:write", "payments:read"],
      "current": true,
      "status": "ACTIVE",
      "created_at": "2026-10-10T09:59:44.980Z",
      "last_used_at": "2026-10-10T09:59:45.623Z",
      "revoked_at": null
    }
  ]
}
POST/api/v1/api-keys

Menerbitkan kunci baru. Kolom scopes boleh dikosongkan untuk menyalin izin kunci pemanggil. Lingkungan tidak dapat diminta, selalu mengikuti kunci pemanggil.

curl -X POST https://api.langsunggas.shop/api/v1/api-keys \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "scopes": ["payments:read"] }'
Izin tidak dapat dinaikkan
Izin kunci baru wajib merupakan bagian dari izin kunci pemanggil. Permintaan yang melampauinya dijawab 403 SCOPE_ESCALATION. Tanpa aturan ini, satu kunci pembayaran yang bocor dapat dipakai menerbitkan kunci pencairan, lalu mengeluarkan dana Anda.

Pola yang disarankan adalah menerbitkan kunci sesempit mungkin untuk setiap pemakaian. Kunci yang hanya membaca status pembayaran cukup berizin payments:read, sehingga kebocorannya tidak dapat dipakai membuat tagihan.

DELETE/api/v1/api-keys

Mencabut kunci. Id dapat dikirim lewat kueri ?api_key_id= atau badan JSON. Pencabutan bersifat idempoten; mengulanginya dijawab 200 dengan already_revoked: true.

curl -X DELETE \
  "https://api.langsunggas.shop/api/v1/api-keys?api_key_id=key_xxxxxxxxxxxx" \
  -H "Authorization: Bearer lg_sandbox_kunci_anda"
{
  "id": "key_xxxxxxxxxxxx",
  "prefix": "1ff77c9739cc",
  "status": "REVOKED",
  "already_revoked": false,
  "self_revoked": false,
  "request_id": "req_xxxxxxxx"
}
Rotasi yang aman
Terbitkan kunci baru lebih dahulu, pasang pada layanan Anda, pastikan berjalan, baru cabut yang lama. Kunci aktif terakhir tidak dapat dicabut lewat API agar satu permintaan tidak dapat menutup seluruh akses Anda.
KodeArti
403 SCOPE_ESCALATIONIzin yang diminta melampaui izin kunci pemanggil
409 KEY_LIMIT_REACHEDKunci aktif sudah 10 pada lingkungan ini
409 LAST_ACTIVE_KEYKunci aktif terakhir tidak dapat dicabut
404 NOT_FOUNDKunci bukan milik merchant Anda atau berada di lingkungan lain

Kolom environment menentukan lingkungan kunci baru. Dikosongkan berarti menyalin lingkungan kunci pemanggil. Inilah cara memperoleh kunci sungguhan tanpa membuka dasbor: kunci pertama Anda selalu lahir sebagai kunci uji, sehingga tanpa kolom ini mode langsung tidak akan pernah terjangkau.

Yang membatasi adalah status akun, bukan kunci pemanggil
Kunci uji boleh meminta kunci sungguhan, tetapi hanya berhasil bila akun Anda sudah berstatus ACTIVE_LIVE. Selama masih ACTIVE_SANDBOX, permintaan dijawab 409 ENVIRONMENT_NOT_ALLOWED. Akun yang ditangguhkan kehilangan kedua lingkungan.

Aturan izin tetap berlaku terpisah dari lingkungan: kunci baru tidak pernah boleh melampaui izin pemanggilnya, sehingga kunci sungguhan yang Anda terbitkan sendiri paling banyak memegang izin yang sudah Anda miliki. Untuk menambah izin menulis pencairan, hubungi operator.

Keterangan akun

GET/api/v1/me

Memeriksa keadaan akun Anda sendiri beserta izin kunci yang sedang dipakai. Berguna bagi integrasi yang berjalan tanpa membuka dasbor, karena tiga hal pada jawaban ini menentukan perilaku yang benar: lingkungan yang boleh dipakai, cara pencairan diproses, dan tarif yang dikenakan.

curl https://api.langsunggas.shop/api/v1/me \
  -H "Authorization: Bearer lg_sandbox_kunci_anda"
{
  "merchant": {
    "id": "mch_xxxxxxxxxxxx",
    "legal_name": "PT Usaha Anda Sejahtera",
    "display_name": "Usaha Anda",
    "status": "ACTIVE_SANDBOX",
    "country_code": "ID",
    "created_at": "2026-10-10T10:31:21.302Z",
    "suspended_at": null,
    "suspended_reason": null,
    "kyb_status": "PENDING",
    "live_enabled": false,
    "allowed_environments": ["SANDBOX"],
    "disbursement_mode": "MANUAL"
  },
  "api_key": {
    "id": "key_xxxxxxxxxxxx",
    "environment": "SANDBOX",
    "scopes": ["payments:write", "payments:read"]
  },
  "fees": {
    "plan": "Tarif Umum v1",
    "rules": [
      { "payment_method": "VIRTUAL_ACCOUNT", "calc_type": "FLAT", "flat_amount": 4000, "percentage_bps": 0 },
      { "payment_method": "QRIS", "calc_type": "PERCENTAGE", "flat_amount": 0, "percentage_bps": 70 }
    ]
  }
}
KolomArti
statusKeadaan merchant. ACTIVE_SANDBOX hanya dapat menguji, ACTIVE_LIVE dapat memproses dana sungguhan
live_enabledJalan pintas untuk memeriksa apakah mode langsung sudah terbuka
allowed_environmentsLingkungan yang sah bagi status saat ini. Kosong berarti akun sedang tidak dapat memproses apa pun
kyb_statusKeadaan pemeriksaan kelayakan usaha yang menentukan kenaikan ke mode langsung
disbursement_modeMANUAL berarti pencairan menunggu persetujuan operator, AUTOMATIC langsung diteruskan
suspended_reasonSebab penangguhan, bila ada
fees.rulesTarif yang sedang berlaku. percentage_bps bersatuan seperseratus persen, sehingga 70 berarti 0,7 persen
Periksa di sini sebelum menyalahkan jaringan
Bila tagihan mendadak dijawab MERCHANT_NOT_ACTIVE, bacalah status dan suspended_reason lebih dahulu. Keduanya menjelaskan sebabnya tanpa perlu menghubungi kami.
Mode pencairan menentukan alur Anda
Pada MANUAL, pencairan berhenti pada status menunggu sampai operator menyetujui, sehingga sistem Anda tidak boleh menganggapnya gagal hanya karena belum selesai. Pada AUTOMATIC, pencairan diteruskan ke penyedia seketika.

Endpoint ini tidak menuntut izin khusus, karena mengetahui keadaan akun sendiri adalah hak setiap pemegang kunci yang sah dan tidak ada angka dana yang disertakan. Untuk saldo, pakai GET /api/v1/balance.

Kelayakan usaha (KYB)

GET/api/v1/kyb
POST/api/v1/kyb

Kunci yang terbit dari pendaftaran lewat API selalu berlingkungan uji. Agar akun dapat memproses dana sungguhan, data kelayakan usaha harus dikirim dan ditinjau lebih dahulu. Kedua endpoint ini membuat seluruh langkahnya dapat ditempuh tanpa membuka dasbor.

Berkas kelayakan mengikat badan usaha, bukan lingkungan, sehingga kunci uji pun dapat memakainya. Memang justru merchant yang masih di lingkungan uji yang membutuhkannya.

curl https://api.langsunggas.shop/api/v1/kyb \
  -H "Authorization: Bearer lg_sandbox_kunci_anda"
{
  "status": "PENDING",
  "next_step": "SUBMIT_DOCUMENTS",
  "can_submit": true,
  "merchant_status": "ACTIVE_SANDBOX",
  "live_enabled": false,
  "submitted_at": null,
  "reviewed_at": null,
  "rejection_reason": null,
  "business": {
    "business_type": null,
    "registration_number": null,
    "tax_number": null,
    "website_url": null,
    "contact_phone": null,
    "business_address": null,
    "document_urls": []
  }
}

Bacalah next_step dan ikuti nilainya, jangan menyimpulkan sendiri dari status. Pemetaan status ke tindakan adalah aturan kami dan dapat berkembang, sedangkan nilai pada kolom ini akan tetap menunjuk tindakan yang benar.

next_stepYang perlu Anda lakukan
SUBMIT_DOCUMENTSBerkas belum pernah dikirim. Kirim dengan POST /api/v1/kyb
WAIT_REVIEWBerkas sedang ditinjau operator. Tidak ada yang perlu dilakukan selain menunggu
RESUBMIT_DOCUMENTSBerkas ditolak. Baca rejection_reason, perbaiki, lalu kirim ulang
WAIT_LIVE_ACTIVATIONBerkas disetujui. Tinggal menunggu operator membuka lingkungan sungguhan
NONELingkungan sungguhan sudah terbuka. Tidak ada langkah tersisa
curl -X POST https://api.langsunggas.shop/api/v1/kyb \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{
    "business_type": "Perdagangan daring",
    "registration_number": "AHU-0012345-AH-01-01",
    "tax_number": "01.234.567.8-901.000",
    "website_url": "https://usahaanda.co.id",
    "contact_phone": "081200001111",
    "business_address": "Jalan Merdeka Nomor 10, Jakarta Selatan, DKI Jakarta",
    "document_urls": [
      "https://berkas.usahaanda.co.id/akta.pdf",
      "https://berkas.usahaanda.co.id/npwp.pdf"
    ]
  }'
{
  "status": "IN_REVIEW",
  "next_step": "WAIT_REVIEW",
  "message": "Berkas dikirim dan menunggu peninjauan operator."
}
KolomWajibKeterangan
business_typeYaJenis usaha, 2 sampai 80 aksara
registration_numberYaNomor izin usaha, 3 sampai 60 aksara
contact_phoneYaNomor telepon yang dapat dihubungi peninjau, 8 sampai 30 aksara
business_addressYaAlamat usaha lengkap, 10 sampai 300 aksara
document_urlsYa1 sampai 6 tautan https ke dokumen pendukung
tax_numberTidakNomor pokok wajib pajak, paling banyak 60 aksara
website_urlTidakAlamat situs usaha. Bila diisi harus berupa http atau https yang sah
Dokumen dikirim sebagai tautan
Berkas dokumen tidak diunggah ke sistem ini, hanya tautannya yang dicatat, dan tautan itu wajib https. Pastikan tautan dapat dibuka peninjau selama masa peninjauan, karena tautan mati akan berujung penolakan.
Persetujuan belum berarti mode langsung
Status APPROVED menyatakan kelayakan usaha Anda diterima, bukan bahwa lingkungan sungguhan sudah terbuka. Pembukaan jalur dana nyata adalah tindakan terpisah, agar tidak ada satu keputusan yang sekaligus menilai kelayakan dan mengalirkan dana. Pantau live_enabled.
GalatSebab
400 VALIDATION_ERRORKolom wajib kurang, alamat usaha terlalu pendek, atau tautan dokumen bukan https
409 KYB_STATE_CONFLICTBerkas sedang ditinjau atau sudah disetujui, sehingga tidak dapat ditimpa
403 FORBIDDENScope kunci tidak mencukupi. Membaca butuh payments:read, mengirim butuh payments:write
429 RATE_LIMITEDPengiriman dibatasi per merchant karena setiap kiriman memicu peninjauan manusia

Kanal pembayaran

GET/api/v1/channels

Daftar kanal yang benar-benar dapat Anda pakai, sudah dikelompokkan dan mengikuti lingkungan kunci yang dipakai. Gunakan ini untuk menyusun pilihan pembayaran, agar Anda tidak menawarkan kanal yang akan ditolak saat tagihan dibuat.

curl https://api.langsunggas.example/api/v1/channels \
  -H "Authorization: Bearer lg_sk_test_xxxxxxxx"
{
  "environment": "SANDBOX",
  "total": 9,
  "data": [
    {
      "group": "VIRTUAL_ACCOUNT",
      "group_name": "Virtual Account",
      "channels": [
        {
          "code": "BNI",
          "name": "Bank BNI",
          "method_type": "VIRTUAL_ACCOUNT",
          "description": "Virtual account nominal terkunci",
          "available": true
        }
      ]
    },
    {
      "group": "QR",
      "group_name": "QRIS",
      "channels": [
        {
          "code": "QRIS",
          "name": "QRIS",
          "method_type": "QRIS",
          "description": "Pindai kode dari aplikasi apa pun yang mendukung QRIS",
          "available": true
        }
      ]
    }
  ]
}
KelompokIsi
VIRTUAL_ACCOUNTTransfer ke nomor virtual account bank
QRQRIS
OVER_THE_COUNTERBayar tunai di minimarket
EWALLETE-Money
CRYPTOUSDT di jaringan TRON dan BSC
PAYLATERBayar nanti

Kolom code adalah nilai yang Anda kirim padamethod.channel, sedangkan method_typeadalah nilai untuk method.type. Khusus kelompokCRYPTO, keduanya bernilai sama danmethod.channel tidak perlu dikirim.

Kanal kripto mati secara bawaan
Kanal rupiah menyala begitu akun dibuat, tetapiUSDT_TRC20 dan USDT_BEP20 harus dinyalakan lebih dahulu pada menu Kanal Pembayaran. Menerima pembayaran kripto adalah keputusan usaha, sehingga tidak pernah menyala tanpa tindakan Anda.
Kanal dapat berubah sewaktu-waktu
Kanal dapat dimatikan oleh Anda sendiri pada halaman pengaturan, atau ditutup operator ketika bank sedang bermasalah. Ambil daftar ini secara berkala, jangan menyalinnya menjadi daftar tetap di sisi Anda. Tambahkan?include_disabled=true bila Anda perlu melihat kanal yang sedang mati beserta alasannya.

Pada mode tersebut, setiap kanal membawa available danblocked_reason.

AlasanArti
DIMATIKAN_OPERATORDitutup operator, biasanya karena bank sedang bermasalah. Tidak dapat Anda nyalakan sendiri.
DIMATIKAN_MERCHANTAnda matikan sendiri. Nyalakan lagi dari halaman Kanal Pembayaran.
TIDAK_DIDUKUNGKanal belum tersedia pada lingkungan ini.

Menyalakan dan mematikan kanal

PATCH/api/v1/channels

Mengubah kanal yang Anda tawarkan tanpa membuka dasbor. Lingkungan tidak pernah diminta, selalu diambil dari kunci, sehingga kanal produksi tidak dapat diubah memakai kunci uji.

curl -X PATCH https://api.langsunggas.shop/api/v1/channels \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "BNI", "enabled": false }'
{
  "channel": "BNI",
  "enabled": false,
  "environment": "SANDBOX"
}
Penutupan operator tetap mengikat
Bila kami menutup sebuah kanal, menyalakannya dijawab 409 BLOCKED_BY_PLATFORM. Menawarkan kanal yang tidak dapat ditepati saat pelanggan membayar lebih merugikan daripada tidak menawarkannya. Mematikan kanal selalu boleh.
GalatSebab
404 UNKNOWN_CHANNELKode kanal tidak ada pada katalog
409 BLOCKED_BY_PLATFORMKanal sedang ditutup operator
400 UNSUPPORTED_CHANNELKanal ada pada katalog tetapi belum didukung sistem
403 FORBIDDENScope payments:write tidak dimiliki kunci

Membuat pembayaran

POST/api/v1/payments

Virtual Account:

{
  "reference_id": "PESANAN-2026-0001",
  "amount": 275000,
  "method": { "type": "VIRTUAL_ACCOUNT", "channel": "BNI" },
  "customer": { "name": "Budi Santoso" }
}

QRIS:

{
  "reference_id": "PESANAN-2026-0002",
  "amount": 45000,
  "method": { "type": "QRIS" }
}

Jawaban berhasil dengan kode 201:

{
  "id": "pay_xxxxxxxxxxxx",
  "reference_id": "PESANAN-2026-0001",
  "status": "PENDING",
  "amount": 275000,
  "currency": "IDR",
  "method": {
    "type": "VIRTUAL_ACCOUNT",
    "channel": "BNI",
    "accountNumber": "8808999979278987",
    "accountName": "XDT-InoPay"
  },
  "expires_at": "2026-08-21T03:59:21.317Z",
  "created_at": "2026-08-21T02:59:21.631Z"
}
KolomWajibKeterangan
reference_idYaNomor pesanan Anda. Harus unik untuk setiap tagihan.
amountYaNominal dalam rupiah penuh, tanpa desimal. Paling sedikit 1.000.
method.typeYaVIRTUAL_ACCOUNT atau QRIS.
method.channelUntuk VAKode bank dari GET /api/v1/channels, misalnya BNI, BRI, MANDIRI.
expires_atTidakWaktu kedaluwarsa. Bawaannya enam puluh menit.
customerTidakNama dan surel pelanggan.
metadataTidakData tambahan milik Anda, dikembalikan apa adanya.

Idempotensi

Tajuk Idempotency-Key wajib disertakan pada setiap pembuatan pembayaran. Tajuk ini mencegah tagihan ganda ketika jaringan bermasalah dan permintaan yang sama terkirim dua kali.

KeadaanYang terjadi
Kunci sama, isi samaJawaban pertama dikembalikan. Tidak ada pembayaran baru.
Kunci sama, isi berbedaDitolak dengan kode 409.
Kunci berbedaPembayaran baru dibuat.
Gunakan nomor pesanan Anda
Nilai yang baik adalah nomor pesanan itu sendiri, karena tetap sama pada setiap percobaan ulang. Nilai acak yang dibuat ulang setiap percobaan justru menghilangkan seluruh manfaatnya.

Membaca pembayaran

GET/api/v1/payments/{id}
GET/api/v1/payments?status=PAID&limit=50

Daftar memakai penomoran berbasis kursor. Sertakan nilainext_cursor dari jawaban sebelumnya untuk mengambil halaman berikutnya.

ParameterBawaanKeterangan
statussemuaSaring menurut satu status, misalnya PAID atau EXPIRED.
limit20Jumlah baris per halaman, paling banyak 100.
cursorkosongIsi dengan next_cursor dari jawaban sebelumnya.
{
  "data": [
    {
      "id": "pay_xxxxxxxxxxxx",
      "status": "PAID",
      "amount": 275000,
      "paid_at": "2026-08-21T03:01:04Z"
    }
  ],
  "has_more": true,
  "next_cursor": "cmt2xxxxxxxxxx"
}
Daftar mengikuti lingkungan kunci
Kunci uji hanya melihat tagihan uji, dan kunci sungguhan hanya melihat tagihan sungguhan. Tidak ada cara membaca lintas lingkungan.

Biaya layanan

Setiap pembayaran yang lunas dikenai biaya layanan. Nominal yang masuk ke dompet Anda adalah nominal yang dibayar pelanggan dikurangi biaya tersebut, bukan nominal penuh.

Besaran biaya ditetapkan operator dan dapat berbeda antar merchant sesuai kesepakatan. Tarif yang berlaku bagi akun Anda dapat ditanyakan kepada operator.

Biaya dikunci saat tagihan dibuat
Tarif yang dipakai adalah tarif pada saat tagihan dibuat, bukan saat pelanggan membayar. Perubahan tarif tidak pernah berlaku mundur, sehingga tagihan yang sudah terbit tetap memakai perhitungan lamanya.

Karena itu, jangan menghitung penerimaan dari nominal tagihan. Pakai saldo pada halaman Dompet atau catatan mutasi, yang sudah memperhitungkan potongan.

Saldo dan mutasi dompet

Saldo dan mutasi dapat dibaca langsung dari peladen Anda untuk keperluan rekonsiliasi, tanpa membuka dasbor.

GET/api/v1/balance
curl https://api.langsunggas.shop/api/v1/balance \
  -H "Authorization: Bearer lg_live_kunci_anda"
{
  "environment": "LIVE",
  "idr": {
    "currency": "IDR",
    "available": 542000,
    "pending": 0,
    "total": 542000,
    "minimum_payout": 10000
  },
  "usdt": {
    "currency": "USDT",
    "available": 0,
    "available_display": "0",
    "decimals": 6
  }
}
KolomArti
idr.availableDana rupiah yang dapat dicairkan saat ini
idr.pendingDana yang sedang dalam proses pencairan
idr.totalJumlah available dan pending
idr.minimum_payoutNominal pencairan terkecil yang akan diterima. Ditentukan operator dan dapat berubah
usdt.availableSaldo USDT dalam mikro-USDT, yaitu USDT dikali satu juta
usdt.available_displaySaldo USDT yang sudah dijadikan bentuk mudah dibaca
Baca minimum dari sini, jangan disalin
Nilai minimum_payout ditentukan operator dan dapat berubah. Menyalinnya menjadi tetapan di sisi Anda membuat pengajuan pencairan tertolak tanpa sebab yang jelas ketika nilainya berubah.
GET/api/v1/wallet-entries

Mutasi dompet rupiah, terbaru lebih dahulu. Parameter limit paling banyak 100 dengan bawaan 30, dan offset dipakai untuk melanjutkan ke halaman berikutnya selama has_more masih benar.

curl \
  "https://api.langsunggas.shop/api/v1/wallet-entries?limit=20&offset=0" \
  -H "Authorization: Bearer lg_live_kunci_anda"
{
  "environment": "LIVE",
  "currency": "IDR",
  "limit": 20,
  "offset": 0,
  "has_more": false,
  "data": [
    {
      "occurred_at": "2026-10-10T10:24:06.514Z",
      "description": "Pembayaran diterima",
      "direction": "CREDIT",
      "amount": 346000,
      "reference_id": "PESANAN-2026-0001"
    },
    {
      "occurred_at": "2026-10-09T08:11:02.003Z",
      "description": "Pencairan selesai",
      "direction": "DEBIT",
      "amount": 500000,
      "reference_id": null
    }
  ]
}

Nilai direction dilihat dari sudut pandang dompet Anda: CREDIT berarti dana masuk, dan DEBIT berarti dana keluar.

Angka berasal dari buku besar
Saldo dihitung dari jurnalnya, bukan dari kolom saldo yang disimpan terpisah. Karena itu jumlah seluruh mutasi selalu sama dengan idr.available, dan selisih di antara keduanya tidak mungkin terjadi.

Kedua endpoint menuntut izin payouts:read, bukan payments:read. Saldo menyangkut dana yang tersimpan, sehingga kunci yang hanya membuat tagihan tidak perlu mengetahuinya.

Pencairan dana

Saldo yang terkumpul dari pembayaran lunas dapat ditarik ke rekening bank yang sudah terverifikasi. Nominal ditahan sejak permintaan dibuat, sehingga saldo tidak dapat dipakai dua kali.

GET/api/v1/payout-banks

Kode bank tujuan tidak boleh ditebak. Ambil daftarnya dari sini, lalu pakai nilai code apa adanya saat menambah rekening tujuan. Scope yang dibutuhkanpayouts:read.

ParameterWajibKeterangan
searchTidakMenyaring berdasarkan kode atau nama bank, tanpa membedakan huruf besar kecil.
curl https://api.langsunggas.example/api/v1/payout-banks \
  -H "Authorization: Bearer lg_live_a1b2c3d4_xxxxxxxxxxxxxxxx"
{
  "environment": "LIVE",
  "data": [
    {
      "code": "BCA",
      "name": "Bank Central Asia (BCA)",
      "can_validate_name": true
    },
    {
      "code": "BNI",
      "name": "Bank Negara Indonesia (BNI)",
      "can_validate_name": true
    }
  ],
  "count": 149,
  "request_id": "req_2f2f9a1c-2a6f-4f4a-9d18-0d6f6a52e9c1"
}
Daftar mengikuti lingkungan kunci
Kunci uji mengembalikan daftar bank lingkungan uji, kunci sungguhan mengembalikan daftar sungguhan. Jumlahnya dapat berbeda. Jawaban disimpan sementara selama enam jam, sehingga perubahan di sisi penyedia tidak langsung terlihat.

Kolom can_validate_name menyatakan bahwa bank tersebut mendukung pencocokan nama pemilik rekening di sisi penyedia. Pemeriksaan itu belum aktif pada akun kami, sehingga nama pemilik tetap dicocokkan operator sebelum rekening dapat menerima dana. Perlakukan kolom ini sebagai keterangan, bukan jaminan bahwa nama sudah diperiksa.

POST/api/v1/payouts
KolomWajibKeterangan
reference_idYaPenanda milik Anda, paling panjang 128 huruf. Dipakai menolak permintaan kembar. Mengirim ulang dengan penanda dan rincian yang sama mengembalikan catatan yang sudah ada.
amountYaBilangan bulat rupiah. Batas terkecil ditetapkan operator; bawaannya 50.000.
bank_codeYaKode bank tujuan. Ambil dari GET /api/v1/payout-banks.
account_numberYaNomor rekening tujuan, hanya angka.
account_nameYaNama pemilik rekening sesuai buku tabungan.
payout_account_idTidakPenanda rekening tersimpan. Bila diisi, tiga kolom di atas tidak perlu.
metadataTidakObjek bebas, dikembalikan apa adanya pada catatan pencairan.

Rekening tujuan disertakan langsung pada permintaan. Tidak ada pendaftaran yang harus dilakukan lebih dahulu.

Periksa nomor rekening sebelum mengirim
Kami tidak dapat memastikan nama pemilik rekening cocok dengan nomornya, karena penyedia tidak menyediakan pemeriksaan itu. Dana yang terlanjur masuk ke rekening orang lain tidak dapat ditarik kembali. Ketepatan rincian sepenuhnya menjadi tanggung jawab Anda.

Kode bank tidak boleh ditebak. Ambil dari GET /api/v1/payout-banks, lalu pakai nilai code apa adanya. Kode yang tidak dikenal penyedia dijawab BANK_NOT_FOUND.

Rekening tersimpan tetap dapat dipakai
Bila Anda sudah menambahkan rekening pada halaman Dompet, cukup kirim payout_account_id tanpa rincian bank. Rekening tersimpan yang belum lolos pemeriksaan operator ditolak dengan ACCOUNT_NOT_VERIFIED.
curl -X POST https://api.langsunggas.example/api/v1/payouts \
  -H "Authorization: Bearer $LG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "PO-2026-0001",
    "amount": 250000,
    "bank_code": "BNI",
    "account_number": "1234567890",
    "account_name": "PT Contoh Sejahtera",
    "metadata": { "catatan": "penarikan mingguan" }
  }'
{
  "id": "cmt2tsz6o000k3sywuw48ja4k",
  "reference_id": "PO-2026-0001",
  "amount": 250000,
  "status": "PENDING_APPROVAL",
  "mode": "MANUAL",
  "requires_approval": true,
  "created_at": "2026-08-21T10:48:11.040Z",
  "request_id": "req_1cdd48a7-b96f-431c-b8b5-5e12f100cea9"
}

Kunci API harus memiliki scope payouts:write untuk mengajukan dan payouts:read untuk membaca.

Jawaban 201 belum berarti dana terkirim
Perhatikan requires_approval. Bila bernilai benar, pencairan menunggu persetujuan operator dan belum diteruskan ke bank. Jangan menandai penarikan sebagai selesai sebelum status menjadiCOMPLETED.

Mode pencairan ditetapkan oleh operator pada setiap merchant.

ModePerilaku
AUTOMATICDiteruskan ke bank seketika, selama nominal masih di bawah batas otomatis merchant.
MANUALSelalu menunggu persetujuan operator.

Nominal di atas batas otomatis tetap menunggu persetujuan, apa pun modenya.

StatusArti
PENDING_APPROVALMenunggu persetujuan operator. Saldo sudah ditahan.
APPROVEDDisetujui, sedang menuju bank.
PROCESSINGSudah diterima bank, menunggu penyelesaian.
COMPLETEDDana sampai di rekening tujuan. Status akhir.
FAILEDGagal. Dana dikembalikan ke saldo. Lihat failure_code.
REJECTEDDitolak operator. Dana dikembalikan ke saldo.
GET/api/v1/payouts/{id}
{
  "id": "cmt2tsz6o000k3sywuw48ja4k",
  "reference_id": "PO-2026-0001",
  "amount": 250000,
  "status": "COMPLETED",
  "mode": "MANUAL",
  "failure_code": null,
  "failure_message": null,
  "created_at": "2026-08-21T10:48:11.040Z",
  "approved_at": "2026-08-21T11:02:44.180Z",
  "sent_at": "2026-08-21T11:02:46.902Z",
  "completed_at": "2026-08-21T11:04:10.551Z",
  "request_id": "req_d9019bf2-550f-4323-9e5d-8d3482ea00c6"
}
GET/api/v1/payouts?limit=20

Daftar memakai penomoran berbasis kursor, sama seperti daftar pembayaran. Hanya pencairan pada lingkungan kunci yang dipakai yang ditampilkan.

{
  "data": [
    {
      "id": "cmt2tsz6o000k3sywuw48ja4k",
      "reference_id": "PO-2026-0001",
      "amount": 250000,
      "status": "PENDING_APPROVAL",
      "mode": "MANUAL",
      "failure_code": null,
      "failure_message": null,
      "created_at": "2026-08-21T10:48:11.040Z",
      "completed_at": null
    }
  ],
  "has_more": true,
  "next_cursor": "cmt2tb5oe00333s84ei17uis1",
  "request_id": "req_ee24eaa8-512c-4499-94d3-4251001593e8"
}
KodeHTTPArti
BELOW_MINIMUM400Nominal di bawah batas terkecil yang berlaku.
ACCOUNT_INCOMPLETE400Rincian rekening tidak lengkap dan payout_account_id juga kosong.
ACCOUNT_INVALID400Nomor rekening memuat selain angka.
BANK_NOT_FOUND400Kode bank tidak dikenal penyedia pencairan.
ACCOUNT_NOT_FOUND404Rekening tujuan tidak ditemukan.
ACCOUNT_NOT_VERIFIED422Rekening tujuan belum terverifikasi.
INSUFFICIENT_BALANCE422Saldo tersedia tidak mencukupi.
REFERENCE_CONFLICT409reference_id sudah dipakai untuk pencairan dengan nominal atau rekening berbeda.
PAYOUTS_HALTED503Pencairan sedang dihentikan sementara oleh operator.
{
  "error": {
    "code": "BELOW_MINIMUM",
    "message": "Nominal pencairan paling sedikit 50.000.",
    "request_id": "req_93462893-ed67-4223-ab02-b0d1c0f91330",
    "details": []
  }
}
Jangan memakai ulang reference_id
Penanda yang sama dengan nominal atau rekening berbeda dijawabREFERENCE_CONFLICT, bukan diterima sebagai pencairan baru. Aturan ini menjaga agar penanda yang keliru dipakai ulang tidak diam-diam dianggap sebagai permintaan yang sudah berhasil, sementara pencairan yang Anda maksud tidak pernah terjadi.

Ketika pencairan mencapai keadaan akhir, kami mengirim notifikasi ke alamat yang sama dengan notifikasi pembayaran. Anda tidak perlu menanyakan status berulang kali.

JenisKapan dikirim
payout.completedDana sampai di rekening tujuan.
payout.failedGagal di bank, atau ditolak operator. Dana kembali ke saldo.
{
  "id": "evt_6adb8f7a-f2e4-4d07-8129-5f1cf650744e",
  "type": "payout.completed",
  "created_at": "2026-08-21T12:27:32.976Z",
  "livemode": false,
  "data": {
    "payout_id": "cmt2xcq01000k3sm0uyk2nzoc",
    "reference_id": "PO-2026-0001",
    "amount": 200000,
    "status": "COMPLETED"
  }
}
{
  "id": "evt_66ac771b-5c82-4995-a199-8514ff2198f8",
  "type": "payout.failed",
  "created_at": "2026-08-21T12:27:35.309Z",
  "livemode": false,
  "data": {
    "payout_id": "cmt2xcse600193sm0l3y4kfz1",
    "reference_id": "PO-2026-0002",
    "amount": 150000,
    "status": "FAILED",
    "failure_code": "INVALID_DESTINATION",
    "failure_message": "Rekening tujuan ditolak bank."
  }
}
Membedakan gagal dan ditolak
Keduanya memakai jenis payout.failed. Periksastatus pada isi notifikasi: FAILEDberarti bank menolak, sedangkan REJECTED berarti operator tidak menyetujui. Penolakan operator selalu membawafailure_code bernilaiREJECTED_BY_OPERATOR.

Pembayaran USDT

Selain rupiah, tagihan dapat diterbitkan dalam USDT untuk pelanggan luar negeri. Setiap tagihan memperoleh alamat setor tersendiri, sehingga pembayaran langsung teridentifikasi tanpa memo atau kode unik.

Aktifkan kanal USDT lebih dahulu
Berbeda dengan kanal rupiah, kanal USDT mati secara bawaan. Nyalakan USDT TRC20 atauUSDT BEP20 pada menu Kanal Pembayaran di dasbor sebelum memakainya. Selama kanal masih mati, permintaan dijawabCHANNEL_DISABLED. Periksa kesiapannya kapan saja lewat GET /api/v1/channels, kelompokCRYPTO.
Transfer blockchain tidak dapat dibatalkan
Kirim hanya kontrak USDT resmi pada jaringan yang sama persis dengan yang tertera pada respons. Dana yang dikirim memakai token lain, jaringan lain, atau alamat yang salah ketik tidak dapat dikembalikan oleh pihak mana pun.

Metode yang tersedia: USDT_TRC20 pada jaringan TRON dan USDT_BEP20 pada jaringan BSC. Nominal ditulis pada amount dalam mikro-USDT, yaitu USDT dikali satu juta. Mata uang tagihan mengikuti metode yang dipilih, sehingga currency tidak perlu dikirim. Satuan bulat ini dipakai agar tidak ada pembulatan pecahan yang menyimpang.

POST/api/v1/payments
{
  "reference_id": "order-usdt-001",
  "amount": 25000000,
  "method": { "type": "USDT_TRC20" },
  "customer": { "email": "buyer@example.com" }
}

Nilai 25000000 di atas berarti 25 USDT. Respons memuat alamat setor beserta keterangan jaringan:

{
  "id": "pay_...",
  "reference_id": "order-usdt-001",
  "status": "PENDING",
  "amount": 25000000,
  "currency": "USDT",
  "method": {
    "type": "USDT_TRC20",
    "channel": null,
    "chain": "TRON",
    "deposit_address": "T...",
    "token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
    "amount_atomic": "25000000",
    "decimals": 6,
    "confirmations_required": 20
  },
  "expires_at": "2026-01-01T10:00:00.000Z"
}

Kode kanal untuk kedua metode sama dengan nama metodenya, yaituUSDT_TRC20 dan USDT_BEP20, dan keduanya berada pada kelompok CRYPTO saat daftar kanal dibaca.

JaringanDesimal tokenKonfirmasiBentuk alamat
TRON (TRC20)620 blokdiawali huruf T
BSC (BEP20)1815 blokdiawali 0x

Tagihan berpindah ke PAID setelah jumlah konfirmasi terpenuhi, lalu notifikasipayment.paid dikirim seperti pembayaran rupiah. Sebelum konfirmasi terpenuhi, dana yang sudah terlihat masuk belum diakui sebagai lunas.

Kurang bayar dan lebih bayar
Nominal yang kurang dari tagihan tidak menandai tagihan lunas dan menunggu penyelesaian manual operator. Nominal berlebih dicatat apa adanya; kelebihannya tidak dikembalikan otomatis.

Saldo USDT tercatat terpisah dari saldo rupiah dan ditarik lewat menu USDT pada dasbor. Alamat tujuan penarikan wajib didaftarkan lebih dahulu dan baru dapat dipakai setelah masa tunggu 24 jam berlalu, sehingga alamat asing yang disisipkan pihak lain masih sempat diketahui pemilik akun.

Penarikan
Penarikan USDT tersedia lewat dasbor maupun API. Rinciannya ada pada bagian Penarikan USDT.

Penarikan USDT

Saldo USDT ditarik ke alamat milik merchant sendiri. Alurnya dua langkah: daftarkan alamat lebih dahulu, lalu ajukan penarikan ke alamat yang sudah aktif.

Masa tunggu alamat tidak dapat dilewati
Alamat baru hanya dapat dipakai setelah 24 jam. Jeda ini ada agar kunci API yang bocor tidak dapat langsung memindahkan dana ke alamat asing. Rencanakan pendaftaran alamat jauh sebelum penarikan pertama dibutuhkan.

Mendaftarkan alamat tujuan:

POST/api/v1/crypto-withdrawals/addresses
curl -X POST https://api.langsunggas.shop/api/v1/crypto-withdrawals/addresses \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "TRON",
    "address": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }'
{
  "id": "cwa_...",
  "chain": "TRON",
  "address": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "active_at": "2026-01-02T09:00:00.000Z",
  "cooldown_hours": 24
}

Membaca daftar alamat tujuan:

GET/api/v1/crypto-withdrawals/addresses

Medan usable bernilai benar hanya bila masa tunggu alamat sudah lewat. Alamat yang sudah dicabut tidak ikut terbawa. Izin yang dibutuhkan crypto-withdrawals:read.

{
  "data": [
    {
      "id": "cwa_...",
      "chain": "TRON",
      "address": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "status": "ACTIVE",
      "usable": true,
      "active_at": "2026-01-02T09:00:00.000Z",
      "created_at": "2026-01-01T09:00:00.000Z"
    }
  ],
  "request_id": "req_xxxxxxxx"
}

Mengajukan penarikan:

POST/api/v1/crypto-withdrawals
curl -X POST https://api.langsunggas.shop/api/v1/crypto-withdrawals \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "reference_id": "wd-2026-0001",
    "chain": "TRON",
    "address": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "amount": "25.5"
  }'
{
  "id": "cw_...",
  "reference_id": "wd-2026-0001",
  "chain": "TRON",
  "amount": "25.500000",
  "amount_micro": "25500000",
  "status": "APPROVED",
  "requires_approval": false,
  "environment": "SANDBOX",
  "idempotent_replay": false,
  "created_at": "2026-01-02T10:00:00.000Z"
}
reference_id adalah kunci idempotensi
Permintaan ulang dengan reference_id yang sama dan nominal sama mengembalikan penarikan yang sudah ada, ditandaiidempotent_replay: true dengan status 200. Selalu kirim ulang memakai penanda yang sama ketika jaringan terputus; penanda baru berarti penarikan kedua.

Status PENDING_APPROVAL berarti nominalnya melampaui ambang dan menunggu persetujuan operator. StatusAPPROVED berarti penarikan masuk antrean pengiriman ke jaringan.

Membaca satu penarikan beserta jejak lengkapnya:

GET/api/v1/crypto-withdrawals/{id}

Pengenal yang diterima berupa id maupunreference_id, sehingga penarikan tetap dapat ditelusuri walau jawaban permintaan pertama tidak pernah diterima.

{
  "id": "cw_...",
  "reference_id": "wd-2026-0001",
  "status": "CONFIRMED",
  "amount": "25.500000",
  "tx_hash": "0x...",
  "failure_code": null,
  "events": [
    {
      "from_status": null,
      "to_status": "APPROVED",
      "message": "Penarikan diajukan dan masuk antrean pengiriman.",
      "actor": "api",
      "created_at": "2026-01-02T10:00:00.000Z"
    },
    {
      "from_status": "APPROVED",
      "to_status": "BROADCASTING",
      "message": "Penarikan sedang disusun dan disiarkan ke jaringan TRON.",
      "actor": "system",
      "created_at": "2026-01-02T10:02:00.000Z"
    },
    {
      "from_status": "BROADCASTING",
      "to_status": "CONFIRMED",
      "message": "Transaksi berhasil disiarkan ke jaringan.",
      "actor": "system",
      "tx_hash": "0x...",
      "created_at": "2026-01-02T10:02:30.000Z"
    }
  ]
}

Daftar penarikan dibaca lewatGET /api/v1/crypto-withdrawals dengan penyaringstatus, limit, dancursor.

StatusArtiTindakan Anda
PENDING_APPROVALMenunggu persetujuan operatorTunggu, dana sudah ditahan
APPROVEDAntre dikirim ke jaringanTunggu
BROADCASTINGSedang disiarkanTunggu, jangan ajukan ulang
CONFIRMEDTerkirim, tx_hash tersediaSelesai
REJECTEDDitolak operatorBaca failure_code dan events
failure_codePenyebab
INSUFFICIENT_BALANCESaldo USDT tidak mencukupi
BELOW_MINIMUMNominal di bawah batas minimum
ADDRESS_IN_COOLDOWNAlamat masih dalam masa tunggu
ADDRESS_NOT_FOUNDAlamat belum didaftarkan pada jaringan itu
REFERENCE_CONFLICTreference_id dipakai ulang dengan nominal berbeda
MERCHANT_DAILY_LIMITMelebihi batas penarikan harian merchant
GAS_EXHAUSTEDGas jaringan habis; dana tetap ditahan dan dicoba ulang
WITHDRAWALS_HALTEDPenarikan dihentikan sementara operator
Pengiriman ke jaringan
Penarikan berhenti pada status APPROVED selama operator belum menyalakan jalur pengiriman kripto. Dana tetap ditahan dan tidak hilang, tetapi transaksi belum disiarkan.

Notifikasi

Ketika status pembayaran berubah, kami mengirim pemberitahuan ke alamat yang Anda daftarkan pada halaman Notifikasi.

{
  "id": "evt_xxxxxxxxxxxx",
  "type": "payment.paid",
  "created_at": "2026-08-21T03:01:05Z",
  "livemode": true,
  "data": {
    "payment": {
      "id": "pay_xxxxxxxxxxxx",
      "reference_id": "PESANAN-2026-0001",
      "status": "PAID",
      "amount": 275000,
      "paid_at": "2026-08-21T03:01:04Z"
    }
  }
}

Kolom type menyatakan peristiwa yang terjadi. Abaikan jenis yang belum Anda kenali, karena daftar ini dapat bertambah.

JenisKapan dikirim
payment.pendingTagihan siap dibayar pelanggan
payment.paidPembayaran diterima
payment.expiredTagihan melewati batas waktu tanpa dibayar
payout.completedPencairan sampai di rekening tujuan
payout.failedPencairan gagal atau ditolak, dana kembali ke saldo
account.kyb.approvedBerkas kelayakan usaha disetujui peninjau
account.kyb.rejectedBerkas kelayakan usaha ditolak, lihat rejection_reason
account.status.changedStatus akun berubah, termasuk pembukaan mode sungguhan dan penangguhan

Tiga jenis account.* menyangkut akun, bukan satu transaksi. Berkas kelayakan mengikat badan usaha dan penangguhan mematikan kedua lingkungan sekaligus, sehingga peristiwa ini dikirim ke setiap alamat notifikasi aktif yang Anda miliki, termasuk alamat uji. Karena itu livemode pada peristiwa akun menyatakan alamat mana yang menerimanya, bukan lingkungan tempat perubahan terjadi.

account.kyb.approved belum berarti boleh pakai kunci sungguhan
Peristiwa ini membawa live_enabled bernilai salah, karena persetujuan kelayakan dan pembukaan jalur dana nyata adalah dua keputusan terpisah. Tunggu account.status.changed dengan live_enabled bernilai benar sebelum beralih ke kunci sungguhan.
JenisKolom pada data
account.kyb.approvedkyb_status, rejection_reason (null), live_enabled
account.kyb.rejectedkyb_status, rejection_reason, live_enabled
account.status.changedprevious_status, status, live_enabled, suspended_reason

Isi data berbeda menurut jenisnya. Notifikasi pembayaran membawa payment_id, sedangkan notifikasi pencairan membawa payout_id,reference_id, amount, danstatus.

Periksa livemode
Alamat notifikasi didaftarkan terpisah untuk lingkungan uji dan sungguhan, dan masing-masing memiliki rahasianya sendiri. Kolomlivemode bernilai benar hanya pada pembayaran dengan dana sungguhan. Periksa kolom ini sebelum memenuhi pesanan, agar pembayaran uji tidak pernah dianggap lunas.
TajukIsi
X-Webhook-IdPenanda unik pengiriman
X-Webhook-TimestampWaktu penandatanganan dalam milidetik
X-Webhook-SignatureTanda tangan HMAC SHA-256
X-Webhook-AttemptNomor percobaan pengiriman

Pengiriman dianggap berhasil hanya bila sistem Anda menjawab dengan kode 2xx dalam sepuluh detik. Bila gagal, pengiriman diulang dengan jeda menaik: satu menit, lima menit, tiga puluh menit, dua jam, delapan jam, lalu dua puluh empat jam.

Notifikasi yang berhenti dicoba tidak hilang
Setelah percobaan keenam gagal, pengiriman berhenti dan ditandai berhenti dicoba. Catatannya tetap tersimpan. Bila alamat penerima Anda sempat bermasalah, perbaiki dahulu, lalu kirim ulang dari halaman Notifikasi pada portal. Tersedia pengiriman ulang satu per satu maupun sekaligus untuk seluruh notifikasi yang berhenti dicoba.
Notifikasi dapat tiba lebih dari sekali
Pastikan penanganan di sisi Anda aman terhadap pengulangan. Periksa apakah pesanan sudah pernah ditandai lunas sebelum memprosesnya lagi, agar satu pembayaran tidak terhitung dua kali.

Mengatur notifikasi lewat API

Alamat notifikasi dapat disetel, diperiksa, dan rahasianya diputar tanpa membuka halaman web. Alamat dan rahasia terpisah per lingkungan, dan lingkungannya selalu diambil dari kunci yang Anda pakai memanggil.

Kunci uji tidak dapat mengubah alamat produksi
Lingkungan tidak dapat diminta lewat badan permintaan. Kunci lg_sandbox_ hanya menyentuh alamat uji, dan kunci lg_live_ hanya menyentuh alamat produksi.
PUT/api/v1/webhook-endpoint

Menyetel alamat. Dipakai juga untuk memperbaruinya di kemudian hari.

curl -X PUT https://api.langsunggas.shop/api/v1/webhook-endpoint \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://usaha-anda.com/notifikasi" }'
{
  "endpoint": {
    "id": "whe_xxxxxxxxxxxx",
    "url": "https://usaha-anda.com/notifikasi",
    "is_active": true,
    "environment": "SANDBOX"
  },
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxx",
  "request_id": "req_xxxxxxxx"
}
Rahasia hanya muncul sekali
Kolom secret hanya ada pada jawaban saat alamat pertama kali dibuat. Simpan nilainya saat itu juga; nilai itu tidak dapat dibaca kembali lewat pembacaan biasa. Memperbarui alamat tidak mengganti rahasia, sehingga pemeriksaan tanda tangan yang sudah Anda pasang tetap berlaku.

Alamat yang mengarah ke jaringan internal, memakai protokol selain http dan https, atau memuat nama pengguna dan sandi akan ditolak. Pembatasan itu menjaga agar peladen kami tidak dapat disuruh mengetuk jaringan pribadi memakai alamat yang Anda tentukan.

GET/api/v1/webhook-endpoint

Memeriksa keadaan alamat beserta ringkasan pengiriman, berguna untuk memantau apakah sistem Anda sanggup menerima notifikasi.

{
  "environment": "SANDBOX",
  "endpoint": {
    "id": "whe_xxxxxxxxxxxx",
    "url": "https://usaha-anda.com/notifikasi",
    "is_active": true,
    "created_at": "2026-10-10T10:12:32.003Z"
  },
  "secret_available": true,
  "deliveries": {
    "total": 128,
    "succeeded": 124,
    "failed": 3,
    "pending": 1,
    "dead_letter": 0
  }
}

Nilai dead_letter yang bertambah berarti ada notifikasi yang berhenti dicoba setelah semua percobaan ulang habis. Itu tanda alamat Anda sedang bermasalah dan perlu diperiksa.

POST/api/v1/webhook-endpoint

Kolom action menerima rotate_secret, enable, atau disable.

curl -X POST https://api.langsunggas.shop/api/v1/webhook-endpoint \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "action": "rotate_secret" }'
Putaran rahasia berlaku seketika
Rahasia lama berhenti berlaku pada saat yang sama. Siapkan layanan Anda menerima dua rahasia sejenak, atau jadwalkan putaran pada waktu yang sepi, agar tidak ada notifikasi yang tertolak karena tanda tangannya dianggap tidak sah.
KodeArti
400 PRIVATE_ADDRESSAlamat mengarah ke jaringan internal
400 PROTOCOL_NOT_ALLOWEDProtokol bukan http atau https
400 PORT_NOT_ALLOWEDNomor porta tidak diizinkan
400 RESOLUTION_FAILEDNama domain tidak dapat diterjemahkan
404 NOT_FOUNDAlamat belum diatur pada lingkungan ini
403 FORBIDDENMembaca menuntut payments:read, mengubah menuntut payments:write

Riwayat notifikasi

GET/api/v1/webhook-deliveries
POST/api/v1/webhook-deliveries

Melihat notifikasi mana yang gagal sampai, beserta sebabnya. Tanpa ini, pembayaran tampak hilang di sisi Anda padahal dana sudah masuk, dan satu-satunya bukti sebabnya hanya terbaca pada dasbor.

curl "https://api.langsunggas.shop/api/v1/webhook-deliveries?status=DEAD_LETTER&limit=10" \
  -H "Authorization: Bearer lg_sandbox_kunci_anda"
{
  "environment": "SANDBOX",
  "data": [
    {
      "id": "whd_xxxxxxxxxxxx",
      "event_id": "evt_xxxxxxxxxxxx",
      "event_type": "payment.paid",
      "attempt": 5,
      "status": "DEAD_LETTER",
      "response_code": 500,
      "error_message": "Peladen Anda menjawab 500.",
      "next_retry_at": null,
      "created_at": "2026-10-15T06:33:47.442Z"
    }
  ],
  "has_more": false,
  "dead_letter_count": 1
}
ParameterKeterangan
limit1 sampai 100, bawaan 30
statusPENDING, SUCCEEDED, FAILED, atau DEAD_LETTER
starting_afterPengenal pengiriman; mengambil yang lebih tua darinya

dead_letter_count adalah jumlah notifikasi yang sudah berhenti dicoba. Angka itulah yang menentukan perlu tidaknya pengiriman ulang, sehingga Anda tidak perlu menyaring seluruh riwayat untuk mengetahuinya.

curl -X POST https://api.langsunggas.shop/api/v1/webhook-deliveries \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "action": "redeliver", "all": true }'
{
  "action": "redeliver",
  "scheduled": 3,
  "environment": "SANDBOX"
}
Isi delivery_id atau all, jangan keduanya
Permintaan yang memuat keduanya ditolak. Menebak mana yang Anda maksud berisiko mengirim ratusan notifikasi yang tidak diminta. Batas sekali borong adalah 200 pengiriman.
GalatSebab
404 NOT_FOUNDPengiriman tidak ada, atau bukan milik lingkungan kunci ini
409 NOT_DEAD_LETTERPengiriman masih dijadwalkan ulang otomatis, belum perlu dipaksa

Pengiriman ulang hanya menjadwalkan; kiriman sesungguhnya tetap dilakukan pekerja latar. scheduled bernilai nol berarti notifikasi itu sudah terjadwal sebelumnya, bukan gagal.

Simulasi pembayaran

POST/api/v1/simulate

Melunasi tagihan uji tanpa dana sungguhan, sehingga alur pembayaran Anda dapat diuji sampai tuntas tanpa membuka dasbor. Tanpa ini, tagihan dapat dibuat tetapi tidak ada cara membuatnya lunas.

curl -X POST https://api.langsunggas.shop/api/v1/simulate \
  -H "Authorization: Bearer lg_sandbox_kunci_anda" \
  -H "Content-Type: application/json" \
  -d '{ "reference_id": "pesanan-001", "apply_directly": true }'
{
  "payment_id": "pay_xxxxxxxxxxxx",
  "reference_id": "pesanan-001",
  "amount": 100000,
  "method": "LOCAL",
  "message": "Pembayaran disimulasikan dan tagihan telah dilunasi."
}
Kunci sungguhan ditolak di sini
Jalur ini membuat tagihan lunas tanpa uang, sehingga hanya kunci lingkungan uji yang diterima. Kunci sungguhan dijawab 403 LIVE_KEY_FORBIDDEN.
KolomWajibKeterangan
reference_idYaNomor pesanan tagihan yang hendak dilunasi
amountTidakNominal yang dibayarkan. Bila kosong, memakai nominal tagihan
apply_directlyTidakBenar berarti langsung dilunasi tanpa menunggu notifikasi penyedia

Kolom method pada jawaban bernilai LOCAL bila tagihan sudah lunas saat jawaban terbit, atau PROVIDER bila status baru berubah setelah notifikasi penyedia tiba. Pakai apply_directly ketika alamat notifikasi Anda belum dapat dijangkau dari luar, misalnya saat masih berjalan di mesin lokal.

Verifikasi tanda tangan

Wajib dilakukan
Tanpa pemeriksaan tanda tangan, siapa pun yang mengetahui alamat notifikasi Anda dapat mengirim pemberitahuan palsu dan membuat sistem menganggap pesanan sudah dibayar. Ini kesalahan integrasi paling sering terjadi dan paling merugikan.

Contoh dengan Node.js:

import { createHmac, timingSafeEqual } from "node:crypto";

// Ambil isi mentah, jangan diuraikan lebih dulu.
const rawBody = await request.text();
const timestamp = request.headers.get("x-webhook-timestamp");
const signature = request.headers.get("x-webhook-signature");

// Tolak notifikasi lama untuk mencegah pengiriman ulang.
if (Math.abs(Date.now() - Number(timestamp)) / 1000 > 300) {
  return new Response(null, { status: 400 });
}

const diharapkan = createHmac("sha256", RAHASIA_ANDA)
  .update(timestamp + "." + rawBody)
  .digest("hex");

const a = Buffer.from(diharapkan);
const b = Buffer.from(signature);

if (a.length !== b.length || timingSafeEqual(a, b) === false) {
  return new Response(null, { status: 400 });
}

const event = JSON.parse(rawBody);
return new Response(null, { status: 200 });

Contoh dengan PHP:

<?php
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'];
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];

if (abs(round(microtime(true) * 1000) - (int)$timestamp) / 1000 > 300) {
    http_response_code(400);
    exit;
}

$diharapkan = hash_hmac('sha256', $timestamp . '.' . $rawBody, RAHASIA_ANDA);

if (!hash_equals($diharapkan, $signature)) {
    http_response_code(400);
    exit;
}

$event = json_decode($rawBody, true);
http_response_code(200);
Tanda tangani isi mentah
Yang ditandatangani adalah isi mentah permintaan, bukan hasil penguraian JSON. Menguraikan lalu menyusun ulang JSON akan mengubah susunan karakter dan membuat tanda tangan tidak cocok. Uraikan JSON setelah tanda tangan terbukti sah.

Status pembayaran

CREATED  ->  PENDING  ->  PAID
                 |
                 +-->  EXPIRED / FAILED / CANCELLED
StatusArti
CREATEDTagihan dibuat, menunggu penyedia
PENDINGSiap dibayar pelanggan
PAIDSudah dibayar
EXPIREDMelewati batas waktu
FAILEDGagal dibuat
CANCELLEDDibatalkan
Status tidak pernah mundur
Pembayaran yang sudah lunas tidak akan kembali menjadi menunggu, dan yang sudah kedaluwarsa tidak dapat menjadi lunas.

Kode kesalahan

Seluruh kesalahan memakai bentuk yang sama.

{
  "error": {
    "code": "INVALID_AMOUNT",
    "message": "Nominal di luar batas yang diizinkan",
    "request_id": "req_xxxxxxxx",
    "details": []
  }
}
HTTPKodeArti
401UNAUTHORIZEDKunci tidak ada atau tidak sah
403FORBIDDENCakupan kunci tidak mencukupi
403MERCHANT_NOT_ACTIVEAkun belum aktif atau dihentikan
400VALIDATION_ERRORIsi permintaan tidak sesuai
400INVALID_AMOUNTNominal di luar batas
409IDEMPOTENCY_CONFLICTKunci idempotensi dipakai dengan isi berbeda
404RESOURCE_NOT_FOUNDData tidak ditemukan
429RATE_LIMITEDPermintaan terlalu sering
502PROVIDER_UNAVAILABLEPenyedia sedang bermasalah
400UNKNOWN_CHANNELKode kanal tidak dikenal
422CHANNEL_DISABLEDKanal dimatikan operator atau oleh Anda sendiri
502PROVIDER_STATE_UNKNOWNStatus belum dapat dipastikan
400API_KEY_ERRORPermintaan kunci API ditolak, misalnya nama kunci tidak sah
409NO_OWNERMerchant tidak punya pemilik aktif, kunci tidak dapat diterbitkan
429TOO_MANYPengiriman ulang notifikasi terlalu banyak dalam satu permintaan
400ADDRESS_INVALIDAlamat kripto tidak sesuai bentuk rantai yang dipilih
409ADDRESS_REVOKEDAlamat tujuan sudah dicabut dan tidak dapat dipakai lagi
409PLATFORM_DAILY_LIMITBatas penarikan kripto harian seluruh platform tercapai
409WITHDRAWAL_REJECTEDPenarikan kripto ditolak aturan lain, baca pesannya
500INTERNAL_ERRORKesalahan tak terduga di sisi kami, aman untuk dicoba ulang
Menangani PROVIDER_STATE_UNKNOWN
Kode ini berarti permintaan mungkin sudah diproses penyedia, tetapi jawabannya tidak sampai kepada kami. Jangan membuat pembayaran baru dengan kunci idempotensi berbeda, karena berisiko menagih pelanggan dua kali. Kirim ulang permintaan yang sama dengan kunci idempotensi yang sama, atau periksa statusnya lewat daftar pembayaran.

Batas permintaan

JenisBatas
Membuat pembayaran60 per menit untuk setiap kunci
Membaca data300 per menit untuk setiap kunci
Mengajukan pencairan20 per menit untuk setiap kunci
Pendaftaran merchant lewat API5 per jam untuk setiap alamat IP
Mengelola kunci, notifikasi, dan KYB300 per menit untuk setiap kunci
Pengiriman ulang notifikasi20 per menit untuk setiap kunci

Melampaui batas menghasilkan kode 429. Tunggu sesuai tajukRetry-After sebelum mencoba lagi.

Praktik yang disarankan

Jangan percaya pengalihan halaman
Pengalihan halaman peramban setelah pembayaran bukan bukti bahwa pelanggan sudah membayar. Siapa pun dapat membuka alamat halaman sukses secara langsung. Jadikan notifikasi sebagai satu-satunya sumber kebenaran status pembayaran.
  • Simpan nilai id yang kami kembalikan, bukan hanya nomor pesanan Anda sendiri.
  • Sediakan pemeriksaan berkala sebagai cadangan bila notifikasi tidak kunjung tiba, misalnya karena peladen Anda sempat mati.
  • Catat setiap notifikasi yang masuk beserta hasil pemeriksaan tanda tangannya, agar penelusuran masalah lebih mudah.
  • Uji seluruh alur pada lingkungan sandbox, termasuk keadaan gagal, sebelum beralih ke produksi.