Resolves "canceled" only when the PSP confirms it. Under getCapabilities().modificationOutcome "asynchronous" the PSP merely acknowledges the request and the outcome arrives by webhook, so the returned status is "processing" — never an unconfirmed terminal state.
Only present if getCapabilities().supportsManualCapture. Capture is the canonical double-charge operation — the idempotency key is REQUIRED, and under supportsMultiCapture each partial capture is its own charge with its own key.
Off-session (merchant-initiated) charge of a vaulted instrument. The
networks' credential-on-file rules ride the off_session flag; a bank
demanding authentication surfaces as authentication_required
(retryable ONLY by bringing the customer back on-session).
Creates the PSP-side customer that saved instruments attach to.
POST /v1/subscriptions against customer + default_payment_method (the
vaulted token — it must belong to that customer, so pspCustomerId is
required here). planId (when given) is an existing Stripe Price id;
without one the adapter builds the price inline via items[].price_data,
which requires an existing Product — created on the fly (name
merchantRefNum, falling back to "Subscription"; it shows on customer
invoices) under a derived idempotency key so replays reuse it.
off_session: true + payment_behavior: "error_if_incomplete": a first
invoice that cannot be paid rejects with the mapped card error instead of
leaving an incomplete subscription behind — server-only creation has no
customer present to finish authentication. startAt maps to a future
billing_cycle_anchor with proration_behavior: "none", making the span
until the anchor free and the first full invoice due at startAt.
Deletes by SavedPaymentMethod.token — the token is dead afterwards.
Missed-webhook recovery over GET /v1/events: the same payload shapes webhooks deliver, normalized by the same mapper — dedupe by event.id works identically for delivered and fetched events. Stripe retains ~30 days.
Pages GET /v1/subscriptions. Stripe's default listing is what this returns: every subscription that has NOT been canceled (canceled ones are excluded unless queried directly by id) — the live set an adoption flow needs, newest first.
Reconciliation passthrough. Only present if getCapabilities().supportsListing.
Reconciliation passthrough. Only present if getCapabilities().supportsListing.
Throws PayFanoutError (code "invalid_request") on unparseable payloads; maps genuinely unknown-but-valid event types to type "unknown" rather than throwing.
Stripe keys subscriptions by id alone — savedPaymentMethodToken is not needed and ignored.
Only present if getCapabilities().supportsPaymentRetrieval. Push-only PSPs expose no read for a single payment — their hosts follow payment state through webhook deliveries instead.
Polls an async refund ("pending" from refundPayment) to its terminal state.
Cart total changed? Stripe PaymentIntents update in place — same pspSessionId, same clientSecret, no client remount needed (the Payment Element re-fetches the amount on confirm).
"Test connection" probe: one read-only GET /v1/events, classified so a host
UI can tell a bad key (auth) from a transient outage (network). Returns a
result on every path instead of throwing, and never surfaces the credential.
Zero-amount verification via SetupIntent (§8 option a). A succeeded SetupIntent attaches a PaymentMethod, which collides with no-vaulting — so the PaymentMethod is detached on EVERY path (success, failed verification, or error) before this method returns.
Webhook handling — same adapter, separate concerns. Both async: future PSPs may need remote key retrieval; the contract stays uniform. MUST operate on the RAW request body bytes/string — re-serializing a parsed body breaks signatures (the conformance suite tests this).
DELETE /v1/subscriptions/:id — immediate cancellation. Stripe ignores idempotency keys on DELETE ("no effect" per its idempotency docs), so none is sent; replay safety is verified instead: when the cancel call rejects, the subscription is re-fetched and an already-canceled one resolves as success — a replayed cancel can never fail on its own earlier success.