Concepts
This page covers the building blocks of Plugipay: what each object is, how they relate to each other, and the lifecycle they go through. Read this before you go deep on any specific feature — the rest of the docs assume you know what a checkout session is and how it differs from a payment.
The data model
Workspace
├── Customers
│ └── Payments
│ └── Refunds
├── Checkout sessions ─────→ Payments
├── Subscriptions
│ ├── Plans (referenced)
│ └── Invoices
├── Webhooks
│ └── Webhook deliveries
├── API keys
└── Team members
Everything is scoped to a workspace. Cross-workspace queries don't exist — if you operate two businesses, you'll have two workspaces and treat them as fully separate.
Workspace
A workspace is the top-level tenant boundary. Each workspace has:
- Its own customer list
- Its own payments, refunds, invoices
- Its own API keys
- Its own team members and roles
- Its own webhook endpoints
- Its own payment provider configuration (BYO today; managed coming soon)
- Its own billing relationship with Plugipay (for non-managed mode)
Workspaces are isolated. Data never crosses between them.
Managed payments (Plugipay owns the provider relationship) are launching soon — for now each workspace connects its own Xendit, Midtrans, or PayPal account, or uses manual transfer.
You can be a member of multiple workspaces with different roles in each — admin in your own, member or read-only in someone else's. Switching is done via the top-left workspace switcher in the dashboard.
A workspace identifier looks like ws_01H… (26-char ULID prefixed by type).
Customer
A customer is a persistent record of someone who can pay you. Two reasons you'd create one:
- Identity across payments. If Alice pays you three times, you want one customer record with three payments attached, not three orphaned payments.
- Stored payment methods. Subscriptions and one-click checkouts need a stored card. That card is owned by a customer.
You don't need a customer to take a one-time payment — you can pass guest details inline to a checkout session. But you lose the historical link.
Identifier: cus_01H….
Fields you'll set: email, name, phone (optional), address (optional), metadata (up to 50 key-value pairs of your own data).
Checkout session
A checkout session represents a customer's intent to pay you. It's short-lived — created when the customer is about to pay, alive for up to 24 hours, then expired.
A session has:
- An amount and currency
- A description that shows on the payment page
- A success URL to redirect to after payment
- A cancel URL to redirect to if the customer abandons
- Optionally, a customer reference (
customerId) - Optionally, metadata you provide
Plugipay returns a hosted checkout URL — you redirect the user there. The user pays. Plugipay redirects them to your success URL.
Identifier: sess_01H….
States:
| State | Means |
|---|---|
open |
Created, awaiting payment |
complete |
Payment succeeded |
expired |
TTL ran out before payment |
canceled |
Customer hit the back button |
A session is not a payment. The payment is the resulting financial record (see below). One session produces zero or one payments.
Payment
A payment is the financial record of money received. It's created when a checkout session completes (or via direct API for inline flows).
A payment has:
- An amount, currency, and net amount (after fees)
- A status (see below)
- A reference to the customer (if any)
- A reference to the checkout session (if applicable)
- A reference to the payment method used
- Provider data (the upstream transaction ID, fees, etc.)
Identifier: pay_01H….
States:
| State | Means |
|---|---|
pending |
Provider is processing — rare for cards, common for bank transfers |
succeeded |
Funds confirmed received |
failed |
Provider declined or errored |
refunded |
One or more refunds totaling the original amount |
partially_refunded |
Some money refunded but not all |
A succeeded payment can transition to refunded or partially_refunded. Other states are terminal.
Refund
A refund moves money back from your account to the customer's. Refunds reference an existing payment.
A refund has:
- An amount (full or partial)
- A reason (
requested_by_customer,duplicate,fraudulent,other) - A status (
pending,succeeded,failed) - A reference back to the payment
You can issue multiple partial refunds against one payment, totaling up to (but not exceeding) the payment amount.
Identifier: re_01H….
Refunds are async — the provider takes anywhere from seconds (cards) to days (bank transfers) to actually move money. Listen for the refund.succeeded and refund.failed webhooks.
Plan
A plan is a recurring offer. "Pro subscription, IDR 100,000/month" is a plan.
A plan has:
- A name
- An amount and currency
- A billing interval (
day,week,month,year) - An interval count (e.g., every 3 months)
- An optional trial period
- Metadata
Identifier: plan_01H….
Plans are templates. They don't bill anyone by themselves — that requires a subscription.
Subscription
A subscription is a customer's commitment to a plan. It bills the customer's stored payment method on the plan's schedule.
A subscription has:
- A reference to the customer
- A reference to the plan
- A status (
active,trialing,past_due,canceled,paused) - A current period (
currentPeriodStart,currentPeriodEnd) - A cancellation policy (
atPeriodEndorimmediately)
Identifier: sub_01H….
Each billing cycle, Plugipay creates an invoice and attempts payment. On success, an invoice.paid event fires and the period rolls forward. On failure, the subscription enters past_due and dunning begins.
Invoice
An invoice is a record of an attempted or completed billing event. They appear for:
- Subscription billing cycles
- One-off invoices you issue manually (via API or portal)
- Refunds (as negative-amount invoice line items)
Invoices have a status lifecycle: draft → open → paid (or void).
Identifier: inv_01H….
Webhook endpoint
A webhook endpoint is a URL you control where Plugipay POSTs events. You configure one or more per workspace.
Each endpoint has:
- A URL
- A list of subscribed event types
- A signing secret (used to verify event authenticity)
- A status (
active,disabled)
When an event occurs, Plugipay queues a delivery to every subscribed endpoint. We retry failures with exponential backoff for up to three days.
See API → Webhooks for the event catalog and signature scheme.
API key
An API key authenticates server-to-server calls. Keys have:
- An identifier (
pk_test_…orpk_live_…) - A secret (
sk_test_…orsk_live_…) shown once at creation - A role (
full_access,read_only,webhook_management_only, etc.) - A creation date
- A last-used timestamp
Keys are per-workspace. They authenticate via HMAC signing of the request — see API → Authentication.
Identifier: pk_test_xxxxxxxxxx.
Test vs live
Plugipay maintains separate, parallel data sets for test and live:
- Test API keys start with
pk_test_/sk_test_. - Live keys start with
pk_live_/sk_live_. - Customers, payments, refunds, etc. created with test keys are visible only to test mode.
- The portal has a test/live toggle (top of the sidebar). Data and counts switch.
You can run both side-by-side — production code uses live keys, your local dev uses test keys.
Identifiers (the prefix system)
Every Plugipay object has a typed prefix on its ID. Use this to read at a glance what kind of object you're holding:
| Prefix | Type |
|---|---|
ws_ |
Workspace |
cus_ |
Customer |
sess_ |
Checkout session |
pay_ |
Payment |
re_ |
Refund |
plan_ |
Plan |
sub_ |
Subscription |
inv_ |
Invoice |
ak_ / sk_ |
API key (access ID / secret) |
whep_ |
Webhook endpoint |
evt_ |
Webhook event |
tmpl_ |
Template |
The suffix is a ULID — sortable by creation time, globally unique.
Why this matters
The model isn't ornamental — it shapes how you build integrations:
- Webhook handlers dispatch on event type, which is named after the object (
payment.succeeded,refund.failed, etc.). - Idempotency is keyed on the operation, not the object, so you can safely retry a session creation without making two sessions.
- Audit log entries record actions on objects by ID, so you can trace "who refunded payment pay_123" precisely.
Once you have this mental model in place, the rest of the docs are linear — each portal page, each API endpoint, each SDK method maps cleanly to one of these objects.
Next
- Pricing model — how we (and you) charge.
- Portal tour — the dashboard, feature by feature.
- API reference — every endpoint.