Skip to content

FRD: Payments Provider Wrapper

Document Summary

FieldDetails
Feature NamePayments Provider Wrapper
StatusDraft
OwnerDavid Holmes
ContributorsEngineering
Target Releasev2.0.0
Related LinksADR-051 (Provider Pattern), src/components/widgets/payment-form.tsx, ADR-027 (Default Tech Stack), ADR-014 (Open Source First)
Last Updated2026-06-02
Open Source Librariesstripe
DocumentationStripe API Docs · Node.js SDK · Payment Intents · Webhooks

This FRD ships @dmwd-io/payments as an optional package that re-exports stripe as the platform standard for payment processing. The value is standards enforcement and drift prevention — not a custom abstraction layer. Per ADR-014, the library’s own API is the interface.


Introduction

Overview

Per ADR-014 (Open Source First), the platform designates stripe as the community library standard for payment processing. Rather than building a custom PaymentsProvider interface and adapter layer, @dmwd-io/payments is a thin re-export package that surfaces stripe directly and documents platform conventions on top: the canonical env var names (STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET), PII sanitization requirements, and event naming conventions. Consuming apps import from @dmwd-io/payments instead of stripe directly, which gives the platform a single upgrade and governance point without hiding the library’s native API.

Goals

  • Designate stripe as the platform standard for payment processing via ADR-014.
  • Ship @dmwd-io/payments as a thin re-export of stripe so all apps share a single governed version.
  • Document STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, and STRIPE_WEBHOOK_SECRET as the canonical platform env var names.
  • Prevent per-app SDK version drift by centralizing the stripe dependency.
  • Include examples showing checkout flow, saved payment methods, and failure handling using the native stripe API.

Non-Goals

  • Building a custom PaymentsProvider interface or adapter layer — the library’s own API is the interface (per ADR-014).
  • Building a production adapter for PayPal or Square.
  • Implementing subscription billing logic (see FRD: Billing Provider Wrapper).
  • PCI compliance scope — tokenization is delegated to the vendor’s client-side SDK (Stripe Elements).
  • Building new payment UI components beyond integrating with the existing payment-form widget.
  • Currency conversion or multi-currency routing.

Scope

In Scope

AreaDescription
Re-export package@dmwd-io/payments with stripe as a peer dependency and export * from 'stripe' barrel
Env var conventionsDocumented platform-standard names: STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET
UI callbacksPaymentFormCallbacks type that the payment-form widget accepts for integration
Webhook eventsPlatform event naming conventions layered on top of Stripe’s native webhook types
PII conventionsDocumentation of which Stripe error fields must be sanitized before logging

Out of Scope

AreaReason
Custom provider interfacePer ADR-014 — library API is the interface
Vendor adapter implementationsNot needed; stripe is the standard
Subscription managementCovered by FRD: Billing Provider Wrapper
PCI-scoped tokenizationHandled by Stripe Elements client-side SDK
Fraud detectionVendor-side or dedicated service
Payment form UI redesignExisting widget is sufficient; visual changes are a separate effort

Users and Pain Points

User Groups

UserDescriptionNeeds
App developersEngineers building checkout flowsA single governed import path for payment processing
QA engineersTesters validating payment scenariosDeterministic test outcomes using Stripe test mode keys
Design system maintainersLibrary contributorsA thin package that enforces version alignment without hiding the library

Pain Points

UserPain PointImpact
App developersEach app pins its own stripe version, causing silent API driftBreaking changes surface inconsistently across apps
App developersThe payment-form widget has no standard callback contract for processingDevelopers wire callbacks ad-hoc; shapes vary between apps
QA engineersNo platform guidance on Stripe test mode key usage or fixture setupPayment error paths are tested inconsistently

Definitions

TermDefinition
Checkout sessionA server-created session representing a payment intent, optionally redirecting to a Stripe-hosted page
Payment methodA stored instrument (card, bank account) associated with a customer
ChargeA completed or attempted payment against a payment method
RefundA full or partial reversal of a completed charge
ReceiptA confirmation record for a completed charge, suitable for display or email
Payment tokenA Stripe-issued opaque string representing card details collected client-side
Thin re-exportA package whose primary job is export * from '<library>' plus platform conventions

Current State

Existing Behavior

The payment-form.tsx widget renders card input fields and a submit button. It accepts an onSubmit callback but has no opinion on what happens after form submission. Each app implements its own Stripe PaymentIntent creation, confirmation, and error display logic by importing stripe directly.

Current Limitations

  • No shared import path — apps depend on different stripe versions independently.
  • No platform guidance on env var naming; apps use inconsistent names like STRIPE_KEY, STRIPE_API_KEY, or NEXT_PUBLIC_STRIPE_KEY.
  • Payment error messages are vendor-specific strings displayed raw to users.
  • No shared receipt generation — each app formats confirmation differently.

