Plugipay — BYO Midtrans Provider Guide (/docs/providers/midtrans) Copy
- Project: plugipay (M4, Forjio split)
- Source brief:
spec/prd.md§10 ·copy/landing-copy.md(voice anchor) ·copy/docs/providers/xendit.md(sibling doc — structural parity) ·copy/docs/providers/managed.md·copy/docs/quickstart-dev.md(voice + step substructure) · Midtrans Server Key / Payment Notification / Snap API reference docs - Audience: Developers or technical founders who already own — or are willing to own — a Midtrans account and want the settlement relationship in their name. Ecosystem norm: English.
- Voice anchor:
copy/landing-copy.md— "Stripe circa 2014, before corporate polish." Three voice rules: (1) admit trade-offs, (2) show with a runnable command, (3) no success theatre. - Register: Imperative mood. Terse. Numbers and exact paths before prose. No second-person marketing. No
journey,seamlessly,empowering,unlock,best-in-class,revolutionary,next-generation,ecosystem solution. - Technical terms: full English.
Midtrans,Core API,Snap,GoPay,ShopeePay,QRIS,BCA VA,Permata VA,Mandiri Bill,webhook,Payment Notification,sandbox,production,Server Key,Client Key,Merchant ID— keep verbatim. Entity names PascalCase:CheckoutSession,Ledger,PaymentToken.
Information architecture
| # | Block | Goal |
|---|---|---|
| 1 | Hero | Name the path, set the "10-minute connect" promise |
| 2 | Prerequisites | Midtrans account, Plugipay on Growth+ |
| 3 | Step 1 | Copy Server Key + Merchant ID from Midtrans |
| 4 | Step 2 | Paste credentials into Plugipay |
| 5 | Step 3 | Register Plugipay webhook as Midtrans Payment Notification URL |
| 6 | Step 4 | Sandbox test charge with card 4811 1111 1111 1114 |
| 7 | Troubleshooting | Signature, notification delivery, test/live, sandbox/prod key mixup |
| 8 | Placement notes | Frontend route + component mapping |
Four numbered steps, each with goal / actions / success checkpoint / pitfall.
1. Hero
Eyebrow
Providers · Bring your own Midtrans
Headline (7 words)
Connect your Midtrans account in ten minutes.
Subheadline (one sentence, 28 words)
Copy one Server Key, paste it into Plugipay, point Midtrans' Payment Notification URL at our ingress, fire a sandbox test charge — your contract with Midtrans stays yours.
Trade-off line (voice rule #1)
BYO Midtrans means you own the relationship — settlement, KYC, pricing, disputes, PCI scope on Core API card flows. Plugipay doesn't take custody of funds. If you'd rather skip the Midtrans dashboard entirely, use
Plugipay managed— same API, zero setup.
What Midtrans notifies on (right column, pinned on desktop)
transaction_status = capture
transaction_status = settlement
transaction_status = pending
transaction_status = deny
transaction_status = cancel
transaction_status = expire
transaction_status = refund
Midtrans does not use event-type subscriptions. One Payment Notification URL receives every transaction status change; Plugipay routes by transaction_status internally.
Primary CTAs
- Start with step 1 →
#step-1(brand-700 button) - Compare with Plugipay managed →
/docs/providers/managed(text link)
2. Prerequisites
Before you start.
- A Midtrans account. Sign up at dashboard.midtrans.com/register — onboarding is 5–10 minutes for sandbox, plus KYC (NPWP, company docs) for production.
- A Plugipay account on Growth tier or higher. BYO provider routing is gated on Starter; upgrade at Settings → Plan.
- Ten minutes, two browser tabs (Midtrans + Plugipay).
You do not need to write code here. Connection is dashboard-to-dashboard. Code (creating a
CheckoutSession, handlingplugipay.checkout_session.completed.v1) is covered in the developer quickstart. New Midtrans accounts land in sandbox, which Midtrans labels both as "Sandbox" in the UI and via theSB-Mid-server-...Server Key prefix — keep it there until you finish §6 sandbox test.
3. Step 1 — Copy your Server Key and Merchant ID
Heading
Step 1 · Get the two credentials Plugipay needs
Goal
Copy the sandbox Server Key from Access Keys and the Merchant ID from Merchant Profile. Leave production alone until §6 passes.
Actions — Server Key
- Open Settings → Access Keys in the Midtrans Dashboard. Select the Sandbox tab (top-right environment toggle) — production keys won't work against
api.sandbox.midtrans.com. - Copy the row labelled Server Key. Sandbox keys start with
SB-Mid-server-; production keys start withMid-server-. Ignore the Client Key — it's browser-safe and cannot create charges; pasting it into Plugipay returns401on every call.
[Screenshot: Midtrans Dashboard — Settings → Access Keys (sandbox tab) with Server Key copy button]
Actions — Merchant ID
- Open Settings → Merchant Profile in the Midtrans Dashboard.
- Copy the Merchant ID (format:
Gfollowed by digits, e.g.G123456789). Midtrans includes it on every Payment Notification asmerchant_id— Plugipay uses it to disambiguate when one Plugipay account connects multiple Midtrans merchants.
[Screenshot: Midtrans Dashboard — Settings → Merchant Profile → Merchant ID row]
Success checkpoint
You have two strings saved: a Server Key (
SB-Mid-server-...for sandbox) and a Merchant ID (G...). The Server Key belongs in a password manager, not Slack, email, or Git.
Pitfall — sandbox vs production Server Key lookalike
Midtrans hides both environments behind one dashboard with a tab toggle. Copying the production Server Key into Plugipay's sandbox slot — or vice versa — fails silently at connect time and produces
401 Unauthorizedon the first charge. Always verify the prefix:SB-Mid-server-for sandbox,Mid-server-for production.
4. Step 2 — Paste credentials into Plugipay
Heading
Step 2 · Connect the key in Plugipay
Goal
Open the BYO Midtrans connect form, paste the two values, test the connection.
Actions
- In the Plugipay Dashboard, go to Settings → Providers → + Add provider → Bring your own Midtrans.
- Fill the form:
- Server Key — paste the Server Key from §3.
- Merchant ID — paste the Merchant ID from §3.
- Environment — pick Sandbox (radio). Keep it on sandbox until §6 passes.
- Click Test connection. Plugipay calls Midtrans
GET /v2/{order_id}/statusagainst a known-bad order ID and expects404 transaction_not_found— that response proves authentication succeeded. Any401means wrong Server Key or wrong environment. - Click Save & continue.
[Screenshot: Plugipay Dashboard — Settings → Providers → Bring your own Midtrans connect form, "Test connection" success state]
Success checkpoint
The provider row shows Midtrans · active · sandbox with the last 4 characters of the Server Key masked.
Pitfall — environment radio mismatch
If the Plugipay radio is set to Production but the pasted Server Key is
SB-Mid-server-..., Plugipay validates the prefix and blocks save. The inverse (production key in sandbox slot) is harder to catch — Plugipay warns but allows save, and the first charge then returns401 Unauthorizedfrom Midtrans. Always match the prefix to the radio before clicking Test connection.
5. Step 3 — Point Midtrans' Payment Notification at Plugipay
Heading
Step 3 · Register the Payment Notification URL
Goal
Add Plugipay's ingress URL to Midtrans' Payment Notification config so every transaction status change reaches Plugipay.
Actions
In Plugipay, go to Settings → Providers → Midtrans and copy the webhook URL:
https://plugipay.com/webhooks/midtrans/{merchant_id}Plugipay substitutes
{merchant_id}automatically — copy the rendered URL, not the template.In Midtrans, go to Settings → Configuration → Notifications.
Paste the URL into the Payment Notification URL field. Leave Finish / Unfinish / Error Redirect URLs blank unless you're overriding Plugipay's hosted checkout redirects.
Save.
[Screenshot: Midtrans Dashboard — Settings → Configuration → Notifications → Payment Notification URL field with Plugipay URL pasted]
Success checkpoint
Midtrans' Notifications page lists Plugipay's URL in the Payment Notification URL row. Unlike Xendit, Midtrans doesn't show a separate "active/inactive" status — the URL is live the moment you save.
Pitfall — test/live Payment Notification are separate fields
Midtrans maintains separate Payment Notification URLs per environment — sandbox and production each have their own Notifications page, reached via the environment toggle. Adding the URL only in sandbox means production charges fire into the void. When you flip Plugipay's environment in §6, re-do §5 in the production tab with the same URL. Plugipay's ingress is identical across environments; the payload tells Plugipay which side the notification came from.
Pitfall — Finish / Unfinish / Error redirects hijacking hosted checkout
If you've previously set Finish / Unfinish / Error Redirect URLs in Midtrans for a different integration, they override Plugipay's
success_url/cancel_urlon the hosted checkout. Blank them out in Midtrans, or accept that Plugipay's per-session redirects won't apply — Plugipay defers to Midtrans when both are set.
6. Step 4 — Sandbox test charge
Heading
Step 4 · Fire a test charge end to end
Goal
Create a
CheckoutSessionvia CLI, complete a card payment in the Midtrans sandbox with test card4811 1111 1111 1114, watch the event reach Plugipay.
Actions
Terminal 1 — create a session:
plugipay checkout create \
--amount 50000 \
--currency IDR \
--methods card,gopay,qris,bank_transfer \
--success-url https://example.com/thanks \
--cancel-url https://example.com/cancel \
--mode test \
--provider midtrans \
--json
Response:
{ "id": "cs_01HYAB...", "hostedUrl": "https://plugipay.com/c/cs_01HYAB...", "status": "open" }
Terminal 2 — listen for the completion event:
plugipay events listen --types plugipay.checkout_session.completed.v1 --mode test
Open hostedUrl in a browser, pick Credit card. Midtrans Snap opens. Enter:
Card number: 4811 1111 1111 1114
Expiry: any future date (e.g. 12/30)
CVV: 123
3DS OTP: 112233
Submit. Snap completes the 3DS challenge automatically.
Success checkpoint
Terminal 2 prints a
plugipay.checkout_session.completed.v1event withdata.object.adapter = "midtrans"andstatus = "completed". In the Midtrans Dashboard under Transactions, the matching order is markedsettlement(card) orcapture(pre-3DS settlement). In Plugipay under Events → Deliveries, the inbound notification shows200and the outboundplugipay.checkout_session.completed.v1shows200.
Pitfall — sandbox card variants + settlement timing
Midtrans publishes multiple sandbox cards.
4811 1111 1111 1114is the 3DS-accept-on-first-try card — use this for happy path.4911 1111 1111 1113fails 3DS (deny); a transposed digit quietly switches you onto the deny card. Also note: sandbox marks cards assettlementinstantly, while production cards sit incapturefor up to T+2 before settling. Plugipay firesplugipay.checkout_session.completed.v1oncapture(notsettlement) so checkout UX stays unblocked — don't assume instant settlement in production flows.
7. Troubleshooting
Pitfall — invalid signature / signature_key mismatch
Midtrans attaches a
signature_keyon every Payment Notification, computed asSHA512(order_id + status_code + gross_amount + ServerKey). Plugipay rejects with401 signature_invalidwhen the recomputed hash doesn't match — usually because the Server Key in Plugipay is stale after you rotated it in Midtrans, or you pasted the Client Key into the Server Key field in §4. Re-copy from Settings → Access Keys and update via Settings → Providers → Midtrans → Edit in Plugipay.
Pitfall — webhook not received
Check Midtrans Settings → Configuration → Notifications → Notification History for delivery attempts. Non-2xx means Plugipay rejected (check signature and environment). No row at all means the Payment Notification URL is blank or set on the wrong environment tab (see §5 pitfall). In Plugipay, Events → Deliveries (direction
inbound) confirms receipts. Midtrans retries on non-2xx with exponential backoff for 12 hours, so transient failures self-heal.
Pitfall — test-mode vs live mismatch
The most common support ticket. Midtrans Server Keys, Payment Notification URLs, and dashboard data are separate between sandbox and production. Flipping Plugipay to production means repeating §3–§5 against the production tab — new Server Key (
Mid-server-...prefix), same Plugipay webhook URL pasted into the production Notifications page, Plugipay provider row set to production. Miss one and live charges silently stayopen.
Pitfall — sandbox/production key mixup
Related but worth its own entry: pasting a production Server Key into a sandbox provider row (or vice versa) isn't always hard-blocked by Plugipay's prefix validation. The first charge then returns
401 Unauthorizedfrom Midtrans and Plugipay surfaces the error verbatim. Fix: delete the provider row in Plugipay and recreate with the matching key + environment.
If your error isn't listed, email support@forjio.com with your Account ID (acc_...) and the step you hit. We don't send canned answers.
8. Placement notes
All sections render in code/frontend/src/app/(marketing)/docs/providers/midtrans/page.tsx as scroll-anchored blocks (#hero, #prerequisites, #step-1–#step-4, #troubleshooting). Sidebar nav from docs/index.md §3.3 Providers. Screenshot assets → code/frontend/public/docs/providers/midtrans/ (PNG, 2x) — Iro/Hikari during implementation. Shared primitives (code-block.tsx, callout.tsx, step-badge.tsx) reused from /docs/quickstart-dev and /docs/providers/xendit. Cross-page links: /docs/providers/managed (shipped), /docs/providers/xendit (shipped), /docs/providers/paypal (placeholder for MVP), /docs/quickstart-dev, /docs/pricing, external https://dashboard.midtrans.com, mailto:support@forjio.com.
End of docs/providers/midtrans.md.