@lastshotlabs/slingshot-billing
npm install @lastshotlabs/slingshot-billing
Functions
Section titled “Functions”Billing
Section titled “Billing”Provider-owned contract object for slingshot-billing.
Source: packages/slingshot-billing/src/public.ts
BILLING_PACKAGE_NAME
Section titled “BILLING_PACKAGE_NAME”Stable identifier for this package; used as the contract/event owner name.
Source: packages/slingshot-billing/src/plugin.ts
BillingEntitlementCap
Section titled “BillingEntitlementCap”Capability resolving an owner’s current Entitlement. Consumers call
ctx.capabilities.require(BillingEntitlementCap)(ownerId) for an on-demand
read; the same value is pushed via the billing:entitlement.changed event.
Source: packages/slingshot-billing/src/public.ts
billingPackageConfigSchema
Section titled “billingPackageConfigSchema”Runtime schema validating createBillingPackage() configuration.
Config Fields
Section titled “Config Fields”| Field | Description |
|---|---|
donations | One-time donation configuration. |
mountPath | URL path prefix for billing routes. Must start with ’/’. Default: /billing. |
plans | Subscription plans mapped to provider prices. Empty ⇒ no paid plans. |
provider | Payment provider credentials. Omit to run billing dormant (unconfigured). |
urls | Checkout / Portal redirect URLs. Required once a provider is configured. |
webhookMaxBodyBytes | Maximum body size (bytes) accepted on the Stripe webhook route. Defaults to 1 MiB. |
Source: packages/slingshot-billing/src/types/config.ts
createBillingPackage
Section titled “createBillingPackage”Create the billing package: provider-abstracted subscriptions, trials, and one-time donations, exposing an app-agnostic entitlement capability + events.
Dormant by default: with no provider configured, the checkout / donate /
portal routes return 503 billing_unavailable and the entitlement
capability (and GET /billing/entitlement) resolves to
FREE_ENTITLEMENT. Adding the package without Stripe keys changes
nothing for the host app.
The client-facing routes (checkout, donate, portal, entitlement) mount through the framework’s typed OpenAPI router; the signature-verified Stripe webhook is a plain (non-OpenAPI) POST mounted only when billing is configured — dormant apps expose no webhook surface at all.
function createBillingPackage(rawConfig: Partial<BillingPackageConfig> = {}, internals: BillingPackageInternals = {},): SlingshotPackageDefinitionSource: packages/slingshot-billing/src/plugin.ts
createEntityBillingStore
Section titled “createEntityBillingStore”The production BillingStore: a thin, stateless mapping over the three
billing entity adapters. Constructed wherever the adapters are reachable
(setupPost via maybeEntityAdapter, or onAdapter wiring callbacks).
function createEntityBillingStore(adapters: BillingEntityAdapters): BillingStoreSource: packages/slingshot-billing/src/lib/store.ts
createStripeProvider
Section titled “createStripeProvider”Create the Stripe BillingProvider.
Stateless beyond the lazily-constructed SDK client; persistence and
entitlement logic live in the package. Customer dedupe is deliberately NOT
here: the routes check billing_customers first and call ensureCustomer
only when no mapping exists, so this implementation always creates.
function createStripeProvider(config: StripeProviderConfig): BillingProviderSource: packages/slingshot-billing/src/lib/providers/stripe.ts
deriveEntitlement
Section titled “deriveEntitlement”Derive an owner’s Entitlement from their stored subscription rows.
Multi-row rule (binding): rows whose plan is not a configured plan key are
discarded (unknown plan ⇒ no entitlement from that row), then the BEST
remaining row wins — ranked active > trialing > past_due > canceled,
tie-broken by the latest currentPeriodEnd. No qualifying rows ⇒
FREE_ENTITLEMENT.
function deriveEntitlement(rows: readonly BillingSubscriptionRow[], plans: readonly PlanConfig[],): EntitlementSource: packages/slingshot-billing/src/lib/entitlement.ts
entitlementEquals
Section titled “entitlementEquals”Structural equality for entitlements — used by sync to report changed.
function entitlementEquals(a: Entitlement, b: Entitlement): booleanSource: packages/slingshot-billing/src/lib/entitlement.ts
isBillingConfigured
Section titled “isBillingConfigured”Whether billing has a usable provider configured. When false the package is “dormant”: routes short-circuit to 503 and the entitlement capability yields a free/none entitlement. This is the single gate every runtime path consults.
function isBillingConfigured(config: BillingPackageConfig): booleanSource: packages/slingshot-billing/src/types/config.ts
normalizeStripeEvent
Section titled “normalizeStripeEvent”Map a verified Stripe event onto billing’s normalized ProviderEvent.
Handled types:
customer.subscription.created/.updated→subscription.updatedcustomer.subscription.deleted→subscription.deletedcheckout.session.completed(modesubscription) →subscription.updatedcheckout.session.completed(modepayment) →payment.completedinvoice.payment_failed→subscription.updatedwith statuspast_due- everything else →
ignored
function normalizeStripeEvent(event: StripeEventLike): ProviderEventSource: packages/slingshot-billing/src/lib/providers/stripe.ts
planKeyForPrice
Section titled “planKeyForPrice”Map a provider price id onto the configured plan key.
Unknown or absent prices map to 'free' — a webhook for a price the app
never configured must not grant an entitlement.
function planKeyForPrice(priceId: string | null | undefined, plans: readonly PlanConfig[],): stringSource: packages/slingshot-billing/src/lib/entitlement.ts
syncProviderEvent
Section titled “syncProviderEvent”Apply one normalized provider event to storage.
async function syncProviderEvent(event: ProviderEvent, store: BillingStore, plans: readonly PlanConfig[],): Promise<SyncOutcome>Source: packages/slingshot-billing/src/lib/sync.ts
Constants
Section titled “Constants”BillingCustomerEntity
Section titled “BillingCustomerEntity”Source: packages/slingshot-billing/src/entities/customer.ts
BillingPaymentEntity
Section titled “BillingPaymentEntity”Source: packages/slingshot-billing/src/entities/payment.ts
BillingSubscriptionEntity
Section titled “BillingSubscriptionEntity”Source: packages/slingshot-billing/src/entities/subscription.ts
FREE_ENTITLEMENT
Section titled “FREE_ENTITLEMENT”The entitlement returned when billing is dormant or the owner has no subscription.
Source: packages/slingshot-billing/src/public.ts
Interfaces
Section titled “Interfaces”BillingCustomerRow
Section titled “BillingCustomerRow”A billing_customers row as billing’s domain logic sees it.
Source: packages/slingshot-billing/src/lib/store.ts
BillingEntitlementChangedPayload
Section titled “BillingEntitlementChangedPayload”Payload for billing:entitlement.changed — an owner’s derived entitlement
actually changed after a verified webhook event was synced.
Delivery is fire-and-forget (the bus swallows and logs handler throws), so
consumers treat this as a cache-invalidation hint and reconcile via
BillingEntitlementCap on read.
Source: packages/slingshot-billing/src/events.ts
BillingEntityAdapter
Section titled “BillingEntityAdapter”The structural slice of a framework entity adapter this store needs
(BareEntityAdapterCrud in slingshot-entity satisfies it). Kept local so
the store compiles against a stable, minimal shape.
Source: packages/slingshot-billing/src/lib/store.ts
BillingEntityAdapters
Section titled “BillingEntityAdapters”The three entity adapters createEntityBillingStore maps over.
Source: packages/slingshot-billing/src/lib/store.ts
BillingPackageInternals
Section titled “BillingPackageInternals”Internal construction seam for createBillingPackage.
Not part of the app-facing configuration surface: tests inject a
FakeBillingProvider here so the full route/lifecycle stack runs without
the Stripe SDK. Production apps never pass this.
Source: packages/slingshot-billing/src/plugin.ts
BillingPaymentCompletedPayload
Section titled “BillingPaymentCompletedPayload”Payload for billing:payment.completed — a one-time payment/donation settled.
Source: packages/slingshot-billing/src/events.ts
BillingPaymentRow
Section titled “BillingPaymentRow”A billing_payments row as billing’s domain logic sees it.
Source: packages/slingshot-billing/src/lib/store.ts
BillingProvider
Section titled “BillingProvider”The provider contract. Implementations are stateless wrappers over a payment SDK; persistence, entitlement derivation, and event emission live in the package, not here.
Source: packages/slingshot-billing/src/lib/provider.ts
BillingStore
Section titled “BillingStore”Everything lib/sync.ts needs from persistence — nothing more. Implemented
over entity adapters in production (createEntityBillingStore) and over a
Map in tests.
Source: packages/slingshot-billing/src/lib/store.ts
BillingSubscriptionRow
Section titled “BillingSubscriptionRow”A billing_subscriptions row as billing’s domain logic sees it.
Source: packages/slingshot-billing/src/lib/store.ts
CheckoutUrls
Section titled “CheckoutUrls”Redirect URLs a hosted checkout / portal session returns to.
Source: packages/slingshot-billing/src/lib/provider.ts
DonationCheckoutInput
Section titled “DonationCheckoutInput”Input to start a one-time donation checkout.
Source: packages/slingshot-billing/src/lib/provider.ts
Entitlement
Section titled “Entitlement”The single app-agnostic answer billing provides: what an owner has paid for
right now. Apps map plan onto their own domain (a spend tier, a perk, …).
When nothing is active this is { plan: 'free', status: 'none', ... }.
Source: packages/slingshot-billing/src/public.ts
HostedSession
Section titled “HostedSession”A hosted-page URL the caller redirects the user to.
Source: packages/slingshot-billing/src/lib/provider.ts
OwnerRef
Section titled “OwnerRef”Who a customer/subscription belongs to. Defaults to a user id; may later be an org.
Source: packages/slingshot-billing/src/lib/provider.ts
PortalInput
Section titled “PortalInput”Input to open a self-service billing portal session.
Source: packages/slingshot-billing/src/lib/provider.ts
ProviderCustomer
Section titled “ProviderCustomer”A provider-side customer handle, persisted as billing_customers.
Source: packages/slingshot-billing/src/lib/provider.ts
StripeEventLike
Section titled “StripeEventLike”The structural slice of a Stripe webhook event normalizeStripeEvent reads.
Deliberately NOT the SDK’s Stripe.Event: tests build these as plain
objects, and the mapping stays honest about which fields it depends on.
Source: packages/slingshot-billing/src/lib/providers/stripe.ts
SubscriptionCheckoutInput
Section titled “SubscriptionCheckoutInput”Input to start a subscription checkout for a configured plan.
Source: packages/slingshot-billing/src/lib/provider.ts
BillingCustomerInput
Section titled “BillingCustomerInput”Input to create a customer row (id is adapter-generated).
Source: packages/slingshot-billing/src/lib/store.ts
BillingPackageConfig
Section titled “BillingPackageConfig”Validated, inferred configuration shape accepted by createBillingPackage().
Source: packages/slingshot-billing/src/types/config.ts
BillingPaymentInput
Section titled “BillingPaymentInput”Input to create a payment row (id is adapter-generated).
Source: packages/slingshot-billing/src/lib/store.ts
BillingSubscriptionInput
Section titled “BillingSubscriptionInput”Input to create a subscription row (id is adapter-generated).
Source: packages/slingshot-billing/src/lib/store.ts
BillingSubscriptionPatch
Section titled “BillingSubscriptionPatch”Patch applied to an existing subscription row.
Source: packages/slingshot-billing/src/lib/store.ts
DonationsConfig
Section titled “DonationsConfig”Source: packages/slingshot-billing/src/types/config.ts
EntitlementStatus
Section titled “EntitlementStatus”Normalized subscription status surfaced to consuming apps.
Source: packages/slingshot-billing/src/public.ts
PlanConfig
Section titled “PlanConfig”Source: packages/slingshot-billing/src/types/config.ts
ProviderEvent
Section titled “ProviderEvent”A provider webhook event normalized to billing’s domain. verifyAndParseWebhook
returns one of these after a successful signature check; lib/sync.ts (Phase 2)
switches on kind without any provider-specific knowledge.
Source: packages/slingshot-billing/src/lib/provider.ts
StripeProviderConfig
Section titled “StripeProviderConfig”Source: packages/slingshot-billing/src/types/config.ts
SubscriptionStatus
Section titled “SubscriptionStatus”Normalized subscription lifecycle status, provider-agnostic.
Source: packages/slingshot-billing/src/lib/provider.ts
SyncNoopReason
Section titled “SyncNoopReason”Why a sync run produced no emittable result.
Source: packages/slingshot-billing/src/lib/sync.ts
SyncOutcome
Section titled “SyncOutcome”What a sync run did. The webhook route switches on kind:
'entitlement'+changed: true→ emitbilling:entitlement.changed.'payment'→ emitbilling:payment.completedwithpayload.'noop'→ acknowledge (200) and emit nothing.
Source: packages/slingshot-billing/src/lib/sync.ts