# Add @suaveplan/payment-provider-stripe sibling adapter **Package**: `@suaveplan/payment-provider-stripe` **Status**: PROPOSED **Type**: new-package ## Why The `2026-05-17-slim-payment-providers` change extracted the five concrete provider adapters (Stripe, PayPal, Square, Mollie, Adyen) out of `@suaveplan/payment-providers` and into sibling packages so consumers pay only for the providers they install. This change lands the **Stripe** sibling. `@suaveplan/payment-provider-stripe` re-lands the `StripeProvider` class (previously `@suaveplan/payment-providers.StripeProvider` prior to the slim-down) verbatim against the slim foundation. It implements `PaymentProvider` from `@suaveplan/payment`, uses `httpRequest` from the slim foundation for every HTTP call, forwards Bearer auth and the caller-supplied `Idempotency-Key` header on every mutating request, maps Stripe payment-intent / refund status codes to the domain `PaymentStatus` / `RefundStatus` enums, and raises only `PaymentProviderError`. ## What changes - ADDED: `@suaveplan/payment-provider-stripe` package at `packages/universal/payment-provider-stripe/` (`0.1.0`, universal tier, `sideEffects: false`). - ADDED: `class StripeProvider implements PaymentProvider` with literal `name: "stripe"`. Constructor accepts a `StripeConfig` extending `ApiKeyConfig` (Stripe secret key + optional `webhookSecret`, plus the shared `ProviderCommonOptions`). - ADDED: All six `PaymentProvider` methods — `createIntent`, `confirmIntent`, `captureIntent`, `cancelIntent`, `refund`, `getIntent` — each routed through `httpRequest` against the Stripe REST API. - ADDED: Stripe-domain → `PaymentIntent` / `Refund` mappers (`mapIntent`, `mapRefund`, `mapStatus`, `mapRefundStatus`, `mapRefundReason`) that translate every Stripe lifecycle state into the `@suaveplan/payment` enums. - ADDED: `StripeConfig` type re-export so consumers can pin the construction shape without reaching into the slim core for `ApiKeyConfig`. - ADDED: Subpath exports for `./stripe/stripe` so consumers can tree-shake the provider class directly. ## Out of scope - Webhook signature verification — `@suaveplan/payment` already ships the `WebhookVerifier`; this package only carries the `webhookSecret` field on `StripeConfig` for downstream wiring. - OpenTelemetry spans / counters on the provider — defers to a monorepo-wide observability pass tracking `@suaveplan/payment`-level instrumentation. - Stripe Connect, Customer / Setup-intent / Subscription APIs — the `PaymentProvider` contract only covers payment-intent + refund lifecycle; non-contract Stripe surfaces are deferred to dedicated follow-up packages. - Currency conversion — Stripe's minor-unit (cents) amount is mapped to `MoneyLike.amount: bigint` 1:1 with currency uppercased; no zero-decimal currency adjustment is performed in this change (parity with the pre-slim shape). ## Source mapping This change derives from `../suaveplan/enterprise/payment-providers.md` and the verbatim upstream Stripe implementation that was bundled into the just-slimmed `@suaveplan/payment-providers` package. | Requirement | Source lines | Notes | |---|---|---| | Universal-tier purity (no Node / browser / React deps) | `enterprise/payment-providers.md` L37 | verbatim cross-cutting rule | | Zero `Math.random` / `Date.now` at module scope; injectable seams | `enterprise/payment-providers.md` L40 | satisfied via shared `ProviderCommonOptions` (fetch/timeoutMs/signal) | | Structured errors extending `@suaveplan/error` | `enterprise/payment-providers.md` L42, L61 | uses `PaymentProviderError` from the slim core; raises no raw `Error` | | `AbortSignal` everywhere async, per-call timeout | `enterprise/payment-providers.md` L43 | inherits `httpRequest` composition of caller `signal` + internal timeout | | Idempotency-key wiring on mutating calls | `enterprise/payment-providers.md` L44 | `createIntent` / `refund` forward `Idempotency-Key` when caller supplies it | | Tree-shakeable, `sideEffects: false` | `enterprise/payment-providers.md` L55 | verbatim | | `workspace:^` internal deps + Bun catalog externals | `enterprise/payment-providers.md` L56 | verbatim | | 100% Istanbul coverage + 100% docs coverage | `enterprise/payment-providers.md` L50-L51 | genesis-wide policy as well | | File-size budget (250 soft / 300 hard / 500 test) | `enterprise/payment-providers.md` L52 | verbatim | | StripeProvider full lifecycle (`createIntent` / `confirmIntent` / `captureIntent` / `cancelIntent` / `refund` / `getIntent`) | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L130-L209 | verbatim port against slim foundation; previously folded as a removed requirement by `2026-05-17-slim-payment-providers` (`specs/payment-providers/spec.md` "Requirement: StripeProvider full lifecycle") | | Stripe status → `PaymentStatus` mapping table | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L45-L56 | verbatim 7-row mapping including `unknown → failed` fallback | | Stripe refund status → `RefundStatus` mapping | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L58-L62 | verbatim 3-branch mapping (`succeeded` / `pending` / `failed`) | | Stripe refund reason → `RefundReason` mapping | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L104-L111 | verbatim 4-branch mapping (`duplicate` / `fraudulent` / `requested_by_customer` / `other`) | | Bearer auth header on every request | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L211-L217 | verbatim `Authorization: Bearer ` header | | `StripeProvider.name === "stripe"` | `/mnt/s/web/suaveplan/packages/universal/payment-providers/src/stripe.ts` L119 | verbatim literal name | ### Requirements with no source line | Requirement | Justification | |---|---| | `provider: "stripe"` discriminant on every raised `PaymentProviderError` | Slim-foundation `httpRequest` accepts a `provider` field on `HttpOptions`; wiring it on every call lets consumers discriminate Stripe failures from sibling-package failures without parsing messages. Not required by the upstream file but mandatory for the slim-core contract. | | Injectable `fetch` / `signal` / `timeoutMs` forwarded to `httpRequest` on every call | The upstream file held its own `timeoutMs` but never forwarded the caller's external `signal` or `fetch`. Required by the slim foundation (every sibling MUST flow through `ProviderCommonOptions`); upgraded from the upstream surface in this migration. | ## Verification approach Every requirement in `specs/payment-provider-stripe/spec.md` carries a `**Verified by:**` line citing concrete `it(...)` titles in: - `src/stripe/stripe.test.ts` — every `PaymentProvider` method, the Bearer / `Idempotency-Key` header forwarding, every status / refund- status / refund-reason branch, `provider` discriminant on raised errors, injected `FetchLike` + `signal` forwarding. - `tests/dep-graph.test.ts` — pins universal-tier purity (no `node:` / `bun:` / `fs` / `path` / etc imports under `src/`). Tests use a real recording `FetchLike` closure — no `vi.mock`, no `vi.stubGlobal`. The Stripe REST API surface is exercised purely through the injected fetch. ## Migration impact None — additive only. This package is brand new under `packages/universal/payment-provider-stripe/` and is not yet consumed by any other package in the monorepo. Consumers that previously imported `StripeProvider` from `@suaveplan/payment-providers` (none exist on genesis; that surface was removed by `2026-05-17-slim-payment-providers` before any consumer landed) will import from the new package per the slim-core migration table. ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/payment-provider-stripe` to enumerate. The package is brand new with no current dependents. | Dependent | Action | |---|---| | (none) | [x] no action needed — new package, no consumers yet | Future dependents will be: `@suaveplan/api-plugin-payment` (when it lands as a Stripe-configurable host) and any application-layer package that wires the `PaymentEngine` to Stripe.