Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5f3e891
feat: add fastify stripe package
BashisthaSudeep Jan 15, 2026
1ce18d4
Merge remote-tracking branch 'origin/main' into feat/stripe-package
BashisthaSudeep Feb 6, 2026
bca19db
chore: sync stripe package with eslint changes
BashisthaSudeep Feb 6, 2026
14ddae0
feat: add promocode support
BashisthaSudeep Feb 6, 2026
9bf83f7
Merge remote-tracking branch 'origin/main' into feat/stripe-package
BashisthaSudeep Feb 23, 2026
98261ad
refactor: update stripe package and types
BashisthaSudeep Feb 23, 2026
69eb362
chore: update readme
BashisthaSudeep Feb 23, 2026
9414ee3
Merge branch 'main' of github.com:prefabs-tech/fastify into feat/stri…
uddhab Apr 28, 2026
121420d
chore: update dependencies and refine shared package typings
premsgr May 7, 2026
4ec31aa
Merge branch 'main' of github.com:prefabs-tech/fastify into feat/stri…
premsgr May 11, 2026
553fdba
test(stripe): add Vitest suite, docs, and package tooling
premsgr May 12, 2026
af998cc
refactor(stripe): fix mode-aware metadata, scope webhook errors, drop…
premsgr May 12, 2026
4b33c39
refactor(stripe): make config.stripe optional and harden webhook fall…
premsgr May 12, 2026
584ff70
docs(stripe): expand ANALYSIS.md with comprehensive code classification
premsgr May 12, 2026
47a629a
feat(stripe): resolve config at register time with fastify.config fal…
premsgr May 14, 2026
2b0da85
feat(stripe)!: require register options and peer Stripe SDK
premsgr May 14, 2026
57c0ec1
feat(stripe): move stripe to runtime dependency and refresh docs
premsgr May 14, 2026
d6c9a6e
feat(stripe): fall back to fastify.config.stripe for empty options
premsgr May 14, 2026
39278f5
refactor(stripe): require explicit register options
premsgr May 14, 2026
33a3571
docs(stripe): add package developer README
premsgr May 14, 2026
940ec73
Merge branch 'main' of github.com:prefabs-tech/fastify into feat/stri…
premsgr Jun 8, 2026
4e8adac
chore: refresh pnpm-lock after fastify-config registry resolution
premsgr Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/stripe/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
dist/
/coverage
97 changes: 97 additions & 0 deletions packages/stripe/ANALYSIS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!-- Package analysis — produced by /analyze-package. Do not edit manually. -->

## Base Library Passthrough Analysis

### `stripe` (Stripe Node SDK) — PARTIAL PASSTHROUGH / MODIFIED

- Options type: Custom `StripeConfig` for this plugin plus `clientConfig?: Stripe.StripeConfig` passed verbatim to `new Stripe(apiKey, clientConfig)`.
- Options passed: `apiKey` and `clientConfig` unmodified to constructor. Webhook verification uses `Stripe.webhooks.constructEvent(rawBody, signature, secret)` (SDK static API) with no extra transformation.
- Features restricted: Consumers do not receive a generic “pass all Checkout params” helper; `createCheckoutSession` only accepts `CreateSessionInput` and builds a fixed `SessionCreateParams`. `getActivePromotionCode` wraps `promotionCodes.list` with `active: true` and returns only the first result.
- Features added: Fastify plugin wiring, webhook route, scoped raw-body parser, signature preHandler, optional handler warning + safe default handler, `StripeClient` helper class, TypeScript augmentations, route constant export.

### `@prefabs.tech/fastify-config` — MODIFIED (integration only)

- This package augments `ApiConfig` with optional `stripe?: StripeConfig`; it does not redefine the config system.
- `StripeClient` reads `config.stripe` from a full `ApiConfig` instance.

### `fastify` / `fastify-plugin` — THEIRS (framework)

- Plugin pattern: default export wrapped with `fastify-plugin` so behavior applies to the enclosing Fastify scope. Webhook controller is a **nested** plugin without `fastify-plugin`, keeping the JSON parser override local.

---

## Summary

### Package metadata

- **Runtime dependency:** `stripe@20.3.1` (bundled; consumers do not declare it unless they use the SDK alongside this package).
- **Peers:** `@prefabs.tech/fastify-config@0.94.0`, `fastify@>=5.2.2`, `fastify-plugin@>=5.0.1`.
- **Entry:** `src/index.ts` → `export *` from `./constants`, `./utils`; default plugin from `./plugin`; types `StripeConfig`, `StripeEvent`.

