@flopay/shared 1.3.1 → 1.3.3

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/dist/index.d.cts CHANGED
@@ -1097,6 +1097,17 @@ interface CreateSessionParams {
1097
1097
  timeoutMs?: number;
1098
1098
  /** UTM and funnel tracking metadata. */
1099
1099
  utmMetadata?: Record<string, string | null | undefined>[];
1100
+ /**
1101
+ * Optional idempotency key identifying exactly **one** logical checkout
1102
+ * creation (TeamFloPay/backend#972). When supplied it is sent unchanged as
1103
+ * the `Idempotency-Key` header and reused across the SDK's transport retries,
1104
+ * so a timeout or lost response cannot mint a second session. Supply this to
1105
+ * keep the key stable across React remounts, multiple SDK instances, or
1106
+ * server retries you control; it **must not** be reused for a new purchase.
1107
+ * When omitted, the SDK generates a fresh, cryptographically random key per
1108
+ * call. Must be non-empty and at most 255 characters.
1109
+ */
1110
+ idempotencyKey?: string;
1100
1111
  }
1101
1112
  /**
1102
1113
  * Parameters for inline session creation via `FloPayCheckout.createSession`.
@@ -1141,6 +1152,16 @@ interface InlineSessionParams {
1141
1152
  tagsData?: TagsData;
1142
1153
  /** UTM and funnel tracking metadata. */
1143
1154
  utmMetadata?: Record<string, string | null | undefined>[];
1155
+ /**
1156
+ * Optional idempotency key identifying exactly **one** logical checkout
1157
+ * creation (TeamFloPay/backend#972). When supplied it is sent unchanged as
1158
+ * the `Idempotency-Key` header and reused across the SDK's transport retries.
1159
+ * Supply this to keep the key stable across React remounts, multiple SDK
1160
+ * instances, or server retries you control; it **must not** be reused for a
1161
+ * new purchase. When omitted, the SDK generates a fresh, cryptographically
1162
+ * random key per create. Must be non-empty and at most 255 characters.
1163
+ */
1164
+ idempotencyKey?: string;
1144
1165
  /** Whether AVS is enabled for this checkout. */
1145
1166
  avsCheck?: boolean;
1146
1167
  /** Checkout type: 'standard_checkout' or 'embedded_checkout'. */
@@ -1312,7 +1333,7 @@ declare function getConfiguredBillingApiUrl(): string;
1312
1333
  declare function getFloPayEnvironment(): FloPayEnvironment;
1313
1334
 
1314
1335
  /** Current SDK version. */
1315
- declare const SDK_VERSION = "1.3.1";
1336
+ declare const SDK_VERSION = "1.3.3";
1316
1337
  /**
1317
1338
  * HTTP header the SDK sends on `POST /v1/checkouts/sessions` so the backend
1318
1339
  * can decide whether to embed the vault capture block (the hosted PCI card
@@ -1746,24 +1767,33 @@ declare function getStateFromPostalCode(country: string, postalCode: string): st
1746
1767
 
1747
1768
  /**
1748
1769
  * True when `validator` has an authoritative postcode pattern for the country
1749
- * (ISO 3166-1 alpha-2, case-insensitive). Supported countries validate the
1750
- * postcode format; unsupported / no-postcode countries fail open.
1770
+ * (normalized to ISO 3166-1 alpha-2 via {@link normalizeCountryToIso2}).
1771
+ * Supported countries validate the postcode format; unsupported / no-postcode /
1772
+ * unresolvable countries fail open. Note the 2-letter passthrough means `UK`
1773
+ * (an unsupported `validator` locale) returns `false` here — matching the
1774
+ * backend; use the full name `United Kingdom` for `GB` semantics.
1751
1775
  */
1752
1776
  declare function isPostalCodeSupported(country: string): boolean;
1753
1777
  /**
1754
- * Validate a postcode against the country's expected format.
1778
+ * Validate a postcode against the country's expected format. A faithful mirror
1779
+ * of the backend's `isPostalCodeValidForCountry`, so the SDK never blocks a
1780
+ * value the server would accept, nor opens the submit gate on one it would
1781
+ * reject. Fail-open (returns `true`) in exactly the backend's three cases:
1782
+ *
1783
+ * - `postalCode` is blank / whitespace-only;
1784
+ * - the country can't be normalized to an ISO-2 locale;
1785
+ * - the ISO-2 locale isn't one `validator` recognises.
1755
1786
  *
1756
- * - Unsupported / no-postcode country → `true` (fail open; never block).
1757
- * - Supported country `validator`'s `isPostalCode(zip, locale)` on the
1758
- * trimmed value. An empty string is not a valid postcode, so a supported
1759
- * country with an empty value returns `false`; callers that distinguish
1760
- * "required" (empty) from "malformed" (format) should check emptiness first.
1787
+ * Otherwise returns `validator`'s `isPostalCode(zip.trim(), locale)`. Callers
1788
+ * that distinguish "required" (empty) from "malformed" (bad format) must check
1789
+ * emptiness themselves an empty value fails open here, as it does server-side.
1761
1790
  */
1762
1791
  declare function isValidPostalCode(country: string, postalCode: string): boolean;
1763
1792
  /**
1764
- * A curated example postcode for the country (e.g. US `12345 or 12345-6789`,
1765
- * GB `SW1A 1AA`, CA `A1A 1A1`), or `undefined` when there is no curated example
1766
- * (unsupported country, or a supported country not in the map).
1793
+ * The example postcode the backend would embed in its 400 message for the
1794
+ * country (e.g. US `12345 or 12345-6789`, GB `SW1A 1AA`, CA `A1A 1A1`), or
1795
+ * `undefined` when there is none (unresolvable country, or a supported country
1796
+ * absent from {@link POSTAL_CODE_EXAMPLES}).
1767
1797
  */
1768
1798
  declare function getPostalCodeExample(country: string): string | undefined;
1769
1799
 
@@ -1898,4 +1928,60 @@ declare function isValidSecretKey(key: string): boolean;
1898
1928
  */
