Stripe Kit
Backend Stripe integration: PaymentIntents with server-side totals, signed webhooks and idempotency, optional Connect (Express accounts and platform fee env), Checkout Sessions, and Billing helpers—Express or NestJS, manifest-driven, ORM-aware.
Quick reference
CLI commands
# Interactive feature multiselect (arrow keys + space)
npx @fivfold/api add stripe
# Non-interactive defaults (payments + webhooks)
npx @fivfold/api add stripe --yes
# Explicit features (works with --yes; overrides default feature list)
npx @fivfold/api add stripe --yes --features=payments,webhooks,connect,checkout,billing
# Stack flags (example)
npx @fivfold/api add stripe --framework=express --orm=prisma --database=postgres
# Preview staged files and AST edits without writing disk
npx @fivfold/api add stripe --dry-runOverview
The Stripe Kit is a backend-only module for @fivfold/api. It generates a small hexagonal slice: a framework-agnostic port (IStripePaymentsPort), a StripeNodeAdapter that wraps the official stripe npm package, DTO-validated HTTP endpoints, and—when you enable features—ORM-specific persistence for customers, payment-intent tracking, webhook idempotency, and connected accounts. Delivery is either Express (createStripeRouter) or NestJS (StripeModule + StripeController). Card data never touches your API: amounts are resolved on the server; confirmation uses Stripe's client SDKs or hosted pages.
Feature selection is first-class: you choose payments, webhooks, connect, checkout, and billing independently so the CLI does not multiply templates for every combination. Cosmos DB and DynamoDB paths ship container/table stubs and Nest service placeholders until you wire the Azure or AWS SDKs; TypeORM, Prisma, and Mongoose paths include working persistence in the generated service code.
Use the stack sidebar on this page to match Framework and ORM to your project. The sections below list the exact files emitted for that pair, how to mount the router or module, and a full HTTP reference (bodies, headers, responses, and errors). There is no FivFold UI kit for Stripe; a short note on using Stripe's own hosted and embedded flows appears later so PCI-sensitive work stays on Stripe.
Features at a glance
| Token | What you get | Key env / config |
|---|---|---|
| payments | Stripe Customer + PaymentIntent creation; local customer and transaction rows (SQL/Prisma/Mongoose). Cart totals via resolveCartTotalCents (you replace). | STRIPE_SECRET_KEY, STRIPE_DEFAULT_CURRENCY, optional STRIPE_DEV_FIXTURE_AMOUNT_CENTS, optional STRIPE_PLATFORM_FEE_BPS with Connect destination charges. |
| webhooks | POST /webhook with signature verification and idempotent event storage; updates payment / Connect rows when those features are also on. | STRIPE_WEBHOOK_SECRET; raw body on Nest (rawBody: true) or Express raw parser on the webhook route only. |
| connect | Express connected accounts, account links, optional application fee on destination PaymentIntents via basis points. | Same secret key; platform fee from STRIPE_PLATFORM_FEE_BPS when stripeAccountId is sent on payment-intent creation. |
| checkout | Hosted Checkout Session creation; response returns redirect URL and session id. | Dashboard Prices; STRIPE_SECRET_KEY. |
| billing | Create subscription and Customer Portal session URLs. | Billing products/prices in Dashboard; portal configuration; STRIPE_SECRET_KEY. |
Installation, flags, and feature matrix
Run npx @fivfold/api init in the backend workspace first so fivfold.json exists. The Stripe kit is backend-only: it does not ship React components or card inputs. It scaffolds a hexagonal slice (domain port, Node adapter, optional ORM persistence, HTTP delivery) aligned with Stripe's current APIs (PaymentIntents, Checkout Sessions, Connect Express, Billing helpers, signed webhooks). Legacy Charges API is not generated.
Interactive mode opens a multiselect for features (arrow keys, space to toggle, enter to confirm). Non-interactive: use --yes for defaults payments,webhooks, or pass an explicit --features=payments,webhooks,connect,checkout,billing. If both are present, --features= wins over the default list. Use --dry-run to preview VFS changes without writing disk.
npx @fivfold/api add stripe| Feature token | HTTP surface | DTOs / persistence | Notes |
|---|---|---|---|
| payments | POST …/customers, POST …/payment-intents | create-customer, create-payment-intent DTOs; customer + payment transaction rows (except Cosmos/Dynamo stubs) | Amount is resolved in resolveCartTotalCents — replace with your cart/order service. Optional stripeAccountId for Connect destination charges; STRIPE_PLATFORM_FEE_BPS applies application fee when set. |
| webhooks | POST …/webhook | Webhook event idempotency store (TypeORM / Prisma / Mongoose) | Requires raw body verification. Express: express.raw on webhook path only. Nest: rawBody: true. Handles payment_intent.succeeded (if payments) and account.updated (if connect). |
| connect | POST …/connect/accounts, POST …/connect/account-links | connect DTOs; connected-account row | Express accounts with requested card_payments + transfers capabilities. |
| checkout | POST …/checkout/sessions | create-checkout-session DTO | Creates a Checkout Session; response includes hosted url. |
| billing | POST …/billing/subscriptions, POST …/billing/portal-sessions | subscription + portal DTOs | Uses Stripe Billing APIs; configure Customer Portal in Dashboard when using portal. |
Architecture and flows
The domain defines IStripePaymentsPort; StripeNodeAdapter is the only Stripe SDK touchpoint. HTTP layers validate bodies with class-validator and delegate to the service, which orchestrates persistence then calls the port. Controllers and routes use placeholder auth: replace REPLACE_WITH_USER_ID and wire your session/JWT middleware.
PaymentIntents and asynchronous webhook
cartId; the backend resolves money from the database, creates the PaymentIntent, returns clientSecret for Stripe.js; confirmation goes directly to Stripe. Fulfillment after success should not rely only on the browser—use the highlighted webhook path for idempotent order updates.Figure
Stripe Connect Express onboarding
acct_…, issues an Account Link, and redirects the vendor to Stripe-hosted onboarding. After KYC, Stripe redirects back and sends account.updated; the kit's webhook handler updates payoutsEnabled when the connect feature is enabled.Figure
Generated file structure
Paths use the default kit output root src/modules/stripe (from outputDir + kit folder stripe in fivfold.json). Below is exactly what is written for nestjs + typeorm.
Default: payments + webhooks
src/modules/stripe/domain/stripe.port.ts
src/modules/stripe/adapters/stripe-node.adapter.ts
src/modules/stripe/dto/create-customer.dto.ts
src/modules/stripe/dto/create-payment-intent.dto.ts
src/modules/stripe/entities/stripe-customer.entity.ts
src/modules/stripe/entities/stripe-payment-transaction.entity.ts
src/modules/stripe/entities/stripe-webhook-event.entity.ts
src/modules/stripe/stripe.module.ts
src/modules/stripe/stripe.controller.ts
src/modules/stripe/stripe.service.tsFull inventory (all features enabled)
# Every file the kit can emit for nestjs + typeorm
# (omit dto/entity/schema/model slices when the feature is disabled)
src/modules/stripe/
domain/stripe.port.ts
adapters/stripe-node.adapter.ts
dto/create-customer.dto.ts # payments
dto/create-payment-intent.dto.ts # payments
dto/create-checkout-session.dto.ts # checkout
dto/create-connect-account.dto.ts # connect
dto/create-account-link.dto.ts # connect
dto/create-subscription.dto.ts # billing
dto/create-portal-session.dto.ts # billing
src/modules/stripe/
entities/stripe-customer.entity.ts # payments
entities/stripe-payment-transaction.entity.ts # payments
entities/stripe-webhook-event.entity.ts # webhooks
entities/stripe-connected-account.entity.ts # connect
src/modules/stripe/
stripe.module.ts
stripe.controller.ts
stripe.service.ts
# AST / host app (not under src/modules/stripe/ unless your project layout differs)
# Express: src/app.ts — registerStripe router (see Wire into the app)
# NestJS: src/app.module.ts — StripeModule import
# NestJS + webhooks: src/main.ts — NestFactory.create(..., { rawBody: true })Wire into the app
The CLI stages an AST mutation on src/app.ts (Express) or src/app.module.ts (Nest). Adjust import paths if your entry files live elsewhere. Mount point is /api/stripe → routes/controller base stripe, so public paths are /api/stripe/….
// FivFold registers StripeModule in AppModule and imports ./modules/stripe/stripe.module
//
// 1) TypeORM: include Stripe entities in the same connection (forFeature is already in StripeModule).
// 2) Run migrations if you do not use synchronize.
// 3) Webhooks: confirm main.ts contains NestFactory.create(AppModule, { rawBody: true }) (kit AST when webhooks enabled).- Webhooks: Nest must expose raw body on the webhook route; the generated controller reads
req.rawBody. Express applies JSON parser only after the webhook route soPOST /webhookstays raw. - Auth: Express router includes a no-op
authMiddleware; Nest usesreq.user?.idwhen present. Replace with your guards. - TypeORM + Nest: Ensure global TypeORM config loads entities under
src/modules/stripe/entitiesor rely onautoLoadEntities: true.
API reference
Base URL assumed: /api/stripe. All JSON routes expect Content-Type: application/json except the webhook (raw JSON bytes, see below). Successful JSON endpoints return 201 (Express) or Nest default 200/201 as implemented — the tables note status codes.
POST /webhook (feature: webhooks)
| URL | POST /api/stripe/webhook |
| Headers | Stripe-Signature (required). Body must be the raw request bytes stringified by Stripe — do not parse/re-stringify JSON before verify. |
| Body | Raw Buffer (Express) / rawBody (Nest) of the JSON event payload. |
| Success | 200 JSON { "received": true } or { "received": true, "duplicate": true } when the event id was already processed. |
| Errors | Missing secret or signature → adapter throws (Express 400 with { "error": "…" }; Nest webhook → BadRequestException if raw body missing). Invalid signature → Stripe SDK error surfaced as 400 (Express) or 500 depending on global filter (verify in your app). |
| Side effects | Upserts webhook event row; on first process updates related payment transaction or connected account when event type matches (see template). Cosmos/Dynamo: implement storage yourself while keeping verify logic. |
POST /customers (feature: payments)
| Body | email string (required, email). name optional string. metadata optional object of string values. |
| Success | Express 201; JSON { "customerId": "cus_…" } (Stripe customer id). Local row upserted with userId from auth stub. |
| Errors | Validation → Nest 400 BadRequestException with constraint messages; Express 400 { "error": "…" }. Missing STRIPE_SECRET_KEY → adapter throws at first Stripe call. |
POST /payment-intents (feature: payments)
| Body | cartId string (required). stripeAccountId optional Connect account id for destination charges. |
| Server behavior | Calls resolveCartTotalCents(cartId) (stub returns STRIPE_DEV_FIXTURE_AMOUNT_CENTS or 1000). Currency from STRIPE_DEFAULT_CURRENCY (default usd). Creates PaymentIntent with automatic_payment_methods. If stripeAccountId set and STRIPE_PLATFORM_FEE_BPS valid, sets application_fee_amount. |
| Success | JSON { "clientSecret", "paymentIntentId", "amountCents", "currency" }. clientSecret may be null per Stripe if not yet available; normally present for Elements / Payment Element. |
| Errors | Same validation pattern as customers. Stripe API errors bubble as thrown errors (Express 400 stringified). |
POST /checkout/sessions (feature: checkout)
| Body | mode: payment | subscription. priceId required. quantity optional string (coerced to number, default 1). successUrl, cancelUrl valid URLs. customerId, stripeAccountId optional. |
| Success | { "url": "https://checkout.stripe.com/…", "sessionId": "cs_…" } |
| Notes | Single line item from priceId; extend adapter for multi-line carts. |
POST /connect/accounts (feature: connect)
| Body | email required. metadata optional. |
| Success | { "accountId": "acct_…" }; local row stores payouts flag snapshot. |
| Errors | Stub ORM paths (Cosmos/Dynamo) return 400 with notImplemented message until you implement persistence. |
POST /connect/account-links (feature: connect)
| Body | accountId, refreshUrl, returnUrl required. type optional account_onboarding | account_update. |
| Success | { "url": "https://connect.stripe.com/…" } |
POST /billing/subscriptions (feature: billing)
| Body | customerId, priceId required. metadata optional. |
| Success | { "subscriptionId": "sub_…", "status": "…" } |
POST /billing/portal-sessions (feature: billing)
| Body | customerId, returnUrl required. |
| Success | { "url": "https://billing.stripe.com/…" } |
Route summary: webhook (webhooks); customers, payment-intents (payments); checkout/sessions (checkout); connect/accounts, connect/account-links (connect); billing/subscriptions, billing/portal-sessions (billing).
Browsers and Stripe-hosted surfaces
This kit intentionally does not include a client UI layer. For anything involving card numbers or strong customer authentication, use Stripe's own surfaces—Stripe.js with the Payment Element or Card Element, Embedded Checkout, or hosted Checkout / Customer Portal URLs returned by this API—so sensitive data stays inside Stripe's PCI boundaries. Your application should only handle publishable keys in the browser, call these backend endpoints for intents and sessions, and never send raw card data to your server.
Stripe Dashboard, environment variables, and SDK
In the Stripe Dashboard, create a webhook endpoint pointing to your deployed https://your.api/api/stripe/webhook and select the event types you rely on (at minimum payment_intent.succeeded and account.updated if you use the generated handlers). Copy the signing secret into STRIPE_WEBHOOK_SECRET. Use test mode keys until you go live.
# Required for server-side Stripe API calls
STRIPE_SECRET_KEY=sk_test_...
# Webhooks feature — signing secret from Dashboard
STRIPE_WEBHOOK_SECRET=whsec_...
# Optional: pin API version (must match Stripe typings you use)
STRIPE_API_VERSION=2024-11-20.acacia
# PaymentIntents default currency (lowercase ISO code)
STRIPE_DEFAULT_CURRENCY=usd
# Dev-only stub when cart pricing is not wired yet
STRIPE_DEV_FIXTURE_AMOUNT_CENTS=1000
# Connect: optional platform fee (basis points, 100 = 1%) on destination PaymentIntents
STRIPE_PLATFORM_FEE_BPS=250Never expose STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET to a browser. The adapter default API version is 2024-11-20.acacia unless overridden.
Security and operations
- Replace
resolveCartTotalCentswith trusted pricing from your database; never accept money amounts from the client for authorization. - Extend
processWebhook/handleStripeWebhookfor fulfillment logic; keep idempotency onevent.id. - Rotate keys and webhook secrets per environment; use Stripe's test/live mode separation.
- Connect: complete Stripe Connect onboarding requirements before production charges.
- Run database migrations after scaffold (TypeORM, Prisma, or your Mongoose strategy) so Stripe tables exist before traffic.
