Core Concepts

Plugipay exposes nine entities. This page gives you the one-paragraph definition, the shape, and a small example for each.

Read top-to-bottom the first time. After that, jump to whichever concept you need.


Plan

A Plan is a recurring charge template. It holds the product name, the billing interval (day / week / month / year), the trial policy, and a reference to one or more Prices. A Plan does not charge on its own — it's attached to a Subscription, which drives the actual charges.

{
  "id": "plan_01HXZA...",
  "name": "Pro Monthly",
  "interval": "month",
  "intervalCount": 1,
  "trialDays": 14,
  "priceId": "price_01HXZA..."
}

A Plan with trialDays: 14 gives new Subscriptions a two-week grace period before the first Invoice is created.

Price

A Price is a specific amount in a specific currency, attached to a Plan. You can have multiple Prices per Plan (e.g., IDR + USD) and pick the right one at checkout based on merchant location. Prices are immutable — to change pricing, create a new Price and point the Plan at it.

{
  "id": "price_01HXZA...",
  "planId": "plan_01HXZA...",
  "currency": "IDR",
  "unitAmount": 99000,
  "model": "flat"
}

Tiered pricing (model: "tiered") supports graduated + volume structures.

CheckoutSession

A CheckoutSession is a one-time payment page. You create a session server-side, redirect the customer to the returned hostedUrl, and Plugipay handles method selection (QRIS, VA, e-wallet, card), form UI, and the gateway round-trip. The session expires after 30 minutes by default.

When the customer completes payment, Plugipay:

  1. Marks the session completed.
  2. Creates an Invoice (for subscription sessions) or attaches to an existing one.
  3. Emits plugipay.checkout_session.completed.v1.
  4. Redirects the customer to your returnUrl.
await plugipay.checkout.sessions.create({
  planId: 'plan_01HXZA...',
  customerEmail: 'customer@example.com',
  returnUrl: 'https://yourapp.com/billing/success',
});

Subscription

A Subscription connects a Customer to a Plan. Plugipay creates an Invoice at every interval, attempts the charge, and — if it fails — runs the DunningPolicy (retries, dunning emails, suspension). Status transitions: activepast_dueunpaidcanceled.

{
  "id": "sub_01HXZA...",
  "customerId": "cus_01HXZA...",
  "planId": "plan_01HXZA...",
  "status": "active",
  "currentPeriodStart": "2026-04-01T00:00:00Z",
  "currentPeriodEnd": "2026-05-01T00:00:00Z"
}

Pausing a Subscription halts renewals but preserves the customer's access until the current period ends.

Invoice

An Invoice is the bill for a specific period. Plugipay generates one invoice per billing cycle per Subscription, stamps it with your business details + tax (11% VAT for IDR), and attempts the charge automatically. Invoices can also be created standalone (not tied to a Subscription) — useful for one-off consulting or setup fees.

Statuses: draftopenpaid | void | uncollectible.

{
  "id": "inv_01HXZA...",
  "subscriptionId": "sub_01HXZA...",
  "amountDue": 109890,
  "currency": "IDR",
  "taxAmount": 10890,
  "status": "paid",
  "paidAt": "2026-04-01T00:03:12Z"
}

amountDue includes tax. amountPaid reflects what actually settled (can differ on partial refunds).

Ledger

The Ledger is a double-entry audit log. Every charge, refund, payout, and fee is written as a pair of entries — one debit, one credit — so sum(debits) === sum(credits) always holds. You can export the Ledger as CSV for your accountant or pipe it into your data warehouse.

DEBIT   assets:bank              Rp 109,890
CREDIT  revenue:subscriptions    Rp  99,000
CREDIT  liabilities:vat          Rp  10,890

Every entry carries an idempotencyKey derived from the gateway event ID, so replayed webhooks don't double-count.

PortalSession

A PortalSession is a short-lived (15-minute) authenticated link that lets a customer manage their own subscription — update payment method, view invoice history, cancel at period end. You create a PortalSession server-side after verifying the customer, then redirect them to portalUrl. No additional login required on their side.

const session = await plugipay.billing.portal.create({
  customerId: 'cus_01HXZA...',
  returnUrl: 'https://yourapp.com/settings',
});
window.location.href = session.portalUrl;

PaymentToken

A PaymentToken is a stored payment instrument — a tokenized card or a linked e-wallet — you can charge repeatedly without asking the customer to re-authenticate. Created during the first successful CheckoutSession if the customer opts in. Subscription renewals use PaymentTokens to charge without friction.

Plugipay never sees the raw card number. Tokens live in the gateway (Xendit, Midtrans, PayPal); Plugipay just references them.

Dunning

Dunning is what happens when a recurring charge fails. The DunningPolicy attached to a Plan (or globally to your account) says: retry on day 1, 3, 7; email the customer on retry 2 and 3; suspend access on day 14 if still unpaid. Plugipay runs this policy automatically — you write no retry loops.

Default policy:

{
  "retrySchedule": ["1d", "3d", "7d"],
  "emailOnRetry": [2, 3],
  "suspendAfterDays": 14,
  "cancelAfterDays": 30
}

Edit this in Settings → Dunning, or attach a custom policy per Plan.


Next

Plugipay — Payments that don't tax your success