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

Bash
# 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-run

Overview

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

TokenWhat you getKey env / config
paymentsStripe 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.
webhooksPOST /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.
connectExpress 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.
checkoutHosted Checkout Session creation; response returns redirect URL and session id.Dashboard Prices; STRIPE_SECRET_KEY.
billingCreate 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.

Scaffold (matches sidebar stack)
npx @fivfold/api add stripe
Feature tokenHTTP surfaceDTOs / persistenceNotes
paymentsPOST …/customers, POST …/payment-intentscreate-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.
webhooksPOST …/webhookWebhook 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).
connectPOST …/connect/accounts, POST …/connect/account-linksconnect DTOs; connected-account rowExpress accounts with requested card_payments + transfers capabilities.
checkoutPOST …/checkout/sessionscreate-checkout-session DTOCreates a Checkout Session; response includes hosted url.
billingPOST …/billing/subscriptions, POST …/billing/portal-sessionssubscription + portal DTOsUses 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

End-to-end flow: the client sends only 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

Platform creates an Express connected account, persists 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

Plain Text
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.ts

Full inventory (all features enabled)

Plain Text
# 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/….

TypeScript
// 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 so POST /webhook stays raw.
  • Auth: Express router includes a no-op authMiddleware; Nest uses req.user?.id when present. Replace with your guards.
  • TypeORM + Nest: Ensure global TypeORM config loads entities under src/modules/stripe/entities or rely on autoLoadEntities: 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)

URLPOST /api/stripe/webhook
HeadersStripe-Signature (required). Body must be the raw request bytes stringified by Stripe — do not parse/re-stringify JSON before verify.
BodyRaw Buffer (Express) / rawBody (Nest) of the JSON event payload.
Success200 JSON { "received": true } or { "received": true, "duplicate": true } when the event id was already processed.
ErrorsMissing 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 effectsUpserts 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)

Bodyemail string (required, email). name optional string. metadata optional object of string values.
SuccessExpress 201; JSON { "customerId": "cus_…" } (Stripe customer id). Local row upserted with userId from auth stub.
ErrorsValidation → Nest 400 BadRequestException with constraint messages; Express 400 { "error": "…" }. Missing STRIPE_SECRET_KEY → adapter throws at first Stripe call.

POST /payment-intents (feature: payments)

BodycartId string (required). stripeAccountId optional Connect account id for destination charges.
Server behaviorCalls 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.
SuccessJSON { "clientSecret", "paymentIntentId", "amountCents", "currency" }. clientSecret may be null per Stripe if not yet available; normally present for Elements / Payment Element.
ErrorsSame validation pattern as customers. Stripe API errors bubble as thrown errors (Express 400 stringified).

POST /checkout/sessions (feature: checkout)

Bodymode: 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_…" }
NotesSingle line item from priceId; extend adapter for multi-line carts.

POST /connect/accounts (feature: connect)

Bodyemail required. metadata optional.
Success{ "accountId": "acct_…" }; local row stores payouts flag snapshot.
ErrorsStub ORM paths (Cosmos/Dynamo) return 400 with notImplemented message until you implement persistence.

POST /connect/account-links (feature: connect)

BodyaccountId, refreshUrl, returnUrl required. type optional account_onboarding | account_update.
Success{ "url": "https://connect.stripe.com/…" }

POST /billing/subscriptions (feature: billing)

BodycustomerId, priceId required. metadata optional.
Success{ "subscriptionId": "sub_…", "status": "…" }

POST /billing/portal-sessions (feature: billing)

BodycustomerId, 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.

.env (backend)
# 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=250

Never 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 resolveCartTotalCents with trusted pricing from your database; never accept money amounts from the client for authorization.
  • Extend processWebhook / handleStripeWebhook for fulfillment logic; keep idempotency on event.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.

Search docs

Search documentation by title or keywords