1899
1929
  declare function isSetupIntentClientSecret(clientSecret: string | null | undefined): boolean;
1900
1930
 
1901
- export { type AVSFieldConfig, BILLING_API_URL, BILLING_API_URL_PRODUCTION, BILLING_API_URL_STAGING, BOLD_DARK_APPEARANCE, BOLD_LIGHT_APPEARANCE, BUTTONS_LAYOUT_BOLD_DARK, BUTTONS_LAYOUT_BOLD_LIGHT, BUTTONS_LAYOUT_DARK, BUTTONS_LAYOUT_DEFAULT, BUTTONS_LAYOUT_GLASS_DARK, BUTTONS_LAYOUT_GLASS_LIGHT, BUTTONS_LAYOUT_MINIMAL, BUTTONS_LAYOUT_MODERN_DARK, BUTTONS_LAYOUT_MODERN_LIGHT, BUTTONS_LAYOUT_ROUNDED, type BeforeButtonClickEvent, type BillingDetails, type BillingProvider, type BuildCheckoutDisplayDataOptions, type ButtonsLayoutStyles, type ButtonsLayoutTheme, CA_PROVINCES, COUNTRY_OPTIONS, CURRENCY_MAP, type CardCaptureAdapter, type CardCaptureEventType, type CardCaptureMountOptions, type CardCaptureOutcomeEvent, type CardCaptureProviderId, type CheckoutAccount, type CheckoutButtonMethod, type CheckoutDisplayData, type CheckoutGateway, type CheckoutGateways, type CheckoutItem, type CheckoutMode, type CheckoutModeKind, type CheckoutProcessError, type CheckoutProcessingPending, type CheckoutProduct, type CheckoutProductType, type CheckoutSession, type CheckoutSessionProduct, type CheckoutSessionResult, type CheckoutSubscription, type ConfirmCardPaymentParams, type ConfirmCardPaymentResult, type ConfirmPaymentParams, type CountryOption, type CreateCustomerParams, type CreatePaymentMethodResult, type CreateSessionParams, type CurrencyInfo, type Customer, DEFAULT_API_BASE_URL, DEFAULT_API_VERSION, DEFAULT_APPEARANCE, DEFAULT_CURRENCY, type DeclineEvent, type DisplayLineItem, ELEMENT_TYPES, type ElementChangeEvent, type ElementOptions, type ElementType, FLAT_APPEARANCE, FLO_SDK_VERSION_HEADER, type FloPayAppearance, type FloPayConfig, type FloPayEnvironment, FloPayError, type FloPayErrorType, type FloPayThemeVariables, GLASS_DARK_APPEARANCE, GLASS_LIGHT_APPEARANCE, type GatewayEnvironment, type InlineSessionDraft, type InlineSessionParams, type InlineSessionPatch, type LineItem, MODERN_DARK_APPEARANCE, MODERN_LIGHT_APPEARANCE, type MountedElement, NIGHT_APPEARANCE, type NormalizedCheckoutSession, type NormalizedGatewayEnvironment, type PaymentProviderAdapter, type PaymentResult, type PriceData, type ProcessPaymentParams, type RecurringInterval, SDK_VERSION, STRIPE_EXPRESS_METHODS, STRIPE_METHOD_AMOUNT_LIMITS, STRIPE_METHOD_COUNTRIES, STRIPE_METHOD_CURRENCIES, STRIPE_METHOD_MATRIX, SUPPORTED_CARD_BRANDS, type StateOption, type StripeMethodEntry, type StripeMethodThemeVariant, THEMES, type TagsData, type ThemeBundle, type ThemeBundleId, type ThemeId, type TokenizedBody, US_STATES, type UpdateCustomerParams, type VaultCaptureBlock, type VaultCaptureResultMessage, type VaultCardFieldKey, type VaultCardThemeColors, type WebhookEvent, apiError, authenticationError, buildCheckoutDisplayData, buildItemPayload, buildProductPayload, buildSubscriptionPayload, configureFlopay, filterStripeMethodsByAmount, filterStripeMethodsByCountry, filterStripeMethodsByCurrency, foldIntoProducts, getConfiguredBillingApiUrl, getCountryByCode, getCurrencyByCountry, getFloPayEnvironment, getPostalCodeExample, getPostalCodeLabel, getStateFromPostalCode, getStateLabel, getStateOptions, getStripeMethodDisplayName, hasVendoredStripeMethodLogo, isAVSEnabled, isAVSFieldVisible, isPostalCodeSupported, isSetupIntentClientSecret, isValidPostalCode, isValidPublishableKey, isValidSecretKey, needsStripeMethodExplicitConfirm, networkError, normalizeGatewayEnvironment, partitionStripeMethods, rateLimitError, resolveAVSConfig, resolveBillingApiUrl, resolveButtonsLayoutTheme, resolveSessionCurrency, resolveStripeMethodBrandVariant, resolveTheme, stripeExpressMethodToOptionKey, validationError };
1931
+ /**
1932
+ * HTTP header the SDK sends to make checkout-session creation idempotent
1933
+ * (TeamFloPay/backend#972, paired SDK issue TeamFloPay/sdk#133).
1934
+ *
1935
+ * The backend treats the header as **optional**: when it is absent the legacy,
1936
+ * non-idempotent creation path runs unchanged. When present, every transport
1937
+ * retry of the same logical create reuses the same key so a timeout or lost
1938
+ * response cannot mint a second checkout session.
1939
+ */
1940
+ declare const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
1941
+ /**
1942
+ * Maximum length of an idempotency key accepted by the billing API
1943
+ * (TeamFloPay/backend#972). Mirrors the widely-used 255-character ceiling;
1944
+ * automatically generated keys stay well under it.
1945
+ */
1946
+ declare const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
1947
+ /**
1948
+ * Backend `code` (TeamFloPay/backend#972) returned when a checkout-create
1949
+ * replay arrives while the first request carrying the same key is still being
1950
+ * processed. It is a **retryable** in-progress response: the caller should
1951
+ * retry with the *same* key after a short backoff, never with a new key. This
1952
+ * is distinct from a payload-conflict (`409`), which is non-retryable.
1953
+ *
1954
+ * Mirrors a backend-defined code the same way the coupon error codes
1955
+ * (`CouponLimitExceeded`, `CouponCurrencyUnsupported`) mirror billing v1.1.2.
1956
+ */
1957
+ declare const IDEMPOTENCY_IN_PROGRESS_CODE = "IdempotencyKeyInProgress";
1958
+ /**
1959
+ * Generate a cryptographically random, high-entropy idempotency key.
1960
+ *
1961
+ * Prefers `crypto.randomUUID()` and falls back to 16 random bytes from
1962
+ * `crypto.getRandomValues`. Returns `undefined` when no cryptographically
1963
+ * secure RNG is available so the caller falls back to the legacy unkeyed path
1964
+ * rather than emitting a weak or predictable key.
1965
+ *
1966
+ * The key is never derived from request data, customer identity, or time, so
1967
+ * two independent operations never collide by construction.
1968
+ */
1969
+ declare function generateIdempotencyKey(): string | undefined;
1970
+ /**
1971
+ * Resolve the idempotency key for one logical checkout-create operation.
1972
+ *
1973
+ * - A merchant-supplied key is validated (non-empty, within
1974
+ * {@link MAX_IDEMPOTENCY_KEY_LENGTH}) and returned **unchanged** so it can be
1975
+ * preserved across React remounts, multiple SDK instances, or server retries
1976
+ * the merchant controls.
1977
+ * - Otherwise a fresh cryptographically random key is generated. When no secure
1978
+ * RNG is available this returns `undefined` and the caller omits the header,
1979
+ * preserving the legacy path.
1980
+ *
1981
+ * @throws FloPayError('validation_error') when a supplied key is empty,
1982
+ * whitespace-only, or exceeds the length limit. The offending value is never
1983
+ * included in the error message.
1984
+ */
1985
+ declare function resolveIdempotencyKey(supplied?: string): string | undefined;
1986
+
1987
+ export { type AVSFieldConfig, BILLING_API_URL, BILLING_API_URL_PRODUCTION, BILLING_API_URL_STAGING, BOLD_DARK_APPEARANCE, BOLD_LIGHT_APPEARANCE, BUTTONS_LAYOUT_BOLD_DARK, BUTTONS_LAYOUT_BOLD_LIGHT, BUTTONS_LAYOUT_DARK, BUTTONS_LAYOUT_DEFAULT, BUTTONS_LAYOUT_GLASS_DARK, BUTTONS_LAYOUT_GLASS_LIGHT, BUTTONS_LAYOUT_MINIMAL, BUTTONS_LAYOUT_MODERN_DARK, BUTTONS_LAYOUT_MODERN_LIGHT, BUTTONS_LAYOUT_ROUNDED, type BeforeButtonClickEvent, type BillingDetails, type BillingProvider, type BuildCheckoutDisplayDataOptions, type ButtonsLayoutStyles, type ButtonsLayoutTheme, CA_PROVINCES, COUNTRY_OPTIONS, CURRENCY_MAP, type CardCaptureAdapter, type CardCaptureEventType, type CardCaptureMountOptions, type CardCaptureOutcomeEvent, type CardCaptureProviderId, type CheckoutAccount, type CheckoutButtonMethod, type CheckoutDisplayData, type CheckoutGateway, type CheckoutGateways, type CheckoutItem, type CheckoutMode, type CheckoutModeKind, type CheckoutProcessError, type CheckoutProcessingPending, type CheckoutProduct, type CheckoutProductType, type CheckoutSession, type CheckoutSessionProduct, type CheckoutSessionResult, type CheckoutSubscription, type ConfirmCardPaymentParams, type ConfirmCardPaymentResult, type ConfirmPaymentParams, type CountryOption, type CreateCustomerParams, type CreatePaymentMethodResult, type CreateSessionParams, type CurrencyInfo, type Customer, DEFAULT_API_BASE_URL, DEFAULT_API_VERSION, DEFAULT_APPEARANCE, DEFAULT_CURRENCY, type DeclineEvent, type DisplayLineItem, ELEMENT_TYPES, type ElementChangeEvent, type ElementOptions, type ElementType, FLAT_APPEARANCE, FLO_SDK_VERSION_HEADER, type FloPayAppearance, type FloPayConfig, type FloPayEnvironment, FloPayError, type FloPayErrorType, type FloPayThemeVariables, GLASS_DARK_APPEARANCE, GLASS_LIGHT_APPEARANCE, type GatewayEnvironment, IDEMPOTENCY_IN_PROGRESS_CODE, IDEMPOTENCY_KEY_HEADER, type InlineSessionDraft, type InlineSessionParams, type InlineSessionPatch, type LineItem, MAX_IDEMPOTENCY_KEY_LENGTH, MODERN_DARK_APPEARANCE, MODERN_LIGHT_APPEARANCE, type MountedElement, NIGHT_APPEARANCE, type NormalizedCheckoutSession, type NormalizedGatewayEnvironment, type PaymentProviderAdapter, type PaymentResult, type PriceData, type ProcessPaymentParams, type RecurringInterval, SDK_VERSION, STRIPE_EXPRESS_METHODS, STRIPE_METHOD_AMOUNT_LIMITS, STRIPE_METHOD_COUNTRIES, STRIPE_METHOD_CURRENCIES, STRIPE_METHOD_MATRIX, SUPPORTED_CARD_BRANDS, type StateOption, type StripeMethodEntry, type StripeMethodThemeVariant, THEMES, type TagsData, type ThemeBundle, type ThemeBundleId, type ThemeId, type TokenizedBody, US_STATES, type UpdateCustomerParams, type VaultCaptureBlock, type VaultCaptureResultMessage, type VaultCardFieldKey, type VaultCardThemeColors, type WebhookEvent, apiError, authenticationError, buildCheckoutDisplayData, buildItemPayload, buildProductPayload, buildSubscriptionPayload, configureFlopay, filterStripeMethodsByAmount, filterStripeMethodsByCountry, filterStripeMethodsByCurrency, foldIntoProducts, generateIdempotencyKey, getConfiguredBillingApiUrl, getCountryByCode, getCurrencyByCountry, getFloPayEnvironment, getPostalCodeExample, getPostalCodeLabel, getStateFromPostalCode, getStateLabel, getStateOptions, getStripeMethodDisplayName, hasVendoredStripeMethodLogo, isAVSEnabled, isAVSFieldVisible, isPostalCodeSupported, isSetupIntentClientSecret, isValidPostalCode, isValidPublishableKey, isValidSecretKey, needsStripeMethodExplicitConfirm, networkError, normalizeGatewayEnvironment, partitionStripeMethods, rateLimitError, resolveAVSConfig, resolveBillingApiUrl, resolveButtonsLayoutTheme, resolveIdempotencyKey, resolveSessionCurrency, resolveStripeMethodBrandVariant, resolveTheme, stripeExpressMethodToOptionKey, validationError };
package/dist/index.d.ts CHANGED
@@ -1097,6 +1097,17 @@ interface CreateSessionParams {
1097
1097
  timeoutMs?: number;
1098
1098
  /** UTM and funnel tracking metadata. */
1099
1099
  utmMetadata?: Record<string, string | null | undefined>[];
1100
+ /**
1101
+ * Optional idempotency key identifying exactly **one** logical checkout
1102
+ * creation (TeamFloPay/backend#972). When supplied it is sent unchanged as
1103
+ * the `Idempotency-Key` header and reused across the SDK's transport retries,
1104
+ * so a timeout or lost response cannot mint a second session. Supply this to
1105
+ * keep the key stable across React remounts, multiple SDK instances, or
1106
+ * server retries you control; it **must not** be reused for a new purchase.
1107
+ * When omitted, the SDK generates a fresh, cryptographically random key per
1108
+ * call. Must be non-empty and at most 255 characters.
1109
+ */
1110
+ idempotencyKey?: string;
1100
1111
  }
