@flopay/shared 1.9.16 → 1.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -217
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @flopay/shared
|
|
2
2
|
|
|
3
|
-
Shared types, error classes, constants, and validation helpers used
|
|
3
|
+
Shared types, error classes, constants, and validation helpers used by every FloPay SDK package. It is primarily a type and constant library: apart from error factories, validation helpers, and the privacy-safe telemetry builders it has no runtime behavior of its own.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,295 +8,347 @@ Shared types, error classes, constants, and validation helpers used across all F
|
|
|
8
8
|
pnpm add @flopay/shared
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
`@flopay/js`, `@flopay/react`, and `@flopay/node` already depend on this package, so add it directly only when your own code imports from it. Install the same version as the other `@flopay/*` packages you use.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Its one runtime dependency is [`validator`](https://www.npmjs.com/package/validator), used only by the postal-code helpers. It is imported through the `validator/lib/isPostalCode.js` submodule so bundlers include just that check, and it installs automatically.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Configure the billing API environment
|
|
18
|
+
|
|
19
|
+
Choose which FloPay billing API the SDK talks to, once, at application startup. Either set the `NEXT_PUBLIC_FLOPAY_ENV` environment variable to `production` or `staging` (no code needed), or call `configureFlopay()`:
|
|
16
20
|
|
|
17
21
|
```ts
|
|
18
22
|
import { configureFlopay } from '@flopay/shared';
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
configureFlopay({ environment: 'production' });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`resolveBillingApiUrl()` returns the URL every FloPay package uses. It takes, in order: an explicit URL argument, the `NEXT_PUBLIC_FLOPAY_ENV` environment variable, the `configureFlopay()` setting, and finally the staging billing API.
|
|
28
|
+
|
|
29
|
+
### Pick a theme
|
|
30
|
+
|
|
31
|
+
`THEMES` maps each bundle id to a coherent pair: an `appearance` for Stripe-rendered elements and the hosted card form, and `buttonsLayout` styles for the React checkout's own surfaces. The React components accept the same ids through their `theme` prop, so you rarely need the bundle directly.
|
|
22
32
|
|
|
23
|
-
|
|
33
|
+
```ts
|
|
34
|
+
import { THEMES } from '@flopay/shared';
|
|
35
|
+
|
|
36
|
+
const { appearance, buttonsLayout } = THEMES['glass-dark'];
|
|
24
37
|
```
|
|
25
38
|
|
|
26
|
-
###
|
|
39
|
+
### Validate buyer input
|
|
40
|
+
|
|
41
|
+
`isValidEmail()` mirrors the billing API's email rule. It returns `false` for nullish, blank, padded, or malformed values, so trim input yourself when surrounding whitespace is acceptable.
|
|
42
|
+
|
|
43
|
+
The postal-code helpers are a faithful mirror of the billing API's own postcode validator: the same pinned `validator` `isPostalCode` rules, the same country normalization, and the same fail-open cases. The SDK therefore never blocks a postcode the server accepts, and never opens a submit gate for one the server's account update will reject. `@flopay/react` uses them to block a malformed AVS postcode before the card is captured and to show the expected format inline.
|
|
44
|
+
|
|
45
|
+
- A **2-letter code** is passed straight through, so `'UK'` (not a `validator` locale) is unsupported, while the full name `'United Kingdom'` resolves to `GB`.
|
|
46
|
+
- `isValidPostalCode()` **fails open** in exactly the billing API's three cases: a blank postcode, an unresolvable country, or a country `validator` has no rule for. Check emptiness and `isPostalCodeSupported()` yourself to tell "required" apart from "malformed".
|
|
47
|
+
- `getPostalCodeExample()` returns the same example the billing API embeds in its validation message, so an inline hint and a server rejection never contradict each other.
|
|
48
|
+
|
|
49
|
+
### Build your own checkout-create request
|
|
50
|
+
|
|
51
|
+
`@flopay/js` and `@flopay/react` handle this automatically. Use the helpers only if you build the `POST /v1/checkouts/sessions` request yourself for a checkout this SDK renders.
|
|
52
|
+
|
|
53
|
+
- **Idempotency.** Send a stable `Idempotency-Key` so a timeout or lost response cannot mint a second session. `resolveIdempotencyKey()` returns your key unchanged after validating it, or generates a cryptographically random one (`crypto.randomUUID()`, else 16 random bytes). Without a secure random source it returns `undefined` and you should omit the header rather than send a weak key. Keys are never derived from request data. Reuse one key for every retry of one logical checkout, and never for a new purchase. The header is optional: omitting it keeps the billing API's unkeyed behavior.
|
|
54
|
+
|
|
55
|
+
### Gateway decisions belong to the billing API
|
|
56
|
+
|
|
57
|
+
`@flopay/shared` exposes no helper for declaring, selecting, or filtering payment gateways or gateway capabilities, and none should be added. Which gateway processes a session (Stripe, PayPal, Worldpay Corporate Gateway, or a future provider) is decided by the billing API from the merchant's configured gateways and their approved capability records. Clients that build `POST /v1/checkouts/sessions` themselves send the same provider-neutral body the SDK sends and render whatever `gateways` the response advertises.
|
|
58
|
+
|
|
59
|
+
### Privacy-safe telemetry contract
|
|
60
|
+
|
|
61
|
+
`@flopay/shared` exports the versioned, closed telemetry types and builders used by both browser packages. Builders copy only allowlisted enum, numeric, and opaque fields: arbitrary messages, stacks, payloads, URLs, credentials, customer data, and unknown nested fields cannot enter serialized events. `serializeTelemetryBatch()` emits the billing API's v1 envelope with a fixed, closed package identity (`@flopay/shared`, `@flopay/js`, or `@flopay/react`).
|
|
62
|
+
|
|
63
|
+
- `TELEMETRY_LOG_NAMES` is the complete SDK-authored lifecycle catalog, and `TELEMETRY_FAILURE_CATEGORIES` the complete technical-failure catalog: `server_error`, `transport_error`, `invalid_response`, and `provider_runtime`. Only `buildTelemetryErrorEvent()` accepts a `failureCategory`; every other builder drops it.
|
|
64
|
+
- PayPal lifecycle events may carry the closed `inAppBrowser`, `appFamily`, `popupCapability`, `presentationMode`, and `terminalReason` dimensions. Invalid values and raw user-agent or provider fields are dropped.
|
|
65
|
+
- Detached session creation is measured as two stages: `session_shell` (create to a mountable card form) and `session_claim` (the background attachment of catalog and buyer data). A successful `session_create` still spans the whole logical create, so end-to-end measurements are unchanged. Claim failures are reported only under `session_claim`.
|
|
66
|
+
|
|
67
|
+
### Filter third-party errors in your own Sentry client
|
|
68
|
+
|
|
69
|
+
Hosts that already use Sentry can install the dependency-free predicate as their `beforeSend` callback:
|
|
27
70
|
|
|
28
71
|
```ts
|
|
29
|
-
import
|
|
72
|
+
import * as Sentry from '@sentry/browser';
|
|
73
|
+
import { dropThirdPartyOnlyError } from '@flopay/shared';
|
|
30
74
|
|
|
31
|
-
|
|
32
|
-
const url = resolveBillingApiUrl();
|
|
75
|
+
Sentry.init({ beforeSend: dropThirdPartyOnlyError });
|
|
33
76
|
```
|
|
34
77
|
|
|
35
|
-
|
|
78
|
+
It returns `null` only when every exception value has a non-empty stack, no frame resolves to app code or an `@flopay/*` module, and at least one frame resolves to a browser-extension URL or Stripe's hosted `dahlia/stripe.js` controller. Mixed, unrelated-CDN, incomplete, anonymous-only, and genuine app errors are returned unchanged. Frame provenance comes from paths and module names; Sentry's `in_app` flag is ignored because host source-map rewriting can mark third-party frames as app-owned. The helper is structurally typed (`SentryEventLike`), so FloPay adds no `@sentry/*` dependency.
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
|
|
82
|
+
Configure the environment and resolve the billing API URL:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { configureFlopay, getFloPayEnvironment, resolveBillingApiUrl } from '@flopay/shared';
|
|
86
|
+
|
|
87
|
+
configureFlopay({ environment: 'production' });
|
|
88
|
+
|
|
89
|
+
console.log(getFloPayEnvironment()); // 'production'
|
|
90
|
+
console.log(resolveBillingApiUrl()); // the production billing API, unless NEXT_PUBLIC_FLOPAY_ENV overrides it
|
|
91
|
+
console.log(resolveBillingApiUrl('https://billing.example.com')); // an explicit URL always wins
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Validate buyer input with the same rules as the billing API:
|
|
36
95
|
|
|
37
96
|
```ts
|
|
38
97
|
import {
|
|
39
|
-
FloPayError,
|
|
40
|
-
validationError,
|
|
41
|
-
SDK_VERSION,
|
|
42
|
-
DEFAULT_APPEARANCE,
|
|
43
|
-
NIGHT_APPEARANCE,
|
|
44
|
-
THEMES,
|
|
45
|
-
CURRENCY_MAP,
|
|
46
98
|
getCurrencyByCountry,
|
|
47
|
-
|
|
48
|
-
|
|
99
|
+
getPostalCodeExample,
|
|
100
|
+
isPostalCodeSupported,
|
|
49
101
|
isValidEmail,
|
|
102
|
+
isValidPostalCode,
|
|
103
|
+
isValidPublishableKey,
|
|
50
104
|
} from '@flopay/shared';
|
|
51
105
|
|
|
52
|
-
//
|
|
53
|
-
|
|
106
|
+
isValidEmail('buyer@example.com'); // true
|
|
107
|
+
isValidEmail(' buyer@example.com '); // false: trim first when whitespace is acceptable
|
|
108
|
+
isValidPublishableKey('pk_test_abc123'); // true
|
|
109
|
+
|
|
110
|
+
isPostalCodeSupported('US'); // true
|
|
111
|
+
isPostalCodeSupported('AE'); // false: the UAE has no postcodes
|
|
112
|
+
isPostalCodeSupported('United Kingdom'); // true: full names resolve to GB
|
|
113
|
+
isPostalCodeSupported('UK'); // false: 2-letter codes pass straight through
|
|
114
|
+
isValidPostalCode('GB', 'SW1A 1AA'); // true
|
|
115
|
+
isValidPostalCode('GB', '12345'); // false: a US shape in GB
|
|
116
|
+
isValidPostalCode('US', ''); // true: blank fails open, so enforce "required" separately
|
|
117
|
+
getPostalCodeExample('CA'); // 'A1A 1A1'
|
|
118
|
+
|
|
119
|
+
const { currency, symbol } = getCurrencyByCountry('DE'); // 'EUR', '€'; unknown countries fall back to USD
|
|
120
|
+
console.log(currency, symbol);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Build a checkout-create request yourself with the same idempotency header the SDK sends:
|
|
54
124
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
FloPayAppearance,
|
|
125
|
+
```ts
|
|
126
|
+
import {
|
|
127
|
+
IDEMPOTENCY_KEY_HEADER,
|
|
128
|
+
type InlineSessionDraft,
|
|
129
|
+
resolveBillingApiUrl,
|
|
130
|
+
resolveIdempotencyKey,
|
|
62
131
|
} from '@flopay/shared';
|
|
63
132
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
133
|
+
const draft: InlineSessionDraft = {
|
|
134
|
+
clientId: 'your-client-id',
|
|
135
|
+
currency: 'EUR',
|
|
136
|
+
items: [{ providerItemId: 'product-1', providerItemName: 'Widget', totalAmount: 29.99 }],
|
|
137
|
+
account: { userId: 'user_1' },
|
|
138
|
+
successUrl: 'https://shop.example.com/success',
|
|
139
|
+
cancelUrl: 'https://shop.example.com/cancel',
|
|
140
|
+
};
|
|
68
141
|
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
142
|
+
const idempotencyKey = resolveIdempotencyKey('checkout:order-123');
|
|
143
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
144
|
+
if (idempotencyKey) headers[IDEMPOTENCY_KEY_HEADER] = idempotencyKey;
|
|
72
145
|
|
|
73
|
-
|
|
74
|
-
throw validationError('Email is required', 'email');
|
|
146
|
+
console.log(`${resolveBillingApiUrl()}/v1/checkouts/sessions`, headers, JSON.stringify(draft));
|
|
75
147
|
```
|
|
76
148
|
|
|
77
|
-
|
|
149
|
+
Handle SDK errors by their stable `type` and `code`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { FloPayError, resolveIdempotencyKey } from '@flopay/shared';
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
resolveIdempotencyKey('');
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error instanceof FloPayError && error.type === 'validation_error') {
|
|
158
|
+
console.warn(error.code, error.param); // 'InvalidIdempotencyKey' 'idempotencyKey'
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Public API
|
|
78
164
|
|
|
79
165
|
### Types
|
|
80
166
|
|
|
81
167
|
| Type | Description |
|
|
82
168
|
|------|-------------|
|
|
83
169
|
| `FloPayConfig` | Top-level config: `publishableKey`, `locale?`, `appearance?`, `apiVersion?`, `telemetry?` (set `false` to opt out of browser operational telemetry) |
|
|
170
|
+
| `FloPayEnvironment` | Billing API environment accepted by `configureFlopay()`: `'staging'` / `'production'` |
|
|
84
171
|
| `FloPayAppearance` | Theme config: `theme` (`'default'`/`'flat'`/`'night'`/`'none'`), `variables?`, `rules?` |
|
|
85
172
|
| `FloPayThemeVariables` | CSS custom property overrides: `colorPrimary`, `colorBackground`, `colorText`, `colorDanger`, `borderRadius`, `fontFamily`, `fontSizeBase`, `spacingUnit` |
|
|
86
|
-
| `ButtonsLayoutStyles` | Style overrides for the `buttons`
|
|
87
|
-
| `ButtonsLayoutTheme` |
|
|
173
|
+
| `ButtonsLayoutStyles` | Style overrides for the React checkout's `layout="buttons"` surface: `cardButton`, `cardButtonFontSize`, `cardFormContainer`, `cardInputBorder`, `cardInputColor`, `cardInputPlaceholderColor`, `cardInputFontSize`, `cardInputBackground`, `nameInput`, `backButton`, `backButtonFontSize`, `backButtonIcon`, `submitButton`, `submitButtonFontSize`, `title`, `titleFontSize`, `errorBanner` |
|
|
174
|
+
| `ButtonsLayoutTheme` | Legacy buttons-layout preset name: `'default'` / `'minimal'` / `'rounded'` / `'dark'` / `'modern-light'` / `'modern-dark'` / `'bold-light'` / `'bold-dark'` / `'glass-light'` / `'glass-dark'` |
|
|
88
175
|
| `ThemeBundle` | A coherent theme bundle: `{ appearance: FloPayAppearance, buttonsLayout: ButtonsLayoutStyles }` |
|
|
89
176
|
| `ThemeBundleId` | Bundle id: `'modern-light'` / `'modern-dark'` / `'bold-light'` / `'bold-dark'` / `'glass-light'` / `'glass-dark'` |
|
|
90
|
-
| `ThemeId` | Accepted by the React components' `theme` prop: `'classic'`
|
|
91
|
-
| `VaultCardThemeColors` | Theme directive
|
|
92
|
-
| `VaultSubmitButtonTheme` | Complete, bounded snapshot for the
|
|
93
|
-
| `VaultSubmitBackground` | `VaultSolidBackground` (`{ type: 'solid', color }`) or `VaultLinearGradientBackground` (`{ type: 'linearGradient', angle
|
|
94
|
-
| `CardCaptureAdapter` | Hosted card-capture abstraction the React card surfaces depend on: `mount()`, `on()`, `unmount()`, plus optional live controls `applyTheme(theme \| null)` (`null` is the explicit classic reset
|
|
177
|
+
| `ThemeId` | Accepted by the React components' `theme` prop: `'classic'` or any `ThemeBundleId` |
|
|
178
|
+
| `VaultCardThemeColors` | Theme directive sent to the hosted card form: the flat keys every hosted form understands (`primaryColor`, `primaryHoverColor`, `inputBackgroundColor`, `textColor`, `borderColor`, `placeholderColor`, `errorColor`, `successColor`, `fontFamily`, `fontSize`, `fontWeight`, `borderRadius`) plus the optional `submitButton` snapshot. Partial objects stay valid and are forwarded unchanged. |
|
|
179
|
+
| `VaultSubmitButtonTheme` | Complete, bounded snapshot for the hosted submit control (billing API v1.9.6+): `foregroundColor`, `background` / `hoverBackground` / `disabledBackground` (`VaultSubmitBackground`), `typography` (`VaultSubmitTypography`), `spacing` (`VaultSubmitSpacing`), `shadow` (zero to four `VaultShadowLayer` values), `borderRadius`, `disabledOpacity`, `backdropBlur`. Structured values only, never raw CSS: the hosted form ignores an invalid or out-of-range member, and theme input never blocks capture. |
|
|
180
|
+
| `VaultSubmitBackground` | `VaultSolidBackground` (`{ type: 'solid', color }`) or `VaultLinearGradientBackground` (`{ type: 'linearGradient', angle, stops }` with two to five `VaultGradientStop` values in non-decreasing order). Colors are `VaultThemeColor`: `#RRGGBB`, integer-channel `rgb()`, or `rgba()` with alpha `0..1`. |
|
|
181
|
+
| `CardCaptureAdapter` | Hosted card-capture abstraction the React card surfaces depend on: `mount()`, `on()`, `unmount()`, plus the optional live controls `applyTheme(theme \| null)` (`null` is the explicit classic reset), `setSubmitGate()`, `setCardFieldOrder()`, and `setSubmitLabel(label \| null)` (hosted submit wording, billing API v1.9.35+; `null` resets to the session default, `CONFIRM PAYMENT` or `ADD CARD`; retained and replayed on every `'ready'` event, and posted only to the exact `VaultCaptureBlock.expectedOrigin` with its `VaultCaptureBlock.messageToken`). `CardCaptureMountOptions.submitLabel` sets the initial wording. |
|
|
95
182
|
| `CheckoutMode` | Checkout mode: `'full'` / `'auto'` / `'confirm'` |
|
|
96
|
-
| `CheckoutSessionMode` | Mode accepted on session reads: `CheckoutMode` plus `'setup'`. Browser session-creation types deliberately
|
|
97
|
-
| `CheckoutMetadata` | Caller-owned checkout snapshot: `Record<string, string>`. Creation accepts this shape or `null`; session reads contain the map only when the
|
|
98
|
-
| `CheckoutSession` | Session object: `id`, `clientSecret`, `mode`, `status` (`open`/`authorized`/`complete`/`expired`), `amount`, `currency`, `captureMethod?`, `paymentId?`, `authorizationExpiresAt?`, `failureOutcome?`, `customer?`, `checkoutMode?`, `checkoutMetadata?`, `buyerIdentified?` (false for a catalog-attached anonymous session), `vault?` (hosted card
|
|
99
|
-
| `SavedCardDisplay` | Display-only saved
|
|
100
|
-
| `
|
|
101
|
-
| `CheckoutSessionProduct` | Unified product from session response: `uuid`, `checkoutSessionId`, `type` (`'item'`/`'subscription'`), `code?`, `name?`, `description?`, `quantity`, `totalAmount?`, `overrideAmount?`, `currency?`, `metadata?` |
|
|
183
|
+
| `CheckoutSessionMode` | Mode accepted on session reads: any `CheckoutMode` plus `'setup'`. Browser session-creation types deliberately reject `'setup'`. |
|
|
184
|
+
| `CheckoutMetadata` | Caller-owned checkout snapshot: `Record<string, string>`. Creation accepts this shape or `null`; session reads contain the map only when the billing API returns it. |
|
|
185
|
+
| `CheckoutSession` | Session object: `id`, `clientSecret`, `mode`, `status` (`open`/`authorized`/`complete`/`expired`), `amount`, `currency`, `captureMethod?`, `paymentId?`, `authorizationExpiresAt?`, `failureOutcome?`, `customer?`, `checkoutMode?`, `checkoutMetadata?`, `buyerIdentified?` (false for a catalog-attached anonymous session), `vault?` (the hosted card form), `products?`, `successUrl?`, `cancelUrl?`, `coupons?`, `subtotalAmount?` / `discountAmount?` / `totalAmount?` (cart-currency major units), `gateways?` (map keyed by gateway code), `accountData?`, `tagsData?` |
|
|
186
|
+
| `SavedCardDisplay` | Display-only saved card: FloPay `id`, `brand`, `lastFour`, `expiryMonth`, `expiryYear`, and lifecycle `status`. It contains no reusable credential. |
|
|
187
|
+
| `CheckoutSessionProduct` | Product from a session response: `uuid`, `checkoutSessionId`, `type` (`'item'`/`'subscription'`), `code?`, `name?`, `description?`, `quantity`, `totalAmount?`, `overrideAmount?`, `currency?`, `metadata?` |
|
|
102
188
|
| `CheckoutProductType` | `'item'` / `'subscription'` |
|
|
103
|
-
| `CheckoutProduct` |
|
|
104
|
-
| `CheckoutGateway` | Per-gateway config from the billing API. `publishableKey?` identifies the keyed gateway, including the direct-PayPal client ID
|
|
105
|
-
| `CheckoutGateways` | Map keyed by gateway code (
|
|
106
|
-
| `GatewayEnvironment` | `'sandbox'` / `'live'
|
|
189
|
+
| `CheckoutProduct` | Product input for session creation: `type?`, `code?`, `name?`, `quantity?`, `totalAmount?`, `overrideAmount?`, `currency?`, `metadata?` |
|
|
190
|
+
| `CheckoutGateway` | Per-gateway config from the billing API. `publishableKey?` identifies the keyed gateway, including the direct-PayPal client ID. `paypalPublishableKey?` is Stripe-only: the dedicated Stripe account key used to render PayPal through Stripe Elements. Also `environment?` (`'sandbox'`/`'live'`), `stripeClientSecret?` (Stripe-only), `providerObjectType?` (direct PayPal: `'order'`/`'subscription'`/`'setup_token'`), `approvalPresentation?` (direct PayPal: `'app_switch'`/`'redirect'`), and `enabledPaymentMethods?` (Stripe-only). |
|
|
191
|
+
| `CheckoutGateways` | Map keyed by gateway code (`stripe`, `paypal`). A session can advertise several gateways at once. A session opted into server-owned card routing may also carry `worldpayCorporateGateway`: a card gateway processed entirely server-side behind the hosted card form, with no browser credentials. Merchant code never needs to branch on it. |
|
|
192
|
+
| `GatewayEnvironment` | `'sandbox'` / `'live'`, which selects the PayPal JS SDK script and other environment-scoped behavior |
|
|
107
193
|
| `PaymentResult` | Payment outcome: `status` (`'authorized'`/`'succeeded'`/`'processing'`/`'requires_action'`/`'failed'`); authorized results carry `paymentId?`, `sessionId?`, and `authorizationExpiresAt?` |
|
|
108
194
|
| `CheckoutFailureOutcome` | Stable authorization lifecycle failure: `'authorization_declined'` / `'authentication_required'` / `'authorization_expired'` / `'capture_failed'` |
|
|
109
195
|
| `PayPalPaymentResult` | Stripe-hosted PayPal outcome: `status`, `paymentIntentId?`, `paymentMethodId?`, `error?` |
|
|
110
|
-
| `CreateSessionIntentRequest` | Discriminated Stripe wallet/APM or direct-PayPal intent request. PayPal sends `intentKind: 'payment'` for Orders
|
|
111
|
-
| `SessionIntent` | Matching discriminated response. Stripe carries `clientSecret`; PayPal carries a server-derived `providerObjectType
|
|
112
|
-
| `PayPalProviderObjectType` | `'order'` / `'subscription'` / `'setup_token'
|
|
113
|
-
| `PayPalApprovalPresentation` | `'app_switch'` / `'redirect'
|
|
196
|
+
| `CreateSessionIntentRequest` | Discriminated Stripe wallet/APM or direct-PayPal intent request. PayPal sends `intentKind: 'payment'` for Orders and subscriptions and `'setup'` for a standalone setup token. Direct card is not representable. |
|
|
197
|
+
| `SessionIntent` | Matching discriminated response. Stripe carries `clientSecret`; PayPal carries a server-derived `providerObjectType`. Provider, payment-method category, type, and id, intent kind, and provider object id are separate fields. |
|
|
198
|
+
| `PayPalProviderObjectType` | `'order'` / `'subscription'` / `'setup_token'`: the direct-PayPal continuation selected by the billing API and verified by the SDK |
|
|
199
|
+
| `PayPalApprovalPresentation` | `'app_switch'` / `'redirect'`: a billing-API-advertised, one-time Order mode. It carries no return URL or resume credential. |
|
|
114
200
|
| `SessionIntentDeclineRequest` | Non-card provider decline classification. Runtime reporting accepts safe classification codes only and rejects messages or identifier-shaped values. |
|
|
115
201
|
| `PaymentProviderAdapter` | Minimal provider interface for initialization, raw-provider access, and lifecycle management. Card collection uses `CardCaptureAdapter`; React owns live Stripe Elements directly. |
|
|
116
202
|
| `Customer` | `id`, `email`, `firstName?`, `lastName?`, `gender?`, `city?`, `state?`, `country?`, `zip?` |
|
|
117
203
|
| `LineItem` | `price?`, `priceData?`, `quantity` |
|
|
118
204
|
| `PriceData` | `currency`, `unitAmount`, `productData`, `recurring?` |
|
|
119
205
|
| `RecurringInterval` | `interval` (`'month'`/`'year'`), `intervalCount?` |
|
|
120
|
-
| `BillingProvider` |
|
|
121
|
-
| `TokenizedBody` | `id?`, `type?`, `threeDSecureActionResultTokenId?`, `originalPaymentMethodId?`, `gateway
|
|
122
|
-
| `NormalizedCheckoutSession` | Provider-agnostic session: `
|
|
206
|
+
| `BillingProvider` | Gateway code advertised for a session: `'stripe'` / `'paypal'` |
|
|
207
|
+
| `TokenizedBody` | `id?`, `type?`, `threeDSecureActionResultTokenId?`, `originalPaymentMethodId?`, `gateway?`, `paymentMethodType?` (the gateway's wire identifier, such as `'card'` or `'apple_pay'` for Stripe and `'paypal'` for PayPal), `isPaypal?` (deprecated in favor of `gateway` and `paymentMethodType`; still populated as the boolean `true` for PayPal) |
|
|
208
|
+
| `NormalizedCheckoutSession` | Provider-agnostic session: `providers` (every advertised gateway code), `mode`, `data`, `checkoutMetadata?`, `raw?`. The session itself is `data.session`, which also exposes `data.session.checkoutMetadata`. |
|
|
123
209
|
| `CheckoutModeKind` | `'tokenize'` / `'redirect'` |
|
|
124
|
-
| `CreateSessionParams` | Full session
|
|
125
|
-
| `CheckoutSessionResult` | `{ status: 201; redirectUrl; nonce }` / `{ status: 204 }` / `{ status: number }
|
|
210
|
+
| `CreateSessionParams` | Full session-creation params: `billingApiUrl`, `checkoutBaseUrl`, `clientId`, `currency` (required, or resolvable from the first item, subscription, or product), `captureMethod?` (`'automatic'`/`'manual'`; omitted keeps immediate capture), `products?` (takes precedence), `items?`, `subscriptions?` (legacy, folded into products before sending), `account`, `successUrl`, `cancelUrl`, `checkoutMode?`, `checkoutMetadata?`, `couponCodes?`, `tagsData?`, `redirectParams?`, `setCookie?`, `timeoutMs?`, `utmMetadata?`, `idempotencyKey?` |
|
|
211
|
+
| `CheckoutSessionResult` | `{ status: 201; redirectUrl; nonce }` / `{ status: 204 }` / `{ status: number }`. The `nonce` is the session-bound checkout token: forward it as `x-checkout-session-token` on every continuation call for the same session. |
|
|
126
212
|
| `CheckoutItem` | `providerItemId`, `providerItemName?`, `quantity?`, `totalAmount`, `overrideAmount?`, `currency?` |
|
|
127
213
|
| `CheckoutSubscription` | `providerPlanId`, `providerPlanName?`, `quantity?`, `totalAmount`, `overrideAmount?`, `currency?` |
|
|
128
214
|
| `CheckoutAccount` | `userId`, `firstName?`, `lastName?`, `email`, `country?`, `gender?`, `city?`, `state?`, `zip?` |
|
|
129
|
-
| `InlineSessionParams` |
|
|
130
|
-
| `InlineSessionDraft` | Browser checkout draft.
|
|
131
|
-
| `InlineSessionPatch` | Just-in-time
|
|
132
|
-
| `DetachedCheckoutSession` | Mountable `shell`,
|
|
133
|
-
| `ProcessPaymentParams` | `sessionId`, `nonce` (required
|
|
215
|
+
| `InlineSessionParams` | Browser session-creation parameters, including nullable `checkoutMetadata?`. For detached creation, `timeoutMs` applies independently to the shell request and the initial catalog claim. |
|
|
216
|
+
| `InlineSessionDraft` | Browser checkout draft. Keeps `account.userId` required but lets `account.email` be omitted and collected during checkout. |
|
|
217
|
+
| `InlineSessionPatch` | Just-in-time draft update. `checkoutMetadata` replaces the whole current map and accepts an explicit `null`. |
|
|
218
|
+
| `DetachedCheckoutSession` | Mountable `shell`, the catalog-attachment `claimed` promise, the session `nonce`, and an independently replay-safe `claim()` that attaches a late buyer identity. |
|
|
219
|
+
| `ProcessPaymentParams` | `sessionId`, `nonce` (required session-bound checkout token), `tokenizedData?`, `accountData`, `chv?` |
|
|
220
|
+
| `PaymentMethodRemovalError` | Merchant-server saved-method removal union: an indistinguishable `404 payment_method_not_found`, three stable `409` conflict codes, and a retry-safe `502 payment_method_deletion_failed` |
|
|
134
221
|
| `CreateCustomerParams` | `email`, `name?`, `metadata?` |
|
|
135
222
|
| `UpdateCustomerParams` | `email?`, `name?`, `metadata?` |
|
|
136
223
|
| `WebhookEvent` | `id`, `type`, `data`, `created` |
|
|
137
224
|
| `CurrencyInfo` | `currency`, `symbol`, `country`, `countryCode`, `tax` |
|
|
138
225
|
| `CountryOption` | `code`, `name`, `flag` |
|
|
139
226
|
| `TagsData` | `googleContainerId?`, `sessionId?`, `testEventCode?` |
|
|
227
|
+
| `SentryEventLike` | The structural event shape `dropThirdPartyOnlyError()` inspects |
|
|
140
228
|
|
|
141
|
-
###
|
|
142
|
-
|
|
143
|
-
| Export | Description |
|
|
144
|
-
|--------|-------------|
|
|
145
|
-
| `FloPayError` | Custom error class with `type`, `code?`, `declineCode?`, `param?`, `statusCode?` |
|
|
146
|
-
| `FloPayErrorType` | `'validation_error'` / `'api_error'` / `'authentication_error'` / `'rate_limit_error'` / `'network_error'` |
|
|
147
|
-
| `validationError(message, param?)` | Factory for validation errors |
|
|
148
|
-
| `apiError(message, code?, statusCode?)` | Factory for API errors |
|
|
149
|
-
| `authenticationError(message)` | Factory for auth errors |
|
|
150
|
-
| `rateLimitError(message)` | Factory for rate limit errors |
|
|
151
|
-
| `networkError(message)` | Factory for network errors |
|
|
152
|
-
|
|
153
|
-
### Privacy-safe telemetry contract
|
|
154
|
-
|
|
155
|
-
`@flopay/shared` exports the versioned closed telemetry types and builders used
|
|
156
|
-
by both browser packages: `buildTelemetryErrorEvent`,
|
|
157
|
-
`buildTelemetryLogEvent`, `buildTelemetryPerformanceEvent`,
|
|
158
|
-
`buildTelemetryTerminalEvent`, and `serializeTelemetryBatch`. Builders copy
|
|
159
|
-
only allowlisted enum/numeric/opaque fields; arbitrary messages, stacks,
|
|
160
|
-
payloads, URLs, credentials, customer data, and unknown nested fields cannot
|
|
161
|
-
enter serialized events.
|
|
162
|
-
|
|
163
|
-
PayPal lifecycle events may additionally carry the closed `inAppBrowser`,
|
|
164
|
-
`appFamily`, `popupCapability`, `presentationMode`, and `terminalReason`
|
|
165
|
-
dimensions. Invalid values and raw user-agent/provider fields are dropped.
|
|
166
|
-
|
|
167
|
-
Detached session creation (TeamFloPay/backend#1099) is measured as two stages
|
|
168
|
-
so each phase can be tracked on its own: `session_shell` is create →
|
|
169
|
-
mountable card form, and `session_claim` is the background attach of buyer and
|
|
170
|
-
catalog data (request category `session_claim`, log names
|
|
171
|
-
`session.shell.ready`, `session.claim.started`, `session.claim.completed`).
|
|
172
|
-
A successful `session_create` continues to span the whole logical create — shell
|
|
173
|
-
*and* claim — so existing end-to-end dashboards keep measuring the same thing,
|
|
174
|
-
and reports the create's attempt count rather than the claim's. Claim failures
|
|
175
|
-
are reported only under `session_claim`, with duration measured from claim
|
|
176
|
-
start; they do not emit a failed `session_create` span after the shell succeeds.
|
|
177
|
-
|
|
178
|
-
`TELEMETRY_LOG_NAMES` is the complete SDK-authored lifecycle catalog. The
|
|
179
|
-
`TELEMETRY_FAILURE_CATEGORIES` export is the complete technical-failure
|
|
180
|
-
catalog: `server_error`, `transport_error`, `invalid_response`, and
|
|
181
|
-
`provider_runtime`. `TelemetryFailureCategory` exposes the corresponding
|
|
182
|
-
union type. `failureCategory` is optional and accepted only by
|
|
183
|
-
`buildTelemetryErrorEvent`; legacy technical events may omit it, and every
|
|
184
|
-
non-error builder drops injected category fields.
|
|
185
|
-
|
|
186
|
-
The serializer emits the backend-owned v1 envelope with the fixed closed
|
|
187
|
-
package identity (`@flopay/shared`, `@flopay/js`, or
|
|
188
|
-
`@flopay/react`) and event classes. See
|
|
189
|
-
[`docs/TELEMETRY.md`](../../docs/TELEMETRY.md) for the privacy and operational
|
|
190
|
-
policy.
|
|
191
|
-
|
|
192
|
-
### Host-owned Sentry filtering
|
|
193
|
-
|
|
194
|
-
Hosts that already use Sentry can install the dependency-free predicate as
|
|
195
|
-
their `beforeSend` callback:
|
|
196
|
-
|
|
197
|
-
```ts
|
|
198
|
-
import * as Sentry from '@sentry/browser';
|
|
199
|
-
import { dropThirdPartyOnlyError } from '@flopay/shared';
|
|
200
|
-
|
|
201
|
-
Sentry.init({ beforeSend: dropThirdPartyOnlyError });
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
It returns `null` only when every exception value has a non-empty stack, no
|
|
205
|
-
frame resolves to app code or an `@flopay/*` module, and at least one frame
|
|
206
|
-
resolves to a browser-extension URL or Stripe's hosted `dahlia/stripe.js`
|
|
207
|
-
controller. Other frames in a dropped stack may be anonymous or pathless.
|
|
208
|
-
Mixed, unrelated-CDN, incomplete, anonymous-only, and genuine app errors are
|
|
209
|
-
returned unchanged by identity. Frame provenance comes from paths and module
|
|
210
|
-
names; Sentry's `in_app` value is deliberately ignored because host source-map
|
|
211
|
-
rewriting can mark third-party frames as app-owned.
|
|
229
|
+
### Configuration
|
|
212
230
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
231
|
+
| Function | Description |
|
|
232
|
+
|----------|-------------|
|
|
233
|
+
| `configureFlopay(config)` | Sets the global billing API environment. Call once at startup. |
|
|
234
|
+
| `getFloPayEnvironment()` | The currently configured environment. |
|
|
235
|
+
| `resolveBillingApiUrl(billingApiUrl?)` | The billing API URL, from an explicit argument, then `NEXT_PUBLIC_FLOPAY_ENV`, then `configureFlopay()`, then staging. |
|
|
216
236
|
|
|
217
237
|
### Constants
|
|
218
238
|
|
|
219
|
-
|
|
|
220
|
-
|
|
221
|
-
| `SDK_VERSION` |
|
|
222
|
-
| `
|
|
239
|
+
| Constant | Description |
|
|
240
|
+
|----------|-------------|
|
|
241
|
+
| `SDK_VERSION` | The exact version of this release, identical to the installed package version |
|
|
242
|
+
| `BILLING_API_URL_PRODUCTION` | `'https://api.flopay.com'` |
|
|
243
|
+
| `BILLING_API_URL_STAGING` | `'https://api.stage.flopay.com'` |
|
|
244
|
+
| `DEFAULT_API_BASE_URL` | Alias of the staging billing API, `'https://api.stage.flopay.com'`. Prefer `resolveBillingApiUrl()`. |
|
|
223
245
|
| `DEFAULT_API_VERSION` | `'2024-01-01'` |
|
|
224
246
|
| `DEFAULT_APPEARANCE` | Default theme (primary `#4A49FF`, white background, Poppins font) |
|
|
225
247
|
| `FLAT_APPEARANCE` | Flat theme (minimal borders, 4px radius) |
|
|
226
248
|
| `NIGHT_APPEARANCE` | Dark theme (dark background `#1A1A2E`, light text) |
|
|
227
|
-
| `MODERN_LIGHT_APPEARANCE` | Clean
|
|
228
|
-
| `MODERN_DARK_APPEARANCE` | Clean
|
|
229
|
-
| `BOLD_LIGHT_APPEARANCE` | Bold
|
|
230
|
-
| `BOLD_DARK_APPEARANCE` | Bold
|
|
231
|
-
| `GLASS_LIGHT_APPEARANCE` | Glass
|
|
232
|
-
| `GLASS_DARK_APPEARANCE` | Glass
|
|
233
|
-
| `BUTTONS_LAYOUT_DEFAULT`
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
249
|
+
| `MODERN_LIGHT_APPEARANCE` | Clean and airy light: Inter, FloPay-blue primary `#1785E0`, slate text, 14px radius, soft input shadows |
|
|
250
|
+
| `MODERN_DARK_APPEARANCE` | Clean and airy dark: Inter, sky-blue primary `#60A5FA`, deep-blue surface `#0B1220`, 14px radius |
|
|
251
|
+
| `BOLD_LIGHT_APPEARANCE` | Bold and vibrant light: Inter, saturated FloPay blue `#1785E0`, 2px borders, heavier font weight |
|
|
252
|
+
| `BOLD_DARK_APPEARANCE` | Bold and vibrant dark: Inter, sky `#60A5FA` on deep-blue `#0C2B4D`, accented borders |
|
|
253
|
+
| `GLASS_LIGHT_APPEARANCE` | Glass light: translucent inputs with backdrop blur, FloPay-blue primary `#1785E0`, 16px radius |
|
|
254
|
+
| `GLASS_DARK_APPEARANCE` | Glass dark: translucent inputs with backdrop blur, `#60A5FA` on slate `#0F172A` |
|
|
255
|
+
| `BUTTONS_LAYOUT_DEFAULT` | Original default buttons-layout styles |
|
|
256
|
+
| `BUTTONS_LAYOUT_MINIMAL` | Original minimal buttons-layout styles |
|
|
257
|
+
| `BUTTONS_LAYOUT_ROUNDED` | Original rounded buttons-layout styles |
|
|
258
|
+
| `BUTTONS_LAYOUT_DARK` | Original dark buttons-layout styles |
|
|
259
|
+
| `BUTTONS_LAYOUT_MODERN_LIGHT` | Buttons-layout styles paired with `MODERN_LIGHT_APPEARANCE` |
|
|
260
|
+
| `BUTTONS_LAYOUT_MODERN_DARK` | Buttons-layout styles paired with `MODERN_DARK_APPEARANCE` |
|
|
261
|
+
| `BUTTONS_LAYOUT_BOLD_LIGHT` | Buttons-layout styles paired with `BOLD_LIGHT_APPEARANCE` |
|
|
262
|
+
| `BUTTONS_LAYOUT_BOLD_DARK` | Buttons-layout styles paired with `BOLD_DARK_APPEARANCE` |
|
|
263
|
+
| `BUTTONS_LAYOUT_GLASS_LIGHT` | Buttons-layout styles paired with `GLASS_LIGHT_APPEARANCE` |
|
|
264
|
+
| `BUTTONS_LAYOUT_GLASS_DARK` | Buttons-layout styles paired with `GLASS_DARK_APPEARANCE` |
|
|
265
|
+
| `THEMES` | `Record<ThemeBundleId, ThemeBundle>`: `THEMES['glass-dark']` is `{ appearance, buttonsLayout }` |
|
|
266
|
+
| `PAYPAL_VAULTED_PAYMENT_METHOD_TYPE` | `'paypal_vaulted'`: the payment-method channel that opts a session intent or process request into Flo-owned PayPal Order and setup-token continuations; legacy `paypal` keeps provider-managed semantics |
|
|
267
|
+
| `SUPPORTED_CARD_BRANDS` | `'visa'`, `'mastercard'`, `'mastercard_debit'`, `'amex'`, `'discover'` |
|
|
268
|
+
| `STRIPE_EXPRESS_METHODS` | `'apple_pay'`, `'google_pay'`, `'paypal'`, `'link'`, `'amazon_pay'`, `'klarna'`: Stripe methods rendered as express buttons. Every other enabled Stripe method appears in the React SDK's payment-method accordion. |
|
|
245
269
|
| `CURRENCY_MAP` | Country code to `CurrencyInfo` mapping (EU, GB, US, CA, NZ, AU) |
|
|
246
|
-
| `DEFAULT_CURRENCY` | USD fallback |
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
|
251
|
-
|
|
252
|
-
|
|
270
|
+
| `DEFAULT_CURRENCY` | The USD fallback `CurrencyInfo` |
|
|
271
|
+
| `IDEMPOTENCY_KEY_HEADER` | `'Idempotency-Key'`, sent on `POST /v1/checkouts/sessions` |
|
|
272
|
+
| `MAX_IDEMPOTENCY_KEY_LENGTH` | `255`, the longest key the SDK accepts |
|
|
273
|
+
| `IDEMPOTENCY_IN_PROGRESS_CODE` | `'IdempotencyKeyInProgress'`: the billing API's retryable in-progress replay code. Retry with the same key. |
|
|
274
|
+
| `TELEMETRY_LOG_NAMES` | The complete SDK-authored lifecycle log catalog |
|
|
275
|
+
| `TELEMETRY_FAILURE_CATEGORIES` | `'server_error'`, `'transport_error'`, `'invalid_response'`, `'provider_runtime'` |
|
|
276
|
+
|
|
277
|
+
### Theme and payment-method helpers
|
|
278
|
+
|
|
279
|
+
| Function | Description |
|
|
280
|
+
|----------|-------------|
|
|
281
|
+
| `resolveTheme(id)` | Resolves a `ThemeId` to its `ThemeBundle`; `'classic'` and missing ids return `undefined` |
|
|
282
|
+
| `resolveButtonsLayoutTheme(name)` | Maps a `ButtonsLayoutTheme` name to its `ButtonsLayoutStyles` preset |
|
|
283
|
+
| `partitionStripeMethods(enabledMethods, options?)` | Splits the billing API's enabled Stripe methods into `{ expressMethods, paymentElementMethods }`. Drops `card`, and also `paypal` when `excludePaypal` is true. |
|
|
284
|
+
| `stripeExpressMethodToOptionKey(method)` | Maps Stripe wire identifiers (`apple_pay`, `google_pay`, `amazon_pay`) to the camelCase option keys Stripe's express checkout expects |
|
|
285
|
+
|
|
286
|
+
### Validation helpers
|
|
287
|
+
|
|
288
|
+
| Function | Description |
|
|
289
|
+
|----------|-------------|
|
|
290
|
+
| `getCurrencyByCountry(countryCode)` | `CurrencyInfo` for a country code, falling back to USD |
|
|
253
291
|
| `isCardHolderNamePlaceholder(...nameParts)` | Rejects only the normalized `Card Holder Name` fallback sentinel |
|
|
254
|
-
| `
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
257
|
-
|
|
258
|
-
|
|
292
|
+
| `isValidEmail(email)` | Browser-safe email validation matching the billing API; `false` for nullish, blank, padded, or malformed values |
|
|
293
|
+
| `isValidPublishableKey(key)` | `true` for keys shaped like `pk_test_...` or `pk_live_...` |
|
|
294
|
+
| `isValidSecretKey(key)` | `true` for keys shaped like `sk_test_...` or `sk_live_...` |
|
|
295
|
+
| `isCardSetupCheckoutSession(session)` | `true` only for a session read with `checkoutMode: 'setup'`, zero amount, and no products. Use it before mounting a no-charge card-setup surface. |
|
|
296
|
+
| `isValidPostalCode(country, postalCode)` | `true` when the trimmed postcode matches the country's format; fails open for a blank postcode, an unresolvable country, or an unsupported country |
|
|
297
|
+
| `isPostalCodeSupported(country)` | `true` when a postcode rule exists for the country |
|
|
298
|
+
| `getPostalCodeExample(country)` | The billing API's example postcode for the country (US `12345 or 12345-6789`, GB `SW1A 1AA`, CA `A1A 1A1`), or `undefined` |
|
|
299
|
+
| `getPostalCodeLabel(countryCode)` | Country-appropriate field label (`ZIP Code`, `Postcode`, `Eircode`), defaulting to `Postal Code` |
|
|
300
|
+
| `getStateFromPostalCode(country, postalCode)` | Derives a US state or Canadian province code from a postcode for AVS enrichment; `null` for other countries or a malformed code |
|
|
259
301
|
|
|
260
|
-
|
|
302
|
+
### Idempotency helpers
|
|
261
303
|
|
|
262
|
-
|
|
|
263
|
-
|
|
264
|
-
| `
|
|
265
|
-
| `
|
|
266
|
-
| `isValidPostalCode(country, postalCode)` | `true` when the (trimmed) postcode matches the country's format. **Fails open** in exactly the backend's three cases: blank/whitespace postcode, unresolvable country, or an unsupported `validator` locale. Callers distinguish "required" (empty) from "malformed" by checking emptiness + `isPostalCodeSupported` themselves — an empty value fails open here, as it does server-side |
|
|
267
|
-
| `isValidEmail(email)` | Shared browser-safe email validation. Returns `false` for nullish, blank, padded, or malformed values; callers should trim intentionally before validation when surrounding whitespace is acceptable. |
|
|
268
|
-
| `getPostalCodeExample(country)` | The example postcode the backend embeds in its 400 message (US `12345 or 12345-6789`, GB `SW1A 1AA`, CA `A1A 1A1`, …), or `undefined` when there is none. Kept verbatim in sync with the backend's `POSTAL_CODE_EXAMPLES` so the SDK's inline hint and the server's rejection message never contradict |
|
|
269
|
-
| `getStateFromPostalCode(country, postalCode)` | Derives a US state / CA province code from a postcode for AVS enrichment; `null` for other countries or a malformed code |
|
|
304
|
+
| Function | Description |
|
|
305
|
+
|----------|-------------|
|
|
306
|
+
| `generateIdempotencyKey()` | A cryptographically random key, or `undefined` when no secure random source exists |
|
|
307
|
+
| `resolveIdempotencyKey(supplied?)` | Returns a supplied key unchanged after validating it is non-empty and at most `MAX_IDEMPOTENCY_KEY_LENGTH` characters, or a freshly generated key when none is supplied |
|
|
270
308
|
|
|
271
|
-
###
|
|
272
|
-
|
|
273
|
-
Primitives for the stable checkout-create `Idempotency-Key` (TeamFloPay/backend#972). `@flopay/js` and `@flopay/react` use these automatically; import them only if you build your own create flow.
|
|
309
|
+
### Error helpers
|
|
274
310
|
|
|
275
311
|
| Export | Description |
|
|
276
312
|
|--------|-------------|
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
313
|
+
| `FloPayError` | Error class with `type`, `code?`, `declineCode?`, `param?`, `statusCode?`, and `cause?` |
|
|
314
|
+
| `FloPayErrorType` | `'validation_error'` / `'api_error'` / `'authentication_error'` / `'rate_limit_error'` / `'network_error'` |
|
|
315
|
+
| `validationError(message, param?)` | Creates a `validation_error` |
|
|
316
|
+
| `apiError(message, code?, statusCode?)` | Creates an `api_error` |
|
|
317
|
+
| `authenticationError(message)` | Creates an `authentication_error` |
|
|
318
|
+
| `rateLimitError(message)` | Creates a `rate_limit_error` |
|
|
319
|
+
| `networkError(message)` | Creates a `network_error` |
|
|
320
|
+
| `normalizeCheckoutFailureOutcome(value)` | Narrows an unknown value to a `CheckoutFailureOutcome`, or `undefined` |
|
|
321
|
+
|
|
322
|
+
### Telemetry and Sentry helpers
|
|
323
|
+
|
|
324
|
+
| Function | Description |
|
|
325
|
+
|----------|-------------|
|
|
326
|
+
| `buildTelemetryErrorEvent(input)` | Builds a closed technical-error event; the only builder that accepts a `failureCategory` |
|
|
327
|
+
| `buildTelemetryLogEvent(input)` | Builds a closed lifecycle log event |
|
|
328
|
+
| `buildTelemetryPerformanceEvent(input)` | Builds a closed performance measurement |
|
|
329
|
+
| `buildTelemetryTerminalEvent(input)` | Builds a closed terminal-outcome event |
|
|
330
|
+
| `serializeTelemetryBatch(events, options)` | Serializes events into the billing API's v1 telemetry envelope |
|
|
331
|
+
| `dropThirdPartyOnlyError(event)` | Sentry `beforeSend` predicate that drops recognized third-party-only exception stacks and otherwise returns the original event |
|
|
332
|
+
|
|
333
|
+
## Errors
|
|
334
|
+
|
|
335
|
+
Errors the FloPay SDK creates or normalizes are `FloPayError`s. Other failures can still surface unchanged, such as an underlying `fetch()` rejection, a Stripe error, or a native `Error` from a payload builder, so check `error instanceof FloPayError` before reading `type` or `code`. Branch on its stable `type` and `code`, never on `message`.
|
|
336
|
+
|
|
337
|
+
| `type` | Meaning |
|
|
338
|
+
|--------|---------|
|
|
339
|
+
| `validation_error` | The input was rejected before or by the billing API. `param` names the offending field when known. |
|
|
340
|
+
| `api_error` | The billing API or a payment provider returned an error. `code` carries the billing API's stable code and `statusCode` the HTTP status. |
|
|
341
|
+
| `authentication_error` | A key or session credential was rejected. |
|
|
342
|
+
| `rate_limit_error` | The request was rate limited; retry later. |
|
|
343
|
+
| `network_error` | No usable response arrived. |
|
|
344
|
+
|
|
345
|
+
Codes raised by this package's helpers:
|
|
346
|
+
|
|
347
|
+
- `InvalidIdempotencyKey` (`validation_error`, `param: 'idempotencyKey'`) from `resolveIdempotencyKey()` when a supplied key is blank or longer than `MAX_IDEMPOTENCY_KEY_LENGTH`. The message never echoes the key.
|
|
348
|
+
- `CaptureMethodUnsupportedForSubscription` (`validation_error`, `param: 'captureMethod'`) when manual capture is requested for a cart that contains a subscription.
|
|
349
|
+
|
|
350
|
+
Billing API outcomes described by shared types:
|
|
351
|
+
|
|
352
|
+
- `IDEMPOTENCY_IN_PROGRESS_CODE` marks a retryable in-progress replay of the same idempotency key; retry with the same key. A `409` payload conflict for a reused key is not retryable.
|
|
353
|
+
- `CheckoutFailureOutcome` classifies an authorization that did not complete: `authorization_declined`, `authentication_required`, `authorization_expired`, or `capture_failed`.
|
|
354
|
+
- `PaymentMethodRemovalError` covers saved-method removal from your server: `payment_method_not_found` (404, also returned for a method you cannot access), the `409` conflicts `payment_method_has_active_payment`, `payment_method_in_use`, and `payment_method_reassignment_requires_payer_action`, and `payment_method_deletion_failed` (502, retry-safe, with a `reason` and `resolution`).
|