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, handling plugipay.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 the SB-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

  1. 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.
  2. Copy the row labelled Server Key. Sandbox keys start with SB-Mid-server-; production keys start with Mid-server-. Ignore the Client Key — it's browser-safe and cannot create charges; pasting it into Plugipay returns 401 on every call.

[Screenshot: Midtrans Dashboard — Settings → Access Keys (sandbox tab) with Server Key copy button]

Actions — Merchant ID

  1. Open Settings → Merchant Profile in the Midtrans Dashboard.
  2. Copy the Merchant ID (format: G followed by digits, e.g. G123456789). Midtrans includes it on every Payment Notification as merchant_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 Unauthorized on 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

  1. In the Plugipay Dashboard, go to Settings → Providers → + Add provider → Bring your own Midtrans.
  2. 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.
  3. Click Test connection. Plugipay calls Midtrans GET /v2/{order_id}/status against a known-bad order ID and expects 404 transaction_not_found — that response proves authentication succeeded. Any 401 means wrong Server Key or wrong environment.
  4. 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 returns 401 Unauthorized from 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

  1. 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.

  2. In Midtrans, go to Settings → Configuration → Notifications.

  3. 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.

  4. 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_url on 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 CheckoutSession via CLI, complete a card payment in the Midtrans sandbox with test card 4811 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.v1 event with data.object.adapter = "midtrans" and status = "completed". In the Midtrans Dashboard under Transactions, the matching order is marked settlement (card) or capture (pre-3DS settlement). In Plugipay under Events → Deliveries, the inbound notification shows 200 and the outbound plugipay.checkout_session.completed.v1 shows 200.

Pitfall — sandbox card variants + settlement timing

Midtrans publishes multiple sandbox cards. 4811 1111 1111 1114 is the 3DS-accept-on-first-try card — use this for happy path. 4911 1111 1111 1113 fails 3DS (deny); a transposed digit quietly switches you onto the deny card. Also note: sandbox marks cards as settlement instantly, while production cards sit in capture for up to T+2 before settling. Plugipay fires plugipay.checkout_session.completed.v1 on capture (not settlement) 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_key on every Payment Notification, computed as SHA512(order_id + status_code + gross_amount + ServerKey). Plugipay rejects with 401 signature_invalid when 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 stay open.

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 Unauthorized from 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.

Plugipay — Payments that don't tax your success