@roostjs/billing
v0.2.0
Published
Provider-agnostic billing abstraction for Cloudflare Workers. Ships with a Stripe implementation that uses raw REST calls instead of the Stripe SDK, keeping your bundle lean.
Readme
@roostjs/billing
Provider-agnostic billing abstraction for Cloudflare Workers. Ships with a Stripe implementation that uses raw REST calls instead of the Stripe SDK, keeping your bundle lean.
Part of Roost — the Laravel of Cloudflare Workers.
Installation
bun add @roostjs/billingQuick Start
import { StripeProvider } from '@roostjs/billing';
const billing = new StripeProvider(env.STRIPE_SECRET_KEY, env.STRIPE_WEBHOOK_SECRET);
// Create a customer and start a subscription
const { providerId } = await billing.createCustomer({ name: 'Acme', email: '[email protected]' });
const { subscriptionId, status } = await billing.subscribe({
customerId: providerId,
priceId: 'price_xxx',
trialDays: 14,
});
// Send them to a hosted checkout page
const { url } = await billing.createCheckoutSession({
customerId: providerId,
priceId: 'price_xxx',
successUrl: 'https://app.example.com/welcome',
cancelUrl: 'https://app.example.com/pricing',
});Features
BillingProviderinterface — swap providers without touching application codeStripeProviderbacked by raw Stripe REST API (nostripenpm package required)- Subscriptions: create, cancel, resume, swap price
- Checkout sessions and customer portal sessions
- Usage-based billing via
reportUsage() - Stripe webhook verification with
parseWebhookEvent() FakeBillingProviderwith assertion helpers for unit testsSubscribedMiddlewareandOnTrialMiddlewarefor route-level subscription gates
API
StripeProvider
new StripeProvider(secretKey: string, webhookSecret: string)billing.createCustomer({ name, email, metadata? })
// => { providerId: string }
billing.subscribe({ customerId, priceId, trialDays?, metadata? })
// => { subscriptionId, status, currentPeriodEnd }
billing.cancelSubscription(subscriptionId)
billing.resumeSubscription(subscriptionId)
billing.swapSubscription(subscriptionId, newPriceId)
// => { subscriptionId, status, currentPeriodEnd }
billing.getSubscriptionStatus(subscriptionId)
// => SubscriptionStatus: 'active' | 'trialing' | 'past_due' | 'canceled' | ...
billing.createCheckoutSession({ customerId, priceId, successUrl, cancelUrl, trialDays? })
// => { sessionId, url }
billing.createPortalSession({ customerId, returnUrl })
// => { url }
billing.reportUsage({ subscriptionItemId, quantity, timestamp? })
billing.parseWebhookEvent(request, secret?)
// => { id, type, data }FakeBillingProvider (testing)
import { Billing, FakeBillingProvider } from '@roostjs/billing';
const fake = Billing.fake(); // installs globally; returns FakeBillingProvider instance
// ... run code that calls billing ...
fake.assertCustomerCreated('[email protected]');
fake.assertSubscribed('cus_fake_1');
fake.assertCanceled('sub_fake_1');
Billing.restore();FakeBillingProvider exposes customers, subscriptions, canceledSubscriptions, usageRecords, and webhookEvents arrays for direct inspection alongside the assertion helpers.
BillingProvider interface
import type { BillingProvider } from '@roostjs/billing';
class MyProvider implements BillingProvider {
// implement the full interface to swap providers
}Documentation
Full documentation at roost.birdcar.dev/docs/reference/billing
License
MIT
