@flopay/react 1.1.4 → 1.1.5

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.mjs CHANGED
@@ -102,7 +102,7 @@ function FloPayProvider({
102
102
  // src/flopay-checkout.tsx
103
103
  import React7, { useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
104
104
  import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
105
- import { SDK_VERSION, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2 } from "@flopay/shared";
105
+ import { SDK_VERSION, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2, resolveTheme as resolveTheme2 } from "@flopay/shared";
106
106
 
107
107
  // src/card-button-content.tsx
108
108
  import "react";
@@ -1221,7 +1221,7 @@ ${debugLines.join("\n")}`
1221
1221
  }
1222
1222
 
1223
1223
  // src/split-card-form.tsx
1224
- import { FloPayError as FloPayError3 } from "@flopay/shared";
1224
+ import { FloPayError as FloPayError3, resolveTheme } from "@flopay/shared";
1225
1225
  import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1226
1226
  var WALLET_RESUME_KEY = "flopay_wallet_resume";
1227
1227
  var FLOPAY_KEYFRAMES = `
@@ -1721,12 +1721,14 @@ function SplitCardFormInner({
1721
1721
  showApplePay = true,
1722
1722
  showGooglePay = true,
1723
1723
  layout = "default",
1724
+ theme,
1724
1725
  buttonsTheme,
1725
1726
  buttonsStyles: buttonsStylesOverride,
1727
+ appearance: appearanceOverride,
1726
1728
  cardButtonContent,
1727
1729
  cardBackButtonContent,
1728
1730
  cardTitleContent,
1729
- showSecurityFooter = true,
1731
+ showSecurityFooter: _showSecurityFooter,
1730
1732
  onButtonClick,
1731
1733
  onBeforeButtonClick,
1732
1734
  enableAVS: enableAVSProp,
@@ -1801,8 +1803,9 @@ function SplitCardFormInner({
1801
1803
  }, [paypalDirectRetry]);
1802
1804
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
1803
1805
  const displayError = externalError ?? error;
1806
+ const themeBundle = useMemo3(() => resolveTheme(theme), [theme]);
1804
1807
  const bStyles = useMemo3(() => {
1805
- const base = resolveButtonsLayoutTheme(buttonsTheme);
1808
+ const base = themeBundle?.buttonsLayout ?? resolveButtonsLayoutTheme(buttonsTheme);
1806
1809
  if (!buttonsStylesOverride) return base;
1807
1810
  return {
1808
1811
  ...base,
@@ -1814,7 +1817,8 @@ function SplitCardFormInner({
1814
1817
  submitButton: { ...base.submitButton, ...buttonsStylesOverride.submitButton },
1815
1818
  title: { ...base.title, ...buttonsStylesOverride.title }
1816
1819
  };
1817
- }, [buttonsTheme, buttonsStylesOverride]);
1820
+ }, [themeBundle, buttonsTheme, buttonsStylesOverride]);
1821
+ const appearance = appearanceOverride ?? themeBundle?.appearance;
1818
1822
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
1819
1823
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
1820
1824
  const isSelfContained = !onTokenizedBody;
@@ -2342,17 +2346,24 @@ function SplitCardFormInner({
2342
2346
  return /* @__PURE__ */ jsx6("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
2343
2347
  }
2344
2348
  const isButtons = layout === "buttons";
2345
- const resolvedBorder = isButtons ? bStyles.cardInputBorder ?? "#e5e7eb" : "#A4A4FF";
2346
- const cardBg = isButtons ? bStyles.cardFormContainer?.backgroundColor ?? "white" : "#EDEDFF";
2347
- const cardInputBg = isButtons ? bStyles.cardInputBackground ?? "white" : "white";
2349
+ const appearanceVars = appearance?.variables;
2350
+ const SDK_DEFAULT_WHITES = /* @__PURE__ */ new Set(["#FFFFFF", "#ffffff", "#fff", "#FFF", "white"]);
2351
+ const appearanceColorBg = appearanceVars?.colorBackground;
2352
+ const themedWrapperBg = appearanceColorBg && !SDK_DEFAULT_WHITES.has(appearanceColorBg) ? appearanceColorBg : void 0;
2353
+ const resolvedBorder = bStyles.cardInputBorder ?? (isButtons ? "#e5e7eb" : "#A4A4FF");
2354
+ const cardBg = bStyles.cardFormContainer?.backgroundColor ?? themedWrapperBg ?? (isButtons ? "white" : "#EDEDFF");
2355
+ const cardInputBg = bStyles.cardInputBackground ?? appearanceVars?.colorBackground ?? "white";
2348
2356
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
2349
2357
  const hideTitle = isEmptySlotContent(cardTitleContent);
2350
- const nameInputOverrides = isButtons ? bStyles.nameInput : void 0;
2351
- const resolvedInputFontSize = bStyles.cardInputFontSize ?? toCssSize(nameInputOverrides?.fontSize) ?? "16px";
2352
- const resolvedInputFontFamily = typeof nameInputOverrides?.fontFamily === "string" ? nameInputOverrides.fontFamily : "Poppins, sans-serif";
2358
+ const nameInputOverrides = bStyles.nameInput;
2359
+ const resolvedInputFontSize = bStyles.cardInputFontSize ?? toCssSize(nameInputOverrides?.fontSize) ?? appearanceVars?.fontSizeBase ?? "16px";
2360
+ const resolvedInputFontFamily = typeof nameInputOverrides?.fontFamily === "string" ? nameInputOverrides.fontFamily : appearanceVars?.fontFamily ?? "Poppins, sans-serif";
2353
2361
  const resolvedInputFontWeight = toCssWeight(nameInputOverrides?.fontWeight) ?? 400;
2354
- const resolvedInputColor = bStyles.cardInputColor ?? (typeof nameInputOverrides?.color === "string" ? nameInputOverrides.color : void 0) ?? "#262833";
2362
+ const resolvedInputColor = bStyles.cardInputColor ?? (typeof nameInputOverrides?.color === "string" ? nameInputOverrides.color : void 0) ?? appearanceVars?.colorText ?? "#262833";
2355
2363
  const resolvedPlaceholderColor = bStyles.cardInputPlaceholderColor ?? "#9ca3af";
2364
+ const resolvedBorderRadius = appearanceVars?.borderRadius ?? "8px";
2365
+ const resolvedPrimaryColor = appearanceVars?.colorPrimary ?? "#4A49FF";
2366
+ const resolvedTitleColor = appearanceVars?.colorText ?? "#262833";
2356
2367
  const sharedInputTypography = {
2357
2368
  fontSize: resolvedInputFontSize,
2358
2369
  fontFamily: resolvedInputFontFamily,
@@ -2382,12 +2393,14 @@ function SplitCardFormInner({
2382
2393
  invalid: { color: "#ef4444" }
2383
2394
  }
2384
2395
  };
2396
+ const containerOverrides = bStyles.cardFormContainer ?? {};
2397
+ const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
2398
+ const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
2385
2399
  const cardFormBlock = /* @__PURE__ */ jsxs4("div", { style: {
2386
2400
  backgroundColor: cardBg,
2387
- borderRadius: "8px",
2388
- padding: isButtons ? "0" : "1rem",
2389
- ...isButtons ? bStyles.cardFormContainer : {},
2390
- ...isButtons ? { padding: bStyles.cardFormContainer?.padding ?? "0" } : {},
2401
+ borderRadius: containerRadius,
2402
+ ...containerOverrides,
2403
+ padding: containerPadding,
2391
2404
  ...sharedInputPlaceholderVars
2392
2405
  }, children: [
2393
2406
  /* @__PURE__ */ jsx6("style", { children: `
@@ -2451,7 +2464,14 @@ function SplitCardFormInner({
2451
2464
  ...bStyles.title
2452
2465
  }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) })
2453
2466
  ] }),
2454
- !isButtons && !hideTitle && /* @__PURE__ */ jsx6("div", { style: { textAlign: "center", fontWeight: 600, fontSize: "1.1rem", padding: "0.5rem 0", color: "#262833" }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) }),
2467
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx6("div", { style: {
2468
+ textAlign: "center",
2469
+ fontWeight: 600,
2470
+ fontSize: "1.1rem",
2471
+ padding: "0.5rem 0",
2472
+ color: resolvedTitleColor,
2473
+ ...bStyles.title
2474
+ }, children: /* @__PURE__ */ jsx6(TitleContentSlot, { content: cardTitleContent }) }),
2455
2475
  /* @__PURE__ */ jsx6("div", { style: {
2456
2476
  backgroundColor: cardInputBg,
2457
2477
  border: `1px solid ${resolvedBorder}`,
@@ -2708,7 +2728,7 @@ function SplitCardFormInner({
2708
2728
  display: "flex",
2709
2729
  alignItems: "center",
2710
2730
  gap: "0.5rem",
2711
- ...isButtons && bStyles.errorBanner ? bStyles.errorBanner : {}
2731
+ ...bStyles.errorBanner ? bStyles.errorBanner : {}
2712
2732
  }, children: [
2713
2733
  /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx6("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2714
2734
  displayError
@@ -2723,29 +2743,19 @@ function SplitCardFormInner({
2723
2743
  width: "100%",
2724
2744
  padding: "0.875rem",
2725
2745
  marginTop: "1rem",
2726
- backgroundColor: "#4A49FF",
2746
+ backgroundColor: resolvedPrimaryColor,
2727
2747
  color: "white",
2728
2748
  border: "none",
2729
- borderRadius: "8px",
2730
- fontSize: isButtons && bStyles.submitButtonFontSize ? bStyles.submitButtonFontSize : "1rem",
2749
+ borderRadius: resolvedBorderRadius,
2750
+ fontSize: bStyles.submitButtonFontSize ?? "1rem",
2731
2751
  fontWeight: 600,
2732
2752
  cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
2733
2753
  opacity: !formReady || isSubmitting ? 0.5 : 1,
2734
- ...isButtons ? bStyles.submitButton : {}
2754
+ ...bStyles.submitButton
2735
2755
  },
2736
2756
  children: isSubmitting ? "PROCESSING..." : submitLabel
2737
2757
  }
2738
- ),
2739
- !isButtons && showSecurityFooter && /* @__PURE__ */ jsx6("div", { style: {
2740
- backgroundColor: "#EFF9F0",
2741
- borderRadius: "8px",
2742
- padding: "0.75rem",
2743
- marginTop: "0.75rem",
2744
- textAlign: "center",
2745
- fontSize: "0.85rem",
2746
- fontWeight: 600,
2747
- color: "#7DAD3A"
2748
- }, children: "Secure Card Checkout" })
2758
+ )
2749
2759
  ] });
2750
2760
  if (layout === "buttons") {
2751
2761
  const isButtonsView = viewState === "buttons" || viewState === "expanding";
@@ -3262,105 +3272,6 @@ async function processSavedPaymentForMode({
3262
3272
  }
3263
3273
  );
3264
3274
  }
3265
- async function processSavedPaymentWithIntent({
3266
- billingApiUrl,
3267
- sessionId,
3268
- session,
3269
- paymentMethodId,
3270
- flopay,
3271
- returnUrl
3272
- }) {
3273
- const customerEmail = session.customer?.email ?? session.accountData?.email ?? "";
3274
- if (!customerEmail) {
3275
- throw Object.assign(
3276
- new FloPayError4("Customer email is required to create a payment intent.", "validation_error", {
3277
- param: "email"
3278
- }),
3279
- { checkoutMethod: "card" }
3280
- );
3281
- }
3282
- const api = new PaymentAPI3(billingApiUrl);
3283
- const intentResponse = await retryOnceOnFetchFailure(() => api.createPaymentIntent(
3284
- sessionId,
3285
- customerEmail,
3286
- paymentMethodId
3287
- ));
3288
- const intentJson = await intentResponse.json().catch(() => null);
3289
- if (!intentResponse.ok) {
3290
- const intentError = buildFloPayApiError(
3291
- intentJson,
3292
- "Failed to create payment intent."
3293
- );
3294
- throw Object.assign(
3295
- intentError,
3296
- { checkoutMethod: "card" }
3297
- );
3298
- }
3299
- const intentClientSecret = getRedirectTokenFromProcessResponse(intentJson, {
3300
- requirePaymentIntentClientSecret: true
3301
- });
3302
- if (!intentClientSecret) {
3303
- throw Object.assign(
3304
- new FloPayError4("No client secret in payment intent response.", "api_error"),
3305
- { checkoutMethod: "card" }
3306
- );
3307
- }
3308
- const confirmResult = await flopay.confirmCardPayment({
3309
- clientSecret: intentClientSecret,
3310
- paymentMethodId
3311
- });
3312
- if (confirmResult.error) {
3313
- throw Object.assign(
3314
- confirmResult.error,
3315
- { checkoutMethod: "card" }
3316
- );
3317
- }
3318
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
3319
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
3320
- rawProvider,
3321
- intentClientSecret
3322
- );
3323
- const confirmedPaymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
3324
- const confirmedPaymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? paymentMethodId;
3325
- if (!confirmedPaymentIntentId || confirmResult.status !== "succeeded" && confirmResult.status !== "processing" && confirmResult.status !== "requires_capture") {
3326
- throw Object.assign(
3327
- new FloPayError4(
3328
- `Unfortunately, your payment could not be processed. Please try again using a different payment method or contact your bank for assistance. If the issue persists, feel free to reach out to us for support.`,
3329
- "api_error",
3330
- {
3331
- code: confirmResult.status === "requires_action" ? "authentication_required" : void 0
3332
- }
3333
- ),
3334
- { checkoutMethod: "card" }
3335
- );
3336
- }
3337
- const followUp = await processSavedPaymentForMode({
3338
- billingApiUrl,
3339
- sessionId,
3340
- session,
3341
- tokenizedData: {
3342
- id: confirmedPaymentMethodId,
3343
- type: "card",
3344
- threeDSecureActionResultTokenId: confirmedPaymentIntentId
3345
- },
3346
- returnUrl
3347
- });
3348
- const finalResult = followUp.type === "success" ? followUp.result : await handleSavedPaymentRedirectResult(followUp, {
3349
- flopay,
3350
- paypalFlopay: null,
3351
- attempt3DS: true,
3352
- billingApiUrl,
3353
- sessionId,
3354
- session,
3355
- returnUrl
3356
- });
3357
- return {
3358
- ...finalResult,
3359
- paymentIntentId: finalResult.paymentIntentId ?? confirmedPaymentIntentId,
3360
- paymentMethodId: finalResult.paymentMethodId ?? confirmedPaymentMethodId,
3361
- checkoutMethod: finalResult.checkoutMethod ?? "card"
3362
- };
3363
- }
3364
3275
  async function handleSavedPaymentRedirectResult(redirectResult, {
3365
3276
  flopay,
3366
3277
  paypalFlopay,
@@ -3682,7 +3593,7 @@ function FloPayCheckout({
3682
3593
  sessionId: sessionIdProp,
3683
3594
  createSession: createSessionParams,
3684
3595
  billingApiUrl,
3685
- appearance,
3596
+ appearance: appearanceOverride,
3686
3597
  locale,
3687
3598
  loading: loadingNode,
3688
3599
  error: errorNode,
@@ -3697,12 +3608,13 @@ function FloPayCheckout({
3697
3608
  showGooglePay = true,
3698
3609
  debug = false,
3699
3610
  layout,
3611
+ theme,
3700
3612
  buttonsTheme,
3701
3613
  buttonsStyles,
3702
3614
  cardButtonContent,
3703
3615
  cardBackButtonContent,
3704
3616
  cardTitleContent,
3705
- showSecurityFooter = true,
3617
+ showSecurityFooter: _showSecurityFooter,
3706
3618
  onButtonClick,
3707
3619
  onBeforeButtonClick,
3708
3620
  enableAVS,
@@ -3717,6 +3629,8 @@ function FloPayCheckout({
3717
3629
  onSessionCompleted
3718
3630
  }) {
3719
3631
  const resolvedBillingUrl = resolveBillingApiUrl3(billingApiUrl);
3632
+ const themeBundle = useMemo4(() => resolveTheme2(theme), [theme]);
3633
+ const appearance = appearanceOverride ?? themeBundle?.appearance;
3720
3634
  const checkoutType = createSessionParams ? "embedded_checkout" : "standard_checkout";
3721
3635
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
3722
3636
  const [unified, setUnified] = useState4(null);
@@ -4479,6 +4393,7 @@ function FloPayCheckout({
4479
4393
  showPayPal,
4480
4394
  showApplePay,
4481
4395
  showGooglePay,
4396
+ theme,
4482
4397
  buttonsTheme,
4483
4398
  buttonsStyles,
4484
4399
  cardButtonContent,
@@ -4623,12 +4538,13 @@ function FloPayCheckout({
4623
4538
  showApplePay,
4624
4539
  showGooglePay,
4625
4540
  layout,
4541
+ theme,
4626
4542
  buttonsTheme,
4627
4543
  buttonsStyles,
4544
+ appearance,
4628
4545
  cardButtonContent,
4629
4546
  cardBackButtonContent,
4630
4547
  cardTitleContent,
4631
- showSecurityFooter,
4632
4548
  onButtonClick,
4633
4549
  onBeforeButtonClick,
4634
4550
  enableAVS,
@@ -4685,6 +4601,7 @@ function InterimButtonsView({
4685
4601
  showPayPal,
4686
4602
  showApplePay,
4687
4603
  showGooglePay,
4604
+ theme,
4688
4605
  buttonsTheme,
4689
4606
  buttonsStyles: stylesOverride,
4690
4607
  cardButtonContent,
@@ -4698,8 +4615,9 @@ function InterimButtonsView({
4698
4615
  setShowCardForm(cardOpen);
4699
4616
  }
4700
4617
  }, [cardOpen, isCardOpenControlled]);
4618
+ const themeBundle = useMemo4(() => resolveTheme2(theme), [theme]);
4701
4619
  const bStyles = useMemo4(() => {
4702
- const base = resolveButtonsLayoutTheme2(buttonsTheme);
4620
+ const base = themeBundle?.buttonsLayout ?? resolveButtonsLayoutTheme2(buttonsTheme);
4703
4621
  if (!stylesOverride) return base;
4704
4622
  return {
4705
4623
  ...base,
@@ -4711,7 +4629,7 @@ function InterimButtonsView({
4711
4629
  submitButton: { ...base.submitButton, ...stylesOverride.submitButton },
4712
4630
  title: { ...base.title, ...stylesOverride.title }
4713
4631
  };
4714
- }, [buttonsTheme, stylesOverride]);
4632
+ }, [themeBundle, buttonsTheme, stylesOverride]);
4715
4633
  const skeleton = (h) => /* @__PURE__ */ jsx7("div", { style: {
4716
4634
  height: h,
4717
4635
  borderRadius: 8,
@@ -5407,10 +5325,9 @@ function PayPalButton({
5407
5325
  // src/automatic-payment-button.tsx
5408
5326
  import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef6, useState as useState7 } from "react";
5409
5327
  import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
5410
- import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3 } from "@flopay/shared";
5328
+ import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
5411
5329
  import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
5412
5330
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
5413
- var PAYPAL_RESUME_STORAGE_KEY2 = "flopay_automatic_payment_button_resume";
5414
5331
  function sleep2(ms) {
5415
5332
  return new Promise((resolve) => setTimeout(resolve, ms));
5416
5333
  }
@@ -5423,9 +5340,6 @@ function coerceError(err, fallbackMessage) {
5423
5340
  "api_error"
5424
5341
  );
5425
5342
  }
5426
- function canUseStorage2() {
5427
- return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
5428
- }
5429
5343
  function shouldShowFallbackCheckout(error, sessionId) {
5430
5344
  if (!sessionId) return false;
5431
5345
  if (error.type === "validation_error") return false;
@@ -5434,47 +5348,6 @@ function shouldShowFallbackCheckout(error, sessionId) {
5434
5348
  }
5435
5349
  return true;
5436
5350
  }
5437
- function readPayPalResumeState2() {
5438
- if (!canUseStorage2()) return null;
5439
- try {
5440
- const raw = window.sessionStorage.getItem(PAYPAL_RESUME_STORAGE_KEY2);
5441
- if (!raw) return null;
5442
- return JSON.parse(raw);
5443
- } catch {
5444
- return null;
5445
- }
5446
- }
5447
- function persistPayPalResumeState2(state) {
5448
- if (!canUseStorage2()) return;
5449
- try {
5450
- window.sessionStorage.setItem(PAYPAL_RESUME_STORAGE_KEY2, JSON.stringify(state));
5451
- } catch (error) {
5452
- console.warn("[FloPayAutomaticPaymentButton] Failed to persist PayPal resume state.", error);
5453
- }
5454
- }
5455
- function clearPayPalResumeState2() {
5456
- if (!canUseStorage2()) return;
5457
- try {
5458
- window.sessionStorage.removeItem(PAYPAL_RESUME_STORAGE_KEY2);
5459
- } catch (error) {
5460
- console.warn("[FloPayAutomaticPaymentButton] Failed to clear PayPal resume state.", error);
5461
- }
5462
- }
5463
- function clearPayPalRedirectParams2() {
5464
- if (typeof window === "undefined") return;
5465
- const url = new URL(window.location.href);
5466
- const keys = ["payment_intent", "payment_intent_client_secret", "redirect_status"];
5467
- let changed = false;
5468
- for (const key of keys) {
5469
- if (url.searchParams.has(key)) {
5470
- url.searchParams.delete(key);
5471
- changed = true;
5472
- }
5473
- }
5474
- if (changed) {
5475
- window.history.replaceState({}, "", url.toString());
5476
- }
5477
- }
5478
5351
  function resolveCreateSessionDraft(props) {
5479
5352
  if (props.createSession) {
5480
5353
  return {
@@ -5501,8 +5374,10 @@ function resolveCreateSessionDraft(props) {
5501
5374
  function FloPayAutomaticPaymentButton({
5502
5375
  sessionId,
5503
5376
  createSession,
5504
- paymentMethodId,
5505
- checkoutMethod,
5377
+ // Deprecated — accepted for back-compat and silently ignored. Backend
5378
+ // resolves the customer's latest payment method server-side.
5379
+ paymentMethodId: _deprecatedPaymentMethodId,
5380
+ checkoutMethod: _deprecatedCheckoutMethod,
5506
5381
  clientId,
5507
5382
  items,
5508
5383
  subscriptions,
@@ -5514,6 +5389,7 @@ function FloPayAutomaticPaymentButton({
5514
5389
  utmMetadata,
5515
5390
  billingApiUrl,
5516
5391
  locale,
5392
+ theme,
5517
5393
  buttonsTheme,
5518
5394
  buttonsStyles: stylesOverride,
5519
5395
  onSuccess,
@@ -5559,20 +5435,7 @@ function FloPayAutomaticPaymentButton({
5559
5435
  const [overlayStatus, setOverlayStatus] = useState7(null);
5560
5436
  const [overlayError, setOverlayError] = useState7(null);
5561
5437
  const [fallbackSession, setFallbackSession] = useState7(null);
5562
- const automaticPaymentToken = useMemo5(
5563
- () => paymentMethodId ? {
5564
- id: paymentMethodId,
5565
- // Saved PayPal `user_payment_method` records are submitted with
5566
- // `type: 'paypal'` so the backend routes the upsell through the
5567
- // direct PayPal gateway's vaulted-token flow. Card and wallet PMs
5568
- // continue to use `type: 'card'`.
5569
- type: checkoutMethod === "paypal" ? "paypal" : "card",
5570
- ...checkoutMethod === "paypal" ? { isPaypal: true } : {}
5571
- } : void 0,
5572
- [checkoutMethod, paymentMethodId]
5573
- );
5574
5438
  const isMountedRef = useRef6(true);
5575
- const resumeAttemptedRef = useRef6(false);
5576
5439
  const fallbackSessionRef = useRef6(fallbackSession);
5577
5440
  const onSuccessRef = useRef6(onSuccess);
5578
5441
  const onErrorRef = useRef6(onError);
@@ -5653,10 +5516,6 @@ function FloPayAutomaticPaymentButton({
5653
5516
  });
5654
5517
  }
5655
5518
  try {
5656
- let paymentResult = null;
5657
- const redirectResult = getRedirectResultFromCheckoutProcessError(apiResult.autoProcessingError);
5658
- const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && (apiResult.autoProcessingAttempted === true || !!apiResult.autoProcessingError || !!redirectResult);
5659
- const shouldRetryPayPalClientSide = checkoutMethod === "paypal" && automaticPaymentToken?.isPaypal === true && options?.fromCreateSession === true;
5660
5519
  if (apiResult.autoProcessingPending) {
5661
5520
  const api = new PaymentAPI7(resolvedBillingUrl);
5662
5521
  const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
@@ -5669,50 +5528,14 @@ function FloPayAutomaticPaymentButton({
5669
5528
  });
5670
5529
  }
5671
5530
  await showSuccess({
5672
- result: {
5673
- status: "succeeded",
5674
- paymentMethodId: paymentMethodId ?? void 0,
5675
- checkoutMethod
5676
- },
5531
+ result: { status: "succeeded" },
5677
5532
  session: completedSession,
5678
5533
  sessionId: completedSession.id || resolvedSessionId,
5679
5534
  autoCompleted: false
5680
5535
  });
5681
5536
  return;
5682
5537
  }
5683
- if (redirectResult) {
5684
- const {
5685
- publishableKey,
5686
- paypalPublishableKey
5687
- } = resolveSavedPaymentPublishableKeys(apiResult);
5688
- if (redirectResult.type === "paypal_redirect_required" && publishableKey) {
5689
- persistPayPalResumeState2({
5690
- sessionId: session.id || resolvedSessionId,
5691
- publishableKey,
5692
- paypalPublishableKey
5693
- });
5694
- }
5695
- const {
5696
- flopay,
5697
- paypalFlopay
5698
- } = await loadSavedPaymentProviders({
5699
- publishableKey,
5700
- paypalPublishableKey,
5701
- billingApiUrl: resolvedBillingUrl,
5702
- locale
5703
- });
5704
- paymentResult = await handleSavedPaymentRedirectResult(redirectResult, {
5705
- flopay,
5706
- paypalFlopay,
5707
- attempt3DS: true,
5708
- billingApiUrl: resolvedBillingUrl,
5709
- sessionId: session.id || resolvedSessionId,
5710
- session
5711
- });
5712
- if (redirectResult.type === "paypal_redirect_required") {
5713
- clearPayPalResumeState2();
5714
- }
5715
- } else if (apiResult.autoProcessingError && !shouldRetryPayPalClientSide) {
5538
+ if (apiResult.autoProcessingError) {
5716
5539
  throw checkoutProcessErrorToFloPayError(
5717
5540
  apiResult.autoProcessingError,
5718
5541
  "Automatic payment failed. Please try again.",
@@ -5720,7 +5543,22 @@ function FloPayAutomaticPaymentButton({
5720
5543
  checkoutMethod: apiResult.autoProcessingError.checkoutMethod
5721
5544
  }
5722
5545
  );
5723
- } else if (shouldTreatCreateSessionFlowAsServerAutoAttempt && !shouldRetryPayPalClientSide) {
5546
+ }
5547
+ if (options?.fromCreateSession && apiResult.autoProcessingAttempted === true) {
5548
+ throw checkoutProcessErrorToFloPayError(
5549
+ {
5550
+ type: "unknown",
5551
+ message: "Automatic payment failed. Please try again."
5552
+ },
5553
+ "Automatic payment failed. Please try again."
5554
+ );
5555
+ }
5556
+ const result = await processSavedPaymentForMode({
5557
+ billingApiUrl: resolvedBillingUrl,
5558
+ sessionId: resolvedSessionId ?? session.id,
5559
+ session
5560
+ });
5561
+ if (result.type !== "success") {
5724
5562
  throw checkoutProcessErrorToFloPayError(
5725
5563
  {
5726
5564
  type: "unknown",
@@ -5728,121 +5566,16 @@ function FloPayAutomaticPaymentButton({
5728
5566
  },
5729
5567
  "Automatic payment failed. Please try again."
5730
5568
  );
5731
- } else {
5732
- const result = await processSavedPaymentForMode({
5733
- billingApiUrl: resolvedBillingUrl,
5734
- sessionId: resolvedSessionId ?? session.id,
5735
- session,
5736
- tokenizedData: automaticPaymentToken
5737
- });
5738
- paymentResult = result.type === "success" ? result.result : null;
5739
- if (result.type !== "success") {
5740
- const {
5741
- publishableKey,
5742
- paypalPublishableKey
5743
- } = resolveSavedPaymentPublishableKeys(apiResult);
5744
- if (result.type === "paypal_redirect_required" && publishableKey) {
5745
- persistPayPalResumeState2({
5746
- sessionId: session.id || resolvedSessionId,
5747
- publishableKey,
5748
- paypalPublishableKey
5749
- });
5750
- }
5751
- const {
5752
- flopay,
5753
- paypalFlopay
5754
- } = await loadSavedPaymentProviders({
5755
- publishableKey,
5756
- paypalPublishableKey,
5757
- billingApiUrl: resolvedBillingUrl,
5758
- locale
5759
- });
5760
- paymentResult = await handleSavedPaymentRedirectResult(result, {
5761
- flopay,
5762
- paypalFlopay,
5763
- attempt3DS: true,
5764
- billingApiUrl: resolvedBillingUrl,
5765
- sessionId: session.id || resolvedSessionId,
5766
- session
5767
- });
5768
- if (result.type === "paypal_redirect_required") {
5769
- clearPayPalResumeState2();
5770
- }
5771
- }
5772
5569
  }
5773
5570
  await showSuccess({
5774
- result: paymentResult ? {
5775
- ...paymentResult,
5776
- paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId,
5777
- checkoutMethod: paymentResult.checkoutMethod ?? checkoutMethod
5778
- } : {
5779
- status: "succeeded",
5780
- paymentMethodId: paymentMethodId ?? void 0,
5781
- checkoutMethod
5782
- },
5571
+ result: result.result,
5783
5572
  session,
5784
5573
  sessionId: session.id || resolvedSessionId,
5785
5574
  autoCompleted: false
5786
5575
  });
5787
5576
  } catch (err) {
5788
- let floPayErr = normalizeSavedPaymentError(err);
5577
+ const floPayErr = normalizeSavedPaymentError(err);
5789
5578
  const fallbackSessionId = session.id || resolvedSessionId;
5790
- const shouldTreatCreateSessionFlowAsServerAutoAttempt = options?.fromCreateSession && apiResult.autoProcessingAttempted === true;
5791
- if (!shouldTreatCreateSessionFlowAsServerAutoAttempt && floPayErr.code === "authentication_required" && fallbackSessionId && automaticPaymentToken?.id && automaticPaymentToken.id.startsWith("pm_")) {
5792
- try {
5793
- const {
5794
- publishableKey,
5795
- paypalPublishableKey
5796
- } = resolveSavedPaymentPublishableKeys(apiResult);
5797
- const {
5798
- flopay
5799
- } = await loadSavedPaymentProviders({
5800
- publishableKey,
5801
- paypalPublishableKey,
5802
- billingApiUrl: resolvedBillingUrl,
5803
- locale
5804
- });
5805
- if (!flopay) {
5806
- throw new FloPayError8(
5807
- "Stripe is not available for 3DS authentication.",
5808
- "api_error",
5809
- { code: "authentication_required" }
5810
- );
5811
- }
5812
- const paymentResult = await processSavedPaymentWithIntent({
5813
- billingApiUrl: resolvedBillingUrl,
5814
- sessionId: fallbackSessionId,
5815
- session,
5816
- paymentMethodId: automaticPaymentToken.id,
5817
- flopay
5818
- });
5819
- await showSuccess({
5820
- result: {
5821
- ...paymentResult,
5822
- paymentMethodId: paymentResult.paymentMethodId ?? paymentMethodId,
5823
- checkoutMethod: paymentResult.checkoutMethod ?? checkoutMethod
5824
- },
5825
- session,
5826
- sessionId: fallbackSessionId,
5827
- autoCompleted: false
5828
- });
5829
- return;
5830
- } catch (intentRecoveryErr) {
5831
- floPayErr = normalizeSavedPaymentError(intentRecoveryErr);
5832
- }
5833
- }
5834
- const inResumeWindow = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent_client_secret") && !!readPayPalResumeState2();
5835
- if (floPayErr.code === "payment_intent_unexpected_state" && inResumeWindow) {
5836
- return;
5837
- }
5838
- const isSilentFallbackCase = (floPayErr.code === "paypal_requires_user_interaction" || floPayErr.code === "payment_intent_unexpected_state") && shouldShowFallbackCheckout(floPayErr, fallbackSessionId) && !!fallbackSessionId && isMountedRef.current;
5839
- if (isSilentFallbackCase) {
5840
- setFallbackSession({
5841
- sessionId: fallbackSessionId,
5842
- errorMessage: ""
5843
- });
5844
- return;
5845
- }
5846
5579
  await showError(floPayErr, {
5847
5580
  emitDecline: true,
5848
5581
  method: floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD
@@ -5855,10 +5588,6 @@ function FloPayAutomaticPaymentButton({
5855
5588
  }
5856
5589
  }
5857
5590
  }, [
5858
- checkoutMethod,
5859
- locale,
5860
- automaticPaymentToken,
5861
- paymentMethodId,
5862
5591
  resolvedBillingUrl,
5863
5592
  showError,
5864
5593
  showSuccess
@@ -5905,8 +5634,7 @@ function FloPayAutomaticPaymentButton({
5905
5634
  }
5906
5635
  try {
5907
5636
  const result = await api.createAndFetchSession({
5908
- ...createSessionDraft,
5909
- ...automaticPaymentToken ? { tokenizedData: automaticPaymentToken } : {}
5637
+ ...createSessionDraft
5910
5638
  });
5911
5639
  await processResolvedSession(result, result.data.session?.id ?? null, {
5912
5640
  fromCreateSession: true
@@ -5961,146 +5689,16 @@ function FloPayAutomaticPaymentButton({
5961
5689
  const handleFallbackDecline = useCallback5((decline) => {
5962
5690
  onDeclineRef.current?.(decline);
5963
5691
  }, []);
5964
- useEffect8(() => {
5965
- if (typeof window === "undefined" || resumeAttemptedRef.current) {
5966
- return;
5967
- }
5968
- const params = new URLSearchParams(window.location.search);
5969
- const clientSecret = params.get("payment_intent_client_secret");
5970
- if (!clientSecret) {
5971
- return;
5972
- }
5973
- const resumeState = readPayPalResumeState2();
5974
- if (!resumeState) {
5975
- return;
5976
- }
5977
- resumeAttemptedRef.current = true;
5978
- void (async () => {
5979
- setIsProcessing(true);
5980
- setOverlayError(null);
5981
- setOverlayStatus("processing");
5982
- try {
5983
- if (params.get("redirect_status") === "failed") {
5984
- throw Object.assign(
5985
- new FloPayError8("PayPal payment was declined. Please try again.", "api_error"),
5986
- { checkoutMethod: "paypal" }
5987
- );
5988
- }
5989
- const {
5990
- flopay,
5991
- paypalFlopay
5992
- } = await loadSavedPaymentProviders({
5993
- publishableKey: resumeState.publishableKey,
5994
- paypalPublishableKey: resumeState.paypalPublishableKey,
5995
- billingApiUrl: resolvedBillingUrl,
5996
- locale
5997
- });
5998
- const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
5999
- if (!paypalStripe) {
6000
- throw Object.assign(
6001
- new FloPayError8("PayPal is not available.", "api_error"),
6002
- { checkoutMethod: "paypal" }
6003
- );
6004
- }
6005
- const { paymentIntent, error } = await paypalStripe.retrievePaymentIntent(clientSecret);
6006
- if (error) {
6007
- throw Object.assign(
6008
- new FloPayError8(
6009
- error.message ?? "Failed to retrieve PayPal payment status.",
6010
- "api_error",
6011
- { code: error.code }
6012
- ),
6013
- { checkoutMethod: "paypal" }
6014
- );
6015
- }
6016
- const resultStatus = mapPayPalIntentStatusToPaymentResult(paymentIntent?.status);
6017
- if (!paymentIntent || resultStatus === "failed") {
6018
- throw Object.assign(
6019
- new FloPayError8("PayPal payment was not completed. Please try again.", "api_error"),
6020
- { checkoutMethod: "paypal" }
6021
- );
6022
- }
6023
- const paymentMethodId2 = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
6024
- if (!resumeState.sessionId) {
6025
- throw Object.assign(
6026
- new FloPayError8("Missing session id on PayPal resume.", "api_error"),
6027
- { checkoutMethod: "paypal" }
6028
- );
6029
- }
6030
- const api = new PaymentAPI7(resolvedBillingUrl);
6031
- const sessionResult = await api.getUnifiedCheckoutSession(resumeState.sessionId);
6032
- let resumeSession = sessionResult.data.session;
6033
- if (!resumeSession) {
6034
- throw Object.assign(
6035
- new FloPayError8("Could not load session to capture PayPal payment.", "api_error"),
6036
- { checkoutMethod: "paypal" }
6037
- );
6038
- }
6039
- let finalResultStatus = resultStatus;
6040
- if (resumeSession.status !== "complete") {
6041
- const processResult = await processSavedPaymentForMode({
6042
- billingApiUrl: resolvedBillingUrl,
6043
- sessionId: resumeState.sessionId,
6044
- session: resumeSession,
6045
- tokenizedData: {
6046
- id: paymentMethodId2 ?? paymentIntent.id,
6047
- type: "card",
6048
- threeDSecureActionResultTokenId: paymentIntent.id,
6049
- isPaypal: true
6050
- }
6051
- });
6052
- if (processResult.type !== "success") {
6053
- throw Object.assign(
6054
- new FloPayError8("Failed to finalize PayPal payment.", "api_error"),
6055
- { checkoutMethod: "paypal" }
6056
- );
6057
- }
6058
- finalResultStatus = processResult.result.status;
6059
- try {
6060
- const refreshed = await api.getUnifiedCheckoutSession(resumeState.sessionId);
6061
- if (refreshed.data.session) {
6062
- resumeSession = refreshed.data.session;
6063
- }
6064
- } catch {
6065
- }
6066
- }
6067
- await showSuccess({
6068
- result: {
6069
- status: finalResultStatus,
6070
- paymentIntentId: paymentIntent.id,
6071
- paymentMethodId: paymentMethodId2,
6072
- checkoutMethod: "paypal"
6073
- },
6074
- session: resumeSession,
6075
- sessionId: resumeState.sessionId,
6076
- autoCompleted: false
6077
- });
6078
- } catch (err) {
6079
- const floPayErr = normalizeSavedPaymentError(err);
6080
- await showError(floPayErr, {
6081
- emitDecline: true,
6082
- method: floPayErr.checkoutMethod ?? "paypal"
6083
- });
6084
- } finally {
6085
- clearPayPalResumeState2();
6086
- clearPayPalRedirectParams2();
6087
- if (isMountedRef.current) {
6088
- setOverlayStatus(null);
6089
- setOverlayError(null);
6090
- setIsProcessing(false);
6091
- }
6092
- }
6093
- })();
6094
- }, [locale, resolvedBillingUrl, showError, showSuccess]);
5692
+ const themeBundle = useMemo5(() => resolveTheme3(theme), [theme]);
6095
5693
  const bStyles = useMemo5(() => {
6096
- const base = resolveButtonsLayoutTheme3(buttonsTheme);
5694
+ const base = themeBundle?.buttonsLayout ?? resolveButtonsLayoutTheme3(buttonsTheme);
6097
5695
  if (!stylesOverride) return base;
6098
5696
  return {
6099
5697
  ...base,
6100
5698
  ...stylesOverride,
6101
5699
  cardButton: { ...base.cardButton, ...stylesOverride.cardButton }
6102
5700
  };
6103
- }, [buttonsTheme, stylesOverride]);
5701
+ }, [themeBundle, buttonsTheme, stylesOverride]);
6104
5702
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
6105
5703
  return /* @__PURE__ */ jsxs8(Fragment6, { children: [
6106
5704
  /* @__PURE__ */ jsx10(
@@ -6197,9 +5795,12 @@ function FloPayAutomaticPaymentButton({
6197
5795
  sessionId: fallbackSession.sessionId,
6198
5796
  checkoutMode: "full",
6199
5797
  billingApiUrl: resolvedBillingUrl,
5798
+ locale,
5799
+ theme,
5800
+ buttonsTheme,
5801
+ buttonsStyles: stylesOverride,
6200
5802
  initialErrorMessage: fallbackSession.errorMessage,
6201
5803
  cardTitleContent: null,
6202
- showSecurityFooter: false,
6203
5804
  onComplete: handleFallbackComplete,
6204
5805
  onError: handleFallbackError,
6205
5806
  onDecline: handleFallbackDecline