• Модуль интеграций
  • Сообщество
  • Блог
Документация
Плагины и интеграцииВсе расширения для Medusa от сообществаСтартерыЗапускайте проекты быстрее с готовыми решениями
ЭкспертыПодберите специалиста для разработки и развития вашего проекта на MedusaКейсыПосмотрите примеры Medusa в продакшене и успешные внедрения
Т-Касса от Т-Банка для Medusa: приём платежей для РФ

16 октября 2026 г. · Продукт

Gorgo снижает затраты на адаптацию Medusa к локальным рынкам.

Мы разрабатываем плагины интеграции, осуществляем поддержку и развиваем сообщество разработчиков на Medusa в Telegram.

  • Ресурсы Medusa
  • Плагины и интеграции
  • Модуль интеграций
  • Стартеры
  • Эксперты
  • Кейсы
  • Medusa Чат в Telegram
  • Medusa Новости в Telegram
  • Документация Gorgo
  • Связаться с нами
  • TelegramGitHub
Плагины
D

Doit

doit.id payment gateway (QRIS, Virtual Account, bank transfer) for Medusa v2 — by Haus Kayu

npm install medusa-payment-doit
Категория
Платежи
Создано
Hauskayu
Версия
0.1.3
Последнее обновление
1 день назад
Ежемесячные загрузки
Загрузка данных
Звезды на Github
1
npmNPMGitHubGithub

PG Medusa Doit.id

Terima QRIS, VA, dan transfer di Medusa lewat doit.id. Dibuat dan dipakai sendiri oleh Haus Kayu.
Accept QRIS, Virtual Account and bank transfer in Medusa v2 through doit.id. Built and used in production by Haus Kayu.

Community plugin, not an official doit.id product. Independent open source (MIT), not affiliated with or endorsed by doit.id. Plugin komunitas, bukan produk resmi doit.id. Open source independen (MIT), tidak berafiliasi dengan doit.id.

English · Bahasa Indonesia

Admin dashboardStorefront (Next.js Starter)

More: settings · withdrawals · needs action · Bahasa Indonesia UI · VA on mobile · order confirmed


English

Features

  • Payment provider for Medusa 2.19+: QRIS, Virtual Account (8 banks), the doit.id hosted page (), and bank transfer (, see Limitations). Amounts are whole rupiah, exactly as Medusa stores them.
  • Credentials in the Admin, encrypted with AES-256-GCM. Falls back to provider options or env vars.
  • Webhooks: signature checked on the raw body (HMAC-SHA256, ±5 min), deduplicated by event id in the database, processed by the worker with retries. A webhook is never trusted alone: Medusa re-reads the payment from doit.id before capturing.
  • Reconciliation job every 15 minutes: completes orders whose webhook was lost and corrects the ledger.
  • Ledger and "needs action" list for double payments (), payments after cancellation (), refund mismatches and failed webhooks. Nothing is refunded automatically.
  • Admin dashboard: totals, fees, net, an estimated balance, a daily chart, transactions with CSV export, withdrawals (manual or settlement CSV import). English and Indonesian.
  • Storefront API for a "waiting for payment" screen, plus a drop-in example for the Medusa Next.js Starter: method picker with fees, QRIS code, VA number with a copy button, countdown, polling and the hosted-page option.
  • Fees on the order: records the method, the fee and who paid it.
  • Invoice reuse: if a shopper switches method and comes back, the invoice that is still open is used again.

Requirements

  • Medusa 2.19 or newer 2.x (tested in CI on 2.19.0 and 2.22.0), Node 20.19+ or 22.12+
  • Redis for the event bus, workflow engine and locking (strongly recommended; webhooks are processed in the worker)
  • A doit.id account: key for the sandbox, for production

Installation

:

Then run the migrations and set the encryption key:

Finally enable doit.id () as a payment provider in your IDR region.

Configuration

Open Settings → doit.id in the Admin:

  1. Paste the API key ( or ) and the webhook signing secret. Both are stored encrypted and shown masked. The mode (test/live) follows the key prefix.
  2. Pick the default method (QRIS, a VA bank, ), how long invoices stay valid, and the dashboard time zone.
  3. Click Test connection: it lists the methods active on your doit.id account.

