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:

  1. Identity across payments. If Alice pays you three times, you want one customer record with three payments attached, not three orphaned payments.
  2. 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 (atPeriodEnd or immediately)

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_… or pk_live_…)
  • A secret (sk_test_… or sk_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

Plugipay — Payments that don't tax your success