1101
1112
  /**
1102
1113
  * Parameters for inline session creation via `FloPayCheckout.createSession`.
@@ -1141,6 +1152,16 @@ interface InlineSessionParams {
1141
1152
  tagsData?: TagsData;
1142
1153
  /** UTM and funnel tracking metadata. */
1143
1154
  utmMetadata?: Record<string, string | null | undefined>[];
1155
+ /**
1156
+ * Optional idempotency key identifying exactly **one** logical checkout
1157
+ * creation (TeamFloPay/backend#972). When supplied it is sent unchanged as
1158
+ * the `Idempotency-Key` header and reused across the SDK's transport retries.
1159
+ * Supply this to keep the key stable across React remounts, multiple SDK
1160
+ * instances, or server retries you control; it **must not** be reused for a
1161
+ * new purchase. When omitted, the SDK generates a fresh, cryptographically
1162
+ * random key per create. Must be non-empty and at most 255 characters.
1163
+ */
1164
+ idempotencyKey?: string;
1144
1165
  /** Whether AVS is enabled for this checkout. */
1145
1166
  avsCheck?: boolean;
1146
1167
  /** Checkout type: 'standard_checkout' or 'embedded_checkout'. */
@@ -1312,7 +1333,7 @@ declare function getConfiguredBillingApiUrl(): string;
1312
1333
  declare function getFloPayEnvironment(): FloPayEnvironment;
