Files
skills/docs/money/payment/payment-providers/README.md
2026-05-29 08:20:56 +01:00

9.1 KiB

@suaveplan/payment-providers - Slim universal foundation for the @suaveplan/payment-provider-* sibling packages. Ships ProviderRegistry, definePaymentProvider factory, shared httpRequest/composeAbortSignal helpers, FetchLike, the ApiKeyConfig / OAuthConfig / MerchantKeyConfig / HttpOptions / ProviderResponse types, and the PAYMENT_PROVIDER_CODES + PaymentProviderError error model. Concrete Stripe / PayPal / Square / Mollie / Adyen adapters ship as separate @suaveplan/payment-provider- packages that depend on this foundation.

Universal foundation for the @suaveplan/payment-provider-* sibling-package family. This package ships only the shared abstractions every concrete adapter needs — the ProviderRegistry, the definePaymentProvider factory, the httpRequest + composeAbortSignal HTTP helpers, the FetchLike type, the shared config shapes (ApiKeyConfig, OAuthConfig, MerchantKeyConfig, HttpOptions, ProviderResponse), and the PAYMENT_PROVIDER_CODES registry + PaymentProviderError class. Concrete adapters (Stripe, PayPal, Square, Mollie, Adyen) live in sibling packages so consumers pay only for the providers they install.

