Subscriptions
SAPA recurring offers use explicit periods. A settled initial checkout creates
a subscription; a subscription_renewal checkout can establish a later manual
period.
import { createCheckout } from "@byfungsi/sapa";
const renewal = createCheckout({ type: "subscription_renewal", subscriptionId: "sub-00000000-0000-4000-8000-000000000001", provider: "midtrans", customer: { externalId: "customer-42", email: "owner@example.com", name: "Workspace Owner", }, successUrl: "https://merchant.example/billing/renewed", cancelUrl: "https://merchant.example/billing/renewal-cancelled", clientIp: null, idempotencyKey: "subscription-42-renewal-2026-10",});Plan and quantity changes
Section titled “Plan and quantity changes”Changes apply at the next period boundary. A complete quote retains current and
target terms, an effective time, an expiry, and the expected subscription
version. scheduleSubscriptionPlanChange accepts the quote together with an
idempotent acceptance command.
The target must:
- be recurring;
- belong to the same subscription family;
- use the same currency;
- be an exact registered offer revision;
- have enough target seat capacity for existing assignments.
Quote mismatch, expiry, and version conflict require a fresh quote. Never apply only a mutable “latest plan” identifier at renewal time—the accepted complete terms are the historical contract.
Period access
Section titled “Period access”Benefits with duration: "purchased_period" are active only inside settled
periods. A late renewal starts a new period at settlement; it does not silently
backfill an unpaid gap. Use checkAccess for a boolean and explainAccess when
support or policy code needs provenance.
Metered postpaid subscriptions use a separate enrollment and invoice lifecycle; see metered billing.