177 lines
5.6 KiB
Markdown
177 lines
5.6 KiB
Markdown
# OpenSpec Change: enterprise-payment-providers
|
|
|
|
- **Change ID**: enterprise-payment-providers
|
|
- **Package**: `@suaveplan/payment-providers`
|
|
- **Category**: universal
|
|
- **Target path**: `universal/commerce/payment-providers`
|
|
- **Status**: IN PROGRESS
|
|
- **Date**: 2026-04-24
|
|
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/payment-providers.md`
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
Deliver `@suaveplan/payment-providers` on the genesis branch from scratch at enterprise quality. The package provides concrete `PaymentAdapter` implementations for Stripe, PayPal, Square, Mollie, and Adyen. Each adapter wraps the provider's REST API using injectable fetch (no provider SDK dependency), implements the full `PaymentAdapter` interface from `@suaveplan/payment`, handles provider-specific webhook signature verification, and normalises provider error codes to the canonical `PAYMENT.*` code registry. The main-branch version sat at 100% coverage. Genesis adds structured errors, Zod schemas, OTel instrumentation, retry with circuit breaker, and integration-test harnesses.
|
|
|
|
---
|
|
|
|
## Current-state audit (genesis)
|
|
|
|
The package does not exist on genesis yet — this is a fresh build.
|
|
|
|
---
|
|
|
|
## Available workspace deps on genesis
|
|
|
|
| Package | Role |
|
|
|---|---|
|
|
| `@suaveplan/error` (`workspace:^`) | `PaymentProviderError`; extends `PAYMENT.*` code set |
|
|
| `@suaveplan/types` (`workspace:^`) | `LoggerLike`, `Clock` |
|
|
| `@suaveplan/payment` (`workspace:^`) | `PaymentAdapter` interface; `PaymentIntent`, `PaymentWebhookEvent` types |
|
|
| `@suaveplan/retry` (`workspace:^`) | Retry + circuit breaker for provider API calls |
|
|
| `@suaveplan/crypto` (`workspace:^`) | HMAC webhook signature verification |
|
|
| `zod` (`catalog:forms`) | Provider response schema validation |
|
|
| `@opentelemetry/api` (`catalog:observability`) | Spans + counters |
|
|
|
|
Dev deps: `vitest`, `@vitest/coverage-istanbul` (all `catalog:testing`).
|
|
|
|
---
|
|
|
|
## Public API surface
|
|
|
|
### Provider factories
|
|
|
|
```ts
|
|
function createStripeAdapter(options: StripeAdapterOptions): PaymentAdapter
|
|
function createPayPalAdapter(options: PayPalAdapterOptions): PaymentAdapter
|
|
function createSquareAdapter(options: SquareAdapterOptions): PaymentAdapter
|
|
function createMollieAdapter(options: MollieAdapterOptions): PaymentAdapter
|
|
function createAdyenAdapter(options: AdyenAdapterOptions): PaymentAdapter
|
|
```
|
|
|
|
All option types share a common base:
|
|
|
|
```ts
|
|
interface BaseAdapterOptions {
|
|
apiKey: string
|
|
webhookSecret?: string
|
|
environment?: 'sandbox' | 'production' // default 'production'
|
|
retryOptions?: RetryOptions
|
|
fetch?: typeof globalThis.fetch
|
|
clock?: Clock
|
|
logger?: LoggerLike
|
|
}
|
|
|
|
interface StripeAdapterOptions extends BaseAdapterOptions {
|
|
apiVersion?: string // default 'latest'
|
|
}
|
|
|
|
interface PayPalAdapterOptions extends BaseAdapterOptions {
|
|
clientId: string
|
|
clientSecret: string
|
|
}
|
|
|
|
interface AdyenAdapterOptions extends BaseAdapterOptions {
|
|
merchantAccount: string
|
|
liveUrlPrefix?: string
|
|
}
|
|
```
|
|
|
|
### Provider-normalised types
|
|
|
|
Each provider's raw response is normalised to `PaymentIntent` via a per-provider mapper; these mappers are exported for testing:
|
|
|
|
```ts
|
|
function normalizeStripePaymentIntent(raw: unknown): PaymentIntent
|
|
function normalizePayPalOrder(raw: unknown): PaymentIntent
|
|
function normalizeSquarePayment(raw: unknown): PaymentIntent
|
|
function normalizeMolliePayment(raw: unknown): PaymentIntent
|
|
function normalizeAdyenPayment(raw: unknown): PaymentIntent
|
|
```
|
|
|
|
### Error codes
|
|
|
|
Provider errors are wrapped in `PaymentError` with existing `PAYMENT_PROVIDER_ERROR` code plus an added detail field:
|
|
|
|
```ts
|
|
interface PaymentProviderErrorDetail {
|
|
provider: string
|
|
providerCode?: string
|
|
providerMessage?: string
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Genesis additions
|
|
|
|
1. **Zod schemas** for all provider-specific option types and raw response shapes.
|
|
2. **Sandbox/production switching** — `environment: 'sandbox'` routes to provider test endpoints automatically.
|
|
3. **Idempotency key passthrough** — all create/capture/refund calls forward the caller-supplied idempotency key as the provider-specific header (`Idempotency-Key` for Stripe, etc.).
|
|
4. **Circuit breaker** — wraps all provider API calls via `@suaveplan/retry`; opens after 5 consecutive provider errors.
|
|
5. **OTel instrumentation** — span `payment_provider.api_call` with attributes `payment_provider.name`, `payment_provider.method`; counter `payment_provider.api_total`; histogram `payment_provider.api_duration_ms`.
|
|
6. **Provider error normalisation** — 4xx/5xx provider responses are mapped to the closest `PAYMENT.*` error code with provider details preserved in the error context.
|
|
|
|
---
|
|
|
|
## File layout
|
|
|
|
```text
|
|
universal/commerce/payment-providers/
|
|
package.json
|
|
tsconfig.json
|
|
vitest.config.ts
|
|
src/
|
|
index.ts
|
|
error-codes.ts
|
|
constants/
|
|
index.ts
|
|
types/
|
|
common.ts
|
|
common.md
|
|
providers/
|
|
stripe/
|
|
stripe.ts
|
|
stripe.md
|
|
stripe.types.ts
|
|
stripe.test.ts
|
|
index.ts
|
|
paypal/
|
|
paypal.ts
|
|
paypal.md
|
|
paypal.types.ts
|
|
paypal.test.ts
|
|
index.ts
|
|
square/
|
|
square.ts
|
|
square.md
|
|
square.types.ts
|
|
square.test.ts
|
|
index.ts
|
|
mollie/
|
|
mollie.ts
|
|
mollie.md
|
|
mollie.types.ts
|
|
mollie.test.ts
|
|
index.ts
|
|
adyen/
|
|
adyen.ts
|
|
adyen.md
|
|
adyen.types.ts
|
|
adyen.test.ts
|
|
index.ts
|
|
__benchmarks__/
|
|
payment-providers.bench.ts
|
|
tests/
|
|
integration.test.ts
|
|
```
|
|
|
|
---
|
|
|
|
## Impact
|
|
|
|
**Downstream consumers**: `@suaveplan/booking`, any server-side checkout flow.
|
|
|
|
**Workspace dep prerequisites**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/payment`, `@suaveplan/retry`, `@suaveplan/crypto`.
|