Desktop bundle
import { Access, Billing, Credits, Licensing, Offer, Purchase, checkAccess, createCheckout, getCreditBalance, issueLicenseCredential, processPaymentNotification, registerOffers,} from "@byfungsi/sapa";
const desktopAccess = yield * Access.entitlements({ entitlements: ["desktop.use"], duration: "perpetual", });const updateAccess = yield * Access.entitlements({ entitlements: ["desktop.updates"], duration: { type: "fixed", months: 12, anchor: "settlement", calendar: "utc_clamped", }, });const desktopLicense = yield * Licensing.eligibility({ entitlement: "desktop.use", activationLimit: 3, });const processingCredits = yield * Credits.pack({ unit: "desktop.processing", quantity: 100n });const bundlePurchase = yield * Purchase.parse({ key: "desktop.bundle.benefits", revision: 1, provides: [desktopAccess, updateAccess, desktopLicense, processingCredits], });const bundleBilling = yield * Billing.oneTime({ price: { currency: "IDR", amount: 1_999_000n }, });const bundle = yield * Offer.parse({ key: "desktop.bundle", revision: 1, purchase: bundlePurchase, billing: bundleBilling, });
yield * registerOffers([bundle]);yield * createCheckout({ type: "offer", offerKey: bundle.key, offerRevision: bundle.revision, quantity: null, provider: "midtrans", customer, successUrl, cancelUrl, clientIp: null, idempotencyKey: "desktop-bundle-42", });yield * processPaymentNotification({ provider: "midtrans", payload: rawProviderPayload, });
// Run fulfillment before explicit license issuance.const license = yield * issueLicenseCredential({ idempotencyKey: "desktop-bundle-license-42", externalCustomerId: "customer-42", entitlement: "desktop.use", });const canUseApp = yield * checkAccess({ externalCustomerId: "customer-42", entitlement: "desktop.use", });const getsUpdates = yield * checkAccess({ externalCustomerId: "customer-42", entitlement: "desktop.updates", });const credits = yield * getCreditBalance({ customerId: "customer-42", unit: "desktop.processing" });Each benefit keeps its own historical duration and unit. Twelve months after
settlement, desktop.updates becomes inactive while desktop.use, the license
eligibility it supports, and unused non-expiring credits remain. Do not collapse
the bundle into one shared expiry timestamp.