Payment methods
Merchant-scoped setup, list, and deletion contracts for saved payment methods.
Payment methods
All operations are merchant-authenticated and client-scoped. Client Basic authentication uses the client identifier (client.uuid) as the username and an active user-owned API token as the password; OAuth callers use a bearer token with the required scope. Credentials must remain on a trusted server.
Create a card-setup session
POST /v1/checkouts/sessions/setup
Authorization: Basic {base64(clientUuid:apiToken)}
Content-Type: application/json
{
"userId": "3b4d9a11-0ce8-4a88-9cb1-b4f43d03d2b7",
"successUrl": "https://merchant.example/account/cards/added",
"cancelUrl": "https://merchant.example/account/cards",
"country": "GB",
"currency": "GBP",
"futureUse": "off_session"
}| Field | Type | Required | Meaning |
|---|---|---|---|
userId | UUID | Yes | FloPay customer UUID already linked to the authenticated client. |
successUrl | string | No | Destination after verified setup. |
cancelUrl | string | No | Destination when the setup journey is abandoned. |
country | string | No | Optional, nullable ISO 3166-1 alpha-2 country the card will be used in. When omitted, FloPay may fill it from this customer's billed-address history with this client. |
currency | string | No | Optional, nullable three-letter intended transaction currency. When omitted, FloPay may fill it from this customer's most recent successful purchase with this client. When supplied, it becomes the setup session's currency. |
futureUse | string | No | Optional, nullable: on_session or off_session. Omission defaults to off-session setup. |
country, currency, and futureUse are independent transaction context. They are trimmed, uppercased, and validated by the billing API, a country is never inferred from a currency or the reverse, and the context never selects or hints a gateway: the billing API alone decides which gateway verifies the card. @flopay/shared publishes this body as the type-only CreateCardSetupSessionRequest, with CardSetupFutureUse for the enum.
The setup request's userId and the list endpoint's userUuid query parameter both identify the same FloPay customer UUID. Keep the endpoint-specific key names shown here when making each request.
201 Created returns the existing checkout-session shape with checkoutMode: 'setup', no products, no amount, and an embedded vault block. Give the browser only the session id and nonce; the setup-specific SDK component reads the session through that scoped handoff. Do not call the purchase process endpoint for a setup session.
The same request creates a setup session for every payment method FloPay can save for this merchant and customer, not only cards. The change is additive: the request gains no field, existing response fields keep their types and meaning, and a card-only integration keeps working unchanged. Pass the session id and nonce to FloPayPaymentMethodSetup to render every advertised method, or to FloPayCardSetup for the card form alone. See Save and manage payment methods.
Setup session response fields
The create response, and every later read of the setup session, carry two additive fields that a purchase session does not:
| Field | Meaning |
|---|---|
advertisedPaymentMethods | The methods this setup session can save, decided once when the session is created. Absent on a session created before the field existed, which a card-only client can treat as a card. |
currency | The session currency as a three-letter upper-case code: the currency resolved for the customer (the currency you sent, else their most recent successful purchase with you), else the currency of the card gateway the setup selected. Absent when neither is known. |
Each advertisedPaymentMethods entry describes one method:
| Field | Meaning |
|---|---|
family | How to group the method: card, wallet, paypal, or apm. |
paymentMethodType | What to render: card, a wallet or bank method such as apple_pay or sepa_debit, or paypal_vaulted for PayPal. |
gatewayId | The gateway account FloPay selected for this method. |
providerType | The provider of that account. |
publishableKey | The publishable key or client id of that account, for the provider's browser SDK. It is not a secret. |
environment | The environment of that account. |
FloPay decides the list from the merchant's connected gateways and the resolved context, using the same eligibility policy as checkout. A client renders what is listed and never adds, removes, or reorders a method. The methods FloPay can advertise today are:
| Method | paymentMethodType | Notes |
|---|---|---|
| Card | card | FloPay's hosted card form. |
| Apple Pay | apple_pay | Saved as a card. Offered on devices and browsers that support it. |
| Google Pay | google_pay | Saved as a card. Offered on devices and browsers that support it. |
| SEPA Direct Debit | sepa_debit | Needs a EUR session. |
| US bank account | us_bank_account | Needs a USD session. |
| PayPal | paypal_vaulted | Saved through PayPal's own approval. |
Methods that leave nothing reusable, such as one-off redirect and buy-now-pay-later methods, are never advertised. A merchant with only card gateways gets exactly one card entry.
The session's status reads completed once the method the customer saved has been verified, for every method type. A setup that is still processing, was declined, or was abandoned leaves the session pending.
A saved card, PayPal account, Apple Pay wallet, or Google Pay wallet is used for later payments and subscription rebills. A wallet saved with futureUse: "on_session" is listed and is not charged.
A saved SEPA Direct Debit or US bank account is listed and removable, and FloPay does not charge it yet.
The setup session is routed to a currently eligible gateway by the same decision that routes a payment, from gateway eligibility and the merchant's preference order, with country and currency as context. FloPay then verifies the card with a Stripe SetupIntent matching futureUse, which is off-session unless on_session was sent. The exact gateway, customer, payment method, and vault credential produced by a successful verification are retained together, so the card keeps that binding.
The saved method remains pending until verification succeeds. A declined, abandoned, processing, or authentication-incomplete setup does not create an active method and leaves existing cards untouched. Successful verification promotes the card atomically to active and makes it the customer's preferred card.
Unknown and cross-merchant users both return 404 before provider or vault work. Card setup is Stripe-only; it does not charge the card and does not create purchase or fulfilment events.
List payment methods
GET /v1/payment-methods?userUuid={floPayUserUuid}&status[eq]=active
Authorization: Basic {base64(clientUuid:apiToken)}Filters include uuid, userUuid, type, provider, status, createdAt, and updatedAt, with the standard pagination and sorting parameters.
Filter by method type with type, alone or combined with status:
GET /v1/payment-methods?userUuid={floPayUserUuid}&type[eq]=card&status[eq]=active
Authorization: Basic {base64(clientUuid:apiToken)}type[eq]=card returns only cards, type[eq]=paypal returns only PayPal, and type[eq]=apple_pay returns only saved Apple Pay wallets. type[in]=card,apple_pay takes comma-separated values and returns several types at once. Leave type out to list every saved method together.
The accepted values are card, paypal, apple_pay, google_pay, sepa_debit, and us_bank_account. Any other value, or a blank entry, returns 400. The filter takes the method type, not a family: wallet and apm are not accepted.
view chooses how the rows are presented:
| Value | Rows returned |
|---|---|
flo_card (default) | One stable row per Flo card, presented as its oldest binding, with gateways describing every binding. Saved methods with no Flo card identity stay one row each. |
binding | One row per gateway binding, un-collapsed. Use it for reconciliation and diagnostics. |
Filters match any binding before that projection, so filtering by an account-specific uuid, provider, or status still returns the canonical card row. Sorting, pagination, pages, and total all operate on the projected rows, so extra bindings never appear as duplicate cards.
{
"data": [
{
"id": "452d914f-078d-4a68-a9ef-16cac0dd2ed4",
"floCardId": "9f1c5f60-6a1e-4a5f-91a1-6b0b0b1f9e21",
"type": "card",
"brand": "visa",
"lastFour": "4242",
"expiryMonth": 12,
"expiryYear": 2030,
"payerEmail": null,
"status": "active",
"isPreferred": true,
"gateways": [
{
"id": "6b2f3c74-4f1a-4a2f-9a3d-2f7c1d5e8a90",
"code": "acct_1Example",
"name": "Card processing",
"providerType": "stripe",
"environment": "production",
"currency": "GBP",
"sessionMode": "both"
}
],
"createdAt": "2026-08-06T10:00:00.000Z"
}
],
"page": 1,
"limit": 20,
"pages": 1,
"total": 1
}The display DTO contains only id, type, brand, lastFour, expiryMonth, expiryYear, payerEmail, status, createdAt, non-secret gateway data when loaded, and the three Flo card fields below.
| Field | Meaning |
|---|---|
floCardId | Stable, gateway-agnostic Flo card UUID. |
gateways | Non-secret gateway summaries for the same-merchant, same-customer bindings of this Flo card. |
isPreferred | Whether this Flo card is the customer preference. |
All three are omitted only while a saved method has no Flo card identity, whatever the date it was created on: a method saved before the change carries all three from the moment it is adopted. Treat them as optional. id and gateway keep their released meanings: the FloPay payment-method UUID, and the gateway for that binding.
A Flo card is not tied to the gateway that captured it. Which account a new payment is charged on is decided by the billing API from the merchant's connected gateways and preference order, never by the client and never by the saved-method id. See Gateway selection.
The API never returns a PAN. It never returns a CVC. It never returns a vault token, provider payment-method id, client secret, billing-address snapshot, or reusable capture credential.
Customers may retain multiple active cards. Use the returned FloPay id for server-side removal and the display fields for recognition.
Methods other than cards
The same list returns every saved method type in the same envelope: cards, PayPal, and the wallets and bank methods saved through a setup session. Methods FloPay can charge come first (cards, PayPal, and Apple Pay and Google Pay wallets saved for off-session use), then the saved methods FloPay does not charge (bank methods, and wallets saved with futureUse: "on_session"), each in the requested sort order.
Every row carries two additive display fields:
| Field | Meaning |
|---|---|
methodType | The method type: card, paypal, apple_pay, google_pay, sepa_debit, or us_bank_account. type carries the same value. |
displayLabel | A safe label to show the customer: the wallet or bank method name, such as Apple Pay or SEPA Direct Debit, or PayPal with a masked account email. null for a card, which keeps brand and lastFour. |
A row that is not a card has every card field null. A wallet or bank row also has payerEmail null. A wallet FloPay can charge carries the non-secret gateway it was saved on, as a card does; a bank row, or a wallet saved with futureUse: "on_session", has gateway null. None of these rows has a Flo card identity, so floCardId, gateways, and isPreferred are omitted:
{
"data": [
{
"id": "0d6c2a8e-5b1f-4f7e-9c3a-7e2b4d1a6f90",
"type": "paypal",
"methodType": "paypal",
"displayLabel": "PayPal (c•••@example.com)",
"brand": null,
"lastFour": null,
"expiryMonth": null,
"expiryYear": null,
"payerEmail": "customer@example.com",
"status": "active",
"createdAt": "2026-08-06T10:05:00.000Z"
},
{
"id": "7a1e4c52-9d3b-4c0f-8a66-2b9f5e1d7c34",
"type": "apple_pay",
"methodType": "apple_pay",
"displayLabel": "Apple Pay",
"brand": null,
"lastFour": null,
"expiryMonth": null,
"expiryYear": null,
"payerEmail": null,
"status": "active",
"gateway": {
"id": "6b2f3c74-4f1a-4a2f-9a3d-2f7c1d5e8a90",
"code": "acct_1Example",
"name": "Card processing",
"providerType": "stripe",
"environment": "production",
"currency": "GBP",
"sessionMode": "both"
},
"createdAt": "2026-10-07T12:40:00.000Z"
}
],
"page": 1,
"limit": 20,
"pages": 1,
"total": 2
}status uses the same pending, active, and deleted values for every type. A setup that failed saved nothing and is never listed. Read type defensively and present a type your interface does not recognize with its displayLabel, not by dropping the row. See Save and manage payment methods for the customer-facing workflow for every method type.
Delete a payment method
DELETE /v1/payment-methods/{id}
Authorization: Basic {base64(clientUuid:apiToken)}The payment method is accessible only when its owning gateway belongs to the authenticated client and its user is linked to that client.
| Status | Code | Meaning |
|---|---|---|
204 | — | Deletion completed, or an owned completed tombstone was replayed. The operation is idempotent. |
400 | — | id is not a UUID. |
404 | payment_method_not_found | Missing, foreign-gateway, and unlinked-user methods are indistinguishable before provider calls. |
409 | payment_method_has_active_payment | A pending/processing payment, unknown outcome, customer action, or use claim blocks removal. |
409 | payment_method_in_use | A linked subscription has no eligible replacement. Add and verify another method first. |
409 | payment_method_reassignment_requires_payer_action | PayPal funding requires an interactive payer-authorisation flow. |
502 | payment_method_deletion_failed | Provider reassignment, deletion, vault cleanup, or verification failed. Inspect reason, retrySafe, and resolution. |
For a 502, retrySafe: true means repeating the same DELETE is idempotent; it does not promise that retrying alone will fix provider configuration or verification. Keep the method visible until a later list confirms its terminal state.
Stripe replacement candidates must be active and belong to the same user, gateway, provider, type, and Stripe Customer. FloPay chooses the most recently successfully charged or setup-verified candidate, reassigns subscription/customer funding, verifies it, and only then removes the target. A failure preserves the old method.
See Saved-card management for consent, browser handoff, recognition, and safe replacement sequencing.