1313
1334
 
1314
1335
  /** Current SDK version. */
1315
- declare const SDK_VERSION = "1.3.1";
1336
+ declare const SDK_VERSION = "1.3.3";
1316
1337
  /**
1317
1338
  * HTTP header the SDK sends on `POST /v1/checkouts/sessions` so the backend
1318
1339
  * can decide whether to embed the vault capture block (the hosted PCI card
@@ -1746,24 +1767,33 @@ declare function getStateFromPostalCode(country: string, postalCode: string): st
1746
1767
 
1747
1768
  /**
1748
1769
  * True when `validator` has an authoritative postcode pattern for the country
1749
- * (ISO 3166-1 alpha-2, case-insensitive). Supported countries validate the
1750
- * postcode format; unsupported / no-postcode countries fail open.
1770
+ * (normalized to ISO 3166-1 alpha-2 via {@link normalizeCountryToIso2}).
1771
+ * Supported countries validate the postcode format; unsupported / no-postcode /
1772
+ * unresolvable countries fail open. Note the 2-letter passthrough means `UK`
1773
+ * (an unsupported `validator` locale) returns `false` here — matching the
1774
+ * backend; use the full name `United Kingdom` for `GB` semantics.
1751
1775
  */
1752
1776
  declare function isPostalCodeSupported(country: string): boolean;
1753
1777
  /**
1754
- * Validate a postcode against the country's expected format.
1778
+ * Validate a postcode against the country's expected format. A faithful mirror
1779
+ * of the backend's `isPostalCodeValidForCountry`, so the SDK never blocks a
1780
+ * value the server would accept, nor opens the submit gate on one it would
1781
+ * reject. Fail-open (returns `true`) in exactly the backend's three cases:
1782
+ *
1783
+ * - `postalCode` is blank / whitespace-only;
1784
+ * - the country can't be normalized to an ISO-2 locale;
1785
+ * - the ISO-2 locale isn't one `validator` recognises.
1755
1786
  *
1756
- * - Unsupported / no-postcode country → `true` (fail open; never block).
1757
- * - Supported country `validator`'s `isPostalCode(zip, locale)` on the
1758
- * trimmed value. An empty string is not a valid postcode, so a supported
1759
- * country with an empty value returns `false`; callers that distinguish
1760
- * "required" (empty) from "malformed" (format) should check emptiness first.
1787
+ * Otherwise returns `validator`'s `isPostalCode(zip.trim(), locale)`. Callers
1788
+ * that distinguish "required" (empty) from "malformed" (bad format) must check
1789
+ * emptiness themselves an empty value fails open here, as it does server-side.
1761
1790
  */
