@synza/stripe
v0.1.0-alpha.0
Published
Guarded Stripe operations and billing projections for Jazz applications
Readme
@synza/stripe
Stripe billing operations and selected billing facts in your existing Jazz app. This experimental 0.1.0-alpha.0 package borrows your backend database and official Stripe SDK; your application owns authentication, organization policy, and fulfillment.
Table of contents
Install
Use Node.js >=22.12 and ESM. Install the Jazz and Stripe peers alongside Synza:
pnpm add @synza/stripe@alpha [email protected] stripe@^23.0.0The source targets Stripe SDK major 23. Effect v4 is an internal runtime dependency; the Promise entry point requires no Effect imports. Match the host SDK's credentials and account settings to provider.stripeAccountId and provider.livemode. Keep credentials on the server.
Create a billing boundary
Compose synzaTables into your existing Jazz schema and provide a ready, trusted backend Db. The function below declares application boundaries as parameters; Synza supplies no authentication helper.
// Connect host-owned identity and resources to personal billing operations.
import { createSynza, type Caller, type OrganizationAuthorizationPolicy } from '@synza/stripe'
import type { Db } from 'jazz-tools'
import Stripe from 'stripe'
export async function createPersonalCustomer(input: {
db: () => Db | Promise<Db>
stripeSecretKey: string
provider: { stripeAccountId: string; livemode: boolean }
verifiedCaller: Caller
authorizeOrganization: OrganizationAuthorizationPolicy
}) {
const billing = createSynza({
db: input.db,
stripe: new Stripe(input.stripeSecretKey),
provider: input.provider,
authorization: { authorizeOrganization: input.authorizeOrganization },
})
return billing.getOrCreateCustomer({
caller: input.verifiedCaller,
scope: { type: 'account' },
})
}verifiedCaller.accountId must be the admitted Jazz account UUID, represented as a string. Never use a token subject, email, external user ID, or privileged backend identity. The factory accepts the full official Stripe SDK, not a partial customer client. Construction is lazy; execution borrows resources without closing them. Customer results contain local customerId and native stripeCustomerId.
See host integration for schema composition, verified caller resolution, configured offers, Portal, and quantity policies.
Choose APIs
The root createSynza returns Promise methods grouped by purpose:
- Customers:
createCustomer,getOrCreateCustomer. - Checkout: trusted server-constructed
createCheckoutSession, configuredcreateOfferCheckoutSession, andrefreshCheckoutSession. - Management:
createCustomerPortalSession,cancelSubscription,reactivateSubscription, andupdateSubscriptionQuantity. - Scoped reads: Customer getter, plus getters and lists for Subscriptions, Checkout Sessions, Payments, and Invoices. These return narrower DTOs than native Jazz rows.
- Events:
handleWebhookverifies and projects supported provider resources.
@synza/stripe/effect exposes createSynza for lazy native Effects with host-owned execution. The root also exports synzaTables, defineSynzaPermissions, public types, error classes, and describeBillingFailure. Keep failure descriptions in trusted server diagnostics; they can contain causes and provider results.
Synza stores billing facts. It does not derive entitlements, fulfill purchases, or provide generic reconciliation. See the billing contract and source exports.
Control access
Register runtime organization policy once at SynzaConfig.authorization.authorizeOrganization. Its input is { caller, orgId, action }; only true grants access. Missing policy denies organization access, and policy failure becomes AuthorizationError. Personal calls derive ownership from the verified caller.
The exported OrganizationBillingAction union has nine actions: createCustomer, getOrCreateCustomer, createCheckoutSession, refreshCheckoutSession, createCustomerPortalSession, cancelSubscription, reactivateSubscription, updateSubscriptionQuantity, and readBilling. Configured offer creation uses createCheckoutSession. Decide each action independently; read permission grants no management permission.
defineSynzaPermissions(app, { provider, authorizeOrganization }) is a separate direct Jazz read boundary. Its organization callback synchronously builds a Jazz permission expression from trusted membership, rather than calling runtime policy. Merge its five-table bundle into host permissions without replacing host rules or overriding Synza policy keys. It denies user-session writes and grants complete-row reads, including metadata, descriptors, native IDs, and amounts. Keep secrets out of metadata. Selecting columns does not restrict a granted read; scoped DTOs do not limit separately granted Jazz access.
Process webhooks
Configure webhooks: { secret, onEvent } and pass the original body string and Stripe signature to handleWebhook. The optional onEvent receives a native Stripe.Event after applicable publication. Hooks repeat on every delivery, including unsupported and unmapped events; the host must deduplicate business effects. Hook failures propagate. Synza provides no event ledger, queue, or global ordering guarantee.
Use @synza/http for an optional Web Request/Response adapter, or register your own host route.
Validation limits
Combined personal-and-organization direct Jazz read validation is blocked by Jazz #3981 on 2.0.0-alpha.59. Do not deploy that permission bundle as validated combined access. The implementation ledger records failing full gates and scoped evidence. Controlled SDK tests do not certify live Stripe service behavior, credentials, or arbitrary host configuration. This alpha makes no release-readiness claim.
License
MIT.