Existing Workarounds

  • Apps import Stripe SDK directly in API routes and client components.
  • Storybook stories for payment-form pass a no-op onSubmit that logs to console.

Proposed Solution

Summary

Per ADR-014, @dmwd-io/payments is a thin re-export package: stripe is declared as a peer dependency, and src/index.ts re-exports everything from it. No custom interface is built — the library’s API is the interface. Platform conventions (env var names, PII sanitization, event naming) are documented alongside the package but do not wrap or replace any native API surface.

packages/payments/src/index.ts
export * from "stripe";
// Consuming app — import from the platform package, not stripe directly
import Stripe from "@dmwd-io/payments";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

Platform env var names

VariablePurpose
STRIPE_SECRET_KEYServer-side secret key for API calls
STRIPE_PUBLISHABLE_KEYClient-side publishable key for Stripe Elements
STRIPE_WEBHOOK_SECRETWebhook endpoint signing secret

No custom interface is built — the library’s API is the interface.

Key Capabilities

  • All native stripe SDK capabilities are available via the re-export.
  • Platform env var names are the single source of truth across all apps.
  • PaymentFormCallbacks type bridges the existing payment-form widget to Stripe’s native callback shapes.
  • PII sanitization conventions prevent card numbers and tokens from appearing in logs.

User Experience

End users see more consistent error messages when payments fail because all apps follow the same platform conventions for error handling. No direct user-facing changes — the package governs the data layer that powers existing UI components.

Developer Experience

Developers import from @dmwd-io/payments instead of stripe. They initialize Stripe using STRIPE_SECRET_KEY and use the full native stripe API without any custom wrapper. The PaymentFormCallbacks type is available as a platform-standard bridge to the payment-form widget.


Requirements

IDRequirementPriorityNotes
FR-001@dmwd-io/payments re-exports all stripe exportsMust-
FR-002stripe is declared as a peer dependency in package.jsonMust-
FR-003STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET are documented as canonical env var namesMust-
FR-004Export PaymentFormCallbacks type compatible with payment-form widget’s onSubmit/onError propsShould-
FR-005Document PII sanitization requirements for Stripe error objectsMustPer NFR-006
FR-006Export PaymentEvent platform wrapper type for webhook normalizationShouldPer ADR-065

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001import Stripe from "@dmwd-io/payments" works identically to import Stripe from "stripe"Single governed import pathMust
FUNC-002Platform docs show new Stripe(process.env.STRIPE_SECRET_KEY) as the canonical initialization patternConsistent initialization across appsMust
FUNC-003PaymentFormCallbacks type wires payment-form widget onSubmit/onSuccess/onError to Stripe’s native typesEliminates ad-hoc wiringShould
FUNC-004Storybook docs page shows checkout session creation, payment method listing, and refund flows using native Stripe APIDevelopers can follow platform examplesMust
FUNC-005PII sanitization helper strips card numbers and tokens from Stripe error objects before loggingPrevents sensitive data leakageMust

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001Package adds zero net runtime dependencies beyond stripe itselfPerformanceMust
NFR-002Re-export barrel does not import or execute any Stripe SDK code at module load timePerformanceMust
NFR-003stripe peer dependency version range is documented and kept currentMaintainabilityMust
NFR-004All monetary amounts in platform docs and examples use integers in smallest currency unit (cents)ReliabilityMust
NFR-005Package does not wrap or proxy any native stripe methodMaintainabilityMust
NFR-006Payment tokens and card numbers must never appear in platform error conventions or log helpersSecurityMust

API / Interface Requirements

Public API

NameTypeDescriptionRequired
default (Stripe)re-exportNative Stripe class from stripeYes
All stripe named exportsre-exportexport * from 'stripe' — no wrappingYes
PaymentFormCallbackstype{ onSubmit, onSuccess, onError } callback shape for payment-form widget integrationYes
PaymentEventtypePlatform envelope for Stripe webhook eventsNo

Example Usage

import Stripe from "@dmwd-io/payments";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
// Create a checkout session using the native Stripe API
const session = await stripe.checkout.sessions.create({
customer: "cust_123",
line_items: [{ price: "price_abc", quantity: 1 }],
mode: "payment",
success_url: "/checkout/success",
cancel_url: "/checkout/cancel",
});

API Notes

  • There is no custom PaymentsProvider interface — callers use the native Stripe class directly.
  • The PaymentFormCallbacks type is designed to be spread into the payment-form widget’s props.
  • Stripe errors (Stripe.errors.StripeError) should be caught, sanitized with the PII helper, and re-thrown or converted to user-facing messages.

Accessibility Requirements

IDRequirementNotes
A11Y-001Package is a data layer — accessibility requirements apply to consuming componentspayment-form widget handles its own a11y
A11Y-002Error messages surfaced to users must not contain raw Stripe error codes or card dataPlatform PII conventions enforce this