1762
1791
  declare function isValidPostalCode(country: string, postalCode: string): boolean;
1763
1792
  /**
1764
- * A curated example postcode for the country (e.g. US `12345 or 12345-6789`,
1765
- * GB `SW1A 1AA`, CA `A1A 1A1`), or `undefined` when there is no curated example
1766
- * (unsupported country, or a supported country not in the map).
1793
+ * The example postcode the backend would embed in its 400 message for the
1794
+ * country (e.g. US `12345 or 12345-6789`, GB `SW1A 1AA`, CA `A1A 1A1`), or
1795
+ * `undefined` when there is none (unresolvable country, or a supported country
1796
+ * absent from {@link POSTAL_CODE_EXAMPLES}).
1767
1797
  */
1768
1798
  declare function getPostalCodeExample(country: string): string | undefined;
1769
1799
 
@@ -1898,4 +1928,60 @@ declare function isValidSecretKey(key: string): boolean;
1898
1928
  */
1899
1929
  declare function isSetupIntentClientSecret(clientSecret: string | null | undefined): boolean;
1900
1930
 
1901
- export { type AVSFieldConfig, BILLING_API_URL, BILLING_API_URL_PRODUCTION, BILLING_API_URL_STAGING, BOLD_DARK_APPEARANCE, BOLD_LIGHT_APPEARANCE, BUTTONS_LAYOUT_BOLD_DARK, BUTTONS_LAYOUT_BOLD_LIGHT, BUTTONS_LAYOUT_DARK, BUTTONS_LAYOUT_DEFAULT, BUTTONS_LAYOUT_GLASS_DARK, BUTTONS_LAYOUT_GLASS_LIGHT, BUTTONS_LAYOUT_MINIMAL, BUTTONS_LAYOUT_MODERN_DARK, BUTTONS_LAYOUT_MODERN_LIGHT, BUTTONS_LAYOUT_ROUNDED, type BeforeButtonClickEvent, type BillingDetails, type BillingProvider, type BuildCheckoutDisplayDataOptions, type ButtonsLayoutStyles, type ButtonsLayoutTheme, CA_PROVINCES, COUNTRY_OPTIONS, CURRENCY_MAP, type CardCaptureAdapter, type CardCaptureEventType, type CardCaptureMountOptions, type CardCaptureOutcomeEvent, type CardCaptureProviderId, type CheckoutAccount, type CheckoutButtonMethod, type CheckoutDisplayData, type CheckoutGateway, type CheckoutGateways, type CheckoutItem, type CheckoutMode, type CheckoutModeKind, type CheckoutProcessError, type CheckoutProcessingPending, type CheckoutProduct, type CheckoutProductType, type CheckoutSession, type CheckoutSessionProduct, type CheckoutSessionResult, type CheckoutSubscription, type ConfirmCardPaymentParams, type ConfirmCardPaymentResult, type ConfirmPaymentParams, type CountryOption, type CreateCustomerParams, type CreatePaymentMethodResult, type CreateSessionParams, type CurrencyInfo, type Customer, DEFAULT_API_BASE_URL, DEFAULT_API_VERSION, DEFAULT_APPEARANCE, DEFAULT_CURRENCY, type DeclineEvent, type DisplayLineItem, ELEMENT_TYPES, type ElementChangeEvent, type ElementOptions, type ElementType, FLAT_APPEARANCE, FLO_SDK_VERSION_HEADER, type FloPayAppearance, type FloPayConfig, type FloPayEnvironment, FloPayError, type FloPayErrorType, type FloPayThemeVariables, GLASS_DARK_APPEARANCE, GLASS_LIGHT_APPEARANCE, type GatewayEnvironment, type InlineSessionDraft, type InlineSessionParams, type InlineSessionPatch, type LineItem, MODERN_DARK_APPEARANCE, MODERN_LIGHT_APPEARANCE, type MountedElement, NIGHT_APPEARANCE, type NormalizedCheckoutSession, type NormalizedGatewayEnvironment, type PaymentProviderAdapter, type PaymentResult, type PriceData, type ProcessPaymentParams, type RecurringInterval, SDK_VERSION, STRIPE_EXPRESS_METHODS, STRIPE_METHOD_AMOUNT_LIMITS, STRIPE_METHOD_COUNTRIES, STRIPE_METHOD_CURRENCIES, STRIPE_METHOD_MATRIX, SUPPORTED_CARD_BRANDS, type StateOption, type StripeMethodEntry, type StripeMethodThemeVariant, THEMES, type TagsData, type ThemeBundle, type ThemeBundleId, type ThemeId, type TokenizedBody, US_STATES, type UpdateCustomerParams, type VaultCaptureBlock, type VaultCaptureResultMessage, type VaultCardFieldKey, type VaultCardThemeColors, type WebhookEvent, apiError, authenticationError, buildCheckoutDisplayData, buildItemPayload, buildProductPayload, buildSubscriptionPayload, configureFlopay, filterStripeMethodsByAmount, filterStripeMethodsByCountry, filterStripeMethodsByCurrency, foldIntoProducts, getConfiguredBillingApiUrl, getCountryByCode, getCurrencyByCountry, getFloPayEnvironment, getPostalCodeExample, getPostalCodeLabel, getStateFromPostalCode, getStateLabel, getStateOptions, getStripeMethodDisplayName, hasVendoredStripeMethodLogo, isAVSEnabled, isAVSFieldVisible, isPostalCodeSupported, isSetupIntentClientSecret, isValidPostalCode, isValidPublishableKey, isValidSecretKey, needsStripeMethodExplicitConfirm, networkError, normalizeGatewayEnvironment, partitionStripeMethods, rateLimitError, resolveAVSConfig, resolveBillingApiUrl, resolveButtonsLayoutTheme, resolveSessionCurrency, resolveStripeMethodBrandVariant, resolveTheme, stripeExpressMethodToOptionKey, validationError };
1931
+ /**
1932
+ * HTTP header the SDK sends to make checkout-session creation idempotent
1933
+ * (TeamFloPay/backend#972, paired SDK issue TeamFloPay/sdk#133).
1934
+ *
1935
+ * The backend treats the header as **optional**: when it is absent the legacy,
1936
+ * non-idempotent creation path runs unchanged. When present, every transport
1937
+ * retry of the same logical create reuses the same key so a timeout or lost
1938
+ * response cannot mint a second checkout session.
1939
+ */
1940
+ declare const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
1941
+ /**
1942
+ * Maximum length of an idempotency key accepted by the billing API
1943
+ * (TeamFloPay/backend#972). Mirrors the widely-used 255-character ceiling;
1944
+ * automatically generated keys stay well under it.
1945
+ */
1946
+ declare const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
1947
+ /**
1948
+ * Backend `code` (TeamFloPay/backend#972) returned when a checkout-create
1949
+ * replay arrives while the first request carrying the same key is still being
1950
+ * processed. It is a **retryable** in-progress response: the caller should
1951
+ * retry with the *same* key after a short backoff, never with a new key. This
1952
+ * is distinct from a payload-conflict (`409`), which is non-retryable.
1953
+ *
1954
+ * Mirrors a backend-defined code the same way the coupon error codes
1955
+ * (`CouponLimitExceeded`, `CouponCurrencyUnsupported`) mirror billing v1.1.2.
1956
+ */
1957
+ declare const IDEMPOTENCY_IN_PROGRESS_CODE = "IdempotencyKeyInProgress";
1958
+ /**
1959
+ * Generate a cryptographically random, high-entropy idempotency key.
1960
+ *
1961
+ * Prefers `crypto.randomUUID()` and falls back to 16 random bytes from
1962
+ * `crypto.getRandomValues`. Returns `undefined` when no cryptographically
1963
+ * secure RNG is available so the caller falls back to the legacy unkeyed path
1964
+ * rather than emitting a weak or predictable key.
1965
+ *
1966
+ * The key is never derived from request data, customer identity, or time, so
1967
+ * two independent operations never collide by construction.
1968
+ */
1969
+ declare function generateIdempotencyKey(): string | undefined;
1970
+ /**
1971
+ * Resolve the idempotency key for one logical checkout-create operation.
1972
+ *
1973
+ * - A merchant-supplied key is validated (non-empty, within
1974
+ * {@link MAX_IDEMPOTENCY_KEY_LENGTH}) and returned **unchanged** so it can be
1975
+ * preserved across React remounts, multiple SDK instances, or server retries
1976
+ * the merchant controls.
1977
+ * - Otherwise a fresh cryptographically random key is generated. When no secure
1978
+ * RNG is available this returns `undefined` and the caller omits the header,
1979
+ * preserving the legacy path.
1980
+ *
1981
+ * @throws FloPayError('validation_error') when a supplied key is empty,
1982
+ * whitespace-only, or exceeds the length limit. The offending value is never
1983
+ * included in the error message.
1984
+ */
1985
+ declare function resolveIdempotencyKey(supplied?: string): string | undefined;
1986
+
1987
+ export { type AVSFieldConfig, BILLING_API_URL, BILLING_API_URL_PRODUCTION, BILLING_API_URL_STAGING, BOLD_DARK_APPEARANCE, BOLD_LIGHT_APPEARANCE, BUTTONS_LAYOUT_BOLD_DARK, BUTTONS_LAYOUT_BOLD_LIGHT, BUTTONS_LAYOUT_DARK, BUTTONS_LAYOUT_DEFAULT, BUTTONS_LAYOUT_GLASS_DARK, BUTTONS_LAYOUT_GLASS_LIGHT, BUTTONS_LAYOUT_MINIMAL, BUTTONS_LAYOUT_MODERN_DARK, BUTTONS_LAYOUT_MODERN_LIGHT, BUTTONS_LAYOUT_ROUNDED, type BeforeButtonClickEvent, type BillingDetails, type BillingProvider, type BuildCheckoutDisplayDataOptions, type ButtonsLayoutStyles, type ButtonsLayoutTheme, CA_PROVINCES, COUNTRY_OPTIONS, CURRENCY_MAP, type CardCaptureAdapter, type CardCaptureEventType, type CardCaptureMountOptions, type CardCaptureOutcomeEvent, type CardCaptureProviderId, type CheckoutAccount, type CheckoutButtonMethod, type CheckoutDisplayData, type CheckoutGateway, type CheckoutGateways, type CheckoutItem, type CheckoutMode, type CheckoutModeKind, type CheckoutProcessError, type CheckoutProcessingPending, type CheckoutProduct, type CheckoutProductType, type CheckoutSession, type CheckoutSessionProduct, type CheckoutSessionResult, type CheckoutSubscription, type ConfirmCardPaymentParams, type ConfirmCardPaymentResult, type ConfirmPaymentParams, type CountryOption, type CreateCustomerParams, type CreatePaymentMethodResult, type CreateSessionParams, type CurrencyInfo, type Customer, DEFAULT_API_BASE_URL, DEFAULT_API_VERSION, DEFAULT_APPEARANCE, DEFAULT_CURRENCY, type DeclineEvent, type DisplayLineItem, ELEMENT_TYPES, type ElementChangeEvent, type ElementOptions, type ElementType, FLAT_APPEARANCE, FLO_SDK_VERSION_HEADER, type FloPayAppearance, type FloPayConfig, type FloPayEnvironment, FloPayError, type FloPayErrorType, type FloPayThemeVariables, GLASS_DARK_APPEARANCE, GLASS_LIGHT_APPEARANCE, type GatewayEnvironment, IDEMPOTENCY_IN_PROGRESS_CODE, IDEMPOTENCY_KEY_HEADER, type InlineSessionDraft, type InlineSessionParams, type InlineSessionPatch, type LineItem, MAX_IDEMPOTENCY_KEY_LENGTH, MODERN_DARK_APPEARANCE, MODERN_LIGHT_APPEARANCE, type MountedElement, NIGHT_APPEARANCE, type NormalizedCheckoutSession, type NormalizedGatewayEnvironment, type PaymentProviderAdapter, type PaymentResult, type PriceData, type ProcessPaymentParams, type RecurringInterval, SDK_VERSION, STRIPE_EXPRESS_METHODS, STRIPE_METHOD_AMOUNT_LIMITS, STRIPE_METHOD_COUNTRIES, STRIPE_METHOD_CURRENCIES, STRIPE_METHOD_MATRIX, SUPPORTED_CARD_BRANDS, type StateOption, type StripeMethodEntry, type StripeMethodThemeVariant, THEMES, type TagsData, type ThemeBundle, type ThemeBundleId, type ThemeId, type TokenizedBody, US_STATES, type UpdateCustomerParams, type VaultCaptureBlock, type VaultCaptureResultMessage, type VaultCardFieldKey, type VaultCardThemeColors, type WebhookEvent, apiError, authenticationError, buildCheckoutDisplayData, buildItemPayload, buildProductPayload, buildSubscriptionPayload, configureFlopay, filterStripeMethodsByAmount, filterStripeMethodsByCountry, filterStripeMethodsByCurrency, foldIntoProducts, generateIdempotencyKey, getConfiguredBillingApiUrl, getCountryByCode, getCurrencyByCountry, getFloPayEnvironment, getPostalCodeExample, getPostalCodeLabel, getStateFromPostalCode, getStateLabel, getStateOptions, getStripeMethodDisplayName, hasVendoredStripeMethodLogo, isAVSEnabled, isAVSFieldVisible, isPostalCodeSupported, isSetupIntentClientSecret, isValidPostalCode, isValidPublishableKey, isValidSecretKey, needsStripeMethodExplicitConfirm, networkError, normalizeGatewayEnvironment, partitionStripeMethods, rateLimitError, resolveAVSConfig, resolveBillingApiUrl, resolveButtonsLayoutTheme, resolveIdempotencyKey, resolveSessionCurrency, resolveStripeMethodBrandVariant, resolveTheme, stripeExpressMethodToOptionKey, validationError };
package/dist/index.mjs CHANGED
@@ -79,7 +79,7 @@ var PAYMENT_METHOD_LOGOS = {
79
79
  };