### Public exports (from `src/index.ts` and `src/utils/index.ts`)

| Export | Description |
| ------ | ----------- |
| `default` | `FastifyPluginAsync`-wrapped Stripe plugin; requires non-empty `StripeConfig`. |
| `ROUTE_STRIPE_WEBHOOK` | Constant `"/payment/webhook"`. |
| `StripeClient` | Holds `Stripe` SDK + `createCheckoutSession` + `getActivePromotionCode`. |
| `registerRawBodyParser` | Alias export of the default from `stripeRawBodyParser` — registers JSON parser with `rawBody`. |
| `StripeConfig` | Type of plugin options / `config.stripe`. |
| `StripeEvent` | Type alias for `Stripe.Event`. |

`CreateSessionInput` exists in `src/types/index.ts` but is **not** re-exported from the package entry.

### Internal (not in public API)

| Symbol | Role |
| ------ | ---- |
| `createVerifyStripeSignature` | Factory returning webhook `preHandler`; validates secret, header, `rawBody`; calls `Stripe.webhooks.constructEvent`; sets `request.stripeEvent`. |
| `webhookHandler` | Default fallback when `handlers.webhook` omitted — logs error, responds so Stripe does not retry indefinitely. |
| Webhook `controller` plugin | Registers route, installs raw-body parser in this scope, attaches preHandler. |
| Default export of `stripeRawBodyParser.ts` | Same implementation as `registerRawBodyParser`; registers `application/json` buffer parser setting `request.rawBody` and JSON body. |

### Source modules (one line each)

| File | Responsibility |
| ---- | ---------------- |
| `src/plugin.ts` | Top-level plugin: validate options, optionally register webhook controller. |
| `src/webhook/controller.ts` | Encapsulated route + parser + signature preHandler + dispatch to user or default handler. |
| `src/webhook/handler.ts` | Default no-op handler: log misconfiguration, avoid Stripe retry storms. |
| `src/middlewares/verifyStripeSignature.ts` | `createVerifyStripeSignature` — signature verification preHandler. |
| `src/utils/stripeClient.ts` | `StripeClient` — SDK constructor passthrough + checkout/promo helpers. |
| `src/utils/stripeRawBodyParser.ts` | JSON raw-body parser; augments `FastifyRequest.rawBody`. |
| `src/types/index.ts` | `StripeConfig`, `CreateSessionInput`, webhook types; augments `FastifyRequest.stripeEvent`. |
| `src/constants.ts` | `ROUTE_STRIPE_WEBHOOK`. |

### `StripeClient` methods (ours vs theirs)

| Member | Classification |
| ------ | -------------- |
| `constructor` | **Ours** — requires `config.stripe`; passes `apiKey` + `clientConfig` to `new Stripe(...)`. |
| `stripe` | **Theirs** — live SDK instance. |
| `createCheckoutSession` | **Ours** — builds `SessionCreateParams` (defaults, line item, metadata by mode); **theirs** — `checkout.sessions.create`. |
| `getActivePromotionCode` | **Ours** — restricts to `active: true`, first result; **theirs** — `promotionCodes.list`. |

### Framework / lifecycle

- Registration log: `"Registering Stripe plugin"` (`plugin.ts`).
- Webhook registration log: `"Registering Stripe webhook route"` (`controller.ts`).
- Conditional: `enablePaymentWebhook` registers webhook sub-plugin only when truthy.

### Conditional branches & defaults

- Empty/missing plugin options → throw `"Missing stripe configuration..."`.
- Missing `{ stripeConfig }` on webhook controller options → throw (defensive; normally only called internally).
- No `handlers.webhook` but webhooks enabled → warn at register; route uses default ack handler.
- Webhook path: `stripeConfig.webhookPath || ROUTE_STRIPE_WEBHOOK`.
- Checkout helper defaults: `quantity` → 1, `mode` → `"payment"`, `currency` → `defaultCurrency`, success/cancel URLs from `urls`, `allow_promotion_codes` from `allowPromotionCodes`.

### Default values (ours)

- Fallback webhook handler behavior: log at error level with event id/type; response path returns 200-equivalent completion without throwing.
- JSON parse error in raw-body parser: assign `statusCode: 400` on error for Fastify.

### “Ours” vs “Theirs” highlights

- **Ours:** Validation, logging, warning, error response bodies, `SessionCreateParams` assembly, metadata routing by checkout mode, promotion code list filtering/return shape, plugin encapsulation for parser, module augmentations.
- **Theirs:** `new Stripe(...)`, `stripe.checkout.sessions.create`, `stripe.promotionCodes.list`, `Stripe.webhooks.constructEvent` inputs/outputs as provided by the SDK.
52 changes: 52 additions & 0 deletions packages/stripe/FEATURES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!-- Structured feature inventory — used by automated test generation. Developer docs: see GUIDE.md -->