Lookup order for credentials: Admin → provider (, ) → env (, ). The secret always comes from the same source as the key.

Env varPurpose
64 hex chars (). Required to store credentials in the Admin. Same value on server and worker; back it up with the database.
, Fallback credentials when nothing is saved in the Admin.
Schedule of the reconciliation job (default ).

If the encryption key is missing, malformed or different from the one used to save the credentials, the plugin keeps running: the Admin shows "Kunci enkripsi tidak valid, masukkan ulang kredensial." and payments fall back to options or env. After changing the key, re-enter the API key and the secret.

One doit.id app per store

doit.id's payment list and webhooks cover everything on an API key. Use one doit.id app (key) per store, and a separate app with a key for testing and staging. Don't share a key between two shops, or between production and a staging copy.

The plugin still guards against sharing. Every payment it creates carries (this Medusa's store id) and a reference like . Reconciliation (webhooks, the 15-minute job, refunds) only touches payments with this store's marker. These are ignored: no ledger row, never listed under "Needs action":

  • payments of other apps or stores on the same key (e.g. references like , portal payment links);
  • payments of the other mode (a test payment while a live key is in use, and the other way round);
  • payments created before Settings → doit.id → Reconciliation → "Ignore payments before". The default is when the API key was first saved in the Admin.

Payments made by plugin 0.1.0/0.1.1 (no marker yet) are still reconciled while their Medusa session exists. Items that earlier versions raised for payments that are not this store's are dismissed automatically by the next job run.

Ledger rows that earlier versions wrote for such payments are cleaned up once, by the first job run after upgrading to 0.1.3. Rows of other apps or stores, of the other mode, or old rows whose Medusa session is gone are marked excluded, never deleted. Rows linked to an order are always kept. Excluded rows are left out of every dashboard figure; tick "Show excluded" in Transactions (or on the API and the CSV export) to see them with the reason. The optional reference prefix (same page) only makes your payments easy to recognise in the doit.id portal.

Webhook setup

  1. In the doit.id portal, set the webhook URL to (the Admin settings page shows it with a copy button).
  2. Copy the signing secret from the portal into Settings → doit.id.
  3. Send a test event from the portal; it is answered .

Requests with a missing or wrong , or a timestamp more than 5 minutes off, get . Redeliveries are answered and not processed twice. If a webhook is lost, the 15-minute job completes the order.

Fees: who pays them

doit.id sets the fee payer per method in the doit.id portal (Payment Methods). The API has no parameter for it, so the plugin cannot change it. QRIS fees are always paid by the merchant (Bank Indonesia rule).

  • : the shopper pays the order total; the fee is deducted from your settlement.
  • : the shopper pays the order total plus the fee (). Medusa still captures the order total ().

To keep this transparent, the plugin:

  • shows each method's fee and the total to pay before the shopper picks it (, used by the example method picker), and shows methods outside their amount limits as disabled;
  • writes when the order is placed: , , , , , , , , , , ;
  • stores , , and in the ledger; the dashboard reports gross, fee and net.

Storefront integration

The provider works with any storefront: create a payment session for and optionally pass . The session data contains , , , , and . The order is created when the payment is paid, so the storefront waits for it with two endpoints (publishable key required):

EndpointReturns
Active methods with fee and total for this cart, the Admin default. Cached 5 min.
Payment status and : (poll again after ), (paid: complete the cart), , . when the QR image ran out before the invoice: send the shopper to .

Access rules: the cart must belong to the publishable key's sales channel, and a registered customer's cart answers only to that customer (otherwise ). Only display fields are returned. Rate limits: 40/min per cart and 120/min per IP for the status, 20/min per cart and 30/min per IP for methods (in memory, per server process). doit.id is read at most once per 10 seconds per payment.

Medusa Next.js Starter: copy-in components and a patch in , also shipped in the npm package ().

Switching method creates a new invoice. Switching back to a method whose invoice is still open (same cart, same amount, VA with ≥5 minutes left, QRIS whose QR is still issued) reuses that invoice.

FAQ

Sandbox payments stay pending. In the sandbox, payments are marked paid with the "Simulasikan lunas" button in the doit.id portal. There is no API for it.

Webhooks don't arrive on localhost. doit.id cannot reach your machine. The storefront polling endpoint and the 15-minute job complete orders anyway; use a tunnel if you want to test webhooks.

The shopper paid but the order is missing. Check doit.id → Needs action in the Admin. A payment whose cart or session no longer exists, or arrived after the order was canceled, is listed there for you to fulfil or refund.

The dashboard balance differs from doit.id. It is an estimate (net of successful payments − refunds − withdrawals). doit.id has no balance API; check the portal before withdrawing.

I see payments of another app or a staging store in doit.id, but not in the dashboard. That is intended: only payments carrying this store's marker are reconciled. Use one doit.id app per store (see above).

Can I use another currency? No. doit.id charges in IDR only, and sessions in other currencies are rejected.

Limitations

  • IDR only, whole rupiah.
  • No balance or settlement API at doit.id: the balance is estimated; settlements are entered manually or imported from CSV.
  • rail not yet tested: the sandbox rejects it (it needs a live key and a Mutasi Rekening subscription). The code follows the documentation but is unverified.
  • Old invoices cannot be cancelled: doit.id has no cancel endpoint. An invoice the shopper abandoned stays payable until it expires; if it is paid anyway, it shows up under "Needs action".
  • (paid twice) is captured once and flagged; the excess has to be refunded by hand.
  • The sandbox accepts refunds above the remaining amount; the plugin checks this itself.
  • Storefront rate limits are kept per process.

Security

  • Credentials are encrypted at rest (AES-256-GCM, column-bound) and never returned by the API (only the last 4 characters).
  • Webhook signatures are verified on the raw body in constant time; payment status is always re-read from doit.id before an order is captured.
  • Admin endpoints require an admin session; store endpoints check cart ownership and are rate limited.
  • No tracking: the Admin credit is a plain link; nothing is added to your storefront.

Report vulnerabilities privately: see SECURITY.md.


Bahasa Indonesia

Fitur

  • Payment provider untuk Medusa 2.19+: QRIS, Virtual Account (8 bank), halaman bayar hosted doit.id (), dan transfer bank (, lihat Batasan). Nominal rupiah utuh, persis seperti disimpan Medusa.
  • Kredensial di Admin, terenkripsi AES-256-GCM, dengan cadangan dari options provider atau env.
  • Webhook: signature dicek pada raw body (HMAC-SHA256, ±5 menit), dedupe per event id di database, diproses worker dengan retry. Webhook tidak pernah dipercaya sendirian: Medusa membaca ulang status dari doit.id sebelum capture.
  • Job rekonsiliasi tiap 15 menit: menyelesaikan order yang webhook-nya hilang dan membetulkan ledger.
  • Ledger dan daftar "perlu tindakan" untuk bayar ganda (), bayar setelah dibatalkan (), refund yang tidak cocok, dan webhook gagal. Tidak ada refund otomatis.
  • Dashboard Admin: total, fee, net, saldo estimasi, grafik harian, transaksi dengan export CSV, withdraw (manual atau import CSV settlement). Bahasa Inggris dan Indonesia.
  • Store API untuk layar "menunggu pembayaran", plus contoh siap pakai untuk Medusa Next.js Starter: pilih metode beserta biayanya, kode QRIS, nomor VA dengan tombol salin, hitung mundur, polling, dan opsi halaman hosted.
  • Fee tercatat di order: berisi metode, biaya, dan siapa yang menanggung.
  • Pakai ulang invoice: kalau pembeli ganti metode lalu kembali, invoice yang masih aktif dipakai lagi.

Kebutuhan

  • Medusa 2.19 atau 2.x yang lebih baru (dites di CI pada 2.19.0 dan 2.22.0), Node 20.19+ atau 22.12+
  • Redis untuk event bus, workflow engine, dan locking (sangat disarankan; webhook diproses di worker)
  • Akun doit.id: key untuk sandbox, untuk produksi

Instalasi

Tambahkan plugin dan provider di (contoh lengkap di bagian English di atas): berisi dan provider Payment Module .

Lalu aktifkan doit.id () sebagai payment provider di region IDR.

Konfigurasi

Buka Pengaturan → doit.id di Admin:

  1. Tempel API key ( atau ) dan webhook signing secret. Keduanya disimpan terenkripsi dan tampil tersamar. Mode test/live mengikuti prefix key.
  2. Pilih metode default (QRIS, bank VA, ), masa berlaku tagihan, dan zona waktu dashboard.
  3. Klik Tes koneksi: muncul daftar metode yang aktif di akun doit.id kamu.

Urutan sumber kredensial: Admin → provider (, ) → env (, ). Secret selalu diambil dari sumber yang sama dengan key.

EnvFungsi
64 karakter hex (). Wajib untuk menyimpan kredensial di Admin. Sama di server dan worker; backup bersama database.
, Kredensial cadangan bila belum ada yang disimpan di Admin.
Jadwal job rekonsiliasi (default ).

Kalau kunci enkripsi hilang, rusak, atau berbeda dari kunci saat kredensial disimpan, plugin tetap jalan: Admin menampilkan "Kunci enkripsi tidak valid, masukkan ulang kredensial." dan pembayaran memakai options atau env. Setelah mengganti kunci, isi ulang API key dan secret.

Satu aplikasi doit.id per toko

Daftar pembayaran dan webhook doit.id mencakup semua transaksi pada satu API key. Pakai satu aplikasi doit.id (key) per toko, dan aplikasi terpisah dengan key untuk testing dan staging. Jangan berbagi key antara dua toko, atau antara produksi dan salinan staging.

Plugin tetap menjaga kalau key terlanjur dipakai bersama. Setiap pembayaran yang dibuatnya membawa (id store Medusa ini) dan referensi seperti . Rekonsiliasi (webhook, job 15 menit, refund) hanya menyentuh pembayaran dengan penanda toko ini. Yang berikut diabaikan: tidak masuk ledger, tidak pernah masuk "Perlu tindakan":

  • pembayaran aplikasi atau toko lain pada key yang sama (mis. referensi , link bayar dari portal);
  • pembayaran mode lain (pembayaran test saat key live dipakai, dan sebaliknya);
  • pembayaran yang dibuat sebelum Pengaturan → doit.id → Rekonsiliasi → "Abaikan pembayaran sebelum". Defaultnya saat API key pertama kali disimpan di Admin.

Pembayaran dari plugin 0.1.0/0.1.1 (belum ada penanda) tetap direkonsiliasi selama session Medusa-nya masih ada. Item yang dulu dibuat versi lama untuk pembayaran yang bukan milik toko ini ditutup otomatis pada job berikutnya.

Baris ledger yang dulu ditulis versi lama untuk pembayaran seperti itu dibersihkan sekali, oleh job pertama setelah upgrade ke 0.1.3. Baris aplikasi/toko lain, mode lain, atau baris lama yang session Medusa-nya sudah tidak ada ditandai dikecualikan, tidak dihapus. Baris yang terhubung ke order selalu dipertahankan. Baris yang dikecualikan tidak dihitung di angka dashboard mana pun; centang "Tampilkan yang dikecualikan" di Transaksi (atau di API dan export CSV) untuk melihatnya beserta alasannya. Prefix referensi (opsional, halaman yang sama) hanya supaya pembayaranmu mudah dikenali di portal doit.id.

Setup webhook

  1. Di portal doit.id, isi webhook URL (halaman pengaturan Admin menampilkannya dengan tombol salin).
  2. Salin signing secret dari portal ke Pengaturan → doit.id.
  3. Kirim event tes dari portal; dijawab .

Request tanpa yang benar, atau dengan timestamp lebih dari 5 menit, dijawab . Kiriman ulang dijawab tanpa diproses dua kali. Kalau webhook hilang, job 15 menit menyelesaikan order-nya.

Fee: siapa yang menanggung

doit.id mengatur penanggung biaya per metode di portal doit.id (Metode Pembayaran). API tidak punya parameter untuk itu, jadi plugin tidak bisa mengubahnya. Biaya QRIS selalu ditanggung merchant (ketentuan Bank Indonesia).

  • : pembeli membayar total order; biaya dipotong dari settlement kamu.
  • : pembeli membayar total order ditambah biaya (). Medusa tetap meng-capture total order ().

Supaya transparan, plugin:

  • menampilkan biaya dan total yang dibayar per metode sebelum pembeli memilih (, dipakai komponen contoh), dan menampilkan metode di luar batas nominal dalam keadaan nonaktif;
  • menulis saat order dibuat: , , , , , , , , , , ;
  • menyimpan , , , dan di ledger; dashboard menampilkan gross, fee, dan net.

Integrasi storefront

Provider bisa dipakai storefront apa pun: buat payment session dan (opsional) kirim . Data session berisi , , , , , dan . Order baru dibuat setelah lunas, jadi storefront menunggu lewat dua endpoint (wajib publishable key):

EndpointIsi
Metode aktif beserta biaya dan total untuk cart ini, plus default Admin. Cache 5 menit.
Status dan : (poll lagi setelah ), (lunas: selesaikan cart), , . bila gambar QR habis sebelum tagihan: arahkan pembeli ke .

Aturan akses: cart harus milik sales channel publishable key, dan cart customer terdaftar hanya untuk customer itu (selain itu ). Hanya field tampilan yang dikembalikan. Rate limit: status 40/menit per cart dan 120/menit per IP, methods 20/menit per cart dan 30/menit per IP (di memori, per proses server). doit.id dibaca paling sering sekali per 10 detik per pembayaran.

Medusa Next.js Starter: komponen dan patch di , ikut di paket npm ().

Ganti metode membuat invoice baru. Kembali ke metode yang invoice-nya masih aktif (cart dan nominal sama, VA sisa ≥5 menit, QRIS yang QR-nya masih terbit) memakai ulang invoice itu.

FAQ

Pembayaran sandbox tetap pending. Di sandbox, pembayaran dilunasi lewat tombol "Simulasikan lunas" di portal doit.id. Tidak ada API untuk itu.

Webhook tidak sampai ke localhost. doit.id tidak bisa menjangkau komputermu. Endpoint polling storefront dan job 15 menit tetap menyelesaikan order; pakai tunnel kalau mau mengetes webhook.

Pembeli sudah bayar tapi order tidak ada. Cek doit.id → Perlu tindakan di Admin. Pembayaran yang cart/session-nya sudah tidak ada, atau datang setelah order dibatalkan, tercatat di sana untuk dipenuhi atau direfund.

Saldo dashboard beda dengan doit.id. Itu estimasi (net pembayaran sukses − refund − withdraw). doit.id tidak punya API saldo; cek portal sebelum menarik dana.

Di doit.id ada pembayaran aplikasi lain atau toko staging, tapi tidak muncul di dashboard. Memang begitu: hanya pembayaran dengan penanda toko ini yang direkonsiliasi. Pakai satu aplikasi doit.id per toko (lihat di atas).

Bisa pakai mata uang lain? Tidak. doit.id hanya menerima IDR; session dengan mata uang lain ditolak.

Batasan

  • Hanya IDR, rupiah utuh.
  • Tidak ada API saldo atau settlement di doit.id: saldo berupa estimasi; settlement dicatat manual atau import CSV.
  • Rail belum teruji: sandbox menolaknya (butuh live key dan langganan Mutasi Rekening). Kode mengikuti dokumentasi tetapi belum terverifikasi.
  • Invoice lama tidak bisa dibatalkan: doit.id tidak punya endpoint cancel. Invoice yang ditinggalkan pembeli tetap bisa dibayar sampai kedaluwarsa; kalau tetap dibayar, muncul di "Perlu tindakan".
  • di-capture sekali dan ditandai; kelebihan bayar direfund manual.
  • Sandbox menerima refund melebihi sisa; plugin memeriksanya sendiri.
  • Rate limit storefront disimpan per proses.

Keamanan

  • Kredensial terenkripsi (AES-256-GCM, terikat ke kolom) dan tidak pernah dikembalikan API (hanya 4 karakter terakhir).
  • Signature webhook diverifikasi pada raw body secara constant time; status selalu dibaca ulang dari doit.id sebelum capture.
  • Endpoint Admin butuh sesi admin; endpoint store memeriksa kepemilikan cart dan diberi rate limit.
  • Tanpa tracking: kredit di Admin hanya link biasa; tidak ada apa pun yang ditambahkan ke storefront.

Laporkan celah keamanan secara privat: lihat SECURITY.md.


Development

See CONTRIBUTING.md and CHANGELOG.md.

Used in production

  • hauskayu.com, Haus Kayu's own store.

Using this plugin in your store? Open a pull request to add it here.
Pakai plugin ini di tokomu? Kirim pull request untuk menambahkannya di sini.

About Haus Kayu

Haus Kayu makes handcrafted teak kitchenware and furniture in Jepara, Indonesia. We built this plugin for our own Medusa store and share it so other Indonesian merchants don't have to start from scratch.

Haus Kayu membuat perkakas dapur dan furnitur jati buatan tangan dari Jepara. Plugin ini kami buat untuk toko Medusa kami sendiri dan kami bagikan supaya merchant Indonesia lain tidak perlu mulai dari nol.

Support

  • Bugs & feature requests: GitHub Issues
  • Email: dev@hauskayu.com
  • WhatsApp: chat with Haus Kayu

For questions about your doit.id account, fees or settlement, contact doit.id directly at care@doit.id.
Untuk pertanyaan soal akun doit.id, biaya, atau pencairan dana, hubungi doit.id langsung di care@doit.id.

Admin credit / Kredit di Admin

The plugin's own Admin pages show a small "PG Medusa Doit.id · by Haus Kayu" footer with a help link. It is plain links only: no tracking, no scripts, and nothing is shown on your storefront or checkout. Turn it off with:

The disclaimer stays visible. / Disclaimer tetap tampil.

Disclaimer

PG Medusa Doit.id is a community plugin, not an official doit.id product, and is not affiliated with or endorsed by doit.id. "doit.id" is used only to describe compatibility. Test with a sandbox key before going live.
PG Medusa Doit.id adalah plugin komunitas, bukan produk resmi doit.id, dan tidak berafiliasi dengan doit.id. Tes dengan key sandbox sebelum dipakai live.

License

MIT © Haus Kayu

Еще в этой категории

Посмотреть все
Платежи
Braintree logo

Braintree

От Lambda Curry

Поддержка платежей и 3D Secure через Braintree

Загрузка данных
GitHubnpm
Платежи
Pay. logo

Pay.

От Webbers

Принимайте кредитные карты, цифровые платежи и купи сейчас, плати потом

Загрузка данных
GitHubnpm
Платежи
Mollie logo

Mollie

От Variable Vic

Легко принимайте мультивалютные платежи через Mollie

Загрузка данных
GitHubnpm

Еще от этого автора

Посмотреть все
Платежи
M

Manual transfer

От Hauskayu

Manual bank transfer with payment-proof upload and admin verification for Medusa v2 — by Haus Kayu

Загрузка данных
GitHubnpm
npm install medusa-payment-doit
npm install medusa-payment-doit
1module.exports = defineConfig({
2 // …
3 plugins: [{ resolve: "medusa-payment-doit", options: {} }],
4 modules: [
5 {
6 resolve: "@medusajs/medusa/payment",
7 options: {
8 providers: [
9 {
10 resolve: "medusa-payment-doit/providers/doit",
11 id: "doit",
12 options: {
13 // all optional; the Admin settings take precedence
14 // rail: "qris", va_bank: "bmri", expires_in: 86400, late_payment_policy: "accept",
15 },
16 },
17 ],
18 },
19 },
20 ],
21})
1npx medusa db:migrate
2echo "DOIT_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env # same value on server and worker
npm install medusa-payment-doit
1npx medusa db:migrate
2echo "DOIT_ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env # nilai sama di server dan worker
1npm ci
2npm run lint && npm run typecheck && npm run test:unit && npm run build
3# E2E (Postgres + Redis on the spike ports, see integration-tests/README.md)
4DATABASE_URL=postgres://postgres:spike@127.0.0.1:55432/doit_e2e REDIS_URL=redis://127.0.0.1:56379 npm run test:e2e
1// medusa-config.ts
2plugins: [{ resolve: "medusa-payment-doit", options: { showCredit: false } }]