Documentation tree

  • error-codes — Stable error registry and typed error class for @suaveplan/payment-providers. Every production failure path — non-ok…
    • error-codes — Stable error registry and typed error class for @suaveplan/payment-providers. Every production failure path — non-ok…
  • http — Universal HTTP helper used by every provider to issue typed, cancellable JSON requests. The helper is a thin wrapper ov…
    • http — Universal HTTP helper used by every provider to issue typed, cancellable JSON requests. The helper is a thin wrapper ov…
  • registry — Process-local, synchronous registry that indexes PaymentProvider instances (from @suaveplan/payment) by their `name…
    • define — Factory helper for sibling @suaveplan/payment-provider-* packages that want to expose a PaymentProvider (from `@sua…
    • registry — Process-local, synchronous registry that indexes PaymentProvider instances (from @suaveplan/payment) by their `name…
  • types — Shared configuration interfaces and injectable seams consumed by every sibling @suaveplan/payment-provider-* adapter…
    • provider — Shared configuration interfaces and injectable seams consumed by every sibling @suaveplan/payment-provider-* adapter…

Install

bun add @suaveplan/payment-providers @suaveplan/payment
# plus one or more of:
bun add @suaveplan/payment-provider-stripe
bun add @suaveplan/payment-provider-paypal
bun add @suaveplan/payment-provider-square
bun add @suaveplan/payment-provider-mollie
bun add @suaveplan/payment-provider-adyen

Quick start

import { ProviderRegistry } from "@suaveplan/payment-providers";
import { StripeProvider } from "@suaveplan/payment-provider-stripe";
import { PaymentEngine } from "@suaveplan/payment";

const registry = new ProviderRegistry();
registry.register(new StripeProvider({
    apiKey: process.env.STRIPE_SECRET_KEY ?? "",
}));

const engine = new PaymentEngine({ provider: registry.get("stripe") });

const intent = await engine.createIntent({
    amount: { amount: 5000n, currency: "USD" },
    captureMethod: "automatic",
});

Features

  • ProviderRegistry — process-local, synchronous, insertion-ordered registry of PaymentProvider instances keyed by name; strict get with typed miss, duplicate-safe register, idempotent unregister, frozen list() snapshot
  • definePaymentProvider<TName>(spec) — factory that returns a frozen, literal-typed PaymentProvider for sibling packages that prefer a closure to a class
  • httpRequest<T>(options) — universal FetchLike wrapper with per-call timeout, JSON serialisation, custom-header passthrough, typed errors, empty-body handling, and external-AbortSignal composition
  • composeAbortSignal(internal, external?) — reusable abort-signal composer (extracted from httpRequest so sibling packages can compose their own out-of-band HTTP calls with caller-supplied cancellation)
  • PAYMENT_PROVIDER_CODES + PaymentProviderError — 6-code registry (HTTP_ERROR, TIMEOUT, PARSE_ERROR, OAUTH_TOKEN_FAILURE, PROVIDER_NOT_REGISTERED, PROVIDER_ALREADY_REGISTERED) with optional provider / httpStatus / url / bodyExcerpt discriminants
  • Shared config shapes — ApiKeyConfig, OAuthConfig, MerchantKeyConfig, HttpOptions, ProviderResponse, ProviderCommonOptions, plus NowFn / RandomIdFn / Base64EncodeFn injectable seams
  • Universal tier — zero Node, browser, React, or node:crypto imports; runs unchanged on Node, Bun, browsers, and Web Workers
  • Tree-shakeable — sideEffects: false plus per-feature subpath exports
  • 100% test coverage (Istanbul) + 100% docs coverage (sibling .md per implementation file)

Sibling provider packages

The five concrete provider adapters that used to ship in this package now live in dedicated sibling packages, each depending on this foundation:

  • @suaveplan/payment-provider-stripe — StripeProvider
  • @suaveplan/payment-provider-paypal — PayPalProvider
  • @suaveplan/payment-provider-square — SquareProvider + the Square-specific SquareConfig type
  • @suaveplan/payment-provider-mollie — MollieProvider
  • @suaveplan/payment-provider-adyen — AdyenProvider

Install one or more alongside this foundation. Every sibling exports the same class name + PaymentProvider-interface contract as before the slim-down, so migration from a bundled import is a one-line package swap (see ## Migration below).

Migration

If you were importing a concrete provider from @suaveplan/payment-providers, switch to the matching sibling package. The class names are preserved verbatim:

// Before
import { StripeProvider } from "@suaveplan/payment-providers";

// After
import { StripeProvider } from "@suaveplan/payment-provider-stripe";

All shared types (ApiKeyConfig, OAuthConfig, MerchantKeyConfig, HttpOptions, ProviderResponse, NowFn, RandomIdFn, Base64EncodeFn), httpRequest, composeAbortSignal, the PaymentProviderError class, and the PAYMENT_PROVIDER_CODES registry continue to be exported from @suaveplan/payment-providers unchanged. The Square-specific SquareConfig type now ships with @suaveplan/payment-provider-square.

Modules

Building a custom provider

Sibling provider packages — and any consumer adding a non-standard provider — implement the PaymentProvider interface from @suaveplan/payment and use httpRequest from this foundation for every HTTP call:

import {
    type ApiKeyConfig,
    httpRequest,
    PaymentProviderError,
} from "@suaveplan/payment-providers";
import type {
    CreatePaymentIntentInput,
    PaymentIntent,
    PaymentProvider,
} from "@suaveplan/payment";

export class CustomProvider implements PaymentProvider {
    public readonly name = "custom";
    private readonly config: ApiKeyConfig;

    public constructor(config: ApiKeyConfig) {
        this.config = config;
    }

    public async createIntent(
        input: CreatePaymentIntentInput
    ): Promise<PaymentIntent> {
        const { data } = await httpRequest<{ id: string }>({
            method: "POST",
            url: `${this.config.baseUrl ?? "https://api.custom.com"}/intents`,
            headers: { "X-API-Key": this.config.apiKey },
            body: { amount: Number(input.amount.amount) },
            fetch: this.config.fetch,
            signal: this.config.signal,
            timeoutMs: this.config.timeoutMs,
            provider: this.name,
        });
        return mapToPaymentIntent(data);
    }
    // ...implement remaining PaymentProvider methods
}

function mapToPaymentIntent(_raw: unknown): PaymentIntent {
    throw new PaymentProviderError(
        "PAYMENT_PROVIDER.PARSE_ERROR" as never,
        "implement mapping"
    );
}
  • @suaveplan/payment — PaymentEngine, WebhookVerifier, PaymentProvider interface
  • @suaveplan/money — currency-aware arithmetic
  • @suaveplan/error — base error class + defineCodes registry helper

License

MIT