Skip to content

SaaS subscription

import {
Access,
Billing,
Offer,
Purchase,
checkAccess,
createCheckout,
processPaymentNotification,
registerOffers,
} from "@byfungsi/sapa";
const membershipAccess =
yield *
Access.entitlements({
entitlements: ["app.use", "projects.unlimited"],
duration: "purchased_period",
});
const membershipPurchase =
yield *
Purchase.parse({
key: "saas.pro.access",
revision: 1,
provides: [membershipAccess],
});
const membershipBilling =
yield *
Billing.subscription({
family: "saas",
interval: "month",
price: { currency: "IDR", amount: 149_000n },
});
const membership =
yield *
Offer.parse({
key: "saas.pro",
revision: 1,
purchase: membershipPurchase,
billing: membershipBilling,
});
yield * registerOffers([membership]);
const checkout =
yield *
createCheckout({
type: "offer",
offerKey: membership.key,
offerRevision: membership.revision,
quantity: null,
provider: "midtrans",
customer: {
externalId: "customer-42",
email: "buyer@example.com",
name: "Example Buyer",
},
successUrl: "https://merchant.example/billing/success",
cancelUrl: "https://merchant.example/billing/canceled",
clientIp: null,
idempotencyKey: "saas-initial-42",
});
// In the authenticated provider webhook handler:
const settled =
yield *
processPaymentNotification({
provider: "midtrans",
payload: rawProviderPayload,
});
if (settled.status !== "settled" || settled.subscriptionId === null) {
return;
}
const granted =
yield *
checkAccess({
externalCustomerId: "customer-42",
entitlement: "app.use",
});
const renewal =
yield *
createCheckout({
type: "subscription_renewal",
subscriptionId: settled.subscriptionId,
provider: "midtrans",
customer: {
externalId: "customer-42",
email: "buyer@example.com",
name: "Example Buyer",
},
successUrl: "https://merchant.example/billing/renewed",
cancelUrl: "https://merchant.example/billing/canceled",
clientIp: null,
idempotencyKey: "saas-renewal-42-2026-10",
});

Redirect to checkout.url and later renewal.url; neither result means paid. The renewal derives frozen terms and beneficiary identity from authoritative subscription history. Access is end-exclusive and becomes active only after an authenticated settlement notification.