Payments - Stripe Plugin
This workspace provides a Stripe implementation for the universal @payments/driver API. It plugs into the generated drivers registry and becomes selectable via appConfig.ts.
with/payments-stripe
npx @fullproduct/universal-app install with/payments-stripeThis will add the @payments/stripe workspace and wire it into the driver registry. During npm run dev, the drivers registry is regenerated automatically.
Workspace structure
- stripe.payments.ts ← exports 'driver'
- driver.signature.ts ← shared signatures
- payments.drivers.generated.ts ← includes 'stripe'
- drivers.config.ts ← options (payments.stripe)
Configure Stripe
Add your Stripe credentials to the appropriate env files. Example:
STRIPE_SECRET_KEY=sk_live_or_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_or_test_...EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_or_test_...Only expose publishable keys to the client. Keep secret keys server‑side.
Recommended settings
- Make sure your branding is set up for your checkout and customer portal pages
- Add your products, prices and add their
price_id’s to your env as well - Limit your customers to 1 subscription (this will avoid issues, I swear)
- Add
https://{your-domain}/api/webhooks/stripeas the webhook event endpoint
Select Stripe as the active provider
import { DRIVER_OPTIONS, createDriverConfig } from '@app/registries/drivers.config'
export const appConfig = {
// ...
drivers: createDriverConfig({
db: DRIVER_OPTIONS.db.mongoose,
payments: DRIVER_OPTIONS.payments.stripe,
}),
} as constExports via @payments/driver
Once selected, import from @payments/driver universally. The Stripe implementation fulfills the shared Zod contract defined in @payments/driver/driver.signature.ts:
provider—'stripe'ensureCustomer(input)→{ provider, user, customer }startCheckout(input)→{ provider, user, customer, checkoutUrl }syncPaymentData(input)→{ provider, user?, customer?, subscriptions?, purchases? }startPortalSession(input)→{ provider, customer, portalUrl }paymentEventWebhook({ rawBody, headers, eventType? })→{ received: boolean }wekhookRouteHandler(req, { params })checkoutRouteHandler(req, { params })portalRouteHandler(req, { params })paymentRoutes—{ checkout, portal, webhook }
Typical flows
Checkout
import { startCheckout } from '@payments/driver'
const { checkoutUrl } = await startCheckout({
user,
lineItems: [{ sku: 'pro_monthly', quantity: 1 }],
mode: 'subscription',
successUrl: `${baseURL}/billing/success`,
cancelUrl: `${baseURL}/billing/cancel`,
})
// redirect(checkoutUrl)Portal
import { startPortalSession } from '@payments/driver'
const { portalUrl } = await startPortalSession({
user,
returnUrl: `${baseURL}/settings/billing`,
})
// redirect(portalUrl)Webhooks
Stripe will call your webhook route. The driver exposes a handler that validates and processes events, typically followed by a call to syncPaymentData() under the hood.
import { wekhookRouteHandler } from '@payments/driver'
// Next.js route handler example (RSC/edge-compatible shape)
export const POST = (req, ctx) => wekhookRouteHandler(req, ctx)Double check that you actually set up your webhooks endpoint in the Stripe settings / UI for both your test and live environments.
You’ll likely need to add a webhook secret to your env vars too.
Portal route
import { portalRouteHandler } from '@payments/driver'
export const GET = (req, ctx) => portalRouteHandler(req, ctx)Notes
- For accurate signatures and shapes, see
@payments/driver/driver.signature.ts. - After installation or changes, run the drivers collector if needed:
npx turbo run @green-stack/core#collect-driversIf you need multiple payment providers installed side‑by‑side (e.g., migration or regional split), you can install multiple implementations, but select one main provider in appConfig.ts for @payments/driver.