Checklist

  • Keyboard support is defined. (N/A — no UI)
  • Focus behavior is defined. (N/A — no UI)
  • Screen reader behavior is defined. (N/A — no UI)
  • Color contrast requirements are met. (N/A — no UI)
  • Reduced motion behavior is considered. (N/A — no UI)
  • Semantic HTML expectations are documented. (N/A — no UI)
  • ARIA usage is defined only where needed. (N/A — no UI)

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Storybook docs page explaining re-export approach, env var names, and initialization patternStorybookMust
DOC-002Inline JSDoc on PaymentFormCallbacks and PaymentEvent typesSource codeMust
DOC-003Example: wiring payment-form widget with PaymentFormCallbacksStorybookMust
DOC-004Example: checkout session creation, payment method listing, and refund using native Stripe APIStorybookShould
DOC-005PII sanitization requirements and helper usageStorybookMust

Dependencies

DependencyTypeOwnerStatusNotes
ADR-014 Open Source FirstArchitectureEngineeringReadyDefines thin re-export approach
ADR-051 provider patternArchitectureEngineeringReadyContext for why no custom interface is needed
ADR-065 webhook handlingArchitectureEngineeringReadyDefines webhook normalization conventions
stripe npm packageLibraryStripeReadyPeer dependency
payment-form.tsxComponentDesign systemReadyExisting widget to integrate with
Billing provider wrapperLibraryEngineeringNot StartedBilling delegates to payments for charge creation

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
PCI scope confusion — developers may think the package handles card tokenizationSecurity misunderstandingDocument clearly that tokenization is Stripe Elements’ responsibility
Stripe API breaking changes affect all apps simultaneouslyCoordinated upgrade burdenPin a tested minor version range; document upgrade process
Thin re-export provides less abstraction than a custom interfaceVendor lock-in to StripeADR-014 accepts this tradeoff deliberately — switching vendors is a project, not a config change
Tight coupling between payments and billing providersChanges to one may require changes to the otherKeep the interface boundary clean — billing calls payments conventions, not the reverse

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should Apple Pay / Google Pay setup be documented as platform conventions in this package?David HolmesOpen
Q-002Should the PII sanitization helper be a separate export or inline documentation only?David HolmesOpen
Q-003Should PaymentFormCallbacks include an onValidation hook for client-side card validation?David HolmesOpen
Q-004Should the package document a getPaymentMethodSetupIntent() usage pattern for saving cards without charging?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001import Stripe from "@dmwd-io/payments" resolves to the native Stripe classFR-001
AC-002package.json declares stripe as a peer dependency with a documented version rangeFR-002
AC-003STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, and STRIPE_WEBHOOK_SECRET are documented as canonical namesFR-003
AC-004PaymentFormCallbacks type is compatible with payment-form widget propsFR-004
AC-005PII sanitization requirements are documented; no card numbers or tokens appear in example log outputFR-005, NFR-006
AC-006src/index.ts contains export * from "stripe" and no custom interface definitionsNFR-005
AC-007pnpm typecheck passes with no errorsNFR-001

LLM Handoff Instructions

Expected LLM Behavior

  • Create packages/payments/ directory if it does not exist.
  • Add packages/payments/package.json with stripe as a peer dependency and @dmwd-io/payments as the package name.
  • Create packages/payments/src/index.ts with export * from "stripe" as the primary export.
  • Export PaymentFormCallbacks type from a separate payments-form-callbacks.ts file; re-export it from the barrel.
  • Document STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, and STRIPE_WEBHOOK_SECRET as the canonical env var names in the package README or JSDoc.
  • Run pnpm typecheck after implementation and confirm it passes.

LLM Should Not

  • Build a custom PaymentsProvider interface, adapter, mock, or factory — the library’s own API is the interface per ADR-014.
  • Wrap or proxy any native stripe method.
  • Import any vendor SDK other than stripe.
  • Add runtime dependencies beyond stripe peer dependency.
  • Handle PCI tokenization — that is Stripe Elements’ job.
  • Modify the payment-form widget — only define a callback type compatible with it.

Decision Log

DateDecisionReasonOwner
2026-05-26PaymentFailure extends ErrorNatural try/catch ergonomics; aligns with how auth provider throwsDavid Holmes
2026-05-26Support both redirect URL and client secret on CheckoutSessionAccommodates vendor-hosted (Stripe Checkout) and embedded (Stripe Elements) flowsDavid Holmes
2026-05-26Start with 6 failure codes, not an exhaustive vendor-specific listKeeps the contract simple; vendors map edge cases to processing_errorDavid Holmes
2026-06-02Reframed per ADR-014 (Open Source First): adopt stripe as thin re-export rather than building a custom provider interface. Library API is the interface.ADR-014 establishes that well-maintained community libraries should not be wrapped in custom abstractionsDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft
2026-06-02David HolmesReframed per ADR-014: ship as thin re-export of stripe, drop custom interface.