## Plugin registration

1. The default export is wrapped with `fastify-plugin` so the plugin applies to the parent Fastify scope.
2. If plugin options are missing or an empty object, registration throws with a clear message (`Missing stripe configuration. Did you forget to pass it to the stripe plugin?`).
3. An info-level log is emitted when the Stripe plugin registers (`Registering Stripe plugin`).
4. When `enablePaymentWebhook` is truthy, the webhook controller sub-plugin is registered; when falsy, no webhook route or webhook-scoped parser is installed.

## Webhook route

5. The webhook controller throws if `stripeConfig` is missing from its register options (internal guard).
6. An info-level log is emitted when the webhook route plugin registers (`Registering Stripe webhook route`).
7. If `enablePaymentWebhook` is true and `handlers.webhook` is not set, a warning is logged at registration; the HTTP route still accepts webhooks.
8. The webhook `POST` path is `config.stripe.webhookPath` when set, otherwise the default `ROUTE_STRIPE_WEBHOOK` (`/payment/webhook`).
9. When no custom `handlers.webhook` is provided, the default handler logs an error with event id and type and completes without throwing so Stripe does not retry indefinitely.
10. When `handlers.webhook` is provided, it is invoked with `(request, event)` after successful verification.
11. If `request.stripeEvent` is missing after the preHandler chain (defensive), the route responds with HTTP 500 and a structured error body.

## Webhook signature verification

12. If `webhookSecret` is missing on the resolved config, the preHandler responds with HTTP 400 and `{ error: "Webhook secret not configured" }` and logs an error.
13. If the `stripe-signature` header is missing, responds with HTTP 400 and `{ error: "Missing stripe-signature header" }`.
14. If `request.rawBody` is missing, responds with HTTP 400 and `{ error: "Raw body is not available for signature verification" }`.
15. If `Stripe.webhooks.constructEvent` throws, responds with HTTP 400 and `{ error: "Webhook signature verification failed" }` and logs the error.
16. On success, the verified `Stripe.Event` is assigned to `request.stripeEvent`.

## Raw body parser

17. `registerRawBodyParser` (same implementation as used inside the webhook controller) adds an `application/json` parser using `parseAs: "buffer"`, stores the buffer on `request.rawBody`, and parses JSON for the handler.
18. Invalid JSON in the body results in an error passed to `done` with `statusCode: 400` so clients get a 400 response instead of a generic 500.
19. The parser is registered only on the Fastify instance passed in; when used from the webhook controller, that instance is the non-`fastify-plugin`-wrapped sub-scope so other parent routes keep their default JSON parsing.

## Types and module augmentations

20. Importing the package augments `@prefabs.tech/fastify-config`’s `ApiConfig` with optional `stripe?: StripeConfig`.
21. `FastifyRequest` is augmented with optional `stripeEvent?: Stripe.Event` (from `types/index.ts`).
22. `FastifyRequest` is augmented with optional `rawBody?: Buffer` (from the raw-body parser module).
23. The package entry exports the `StripeConfig` type and `StripeEvent` as a public alias for `Stripe.Event`.

## StripeClient

24. `StripeClient` constructor throws if `config.stripe` is undefined on the provided `ApiConfig`.
25. `StripeClient` constructs `new Stripe(apiKey, clientConfig)` using `config.stripe.apiKey` and optional `config.stripe.clientConfig` unchanged.
26. `createCheckoutSession` builds a single line item from `CreateSessionInput` with defaults: `quantity` defaults to `1`, `mode` defaults to `"payment"`, `currency` defaults to `config.stripe.defaultCurrency`, success and cancel URLs default to `config.stripe.urls`.
27. `createCheckoutSession` sets `allow_promotion_codes` from `config.stripe.allowPromotionCodes` (may be `undefined`).
28. When `metadata` is passed to `createCheckoutSession`, it is set on `metadata` and on the mode-appropriate field only (`payment_intent_data`, `setup_intent_data`, or `subscription_data`); when omitted, metadata and those blocks are left off the payload.
29. `getActivePromotionCode` calls `promotionCodes.list` with `{ active: true, code }` and returns the first element of `data` or `undefined`.

## Constants

30. `ROUTE_STRIPE_WEBHOOK` is exported as the string `/payment/webhook`.
Loading
Loading