80
80
 
81
81
  // src/constants.ts
82
- var SDK_VERSION = "1.3.1";
82
+ var SDK_VERSION = "1.3.3";
83
83
  var FLO_SDK_VERSION_HEADER = "x-flo-sdk-version";
84
84
  var BILLING_API_URL_STAGING = "https://api.stage.flopay.com";
85
85
  var BILLING_API_URL_PRODUCTION = "https://api.flopay.com";
@@ -1868,53 +1868,93 @@ if (typeof isPostalCode !== "function") {
1868
1868
  var SUPPORTED_LOCALES = new Set(
1869
1869
  mod.locales.map((code) => code.toUpperCase())
1870
1870
  );
1871
- var LOCALE_ALIASES = {
1872
- UK: "GB"
1871
+ var COUNTRY_ALIASES = {
1872
+ AUSTRALIA: "AU",
1873
+ BRAZIL: "BR",
1874
+ CANADA: "CA",
1875
+ CH: "CH",
1876
+ DENMARK: "DK",
1877
+ DE: "DE",
1878
+ DEUTSCHLAND: "DE",
1879
+ FRANCE: "FR",
1880
+ GB: "GB",
1881
+ GERMANY: "DE",
1882
+ GREATBRITAIN: "GB",
1883
+ GREAT_BRITAIN: "GB",
1884
+ INDIA: "IN",
1885
+ IRELAND: "IE",
1886
+ ITALY: "IT",
1887
+ JAPAN: "JP",
1888
+ MEXICO: "MX",
1889
+ NETHERLANDS: "NL",
1890
+ NEWZEALAND: "NZ",
1891
+ NORWAY: "NO",
1892
+ POLAND: "PL",
1893
+ PORTUGAL: "PT",
1894
+ SINGAPORE: "SG",
1895
+ SOUTHAFRICA: "ZA",
1896
+ SPAIN: "ES",
1897
+ SWEDEN: "SE",
1898
+ SWITZERLAND: "CH",
1899
+ UK: "GB",
1900
+ UNITEDKINGDOM: "GB",
1901
+ UNITEDSTATES: "US",
1902
+ US: "US",
1903
+ USA: "US",
1904
+ "UNITED STATES": "US",
1905
+ "UNITED KINGDOM": "GB",
1906
+ "GREAT BRITAIN": "GB",
1907
+ ENGLAND: "GB"
1873
1908
  };
1874
- function normalizeCountry(country) {
1875
- const upper = country.trim().toUpperCase();
1876
- return LOCALE_ALIASES[upper] ?? upper;
1909
+ function normalizeCountryToIso2(country) {
1910
+ const upper = (country ?? "").trim().toUpperCase();
1911
+ if (!upper) return null;
1912
+ if (upper.length === 2) return upper;
1913
+ return COUNTRY_ALIASES[upper] ?? null;
1877
1914
  }
1878
1915
  var POSTAL_CODE_EXAMPLES = {
1879
- US: "12345 or 12345-6789",
1880
- GB: "SW1A 1AA",
1881
- CA: "A1A 1A1",
1882
1916
  AU: "2000",
1883
- NZ: "6011",
1884
- IE: "D02 AF30",
1917
+ BR: "01000-000",
1918
+ CA: "A1A 1A1",
1919
+ CH: "8001",
1885
1920
  DE: "10115",
1886
- FR: "75008",
1887
- NL: "1011 AB",
1921
+ DK: "1050",
1888
1922
  ES: "28001",
1889
- IT: "00100",
1923
+ FR: "75008",
1924
+ GB: "SW1A 1AA",
1925
+ IE: "D02 X285",
1890
1926
  IN: "110001",
1927
+ IT: "00118",
1891
1928
  JP: "100-0001",
1892
- BR: "01310-100",
1893
- MX: "01000",
1894
- SE: "114 55",
1895
- CH: "8001",
1929
+ MX: "06500",
1930
+ NL: "1012 JS",
1931
+ NO: "0150",
1932
+ NZ: "6011",
1896
1933
  PL: "00-001",
1897
- PT: "1000-001",
1898
- SG: "570150",
1899
- ZA: "0001",
1900
- NO: "0001",
1901
- DK: "1050",
1902
- FI: "00100",
1903
- AT: "1010",
1904
- BE: "1000",
1905
- CZ: "100 00"
1934
+ PT: "1100-148",
1935
+ SE: "111 22",
1936
+ SG: "018989",
1937
+ US: "12345 or 12345-6789",
1938
+ ZA: "8001"
1906
1939
  };
1907
1940
  function isPostalCodeSupported(country) {
1908
- if (!country) return false;
1909
- return SUPPORTED_LOCALES.has(normalizeCountry(country));
1941
+ const iso2 = normalizeCountryToIso2(country);
1942
+ return iso2 !== null && SUPPORTED_LOCALES.has(iso2);
1910
1943
  }
1911
1944
  function isValidPostalCode(country, postalCode) {
1912
- if (!isPostalCodeSupported(country)) return true;
1913
- return isPostalCode(postalCode.trim(), normalizeCountry(country));
1945
+ const trimmed = (postalCode ?? "").trim();
1946
+ if (!trimmed) return true;
1947
+ const iso2 = normalizeCountryToIso2(country);
1948
+ if (iso2 === null || !SUPPORTED_LOCALES.has(iso2)) return true;
1949
+ try {
1950
+ return isPostalCode(trimmed, iso2);
1951
+ } catch {
1952
+ return true;
1953
+ }
1914
1954
  }
1915
1955
  function getPostalCodeExample(country) {
1916
- if (!country) return void 0;
1917
- return POSTAL_CODE_EXAMPLES[normalizeCountry(country)];
1956
+ const iso2 = normalizeCountryToIso2(country);
1957
+ return iso2 ? POSTAL_CODE_EXAMPLES[iso2] : void 0;
1918
1958
  }
1919
1959
 
1920
1960
  // src/checkout-payload.ts
@@ -2117,6 +2157,49 @@ function isValidSecretKey(key) {
2117
2157
  function isSetupIntentClientSecret(clientSecret) {
2118
2158
  return typeof clientSecret === "string" && clientSecret.startsWith("seti_");
2119
2159
  }
2160
+
2161
+ // src/idempotency.ts
2162
+ var IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
2163
+ var MAX_IDEMPOTENCY_KEY_LENGTH = 255;
2164
+ var IDEMPOTENCY_IN_PROGRESS_CODE = "IdempotencyKeyInProgress";
2165
+ function getSecureRandomSource() {
2166
+ const source = globalThis.crypto;
2167
+ if (!source) return void 0;
2168
+ if (typeof source.randomUUID === "function" || typeof source.getRandomValues === "function") {
2169
+ return source;
2170
+ }
2171
+ return void 0;
2172
+ }
2173
+ function generateIdempotencyKey() {
2174
+ const source = getSecureRandomSource();
2175
+ if (!source) return void 0;
2176
+ if (typeof source.randomUUID === "function") {
2177
+ return source.randomUUID();
2178
+ }
2179
+ const bytes = new Uint8Array(16);
2180
+ source.getRandomValues(bytes);
2181
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
2182
+ }
2183
+ function resolveIdempotencyKey(supplied) {
2184
+ if (supplied === void 0) {
2185
+ return generateIdempotencyKey();
2186
+ }
2187
+ if (typeof supplied !== "string" || supplied.trim() === "") {
2188
+ throw new FloPayError(
2189
+ "idempotencyKey must be a non-empty string identifying exactly one checkout creation.",
2190
+ "validation_error",
2191
+ { code: "InvalidIdempotencyKey", param: "idempotencyKey" }
2192
+ );
2193
+ }
2194
+ if (supplied.length > MAX_IDEMPOTENCY_KEY_LENGTH) {
2195
+ throw new FloPayError(
2196
+ `idempotencyKey must be at most ${MAX_IDEMPOTENCY_KEY_LENGTH} characters.`,
2197
+ "validation_error",
2198
+ { code: "InvalidIdempotencyKey", param: "idempotencyKey" }
2199
+ );
2200
+ }
2201
+ return supplied;
2202
+ }
2120
2203
  export {
2121
2204
  BILLING_API_URL,
2122
2205
  BILLING_API_URL_PRODUCTION,
@@ -2146,6 +2229,9 @@ export {
2146
2229
  FloPayError,
2147
2230
  GLASS_DARK_APPEARANCE,
2148
2231
  GLASS_LIGHT_APPEARANCE,
2232
+ IDEMPOTENCY_IN_PROGRESS_CODE,
2233
+ IDEMPOTENCY_KEY_HEADER,
2234
+ MAX_IDEMPOTENCY_KEY_LENGTH,
2149
2235
  MODERN_DARK_APPEARANCE,
2150
2236
  MODERN_LIGHT_APPEARANCE,
2151
2237
  NIGHT_APPEARANCE,
@@ -2169,6 +2255,7 @@ export {
2169
2255
  filterStripeMethodsByCountry,
2170
2256
  filterStripeMethodsByCurrency,
2171
2257
  foldIntoProducts,
2258
+ generateIdempotencyKey,
2172
2259
  getConfiguredBillingApiUrl,
2173
2260
  getCountryByCode,
2174
2261
  getCurrencyByCountry,
@@ -2195,6 +2282,7 @@ export {
2195
2282
  resolveAVSConfig,
2196
2283
  resolveBillingApiUrl,
2197
2284
  resolveButtonsLayoutTheme,
2285
+ resolveIdempotencyKey,
2198
2286
  resolveSessionCurrency,
2199
2287
  resolveStripeMethodBrandVariant,
2200
2288
  resolveTheme,