@flopay/react 1.4.1 → 1.4.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.mjs CHANGED
@@ -193,7 +193,7 @@ function FloPayProvider({
193
193
  }
194
194
 
195
195
  // src/flopay-checkout.tsx
196
- import React9, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
196
+ import React8, { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef5, useState as useState4 } from "react";
197
197
  import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
198
198
  import { SDK_VERSION as SDK_VERSION2, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2, resolveTheme as resolveTheme2 } from "@flopay/shared";
199
199
 
@@ -264,73 +264,10 @@ function TitleContentSlot({
264
264
  return /* @__PURE__ */ jsx2(Fragment, { children: content === void 0 ? /* @__PURE__ */ jsx2(DefaultTitleContent, {}) : content });
265
265
  }
266
266
 
267
- // src/elements.tsx
268
- import { useEffect as useEffect2, useRef as useRef2, useContext } from "react";
269
- import { jsx as jsx3 } from "react/jsx-runtime";
270
- function createElementComponent(elementType, displayName) {
271
- function ElementComponent({
272
- className,
273
- id,
274
- style,
275
- options,
276
- onChange,
277
- onReady,
278
- onFocus,
279
- onBlur,
280
- onEscape
281
- }) {
282
- const containerRef = useRef2(null);
283
- const elementRef = useRef2(null);
284
- const { elements, reportInteractive } = useContext(FloPayContext);
285
- useEffect2(() => {
286
- if (!elements || !containerRef.current) return;
287
- let mounted = true;
288
- (async () => {
289
- let element = elements.getElement(elementType);
290
- if (!element) {
291
- element = await elements.create(elementType, options);
292
- }
293
- if (!mounted || !containerRef.current) {
294
- return;
295
- }
296
- element.mount(containerRef.current);
297
- elementRef.current = element;
298
- if (onChange) element.on("change", onChange);
299
- element.on("ready", () => {
300
- reportInteractive?.();
301
- onReady?.();
302
- });
303
- if (onFocus) element.on("focus", onFocus);
304
- if (onBlur) element.on("blur", onBlur);
305
- if (onEscape) element.on("escape", onEscape);
306
- })();
307
- return () => {
308
- mounted = false;
309
- if (elementRef.current) {
310
- try {
311
- elementRef.current.unmount();
312
- } catch {
313
- }
314
- elementRef.current = null;
315
- }
316
- };
317
- }, [elements, reportInteractive]);
318
- return /* @__PURE__ */ jsx3("div", { ref: containerRef, className, id, style });
319
- }
320
- ElementComponent.displayName = displayName;
321
- return ElementComponent;
322
- }
323
- var PaymentElement = createElementComponent("payment", "PaymentElement");
324
- var CardElement = createElementComponent("card", "CardElement");
325
- var CardNumberElement = createElementComponent("cardNumber", "CardNumberElement");
326
- var CardExpiryElement = createElementComponent("cardExpiry", "CardExpiryElement");
327
- var CardCvcElement = createElementComponent("cardCvc", "CardCvcElement");
328
- var AddressElement = createElementComponent("address", "AddressElement");
329
-
330
267
  // src/split-card-form.tsx
331
268
  import {
332
269
  ExpressCheckoutElement,
333
- PaymentElement as PaymentElement2,
270
+ PaymentElement,
334
271
  Elements as StripeElements,
335
272
  useElements as useStripeElements,
336
273
  useStripe as useStripeRaw
@@ -358,8 +295,8 @@ import {
358
295
  import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
359
296
 
360
297
  // src/vault-card-fields.tsx
361
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
362
- import { jsx as jsx4 } from "react/jsx-runtime";
298
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
299
+ import { jsx as jsx3 } from "react/jsx-runtime";
363
300
  function VaultCardFields({
364
301
  capture,
365
302
  html,
@@ -371,16 +308,16 @@ function VaultCardFields({
371
308
  onError,
372
309
  onValidation
373
310
  }) {
374
- const containerRef = useRef3(null);
375
- const onReadyRef = useRef3(onReady);
376
- const onErrorRef = useRef3(onError);
377
- const onValidationRef = useRef3(onValidation);
311
+ const containerRef = useRef2(null);
312
+ const onReadyRef = useRef2(onReady);
313
+ const onErrorRef = useRef2(onError);
314
+ const onValidationRef = useRef2(onValidation);
378
315
  onReadyRef.current = onReady;
379
316
  onErrorRef.current = onError;
380
317
  onValidationRef.current = onValidation;
381
- const themeRef = useRef3(theme);
318
+ const themeRef = useRef2(theme);
382
319
  themeRef.current = theme;
383
- useEffect3(() => {
320
+ useEffect2(() => {
384
321
  const el = containerRef.current;
385
322
  if (!el) return;
386
323
  let active = true;
@@ -422,15 +359,15 @@ function VaultCardFields({
422
359
  capture.unmount();
423
360
  };
424
361
  }, [capture, html, messageToken, expectedOrigin]);
425
- useEffect3(() => {
362
+ useEffect2(() => {
426
363
  if (theme) capture.applyTheme?.(theme);
427
364
  }, [capture, theme]);
428
- return /* @__PURE__ */ jsx4("div", { ref: containerRef, "data-testid": "flopay-vault-card-fields", style: containerStyle });
365
+ return /* @__PURE__ */ jsx3("div", { ref: containerRef, "data-testid": "flopay-vault-card-fields", style: containerStyle });
429
366
  }
430
367
 
431
368
  // src/error-banner.tsx
432
369
  import "react";
433
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
370
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
434
371
  function ErrorBanner({
435
372
  children,
436
373
  margin,
@@ -455,7 +392,7 @@ function ErrorBanner({
455
392
  ...styleOverride ?? {}
456
393
  },
457
394
  children: [
458
- icon && /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx5(
395
+ icon && /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx4(
459
396
  "path",
460
397
  {
461
398
  d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
@@ -486,41 +423,41 @@ var PAYMENT_METHOD_LOGO_URLS = {
486
423
  };
487
424
 
488
425
  // src/split-card-form.tsx
489
- import React8, { forwardRef, useCallback as useCallback3, useContext as useContext3, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
426
+ import React7, { useCallback as useCallback3, useContext as useContext2, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState3 } from "react";
490
427
 
491
428
  // src/hooks.ts
492
- import { useContext as useContext2 } from "react";
429
+ import { useContext } from "react";
493
430
  import { resolveBillingApiUrl as resolveBillingApiUrl2 } from "@flopay/shared";
494
431
  function useFloPay() {
495
- const ctx = useContext2(FloPayContext);
432
+ const ctx = useContext(FloPayContext);
496
433
  return ctx.flopay;
497
434
  }
498
435
  function usePayPalFloPay() {
499
- const ctx = useContext2(FloPayContext);
436
+ const ctx = useContext(FloPayContext);
500
437
  return ctx.paypalFlopay ?? null;
501
438
  }
502
439
  function useElements() {
503
- const ctx = useContext2(FloPayContext);
440
+ const ctx = useContext(FloPayContext);
504
441
  return ctx.elements;
505
442
  }
506
443
  function useCheckout() {
507
- return useContext2(CheckoutContext);
444
+ return useContext(CheckoutContext);
508
445
  }
509
446
  function useBillingApiUrl() {
510
- const ctx = useContext2(FloPayContext);
447
+ const ctx = useContext(FloPayContext);
511
448
  return ctx.billingApiUrl || resolveBillingApiUrl2();
512
449
  }
513
450
 
514
451
  // src/processing-overlay.tsx
515
452
  import "react";
516
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
453
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
517
454
  var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
518
455
  var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
519
456
  function ProcessingOverlay({
520
457
  status,
521
458
  errorMessage
522
459
  }) {
523
- return /* @__PURE__ */ jsx6(
460
+ return /* @__PURE__ */ jsx5(
524
461
  "div",
525
462
  {
526
463
  "data-testid": "flopay-processing-overlay",
@@ -564,14 +501,14 @@ function ProcessingOverlay({
564
501
  xmlns: "http://www.w3.org/2000/svg",
565
502
  style: { animation: "flopay-spin 0.8s linear infinite" },
566
503
  children: [
567
- /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
568
- /* @__PURE__ */ jsx6("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
504
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e7eb", strokeWidth: "3" }),
505
+ /* @__PURE__ */ jsx5("path", { d: "M4 12a8 8 0 018-8v3a5 5 0 00-5 5H4z", fill: "#4A49FF" })
569
506
  ]
570
507
  }
571
508
  ),
572
- status === "success" && /* @__PURE__ */ jsx6("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs3("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
573
- /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
574
- /* @__PURE__ */ jsx6(
509
+ status === "success" && /* @__PURE__ */ jsx5("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs3("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
510
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
511
+ /* @__PURE__ */ jsx5(
575
512
  "path",
576
513
  {
577
514
  d: "M7 12.5l3 3 7-7",
@@ -587,9 +524,9 @@ function ProcessingOverlay({
587
524
  }
588
525
  )
589
526
  ] }) }),
590
- status === "error" && /* @__PURE__ */ jsx6("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs3("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
591
- /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
592
- /* @__PURE__ */ jsx6(
527
+ status === "error" && /* @__PURE__ */ jsx5("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs3("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
528
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
529
+ /* @__PURE__ */ jsx5(
593
530
  "path",
594
531
  {
595
532
  d: "M8 8l8 8M16 8l-8 8",
@@ -621,7 +558,7 @@ function ProcessingOverlay({
621
558
  ]
622
559
  }
623
560
  ),
624
- status === "success" && /* @__PURE__ */ jsx6(
561
+ status === "success" && /* @__PURE__ */ jsx5(
625
562
  "p",
626
563
  {
627
564
  style: {
@@ -635,7 +572,7 @@ function ProcessingOverlay({
635
572
  children: "You will be automatically redirected, do not close or navigate away from this window."
636
573
  }
637
574
  ),
638
- status === "error" && errorMessage && /* @__PURE__ */ jsx6(
575
+ status === "error" && errorMessage && /* @__PURE__ */ jsx5(
639
576
  "p",
640
577
  {
641
578
  style: {
@@ -649,7 +586,7 @@ function ProcessingOverlay({
649
586
  children: errorMessage
650
587
  }
651
588
  ),
652
- /* @__PURE__ */ jsx6("style", { children: `
589
+ /* @__PURE__ */ jsx5("style", { children: `
653
590
  @keyframes flopay-spin { to { transform: rotate(360deg); } }
654
591
  @keyframes flopay-pop { 0% { transform: scale(0); } 100% { transform: scale(1); } }
655
592
  @keyframes flopay-draw { to { stroke-dashoffset: 0; } }
@@ -774,13 +711,6 @@ function buildDeclineEvent(method, input, overrides) {
774
711
  ...declineCode ? { declineCode } : {}
775
712
  };
776
713
  }
777
- function isRecord(value) {
778
- return typeof value === "object" && value !== null;
779
- }
780
- function readString(payload, key) {
781
- const value = payload?.[key];
782
- return typeof value === "string" && value.trim() ? value : void 0;
783
- }
784
714
  var FRIENDLY_MESSAGE_OVERRIDES = [
785
715
  {
786
716
  match: /^paypal authorization required\.?$/i,
@@ -796,26 +726,6 @@ function applyFriendlyMessageOverride(message) {
796
726
  }
797
727
  return message;
798
728
  }
799
- function buildFloPayApiError(payload, fallbackMessage) {
800
- const nestedError = isRecord(payload?.error) ? payload.error : null;
801
- const rawMessage = readString(payload, "message") ?? readString(nestedError, "message") ?? fallbackMessage;
802
- const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
803
- const code = readString(payload, "code") ?? readString(payload, "gatewayErrorCode") ?? readString(nestedError, "code");
804
- const declineCode = readString(payload, "declineCode") ?? readString(payload, "gatewayDeclineReason") ?? readString(payload, "decline_code") ?? readString(nestedError, "decline_code");
805
- return new FloPayError(message, "api_error", {
806
- ...code ? { code } : {},
807
- ...declineCode ? { declineCode } : {}
808
- });
809
- }
810
- async function buildFloPayApiErrorFromResponse(response, fallbackMessage) {
811
- const payload = await response.json().catch(() => null);
812
- return buildFloPayApiError(payload, fallbackMessage);
813
- }
814
- function intentRequestHeaders(nonce) {
815
- const headers = { "Content-Type": "application/json" };
816
- if (nonce) headers["x-checkout-session-token"] = nonce;
817
- return headers;
818
- }
819
729
  function isSuccessfulPaymentIntentStatus(status) {
820
730
  return status === "succeeded" || status === "requires_capture" || status === "processing";
821
731
  }
@@ -828,41 +738,6 @@ function mapPayPalIntentStatusToPaymentResult(status) {
828
738
  }
829
739
  return "failed";
830
740
  }
831
- function resolvePaymentIntentPaymentMethodId(paymentIntent) {
832
- const paymentMethod = paymentIntent?.payment_method;
833
- if (typeof paymentMethod === "string" && paymentMethod.startsWith("pm_")) {
834
- return paymentMethod;
835
- }
836
- if (paymentMethod && typeof paymentMethod === "object" && typeof paymentMethod.id === "string") {
837
- return paymentMethod.id;
838
- }
839
- return void 0;
840
- }
841
- async function retrievePaymentIntentFromProvider(provider, clientSecret) {
842
- const retriever = provider;
843
- if (isSetupIntentClientSecret(clientSecret)) {
844
- if (!retriever?.retrieveSetupIntent) {
845
- return null;
846
- }
847
- const { setupIntent, error: error2 } = await retriever.retrieveSetupIntent(clientSecret);
848
- if (error2) {
849
- throw new FloPayError(error2.message ?? "Failed to retrieve setup intent.", "api_error", {
850
- ...error2.code ? { code: error2.code } : {}
851
- });
852
- }
853
- return setupIntent ?? null;
854
- }
855
- if (!retriever?.retrievePaymentIntent) {
856
- return null;
857
- }
858
- const { paymentIntent, error } = await retriever.retrievePaymentIntent(clientSecret);
859
- if (error) {
860
- throw new FloPayError(error.message ?? "Failed to retrieve payment intent.", "api_error", {
861
- ...error.code ? { code: error.code } : {}
862
- });
863
- }
864
- return paymentIntent ?? null;
865
- }
866
741
  function resolveWalletElementsMode(amountInMinorUnits) {
867
742
  if (typeof amountInMinorUnits !== "number" || !Number.isFinite(amountInMinorUnits) || amountInMinorUnits <= 0) {
868
743
  return { mode: "setup" };
@@ -931,10 +806,15 @@ function isInAppBrowser(userAgent) {
931
806
  }
932
807
 
933
808
  // src/direct-paypal-button.tsx
934
- import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState2 } from "react";
809
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState2 } from "react";
935
810
  import { loadScript } from "@paypal/paypal-js";
936
811
  import { PaymentAPI } from "@flopay/js";
937
- import { FloPayError as FloPayError2, SDK_VERSION, normalizeGatewayEnvironment } from "@flopay/shared";
812
+ import {
813
+ FloPayError as FloPayError2,
814
+ SDK_VERSION,
815
+ generateIdempotencyKey,
816
+ normalizeGatewayEnvironment
817
+ } from "@flopay/shared";
938
818
 
939
819
  // src/merchant-callback.ts
940
820
  function invokeMerchantCallback(callback) {
@@ -986,8 +866,14 @@ function isPopupBlockedError(err) {
986
866
  }
987
867
 
988
868
  // src/direct-paypal-button.tsx
989
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
869
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
990
870
  var DEFAULT_BUTTON_HEIGHT = 45;
871
+ var PAYPAL_INIT_RETRY_DELAY_MS = 1e3;
872
+ var PAYPAL_RENDER_WATCHDOG_MS = 11e3;
873
+ var PAYPAL_INIT_ACKNOWLEDGEMENT_HOSTS = [
874
+ "https://www.paypal.com",
875
+ "https://www.sandbox.paypal.com"
876
+ ];
991
877
  var DIRECT_PAYPAL_RECOVERY_MESSAGE = "We couldn't open PayPal. Try again or choose another payment method.";
992
878
  var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
993
879
  width: "100%",
@@ -1002,6 +888,9 @@ var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
1002
888
  fontWeight: 700,
1003
889
  cursor: "pointer"
1004
890
  };
891
+ function isPayPalInitAcknowledgementTimeout(message) {
892
+ return message.includes("No ack for postMessage init()") && PAYPAL_INIT_ACKNOWLEDGEMENT_HOSTS.some((host) => message.includes(host)) && message.includes("10000ms");
893
+ }
1005
894
  function buildDirectPayPalRecoveryMessage(popupBlocked) {
1006
895
  return popupBlocked ? `${DIRECT_PAYPAL_RECOVERY_MESSAGE} Allow pop-ups for this site, then try again.` : DIRECT_PAYPAL_RECOVERY_MESSAGE;
1007
896
  }
@@ -1021,6 +910,8 @@ function DirectPayPalButtonImplementation({
1021
910
  onTechnicalFailure,
1022
911
  isProcessing = false,
1023
912
  onLoadStateChange,
913
+ onInitializationStateChange,
914
+ allowAutomaticRetry = true,
1024
915
  onButtonClick,
1025
916
  runBeforeButtonClick,
1026
917
  session,
@@ -1049,7 +940,7 @@ function DirectPayPalButtonImplementation({
1049
940
  telemetryContext?.layout
1050
941
  ]);
1051
942
  const floPayTelemetry = useMemo2(() => getFloPayTelemetryBridge(flopay), [flopay]);
1052
- useEffect4(() => () => {
943
+ useEffect3(() => () => {
1053
944
  if (!standaloneTelemetry) return;
1054
945
  void standaloneTelemetry.flush().catch(() => {
1055
946
  }).finally(() => standaloneTelemetry.destroy());
@@ -1074,76 +965,104 @@ function DirectPayPalButtonImplementation({
1074
965
  startTiming: () => floPayTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
1075
966
  elapsed: (startedAt) => floPayTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
1076
967
  }), [floPayTelemetry, standaloneTelemetry]);
1077
- const containerRef = useRef4(null);
1078
- const providerStartedAt = useRef4(0);
1079
- const focusTargetRef = useRef4(null);
1080
- const pendingProviderFocusRef = useRef4(false);
968
+ const containerRef = useRef3(null);
969
+ const providerStartedAt = useRef3(0);
970
+ const focusTargetRef = useRef3(null);
971
+ const pendingProviderFocusRef = useRef3(false);
1081
972
  const [ready, setReady] = useState2(false);
973
+ const [recovering, setRecovering] = useState2(false);
1082
974
  const [renderGeneration, setRenderGeneration] = useState2(0);
1083
- const activeRenderGenerationRef = useRef4(0);
975
+ const activeRenderGenerationRef = useRef3(0);
1084
976
  const [showRetryFocusTarget, setShowRetryFocusTarget] = useState2(false);
977
+ const automaticRetryUsedRef = useRef3(false);
978
+ const initializationFailureLoggedRef = useRef3(false);
1085
979
  const [failed, setFailed] = useState2(false);
1086
980
  const [submitting, setSubmitting] = useState2(false);
1087
981
  const baseUrl = useMemo2(() => billingApiUrl.replace(/\/+$/, ""), [billingApiUrl]);
982
+ const initializationConfigKey = useMemo2(
983
+ () => JSON.stringify([
984
+ baseUrl,
985
+ clientId,
986
+ currency,
987
+ environment ?? "",
988
+ isSubscription,
989
+ sessionId,
990
+ existingOrderId ?? ""
991
+ ]),
992
+ [baseUrl, clientId, currency, environment, existingOrderId, isSubscription, sessionId]
993
+ );
994
+ const initializationConfigKeyRef = useRef3(initializationConfigKey);
995
+ const initializationConfigChanged = initializationConfigKeyRef.current !== initializationConfigKey;
1088
996
  const [debugLines, setDebugLines] = useState2([]);
1089
997
  const appendDebug = (line) => {
1090
998
  if (!debug) return;
1091
999
  setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
1092
1000
  };
1093
- const onTokenizedBodyRef = useRef4(onTokenizedBody);
1094
- const onCompleteRef = useRef4(onComplete);
1095
- const onErrorChangeRef = useRef4(onErrorChange);
1096
- const onDeclineRef = useRef4(onDecline);
1097
- const onTechnicalFailureRef = useRef4(onTechnicalFailure);
1098
- const onButtonClickRef = useRef4(onButtonClick);
1099
- const onLoadStateChangeRef = useRef4(onLoadStateChange);
1100
- const runBeforeButtonClickRef = useRef4(runBeforeButtonClick);
1101
- const sessionRef = useRef4(session);
1102
- const emailRef = useRef4(email);
1103
- const nonceRef = useRef4(nonce);
1104
- const beforeClickRef = useRef4(null);
1105
- const attemptGenerationRef = useRef4(0);
1106
- const attemptRef = useRef4(null);
1107
- const invalidatedAttemptGenerationRef = useRef4(null);
1108
- const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
1109
- const approvalContextByTokenRef = useRef4(/* @__PURE__ */ new Map());
1110
- useEffect4(() => {
1001
+ const onTokenizedBodyRef = useRef3(onTokenizedBody);
1002
+ const onCompleteRef = useRef3(onComplete);
1003
+ const onErrorChangeRef = useRef3(onErrorChange);
1004
+ const onDeclineRef = useRef3(onDecline);
1005
+ const onTechnicalFailureRef = useRef3(onTechnicalFailure);
1006
+ const onButtonClickRef = useRef3(onButtonClick);
1007
+ const onLoadStateChangeRef = useRef3(onLoadStateChange);
1008
+ const onInitializationStateChangeRef = useRef3(onInitializationStateChange);
1009
+ const runBeforeButtonClickRef = useRef3(runBeforeButtonClick);
1010
+ const sessionRef = useRef3(session);
1011
+ const emailRef = useRef3(email);
1012
+ const nonceRef = useRef3(nonce);
1013
+ const beforeClickRef = useRef3(null);
1014
+ const attemptGenerationRef = useRef3(0);
1015
+ const attemptRef = useRef3(null);
1016
+ const invalidatedAttemptGenerationRef = useRef3(null);
1017
+ const attemptContextBySurfaceRef = useRef3(/* @__PURE__ */ new Map());
1018
+ const approvalContextByTokenRef = useRef3(/* @__PURE__ */ new Map());
1019
+ const componentMountedRef = useRef3(false);
1020
+ useEffect3(() => {
1021
+ componentMountedRef.current = true;
1022
+ return () => {
1023
+ componentMountedRef.current = false;
1024
+ };
1025
+ }, []);
1026
+ useEffect3(() => {
1111
1027
  onTokenizedBodyRef.current = onTokenizedBody;
1112
1028
  }, [onTokenizedBody]);
1113
- useEffect4(() => {
1029
+ useEffect3(() => {
1114
1030
  onCompleteRef.current = onComplete;
1115
1031
  }, [onComplete]);
1116
- useEffect4(() => {
1032
+ useEffect3(() => {
1117
1033
  onErrorChangeRef.current = onErrorChange;
1118
1034
  }, [onErrorChange]);
1119
- useEffect4(() => {
1035
+ useEffect3(() => {
1120
1036
  onDeclineRef.current = onDecline;
1121
1037
  }, [onDecline]);
1122
- useEffect4(() => {
1038
+ useEffect3(() => {
1123
1039
  onTechnicalFailureRef.current = onTechnicalFailure;
1124
1040
  }, [onTechnicalFailure]);
1125
- useEffect4(() => {
1041
+ useEffect3(() => {
1126
1042
  onButtonClickRef.current = onButtonClick;
1127
1043
  }, [onButtonClick]);
1128
- useEffect4(() => {
1044
+ useEffect3(() => {
1129
1045
  onLoadStateChangeRef.current = onLoadStateChange;
1130
1046
  }, [onLoadStateChange]);
1131
- useEffect4(() => {
1047
+ useEffect3(() => {
1048
+ onInitializationStateChangeRef.current = onInitializationStateChange;
1049
+ }, [onInitializationStateChange]);
1050
+ useEffect3(() => {
1132
1051
  runBeforeButtonClickRef.current = runBeforeButtonClick;
1133
1052
  }, [runBeforeButtonClick]);
1134
- useEffect4(() => {
1053
+ useEffect3(() => {
1135
1054
  sessionRef.current = session;
1136
1055
  }, [session]);
1137
- useEffect4(() => {
1056
+ useEffect3(() => {
1138
1057
  emailRef.current = email;
1139
1058
  }, [email]);
1140
- useEffect4(() => {
1059
+ useEffect3(() => {
1141
1060
  nonceRef.current = nonce;
1142
1061
  }, [nonce]);
1143
- useEffect4(() => {
1062
+ useEffect3(() => {
1144
1063
  activeRenderGenerationRef.current = renderGeneration;
1145
1064
  }, [renderGeneration]);
1146
- useEffect4(() => {
1065
+ useEffect3(() => {
1147
1066
  if (showRetryFocusTarget) focusTargetRef.current?.focus();
1148
1067
  }, [showRetryFocusTarget, renderGeneration]);
1149
1068
  const focusRetryTarget = useCallback2(() => {
@@ -1162,7 +1081,7 @@ function DirectPayPalButtonImplementation({
1162
1081
  );
1163
1082
  (target ?? containerRef.current)?.focus?.();
1164
1083
  }, []);
1165
- useEffect4(() => {
1084
+ useEffect3(() => {
1166
1085
  if (!ready || !pendingProviderFocusRef.current) return;
1167
1086
  pendingProviderFocusRef.current = false;
1168
1087
  focusPayPalSurface();
@@ -1245,7 +1164,7 @@ function DirectPayPalButtonImplementation({
1245
1164
  }
1246
1165
  return token;
1247
1166
  };
1248
- useEffect4(() => {
1167
+ useEffect3(() => {
1249
1168
  const handleVisibilityChange = () => {
1250
1169
  if (document.visibilityState === "visible") {
1251
1170
  scheduleMissingCallbackRecovery();
@@ -1263,11 +1182,21 @@ function DirectPayPalButtonImplementation({
1263
1182
  clearAttemptTimer();
1264
1183
  };
1265
1184
  }, []);
1266
- useEffect4(() => {
1185
+ useEffect3(() => {
1186
+ if (initializationConfigKeyRef.current === initializationConfigKey) return;
1187
+ initializationConfigKeyRef.current = initializationConfigKey;
1188
+ automaticRetryUsedRef.current = false;
1189
+ initializationFailureLoggedRef.current = false;
1190
+ setReady(false);
1191
+ setRecovering(false);
1192
+ setFailed(false);
1193
+ onInitializationStateChangeRef.current?.("loading");
1194
+ }, [initializationConfigKey]);
1195
+ useEffect3(() => {
1267
1196
  invokeMerchantCallback(() => onLoadStateChangeRef.current?.(ready && !failed));
1268
1197
  }, [ready, failed]);
1269
1198
  const normalizedEnv = normalizeGatewayEnvironment(environment);
1270
- useEffect4(() => {
1199
+ useEffect3(() => {
1271
1200
  const maskedClient = clientId ? `${clientId.slice(0, 6)}\u2026(len ${clientId.length})` : "(empty)";
1272
1201
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "(no navigator)";
1273
1202
  appendDebug(`mount clientId=${maskedClient} env=${environment ?? "(unset)"}\u2192${normalizedEnv ?? "live"} ccy=${currency} sub=${isSubscription}`);
@@ -1297,7 +1226,29 @@ function DirectPayPalButtonImplementation({
1297
1226
  paymentMethodCategory: "paypal"
1298
1227
  });
1299
1228
  let cancelled = false;
1300
- let activeButtons = null;
1229
+ let superseded = false;
1230
+ const isInactive = () => cancelled || superseded;
1231
+ let paypalIntentIdempotencyKey;
1232
+ let paypalIntentAttemptActive = false;
1233
+ const beginPaypalIntentAttempt = () => {
1234
+ paypalIntentIdempotencyKey = generateIdempotencyKey();
1235
+ if (!paypalIntentIdempotencyKey) {
1236
+ console.warn(
1237
+ "[FloPay] DirectPayPal: secure idempotency key unavailable; retries may not be deduplicated."
1238
+ );
1239
+ }
1240
+ paypalIntentAttemptActive = true;
1241
+ };
1242
+ let retryTimer = null;
1243
+ let preRenderFailureHandled = false;
1244
+ let ownedButtons = null;
1245
+ let ownedButtonsClosed = false;
1246
+ const closeOwnedButtons = () => {
1247
+ if (!ownedButtons || ownedButtonsClosed) return;
1248
+ ownedButtonsClosed = true;
1249
+ ownedButtons.close().catch(() => {
1250
+ });
1251
+ };
1301
1252
  let overlayStartedAt = null;
1302
1253
  const finishOverlay = () => {
1303
1254
  if (overlayStartedAt === null) return;
@@ -1367,6 +1318,7 @@ function DirectPayPalButtonImplementation({
1367
1318
  };
1368
1319
  const errorMessages = [];
1369
1320
  const onWindowError = (ev) => {
1321
+ if (isInactive()) return;
1370
1322
  const msg = ev.message ?? String(ev.error ?? "(no message)");
1371
1323
  if (msg && (msg.toLowerCase().includes("paypal") || msg.toLowerCase().includes("zoid") || msg.toLowerCase().includes("postrobot") || msg.toLowerCase().includes("storage"))) {
1372
1324
  appendDebug(`window:error ${msg.slice(0, 140)}`);
@@ -1374,6 +1326,7 @@ function DirectPayPalButtonImplementation({
1374
1326
  }
1375
1327
  };
1376
1328
  const onUnhandledRejection = (ev) => {
1329
+ if (isInactive()) return;
1377
1330
  const reason = ev.reason instanceof Error ? ev.reason.message : String(ev.reason ?? "(no reason)");
1378
1331
  if (reason && (reason.toLowerCase().includes("paypal") || reason.toLowerCase().includes("zoid") || reason.toLowerCase().includes("postrobot") || reason.toLowerCase().includes("storage"))) {
1379
1332
  appendDebug(`window:rejection ${reason.slice(0, 140)}`);
@@ -1388,6 +1341,7 @@ function DirectPayPalButtonImplementation({
1388
1341
  if (debug && typeof PerformanceObserver !== "undefined") {
1389
1342
  try {
1390
1343
  perfObserver = new PerformanceObserver((list) => {
1344
+ if (isInactive()) return;
1391
1345
  for (const entry of list.getEntries()) {
1392
1346
  if (!entry.name.toLowerCase().includes("paypal")) continue;
1393
1347
  paypalRequestCount.value += 1;
@@ -1416,17 +1370,13 @@ function DirectPayPalButtonImplementation({
1416
1370
  return lower.includes("zoid destroyed") || lower.includes("destroyed all components") || lower.includes("window closed") || lower.includes("detected container element removed");
1417
1371
  };
1418
1372
  const forwardError = (message) => {
1419
- if (cancelled) return;
1373
+ if (isInactive()) return;
1420
1374
  if (isZoidLifecycleMessage(message)) return;
1421
1375
  const friendly = applyFriendlyMessageOverride(message) ?? message;
1422
1376
  invokeMerchantCallback(() => onErrorChangeRef.current?.(friendly));
1423
1377
  };
1424
1378
  const markRenderFailed = (message) => {
1425
1379
  if (cancelled) return;
1426
- if (isZoidLifecycleMessage(message)) {
1427
- appendDebug(`markRenderFailed:skip-zoid msg=${message.slice(0, 80)}`);
1428
- return;
1429
- }
1430
1380
  setFailed(true);
1431
1381
  if (!message.includes("paypal_ineligible")) {
1432
1382
  telemetrySource.error({
@@ -1439,8 +1389,48 @@ function DirectPayPalButtonImplementation({
1439
1389
  }
1440
1390
  console.error("[FloPay] DirectPayPal load/render failure:", message);
1441
1391
  };
1392
+ const handlePreRenderFailure = (message, retryable = false) => {
1393
+ if (cancelled) return;
1394
+ const isRecoveryAttempt = automaticRetryUsedRef.current || !allowAutomaticRetry;
1395
+ if (preRenderFailureHandled) return;
1396
+ preRenderFailureHandled = true;
1397
+ superseded = true;
1398
+ stopDiagnostics();
1399
+ closeOwnedButtons();
1400
+ const isRetryableInitializationFailure = retryable || isPayPalInitAcknowledgementTimeout(message);
1401
+ if (isRetryableInitializationFailure && allowAutomaticRetry && !automaticRetryUsedRef.current) {
1402
+ automaticRetryUsedRef.current = true;
1403
+ setReady(false);
1404
+ setSubmitting(false);
1405
+ setRecovering(true);
1406
+ onInitializationStateChangeRef.current?.("retrying");
1407
+ retryTimer = setTimeout(() => {
1408
+ if (cancelled) return;
1409
+ setRenderGeneration((generation) => generation + 1);
1410
+ }, PAYPAL_INIT_RETRY_DELAY_MS);
1411
+ return;
1412
+ }
1413
+ if (isRetryableInitializationFailure || isRecoveryAttempt) {
1414
+ setReady(false);
1415
+ setSubmitting(false);
1416
+ setRecovering(false);
1417
+ setFailed(true);
1418
+ onInitializationStateChangeRef.current?.("exhausted");
1419
+ if (!initializationFailureLoggedRef.current) {
1420
+ initializationFailureLoggedRef.current = true;
1421
+ const failureCode = isPayPalInitAcknowledgementTimeout(message) ? "paypal_init_timeout" : retryable ? "paypal_render_timeout" : "paypal_init_failure";
1422
+ console.error(
1423
+ "[FloPay] Direct PayPal initialization failed:",
1424
+ failureCode
1425
+ );
1426
+ }
1427
+ return;
1428
+ }
1429
+ markRenderFailed(message);
1430
+ };
1442
1431
  setFailed(false);
1443
1432
  const dispatchTokenizedBody = async (body, prepared = beforeClickRef.current) => {
1433
+ if (isInactive()) return;
1444
1434
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1445
1435
  if (onTokenizedBodyRef.current) {
1446
1436
  await onTokenizedBodyRef.current(body, {
@@ -1494,41 +1484,45 @@ function DirectPayPalButtonImplementation({
1494
1484
  }
1495
1485
  );
1496
1486
  if (response.ok) {
1487
+ if (superseded || cancelled && componentMountedRef.current) return;
1497
1488
  finishProcessing();
1498
1489
  telemetrySource.terminal({
1499
1490
  outcome: "payment_succeeded",
1500
1491
  provider: "paypal",
1501
1492
  paymentMethodCategory: "paypal"
1502
1493
  });
1503
- } else {
1504
- const json = await response.json().catch(() => null);
1505
- const rawMessage = json?.["message"] ?? "PayPal payment failed.";
1506
- const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
1507
- finishProcessing();
1508
- if (typeof json?.["declineCode"] === "string") {
1509
- telemetrySource.terminal({
1510
- outcome: "payment_declined",
1511
- provider: "paypal",
1512
- paymentMethodCategory: "paypal"
1513
- });
1514
- } else {
1515
- telemetrySource.error({
1516
- errorCode: "PAYMENT_PROCESSING_FAILED",
1517
- stage: "processing",
1518
- provider: "paypal",
1519
- paymentMethodCategory: "paypal",
1520
- requestCategory: "process_payment",
1521
- statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
1522
- });
1523
- }
1524
- forwardError(message);
1525
- invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
1526
- code: json?.["code"],
1527
- declineCode: json?.["declineCode"]
1528
- })));
1494
+ invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
1529
1495
  return;
1530
1496
  }
1497
+ if (isInactive()) return;
1498
+ const json = await response.json().catch(() => null);
1499
+ const rawMessage = json?.["message"] ?? "PayPal payment failed.";
1500
+ const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
1501
+ finishProcessing();
1502
+ if (typeof json?.["declineCode"] === "string") {
1503
+ telemetrySource.terminal({
1504
+ outcome: "payment_declined",
1505
+ provider: "paypal",
1506
+ paymentMethodCategory: "paypal"
1507
+ });
1508
+ } else {
1509
+ telemetrySource.error({
1510
+ errorCode: "PAYMENT_PROCESSING_FAILED",
1511
+ stage: "processing",
1512
+ provider: "paypal",
1513
+ paymentMethodCategory: "paypal",
1514
+ requestCategory: "process_payment",
1515
+ statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
1516
+ });
1517
+ }
1518
+ forwardError(message);
1519
+ invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
1520
+ code: json?.["code"],
1521
+ declineCode: json?.["declineCode"]
1522
+ })));
1523
+ return;
1531
1524
  } catch (err) {
1525
+ if (isInactive()) return;
1532
1526
  finishProcessing();
1533
1527
  telemetrySource.error({
1534
1528
  errorCode: "NETWORK_REQUEST_FAILED",
@@ -1544,14 +1538,21 @@ function DirectPayPalButtonImplementation({
1544
1538
  invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message)));
1545
1539
  return;
1546
1540
  }
1547
- invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
1548
1541
  };
1542
+ const supersededRenderError = () => new FloPayError2(
1543
+ "PayPal render was superseded.",
1544
+ "api_error",
1545
+ { code: "paypal_render_superseded" }
1546
+ );
1547
+ const resolveExistingOrder = (orderId) => isInactive() ? Promise.reject(supersededRenderError()) : Promise.resolve(orderId);
1549
1548
  const createPaypalIntent = async (fallbackMessage) => {
1549
+ if (isInactive()) {
1550
+ throw supersededRenderError();
1551
+ }
1552
+ if (!paypalIntentAttemptActive) beginPaypalIntentAttempt();
1550
1553
  const prepared = beforeClickRef.current;
1551
1554
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1552
- const effectiveEmail = prepared?.accountPatch?.email ?? emailRef.current;
1553
1555
  const currentNonce = prepared?.nonce ?? nonceRef.current;
1554
- const intentHeaders = intentRequestHeaders(currentNonce);
1555
1556
  telemetrySource.log({
1556
1557
  name: "payment.intent.started",
1557
1558
  stage: "processing",
@@ -1559,22 +1560,25 @@ function DirectPayPalButtonImplementation({
1559
1560
  paymentMethodCategory: "paypal",
1560
1561
  requestCategory: "intent_create"
1561
1562
  });
1562
- const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1563
- method: "POST",
1564
- headers: intentHeaders,
1565
- body: JSON.stringify({
1566
- sessionId: effectiveSessionId,
1567
- email: effectiveEmail,
1563
+ const intent = await new PaymentAPI(baseUrl, { telemetry: false }).createSessionIntent(
1564
+ effectiveSessionId,
1565
+ currentNonce ?? "",
1566
+ {
1567
+ provider: "paypal",
1568
+ paymentMethodCategory: "wallet",
1568
1569
  paymentMethodType: "paypal",
1569
- isPaypal: "true"
1570
- })
1571
- });
1572
- const json = await response.json().catch(() => null);
1573
- if (!response.ok) {
1574
- throw new Error(json?.message ?? fallbackMessage);
1570
+ paymentMethodId: null,
1571
+ // Always 'payment': the backend derives order vs subscription from the session's
1572
+ // products and returns it as the response providerObjectType. The order/subscription
1573
+ // split still drives the PayPal JS SDK call locally (see createOrder/createSubscription).
1574
+ intentKind: "payment"
1575
+ },
1576
+ { idempotencyKey: paypalIntentIdempotencyKey }
1577
+ );
1578
+ if (isInactive()) {
1579
+ throw supersededRenderError();
1575
1580
  }
1576
- const id = json?.data?.id;
1577
- if (!id) {
1581
+ if (intent.provider !== "paypal") {
1578
1582
  throw new Error(fallbackMessage);
1579
1583
  }
1580
1584
  telemetrySource.log({
@@ -1585,7 +1589,24 @@ function DirectPayPalButtonImplementation({
1585
1589
  requestCategory: "intent_create",
1586
1590
  statusClass: "2xx"
1587
1591
  });
1588
- return id;
1592
+ return intent.providerObjectId;
1593
+ };
1594
+ const reportPayPalDecline = (error, context) => {
1595
+ const currentNonce = context?.nonce ?? nonceRef.current;
1596
+ if (!currentNonce) return;
1597
+ const effectiveSessionId = context?.sessionId ?? sessionId;
1598
+ const providerDeclineReason = getProviderDeclineCode(error) ?? getProviderErrorCode(error) ?? "provider_declined";
1599
+ void new PaymentAPI(baseUrl, { telemetry: false }).reportSessionIntentDecline(
1600
+ effectiveSessionId,
1601
+ currentNonce,
1602
+ {
1603
+ provider: "paypal",
1604
+ paymentMethodCategory: "wallet",
1605
+ paymentMethodType: "paypal",
1606
+ providerDeclineReason
1607
+ }
1608
+ ).catch(() => {
1609
+ });
1589
1610
  };
1590
1611
  const effectRenderGeneration = renderGeneration;
1591
1612
  appendDebug("loadScript:scheduled (deferred 1 tick)");
@@ -1614,9 +1635,11 @@ function DirectPayPalButtonImplementation({
1614
1635
  ...paypalSdkEnv === "sandbox" ? { dataNamespace: "paypal_sandbox" } : {}
1615
1636
  });
1616
1637
  loadPromise.then((paypal) => {
1617
- appendDebug(`loadScript:resolved cancelled=${cancelled} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
1618
- if (cancelled || !paypal?.Buttons) {
1619
- if (!cancelled && !paypal?.Buttons) appendDebug("FAIL: namespace missing Buttons factory");
1638
+ appendDebug(`loadScript:resolved inactive=${isInactive()} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
1639
+ if (isInactive()) return;
1640
+ if (!paypal?.Buttons) {
1641
+ appendDebug("FAIL: namespace missing Buttons factory");
1642
+ handlePreRenderFailure("PayPal SDK did not expose the Buttons factory.");
1620
1643
  return;
1621
1644
  }
1622
1645
  telemetrySource.log({
@@ -1626,7 +1649,7 @@ function DirectPayPalButtonImplementation({
1626
1649
  paymentMethodCategory: "paypal"
1627
1650
  });
1628
1651
  const handleApprove = async (data) => {
1629
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1652
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1630
1653
  const token = data.subscriptionID ?? data.orderID ?? "";
1631
1654
  const context = token ? approvalContextByTokenRef.current.get(token) : attemptContextBySurfaceRef.current.get(effectRenderGeneration);
1632
1655
  if (!context || !isAttemptActive(context.generation)) return;
@@ -1651,7 +1674,7 @@ function DirectPayPalButtonImplementation({
1651
1674
  } catch (err) {
1652
1675
  forwardError(err instanceof Error ? err.message : "PayPal capture failed.");
1653
1676
  } finally {
1654
- setSubmitting(false);
1677
+ if (!isInactive()) setSubmitting(false);
1655
1678
  }
1656
1679
  };
1657
1680
  const buttons = paypal.Buttons({
@@ -1662,11 +1685,20 @@ function DirectPayPalButtonImplementation({
1662
1685
  // inline-session/account patches land before the order is created,
1663
1686
  // matching the Stripe-rendered PayPal flow.
1664
1687
  onClick: async (_data, actions) => {
1688
+ if (isInactive()) {
1689
+ await actions.reject();
1690
+ return;
1691
+ }
1692
+ beginPaypalIntentAttempt();
1665
1693
  const runner = runBeforeButtonClickRef.current;
1666
1694
  let prepared = {};
1667
1695
  if (runner) {
1668
1696
  try {
1669
1697
  const beforeClick = await runner("paypal");
1698
+ if (isInactive()) {
1699
+ await actions.reject();
1700
+ return;
1701
+ }
1670
1702
  if (!beforeClick.proceed) {
1671
1703
  beforeClickRef.current = null;
1672
1704
  attemptContextBySurfaceRef.current.delete(effectRenderGeneration);
@@ -1720,10 +1752,11 @@ function DirectPayPalButtonImplementation({
1720
1752
  // prior backend round-trip (the `paypal_direct_required` retry
1721
1753
  // path), feed it straight to PayPal instead of creating a new one.
1722
1754
  // Otherwise fall back to the normal create-intent call.
1723
- createOrder: isSubscription ? void 0 : existingOrderId ? () => Promise.resolve(registerApprovalContext(existingOrderId)) : () => createPaypalIntent("Failed to create PayPal order.").then(registerApprovalContext),
1724
- createSubscription: isSubscription ? existingOrderId ? () => Promise.resolve(registerApprovalContext(existingOrderId)) : () => createPaypalIntent("Failed to create PayPal subscription.").then(registerApprovalContext) : void 0,
1755
+ createOrder: isSubscription ? void 0 : existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal order.").then(registerApprovalContext),
1756
+ createSubscription: isSubscription ? existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal subscription.").then(registerApprovalContext) : void 0,
1725
1757
  onApprove: handleApprove,
1726
1758
  onCancel: () => {
1759
+ if (isInactive()) return;
1727
1760
  const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
1728
1761
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1729
1762
  beforeClickRef.current = null;
@@ -1737,7 +1770,7 @@ function DirectPayPalButtonImplementation({
1737
1770
  invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
1738
1771
  },
1739
1772
  onError: (err) => {
1740
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1773
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1741
1774
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1742
1775
  appendDebug(`onError rendered=${rendered} msg=${message.slice(0, 120)}`);
1743
1776
  if (rendered) {
@@ -1757,13 +1790,16 @@ function DirectPayPalButtonImplementation({
1757
1790
  paymentMethodCategory: "paypal",
1758
1791
  requestCategory: "provider_sdk"
1759
1792
  });
1793
+ if (isProviderDecline(err)) {
1794
+ reportPayPalDecline(err, context);
1795
+ }
1760
1796
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1761
1797
  beforeClickRef.current = null;
1762
1798
  invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
1763
1799
  notifyTechnicalFailure(err, { code: "paypal_runtime_failed" });
1764
1800
  return;
1765
1801
  }
1766
- markRenderFailed(message);
1802
+ handlePreRenderFailure(message);
1767
1803
  }
1768
1804
  });
1769
1805
  const eligible = buttons.isEligible();
@@ -1776,17 +1812,19 @@ function DirectPayPalButtonImplementation({
1776
1812
  });
1777
1813
  if (!eligible) {
1778
1814
  setReady(false);
1779
- markRenderFailed(
1815
+ handlePreRenderFailure(
1780
1816
  "PayPal buttons are not eligible to render in this context (paypal_ineligible)."
1781
1817
  );
1782
1818
  return;
1783
1819
  }
1784
1820
  const typedButtons = buttons;
1821
+ ownedButtons = typedButtons;
1785
1822
  if (debug) {
1786
1823
  appendDebug(`container:dims ${formatDims(container)} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"}`);
1787
1824
  }
1788
1825
  if (debug && typeof MutationObserver !== "undefined") {
1789
1826
  containerObserver = new MutationObserver((mutations) => {
1827
+ if (isInactive()) return;
1790
1828
  for (const mutation of mutations) {
1791
1829
  if (mutation.type === "childList") {
1792
1830
  mutation.addedNodes.forEach((node) => {
@@ -1814,7 +1852,7 @@ function DirectPayPalButtonImplementation({
1814
1852
  });
1815
1853
  }
1816
1854
  const snapshotContainer = (when) => {
1817
- if (cancelled || rendered) return;
1855
+ if (isInactive() || rendered) return;
1818
1856
  const iframes = container.querySelectorAll("iframe");
1819
1857
  const hasStorageAccess = typeof document !== "undefined" && "hasStorageAccess" in document;
1820
1858
  appendDebug(`watchdog:${when} container=${formatDims(container)} children=${container.childElementCount} iframes=${iframes.length} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"} cookies=${typeof navigator !== "undefined" ? navigator.cookieEnabled : "?"} hasStorageAccessApi=${hasStorageAccess} paypalNetRequests=${paypalRequestCount.value}`);
@@ -1834,17 +1872,25 @@ function DirectPayPalButtonImplementation({
1834
1872
  watchdogTimers.push(setTimeout(() => snapshotContainer("15s"), 15e3));
1835
1873
  }
1836
1874
  appendDebug("render:start");
1875
+ watchdogTimers.push(setTimeout(() => {
1876
+ if (isInactive() || rendered) return;
1877
+ appendDebug("watchdog:11s retryable render timeout");
1878
+ handlePreRenderFailure(
1879
+ "PayPal Buttons render did not settle within 11000ms.",
1880
+ true
1881
+ );
1882
+ }, PAYPAL_RENDER_WATCHDOG_MS));
1837
1883
  buttons.render(container).then(() => {
1838
- appendDebug(`render:resolved cancelled=${cancelled}`);
1884
+ appendDebug(`render:resolved inactive=${isInactive()}`);
1839
1885
  stopDiagnostics();
1840
- if (cancelled) {
1841
- typedButtons.close().catch(() => {
1842
- });
1886
+ if (isInactive()) {
1887
+ closeOwnedButtons();
1843
1888
  return;
1844
1889
  }
1845
- activeButtons = typedButtons;
1846
1890
  rendered = true;
1847
1891
  setReady(true);
1892
+ setRecovering(false);
1893
+ onInitializationStateChangeRef.current?.("ready");
1848
1894
  telemetrySource.log({
1849
1895
  name: "provider.ready",
1850
1896
  stage: "provider_ready",
@@ -1862,24 +1908,22 @@ function DirectPayPalButtonImplementation({
1862
1908
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1863
1909
  appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
1864
1910
  stopDiagnostics();
1865
- markRenderFailed(message);
1911
+ handlePreRenderFailure(message);
1866
1912
  });
1867
1913
  }).catch((err) => {
1868
1914
  const message = err instanceof Error ? err.message : "PayPal SDK failed to load.";
1869
1915
  appendDebug(`loadScript:rejected msg=${message.slice(0, 120)}`);
1870
- markRenderFailed(message);
1916
+ handlePreRenderFailure(message);
1871
1917
  });
1872
1918
  }, 0);
1873
1919
  return () => {
1874
1920
  cancelled = true;
1875
1921
  clearTimeout(startTimer);
1922
+ if (retryTimer) clearTimeout(retryTimer);
1876
1923
  stopDiagnostics();
1877
- if (activeButtons) {
1878
- activeButtons.close().catch(() => {
1879
- });
1880
- }
1924
+ closeOwnedButtons();
1881
1925
  };
1882
- }, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1926
+ }, [allowAutomaticRetry, baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1883
1927
  const debugPanel = debug ? /* @__PURE__ */ jsxs4(
1884
1928
  "pre",
1885
1929
  {
@@ -1904,8 +1948,8 @@ ${debugLines.join("\n")}`
1904
1948
  ]
1905
1949
  }
1906
1950
  ) : null;
1907
- if (failed) {
1908
- return debug ? /* @__PURE__ */ jsx7("div", { children: debugPanel }) : null;
1951
+ if (failed && !initializationConfigChanged) {
1952
+ return debug ? /* @__PURE__ */ jsx6("div", { children: debugPanel }) : null;
1909
1953
  }
1910
1954
  return (
1911
1955
  // Single wrapper so the parent flex container sees exactly one flex item
@@ -1914,39 +1958,52 @@ ${debugLines.join("\n")}`
1914
1958
  // intentionally has no margin/padding so the parent owns all spacing.
1915
1959
  /* @__PURE__ */ jsxs4("div", { children: [
1916
1960
  debugPanel,
1917
- /* @__PURE__ */ jsxs4("div", { style: { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT }, children: [
1918
- !ready && /* @__PURE__ */ jsx7(
1919
- "div",
1920
- {
1921
- "data-testid": "flopay-direct-paypal-placeholder",
1922
- style: {
1923
- position: "absolute",
1924
- inset: 0,
1925
- borderRadius: 8,
1926
- background: "#e5e7eb",
1927
- animation: "flopay-pulse 1.5s ease-in-out infinite",
1928
- pointerEvents: "none"
1929
- }
1930
- }
1931
- ),
1932
- /* @__PURE__ */ jsx7(
1933
- "div",
1934
- {
1935
- ref: containerRef,
1936
- "data-testid": "flopay-direct-paypal-container",
1937
- tabIndex: -1,
1938
- "aria-label": "PayPal payment method",
1939
- style: {
1940
- minHeight: DEFAULT_BUTTON_HEIGHT,
1941
- display: "flex",
1942
- opacity: ready ? 1 : 0
1943
- },
1944
- "aria-busy": submitting || isProcessing
1945
- },
1946
- renderGeneration
1947
- )
1948
- ] }),
1949
- showRetryFocusTarget && /* @__PURE__ */ jsx7(
1961
+ /* @__PURE__ */ jsxs4(
1962
+ "div",
1963
+ {
1964
+ "aria-hidden": recovering ? true : void 0,
1965
+ style: recovering ? {
1966
+ position: "relative",
1967
+ height: 0,
1968
+ opacity: 0,
1969
+ pointerEvents: "none"
1970
+ } : { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT },
1971
+ children: [
1972
+ !ready && !recovering && /* @__PURE__ */ jsx6(
1973
+ "div",
1974
+ {
1975
+ "data-testid": "flopay-direct-paypal-placeholder",
1976
+ style: {
1977
+ position: "absolute",
1978
+ inset: 0,
1979
+ borderRadius: 8,
1980
+ background: "#e5e7eb",
1981
+ animation: "flopay-pulse 1.5s ease-in-out infinite",
1982
+ pointerEvents: "none"
1983
+ }
1984
+ }
1985
+ ),
1986
+ /* @__PURE__ */ jsx6(
1987
+ "div",
1988
+ {
1989
+ ref: containerRef,
1990
+ "data-testid": "flopay-direct-paypal-container",
1991
+ tabIndex: -1,
1992
+ "aria-label": "PayPal payment method",
1993
+ style: {
1994
+ minHeight: DEFAULT_BUTTON_HEIGHT,
1995
+ display: "flex",
1996
+ opacity: ready ? 1 : 0
1997
+ },
1998
+ "aria-busy": submitting || isProcessing
1999
+ },
2000
+ renderGeneration
2001
+ )
2002
+ ]
2003
+ },
2004
+ renderGeneration
2005
+ ),
2006
+ showRetryFocusTarget && /* @__PURE__ */ jsx6(
1950
2007
  "button",
1951
2008
  {
1952
2009
  ref: focusTargetRef,
@@ -1961,18 +2018,34 @@ ${debugLines.join("\n")}`
1961
2018
  );
1962
2019
  }
1963
2020
  function DirectPayPalButton(props) {
1964
- return /* @__PURE__ */ jsx7(DirectPayPalButtonImplementation, { ...props });
2021
+ return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
1965
2022
  }
1966
2023
  function InstrumentedDirectPayPalButton(props) {
1967
- return /* @__PURE__ */ jsx7(DirectPayPalButtonImplementation, { ...props });
2024
+ return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
1968
2025
  }
1969
2026
 
1970
2027
  // src/split-card-form.tsx
1971
2028
  import { FloPayError as FloPayError3, isSetupIntentClientSecret as isSetupIntentClientSecret2, resolveTheme } from "@flopay/shared";
1972
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
2029
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1973
2030
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1974
2031
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1975
2032
  var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
2033
+ async function reportNonCardDecline({
2034
+ billingApiUrl,
2035
+ sessionId,
2036
+ nonce,
2037
+ request
2038
+ }) {
2039
+ if (!sessionId || !nonce) return;
2040
+ try {
2041
+ await new PaymentAPI2(billingApiUrl, { telemetry: false }).reportSessionIntentDecline(
2042
+ sessionId,
2043
+ nonce,
2044
+ request
2045
+ );
2046
+ } catch {
2047
+ }
2048
+ }
1976
2049
  function isAccountValidationError(err) {
1977
2050
  return err instanceof FloPayError3 && typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500;
1978
2051
  }
@@ -2084,9 +2157,9 @@ function buildExternalMethodRecoveryMessage(method, popupBlocked) {
2084
2157
  return popupBlocked ? `${base} Allow pop-ups for this site, then try again.` : base;
2085
2158
  }
2086
2159
  function useExternalAttemptReconciliation(onMissingTerminal) {
2087
- const generationRef = useRef5(0);
2088
- const invalidatedGenerationRef = useRef5(null);
2089
- const attemptRef = useRef5(null);
2160
+ const generationRef = useRef4(0);
2161
+ const invalidatedGenerationRef = useRef4(null);
2162
+ const attemptRef = useRef4(null);
2090
2163
  const clearAttemptTimer = useCallback3((targetAttempt = attemptRef.current) => {
2091
2164
  const attempt = targetAttempt;
2092
2165
  if (attempt?.timer) {
@@ -2160,7 +2233,7 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
2160
2233
  attempt.yieldedControl = true;
2161
2234
  clearAttemptTimer(attempt);
2162
2235
  }, [clearAttemptTimer]);
2163
- useEffect5(() => {
2236
+ useEffect4(() => {
2164
2237
  const handleVisibilityChange = () => {
2165
2238
  if (document.visibilityState === "visible") {
2166
2239
  scheduleRecoveryIfReturned();
@@ -2202,7 +2275,7 @@ function normalizeBeforeButtonClickError(method, err) {
2202
2275
  );
2203
2276
  }
2204
2277
  function FloPayKeyframes() {
2205
- return /* @__PURE__ */ jsx8("style", { children: FLOPAY_KEYFRAMES });
2278
+ return /* @__PURE__ */ jsx7("style", { children: FLOPAY_KEYFRAMES });
2206
2279
  }
2207
2280
  function toCssSize(value) {
2208
2281
  if (typeof value === "number") return `${value}px`;
@@ -2225,7 +2298,7 @@ function ExpressCheckoutReadySwap({
2225
2298
  }) {
2226
2299
  if (state === "unavailable" || state === "load_error") return null;
2227
2300
  return /* @__PURE__ */ jsxs5("div", { style: { position: "relative", minHeight: 44 }, children: [
2228
- /* @__PURE__ */ jsx8(
2301
+ /* @__PURE__ */ jsx7(
2229
2302
  "div",
2230
2303
  {
2231
2304
  "data-testid": placeholderTestId,
@@ -2244,7 +2317,7 @@ function ExpressCheckoutReadySwap({
2244
2317
  }
2245
2318
  }
2246
2319
  ),
2247
- /* @__PURE__ */ jsx8(
2320
+ /* @__PURE__ */ jsx7(
2248
2321
  "div",
2249
2322
  {
2250
2323
  style: {
@@ -2262,11 +2335,9 @@ function ExpressCheckoutReadySwap({
2262
2335
  function isExpressCheckoutRowVisible(state) {
2263
2336
  return state !== "unavailable" && state !== "load_error";
2264
2337
  }
2265
- var SplitCardForm = forwardRef(
2266
- function SplitCardForm2(props, ref) {
2267
- return /* @__PURE__ */ jsx8(SplitCardFormInner, { ...props, innerRef: ref });
2268
- }
2269
- );
2338
+ function SplitCardForm(props) {
2339
+ return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props });
2340
+ }
2270
2341
  function PayPalButtonInner({
2271
2342
  sessionId,
2272
2343
  nonce,
@@ -2286,18 +2357,17 @@ function PayPalButtonInner({
2286
2357
  const stripe = useStripeRaw();
2287
2358
  const elements = useStripeElements();
2288
2359
  const [loadState, setLoadState] = useState3("loading");
2289
- useEffect5(() => {
2360
+ useEffect4(() => {
2290
2361
  onLoadStateChange?.(loadState);
2291
2362
  }, [loadState, onLoadStateChange]);
2292
2363
  const [submitting, setSubmitting] = useState3(false);
2293
- const paypalResumeAttempted = useRef5(false);
2294
- const focusTargetRef = useRef5(null);
2295
- const recoveryActionRef = useRef5(null);
2364
+ const paypalResumeAttempted = useRef4(false);
2365
+ const focusTargetRef = useRef4(null);
2366
+ const recoveryActionRef = useRef4(null);
2296
2367
  const [surfaceKey, setSurfaceKey] = useState3(0);
2297
2368
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2298
- const pendingProviderFocusRef = useRef5(false);
2299
- const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
2300
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2369
+ const pendingProviderFocusRef = useRef4(false);
2370
+ const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
2301
2371
  const {
2302
2372
  startAttempt,
2303
2373
  finishAttempt,
@@ -2312,7 +2382,7 @@ function PayPalButtonInner({
2312
2382
  setShowRecoveryAction(true);
2313
2383
  setSurfaceKey((key) => key + 1);
2314
2384
  });
2315
- useEffect5(() => {
2385
+ useEffect4(() => {
2316
2386
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2317
2387
  }, [showRecoveryAction, surfaceKey]);
2318
2388
  const resetSurface = useCallback3(() => {
@@ -2330,7 +2400,7 @@ function PayPalButtonInner({
2330
2400
  (target ?? focusTargetRef.current)?.focus();
2331
2401
  }, 0);
2332
2402
  }, []);
2333
- useEffect5(() => {
2403
+ useEffect4(() => {
2334
2404
  if (!pendingProviderFocusRef.current) return;
2335
2405
  pendingProviderFocusRef.current = false;
2336
2406
  focusProviderSurface();
@@ -2340,7 +2410,7 @@ function PayPalButtonInner({
2340
2410
  onErrorChange?.(null);
2341
2411
  focusProviderSurface();
2342
2412
  }, [focusProviderSurface, onErrorChange]);
2343
- useEffect5(() => {
2413
+ useEffect4(() => {
2344
2414
  if (!stripe || paypalResumeAttempted.current) return;
2345
2415
  const params = new URLSearchParams(window.location.search);
2346
2416
  const paymentIntentId = params.get("payment_intent");
@@ -2468,32 +2538,21 @@ function PayPalButtonInner({
2468
2538
  event.paymentFailed({ reason: "fail" });
2469
2539
  return;
2470
2540
  }
2471
- const intentHeaders = intentRequestHeaders(effectiveNonce);
2472
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2473
- method: "POST",
2474
- headers: intentHeaders,
2475
- body: JSON.stringify({
2476
- sessionId: effectiveSessionId,
2477
- email: effectiveEmail,
2478
- paymentMethodType: paymentMethod?.id ?? "paypal",
2479
- isPaypal: "true",
2480
- setupFutureUsage: "off_session",
2481
- setup_future_usage: "off_session"
2482
- })
2483
- });
2484
- if (!intentResponse.ok) {
2485
- const intentError = await buildFloPayApiErrorFromResponse(
2486
- intentResponse,
2487
- "Failed to create payment intent"
2488
- );
2489
- onErrorChange?.(intentError.message);
2490
- onDecline?.(buildDeclineEvent("paypal", intentError));
2491
- event.paymentFailed({ reason: "fail", message: intentError.message });
2492
- return;
2541
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
2542
+ effectiveSessionId,
2543
+ effectiveNonce ?? "",
2544
+ {
2545
+ provider: "stripe",
2546
+ paymentMethodCategory: "wallet",
2547
+ paymentMethodType: "paypal",
2548
+ paymentMethodId: paymentMethod?.id ?? null,
2549
+ intentKind: "payment"
2550
+ }
2551
+ );
2552
+ if (intent.provider !== "stripe") {
2553
+ throw new Error("Invalid provider returned for PayPal intent");
2493
2554
  }
2494
- const intentJson = await intentResponse.json();
2495
- const intentClientSecret = intentJson.data?.id;
2496
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
2555
+ const intentClientSecret = intent.clientSecret;
2497
2556
  const confirmParams = {
2498
2557
  return_url: window.location.href
2499
2558
  };
@@ -2530,6 +2589,17 @@ function PayPalButtonInner({
2530
2589
  code: getProviderErrorCode(confirmError),
2531
2590
  declineCode: getProviderDeclineCode(confirmError)
2532
2591
  }));
2592
+ await reportNonCardDecline({
2593
+ billingApiUrl,
2594
+ sessionId: effectiveSessionId,
2595
+ nonce: effectiveNonce,
2596
+ request: {
2597
+ provider: "stripe",
2598
+ paymentMethodCategory: "wallet",
2599
+ paymentMethodType: "paypal",
2600
+ providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
2601
+ }
2602
+ });
2533
2603
  } else {
2534
2604
  recoverTechnicalFailure(confirmError, "stripe_paypal_confirm_failed", attemptContext?.generation);
2535
2605
  }
@@ -2558,9 +2628,9 @@ function PayPalButtonInner({
2558
2628
  } finally {
2559
2629
  setSubmitting(false);
2560
2630
  }
2561
- }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2631
+ }, [stripe, elements, sessionId, nonce, email, billingApiUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2562
2632
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2563
- /* @__PURE__ */ jsx8(
2633
+ /* @__PURE__ */ jsx7(
2564
2634
  ExpressCheckoutReadySwap,
2565
2635
  {
2566
2636
  state: loadState,
@@ -2575,7 +2645,7 @@ function PayPalButtonInner({
2575
2645
  "aria-label": "PayPal payment method",
2576
2646
  style: { borderRadius: 8, outlineOffset: 4 },
2577
2647
  children: [
2578
- showRecoveryAction && /* @__PURE__ */ jsx8(
2648
+ showRecoveryAction && /* @__PURE__ */ jsx7(
2579
2649
  "button",
2580
2650
  {
2581
2651
  ref: recoveryActionRef,
@@ -2586,7 +2656,7 @@ function PayPalButtonInner({
2586
2656
  children: "Try PayPal again"
2587
2657
  }
2588
2658
  ),
2589
- /* @__PURE__ */ jsx8(
2659
+ /* @__PURE__ */ jsx7(
2590
2660
  ExpressCheckoutElement,
2591
2661
  {
2592
2662
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -2626,7 +2696,7 @@ function PayPalButtonInner({
2626
2696
  )
2627
2697
  }
2628
2698
  ),
2629
- submitting && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: "processing" })
2699
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
2630
2700
  ] });
2631
2701
  }
2632
2702
  function WalletButtonInner({
@@ -2634,6 +2704,7 @@ function WalletButtonInner({
2634
2704
  nonce,
2635
2705
  email,
2636
2706
  billingApiUrl,
2707
+ intentKind,
2637
2708
  expressMethods,
2638
2709
  onTokenizedBody,
2639
2710
  onErrorChange,
@@ -2648,20 +2719,19 @@ function WalletButtonInner({
2648
2719
  const stripe = useStripeRaw();
2649
2720
  const elements = useStripeElements();
2650
2721
  const [loadState, setLoadState] = useState3("loading");
2651
- useEffect5(() => {
2722
+ useEffect4(() => {
2652
2723
  onLoadStateChange?.(loadState);
2653
2724
  }, [loadState, onLoadStateChange]);
2654
2725
  const [submitting, setSubmitting] = useState3(false);
2655
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2656
- const focusTargetRef = useRef5(null);
2657
- const recoveryActionRef = useRef5(null);
2726
+ const focusTargetRef = useRef4(null);
2727
+ const recoveryActionRef = useRef4(null);
2658
2728
  const [surfaceKey, setSurfaceKey] = useState3(0);
2659
2729
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2660
2730
  const [recoveryActionMethod, setRecoveryActionMethod] = useState3("google_pay");
2661
- const pendingProviderFocusRef = useRef5(false);
2662
- const lastWalletProviderMethodRef = useRef5("google_pay");
2663
- const lastWalletMethodRef = useRef5("google_pay");
2664
- const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
2731
+ const pendingProviderFocusRef = useRef4(false);
2732
+ const lastWalletProviderMethodRef = useRef4("google_pay");
2733
+ const lastWalletMethodRef = useRef4("google_pay");
2734
+ const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
2665
2735
  const {
2666
2736
  startAttempt,
2667
2737
  finishAttempt,
@@ -2677,7 +2747,7 @@ function WalletButtonInner({
2677
2747
  setShowRecoveryAction(true);
2678
2748
  setSurfaceKey((key) => key + 1);
2679
2749
  });
2680
- useEffect5(() => {
2750
+ useEffect4(() => {
2681
2751
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2682
2752
  }, [showRecoveryAction, surfaceKey]);
2683
2753
  const resetSurface = useCallback3(() => {
@@ -2696,7 +2766,7 @@ function WalletButtonInner({
2696
2766
  (target ?? focusTargetRef.current)?.focus();
2697
2767
  }, 0);
2698
2768
  }, []);
2699
- useEffect5(() => {
2769
+ useEffect4(() => {
2700
2770
  if (!pendingProviderFocusRef.current) return;
2701
2771
  pendingProviderFocusRef.current = false;
2702
2772
  focusProviderSurface();
@@ -2753,30 +2823,21 @@ function WalletButtonInner({
2753
2823
  if (!effectiveSessionId || !effectiveEmail) {
2754
2824
  throw new Error("Missing sessionId or email for wallet payment");
2755
2825
  }
2756
- const intentHeaders = intentRequestHeaders(effectiveNonce);
2757
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2758
- method: "POST",
2759
- headers: intentHeaders,
2760
- body: JSON.stringify({
2761
- sessionId: effectiveSessionId,
2762
- email: effectiveEmail,
2763
- paymentMethodType: paymentMethod.id,
2764
- isPaypal: false
2765
- })
2766
- });
2767
- if (!intentResponse.ok) {
2768
- const intentError = await buildFloPayApiErrorFromResponse(
2769
- intentResponse,
2770
- "Failed to create payment intent"
2771
- );
2772
- onErrorChange?.(intentError.message);
2773
- onDecline?.(buildDeclineEvent(method, intentError));
2774
- event.paymentFailed({ reason: "fail", message: intentError.message });
2775
- return;
2826
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
2827
+ effectiveSessionId,
2828
+ effectiveNonce ?? "",
2829
+ {
2830
+ provider: "stripe",
2831
+ paymentMethodCategory: "wallet",
2832
+ paymentMethodType: walletType ?? buttonMethod,
2833
+ paymentMethodId: paymentMethod.id,
2834
+ intentKind
2835
+ }
2836
+ );
2837
+ if (intent.provider !== "stripe") {
2838
+ throw new Error("Invalid provider returned for wallet intent");
2776
2839
  }
2777
- const intentJson = await intentResponse.json();
2778
- const intentClientSecret = intentJson.data?.id;
2779
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
2840
+ const intentClientSecret = intent.clientSecret;
2780
2841
  const { error: confirmError, intentId } = isSetupIntentClientSecret2(intentClientSecret) ? await stripe.confirmCardSetup(intentClientSecret, { payment_method: paymentMethod.id }).then((r) => ({ error: r.error, intentId: r.setupIntent?.id })) : await stripe.confirmCardPayment(intentClientSecret, { payment_method: paymentMethod.id }).then((r) => ({ error: r.error, intentId: r.paymentIntent?.id }));
2781
2842
  if (confirmError) {
2782
2843
  if (isProviderDecline(confirmError)) {
@@ -2786,6 +2847,17 @@ function WalletButtonInner({
2786
2847
  code: getProviderErrorCode(confirmError),
2787
2848
  declineCode: getProviderDeclineCode(confirmError)
2788
2849
  }));
2850
+ await reportNonCardDecline({
2851
+ billingApiUrl,
2852
+ sessionId: effectiveSessionId,
2853
+ nonce: effectiveNonce,
2854
+ request: {
2855
+ provider: "stripe",
2856
+ paymentMethodCategory: "wallet",
2857
+ paymentMethodType: walletType ?? buttonMethod,
2858
+ providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
2859
+ }
2860
+ });
2789
2861
  } else {
2790
2862
  recoverTechnicalFailure(walletType, confirmError, "stripe_wallet_confirm_failed", attemptContext?.generation);
2791
2863
  }
@@ -2806,7 +2878,7 @@ function WalletButtonInner({
2806
2878
  setSubmitting(false);
2807
2879
  }
2808
2880
  },
2809
- [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]
2881
+ [stripe, elements, sessionId, nonce, email, billingApiUrl, intentKind, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]
2810
2882
  );
2811
2883
  const expressMethodMap = useMemo3(() => {
2812
2884
  const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
@@ -2827,7 +2899,7 @@ function WalletButtonInner({
2827
2899
  [expressMethods]
2828
2900
  );
2829
2901
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2830
- /* @__PURE__ */ jsx8(
2902
+ /* @__PURE__ */ jsx7(
2831
2903
  ExpressCheckoutReadySwap,
2832
2904
  {
2833
2905
  state: loadState,
@@ -2857,7 +2929,7 @@ function WalletButtonInner({
2857
2929
  ]
2858
2930
  }
2859
2931
  ),
2860
- /* @__PURE__ */ jsx8(
2932
+ /* @__PURE__ */ jsx7(
2861
2933
  ExpressCheckoutElement,
2862
2934
  {
2863
2935
  onReady: (event) => {
@@ -2921,7 +2993,7 @@ function WalletButtonInner({
2921
2993
  )
2922
2994
  }
2923
2995
  ),
2924
- submitting && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: "processing" })
2996
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
2925
2997
  ] });
2926
2998
  }
2927
2999
  function StripeMethodButton({
@@ -2943,7 +3015,7 @@ function StripeMethodButton({
2943
3015
  const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
2944
3016
  const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
2945
3017
  const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
2946
- return /* @__PURE__ */ jsx8(
3018
+ return /* @__PURE__ */ jsx7(
2947
3019
  "button",
2948
3020
  {
2949
3021
  type: "button",
@@ -2990,7 +3062,7 @@ function StripeMethodButton({
2990
3062
  // Pulse-skeleton parity with the wallet ECE: no spinner, just the
2991
3063
  // status text on top of the pulsing background. Keeps the loading
2992
3064
  // affordance shape-equivalent across both kinds of tile.
2993
- /* @__PURE__ */ jsx8("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
3065
+ /* @__PURE__ */ jsx7("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
2994
3066
  ) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
2995
3067
  /* @__PURE__ */ jsxs5(
2996
3068
  "span",
@@ -3010,7 +3082,7 @@ function StripeMethodButton({
3010
3082
  // bytes ship as a sibling file, out of the JS bundle. 3:2 box
3011
3083
  // matching the datatrans logos' 120×80 viewBox; each ships its own
3012
3084
  // white rounded-rect background so it stays legible on any tile.
3013
- /* @__PURE__ */ jsx8(
3085
+ /* @__PURE__ */ jsx7(
3014
3086
  "img",
3015
3087
  {
3016
3088
  src: vendoredLogoUrl,
@@ -3030,7 +3102,7 @@ function StripeMethodButton({
3030
3102
  }
3031
3103
  }
3032
3104
  )
3033
- ) : brand?.logoSvg ? /* @__PURE__ */ jsx8(
3105
+ ) : brand?.logoSvg ? /* @__PURE__ */ jsx7(
3034
3106
  "span",
3035
3107
  {
3036
3108
  "aria-hidden": "true",
@@ -3045,11 +3117,11 @@ function StripeMethodButton({
3045
3117
  dangerouslySetInnerHTML: { __html: brand.logoSvg }
3046
3118
  }
3047
3119
  ) : null,
3048
- /* @__PURE__ */ jsx8("span", { children: getStripeMethodDisplayName(method) })
3120
+ /* @__PURE__ */ jsx7("span", { children: getStripeMethodDisplayName(method) })
3049
3121
  ]
3050
3122
  }
3051
3123
  ),
3052
- hasNextStep && /* @__PURE__ */ jsx8(
3124
+ hasNextStep && /* @__PURE__ */ jsx7(
3053
3125
  "svg",
3054
3126
  {
3055
3127
  width: "14",
@@ -3069,7 +3141,7 @@ function StripeMethodButton({
3069
3141
  opacity: 0.7
3070
3142
  },
3071
3143
  "aria-hidden": "true",
3072
- children: /* @__PURE__ */ jsx8("path", { d: "M9 18l6-6-6-6" })
3144
+ children: /* @__PURE__ */ jsx7("path", { d: "M9 18l6-6-6-6" })
3073
3145
  }
3074
3146
  )
3075
3147
  ] })
@@ -3099,10 +3171,9 @@ function StripeMethodInlineForm({
3099
3171
  const stripe = useStripeRaw();
3100
3172
  const elements = useStripeElements();
3101
3173
  const [submitting, setSubmitting] = useState3(false);
3102
- const submittingRef = useRef5(false);
3174
+ const submittingRef = useRef4(false);
3103
3175
  const [isMethodComplete, setIsMethodComplete] = useState3(false);
3104
3176
  const [loadState, setLoadState] = useState3("loading");
3105
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3106
3177
  const handlePay = useCallback3(async () => {
3107
3178
  if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
3108
3179
  submittingRef.current = true;
@@ -3133,26 +3204,22 @@ function StripeMethodInlineForm({
3133
3204
  if (!effectiveSessionId || !effectiveEmail) {
3134
3205
  throw new Error("Missing sessionId or email for payment.");
3135
3206
  }
3136
- const intentHeaders = intentRequestHeaders(effectiveNonce);
3137
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3138
- method: "POST",
3139
- headers: intentHeaders,
3140
- body: JSON.stringify({
3141
- sessionId: effectiveSessionId,
3142
- email: effectiveEmail,
3143
- paymentMethodType: paymentMethod.type || method,
3144
- isPaypal: false
3145
- })
3146
- });
3147
- if (!intentResponse.ok) {
3148
- const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
3149
- onErrorChange?.(intentError.message);
3150
- onDecline?.(buildDeclineEvent("card", intentError));
3151
- return;
3207
+ const paymentMethodType = paymentMethod.type || method;
3208
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
3209
+ effectiveSessionId,
3210
+ effectiveNonce ?? "",
3211
+ {
3212
+ provider: "stripe",
3213
+ paymentMethodCategory: "apm",
3214
+ paymentMethodType,
3215
+ paymentMethodId: null,
3216
+ intentKind: "payment"
3217
+ }
3218
+ );
3219
+ if (intent.provider !== "stripe") {
3220
+ throw new Error("Invalid provider returned for APM intent");
3152
3221
  }
3153
- const intentJson = await intentResponse.json();
3154
- const intentClientSecret = intentJson.data?.id;
3155
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
3222
+ const intentClientSecret = intent.clientSecret;
3156
3223
  if (typeof window !== "undefined") {
3157
3224
  try {
3158
3225
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3185,6 +3252,17 @@ function StripeMethodInlineForm({
3185
3252
  const message = confirmError.message ?? "Payment failed.";
3186
3253
  onErrorChange?.(message);
3187
3254
  onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
3255
+ await reportNonCardDecline({
3256
+ billingApiUrl,
3257
+ sessionId: effectiveSessionId,
3258
+ nonce: effectiveNonce,
3259
+ request: {
3260
+ provider: "stripe",
3261
+ paymentMethodCategory: "apm",
3262
+ paymentMethodType: method,
3263
+ providerDeclineReason: confirmError.code ?? "provider_declined"
3264
+ }
3265
+ });
3188
3266
  return;
3189
3267
  }
3190
3268
  const piStatus = paymentIntent?.status;
@@ -3234,7 +3312,7 @@ function StripeMethodInlineForm({
3234
3312
  sessionId,
3235
3313
  nonce,
3236
3314
  email,
3237
- baseUrl,
3315
+ billingApiUrl,
3238
3316
  method,
3239
3317
  onTokenizedBody,
3240
3318
  onErrorChange,
@@ -3242,8 +3320,8 @@ function StripeMethodInlineForm({
3242
3320
  ]);
3243
3321
  void onCancel;
3244
3322
  return /* @__PURE__ */ jsxs5("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
3245
- /* @__PURE__ */ jsx8(
3246
- PaymentElement2,
3323
+ /* @__PURE__ */ jsx7(
3324
+ PaymentElement,
3247
3325
  {
3248
3326
  onReady: () => setLoadState("ready"),
3249
3327
  onLoadError: () => {
@@ -3271,7 +3349,7 @@ function StripeMethodInlineForm({
3271
3349
  }
3272
3350
  }
3273
3351
  ),
3274
- /* @__PURE__ */ jsx8(
3352
+ /* @__PURE__ */ jsx7(
3275
3353
  "button",
3276
3354
  {
3277
3355
  type: "button",
@@ -3298,7 +3376,7 @@ function StripeMethodInlineForm({
3298
3376
  children: submitting ? "Processing\u2026" : `Pay with ${getStripeMethodDisplayName(method)}`
3299
3377
  }
3300
3378
  ),
3301
- errorText && /* @__PURE__ */ jsx8(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
3379
+ errorText && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
3302
3380
  ] });
3303
3381
  }
3304
3382
  function StripePaymentElementInner({
@@ -3325,11 +3403,10 @@ function StripePaymentElementInner({
3325
3403
  onExpandApm,
3326
3404
  expandedApmMethod
3327
3405
  }) {
3328
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3329
3406
  const [expandedMethod, setExpandedMethod] = useState3(null);
3330
3407
  const [submittingMethod, setSubmittingMethod] = useState3(null);
3331
- const submittingRef = useRef5(false);
3332
- useEffect5(() => {
3408
+ const submittingRef = useRef4(false);
3409
+ useEffect4(() => {
3333
3410
  if (paymentElementMethods.length > 0 && stripeInstance) {
3334
3411
  onLoadStateChange?.("ready");
3335
3412
  } else if (!stripeInstance) {
@@ -3356,30 +3433,21 @@ function StripePaymentElementInner({
3356
3433
  if (!effectiveSessionId || !effectiveEmail) {
3357
3434
  throw new Error("Missing sessionId or email for payment.");
3358
3435
  }
3359
- const intentHeaders = intentRequestHeaders(effectiveNonce);
3360
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3361
- method: "POST",
3362
- headers: intentHeaders,
3363
- body: JSON.stringify({
3364
- sessionId: effectiveSessionId,
3365
- email: effectiveEmail,
3366
- // Wire type, not pm_xxx — backend scopes `payment_method_types`
3367
- // to this single method so unrelated account-wide methods
3368
- // (apple_pay without domain verification, …) don't poison PI
3369
- // creation.
3436
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
3437
+ effectiveSessionId,
3438
+ effectiveNonce ?? "",
3439
+ {
3440
+ provider: "stripe",
3441
+ paymentMethodCategory: "apm",
3370
3442
  paymentMethodType: method,
3371
- isPaypal: false
3372
- })
3373
- });
3374
- if (!intentResponse.ok) {
3375
- const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
3376
- onErrorChange?.(intentError.message);
3377
- onDecline?.(buildDeclineEvent("card", intentError));
3378
- return;
3443
+ paymentMethodId: null,
3444
+ intentKind: "payment"
3445
+ }
3446
+ );
3447
+ if (intent.provider !== "stripe") {
3448
+ throw new Error("Invalid provider returned for APM intent");
3379
3449
  }
3380
- const intentJson = await intentResponse.json();
3381
- const intentClientSecret = intentJson.data?.id;
3382
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
3450
+ const intentClientSecret = intent.clientSecret;
3383
3451
  if (typeof window !== "undefined") {
3384
3452
  try {
3385
3453
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3420,6 +3488,17 @@ function StripePaymentElementInner({
3420
3488
  const message = confirmError.message ?? "Payment failed.";
3421
3489
  onErrorChange?.(message);
3422
3490
  onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
3491
+ await reportNonCardDecline({
3492
+ billingApiUrl,
3493
+ sessionId: effectiveSessionId,
3494
+ nonce: effectiveNonce,
3495
+ request: {
3496
+ provider: "stripe",
3497
+ paymentMethodCategory: "apm",
3498
+ paymentMethodType: method,
3499
+ providerDeclineReason: confirmError.code ?? "provider_declined"
3500
+ }
3501
+ });
3423
3502
  return;
3424
3503
  }
3425
3504
  const piStatus = paymentIntent?.status;
@@ -3467,7 +3546,7 @@ function StripePaymentElementInner({
3467
3546
  sessionId,
3468
3547
  nonce,
3469
3548
  email,
3470
- baseUrl,
3549
+ billingApiUrl,
3471
3550
  billingName,
3472
3551
  onTokenizedBody,
3473
3552
  onErrorChange,
@@ -3495,7 +3574,7 @@ function StripePaymentElementInner({
3495
3574
  paymentMethodTypes: [localExpandedMethod]
3496
3575
  };
3497
3576
  }, [localExpandedMethod, paymentElementBaseOptions]);
3498
- return /* @__PURE__ */ jsx8(
3577
+ return /* @__PURE__ */ jsx7(
3499
3578
  "div",
3500
3579
  {
3501
3580
  "data-testid": "flopay-stripe-payment-element-region",
@@ -3504,8 +3583,8 @@ function StripePaymentElementInner({
3504
3583
  const isExpanded = expandedApmMethod === method;
3505
3584
  const isSubmittingThis = submittingMethod === method;
3506
3585
  const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
3507
- return /* @__PURE__ */ jsxs5(React8.Fragment, { children: [
3508
- /* @__PURE__ */ jsx8(
3586
+ return /* @__PURE__ */ jsxs5(React7.Fragment, { children: [
3587
+ /* @__PURE__ */ jsx7(
3509
3588
  StripeMethodButton,
3510
3589
  {
3511
3590
  method,
@@ -3522,12 +3601,12 @@ function StripePaymentElementInner({
3522
3601
  fontFamily: buttonAppearance?.fontFamily
3523
3602
  }
3524
3603
  ),
3525
- !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx8(
3604
+ !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx7(
3526
3605
  StripeElements,
3527
3606
  {
3528
3607
  stripe: stripeInstance,
3529
3608
  options: localInlineElementsOptions,
3530
- children: /* @__PURE__ */ jsx8(
3609
+ children: /* @__PURE__ */ jsx7(
3531
3610
  StripeMethodInlineForm,
3532
3611
  {
3533
3612
  method,
@@ -3568,15 +3647,10 @@ function SplitCardFormInner({
3568
3647
  firstName,
3569
3648
  lastName,
3570
3649
  chv,
3571
- submitLabel = "CONFIRM PAYMENT",
3572
3650
  className,
3573
- children,
3574
3651
  isProcessing: externalProcessing,
3575
3652
  error: externalError,
3576
3653
  onErrorChange,
3577
- onFullNameChange,
3578
- onFirstNameChange,
3579
- onLastNameChange,
3580
3654
  showPayPal = true,
3581
3655
  showStripe = true,
3582
3656
  enabledPaymentMethods,
@@ -3615,17 +3689,14 @@ function SplitCardFormInner({
3615
3689
  directPaypal,
3616
3690
  isSubscription = false,
3617
3691
  session,
3618
- debug = false,
3619
- innerRef
3692
+ debug = false
3620
3693
  }) {
3621
3694
  const flopay = useFloPay();
3622
3695
  const paypalFlopay = usePayPalFloPay();
3623
- const elements = useElements();
3624
- const checkout = useContext3(CheckoutContext);
3696
+ const checkout = useContext2(CheckoutContext);
3625
3697
  const contextBillingUrl = useBillingApiUrl();
3626
3698
  const [processing, setProcessing] = useState3(false);
3627
3699
  const [error, setError] = useState3(null);
3628
- const [is3DSActive, setIs3DSActive] = useState3(false);
3629
3700
  const [selectedCountry, setSelectedCountry] = useState3(countryProp ?? "US");
3630
3701
  const [zipCode, setZipCode] = useState3(zipProp ?? "");
3631
3702
  const [addressLine1, setAddressLine1] = useState3(addressLine1Prop ?? "");
@@ -3633,18 +3704,19 @@ function SplitCardFormInner({
3633
3704
  const [city, setCity] = useState3(cityProp ?? "");
3634
3705
  const [stateValue, setStateValue] = useState3(stateProp ?? "");
3635
3706
  const [accountPatch, setAccountPatch] = useState3({});
3636
- const zipCodeRef = useRef5(zipProp ?? "");
3637
- const selectedCountryRef = useRef5(countryProp ?? "US");
3638
- const addressLine1Ref = useRef5(addressLine1Prop ?? "");
3639
- const addressLine2Ref = useRef5(addressLine2Prop ?? "");
3640
- const cityRef = useRef5(cityProp ?? "");
3641
- const stateRef = useRef5(stateProp ?? "");
3707
+ const zipCodeRef = useRef4(zipProp ?? "");
3708
+ const selectedCountryRef = useRef4(countryProp ?? "US");
3709
+ const addressLine1Ref = useRef4(addressLine1Prop ?? "");
3710
+ const addressLine2Ref = useRef4(addressLine2Prop ?? "");
3711
+ const cityRef = useRef4(cityProp ?? "");
3712
+ const stateRef = useRef4(stateProp ?? "");
3642
3713
  const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
3643
3714
  const enableAVS = avsConfig !== null;
3644
3715
  const vaultBlockReady = Boolean(session?.vault?.html);
3645
3716
  const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
3717
+ const vaultCapabilityAvailable = vaultBlockReady || vaultGatewayAdvertised;
3646
3718
  const vaultActive = Boolean(
3647
- showStripe && (vaultBlockReady || vaultGatewayAdvertised) && flopay && sessionId
3719
+ showStripe && vaultCapabilityAvailable && flopay && sessionId
3648
3720
  );
3649
3721
  const cardCapture = useMemo3(() => {
3650
3722
  if (!flopay || !vaultActive || !sessionId) return null;
@@ -3676,7 +3748,9 @@ function SplitCardFormInner({
3676
3748
  const [vaultMount, setVaultMount] = useState3(
3677
3749
  () => toVaultMount(session?.vault)
3678
3750
  );
3679
- const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
3751
+ const [viewState, setViewState] = useState3(
3752
+ initialCardOpen && vaultCapabilityAvailable ? "card" : "buttons"
3753
+ );
3680
3754
  const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
3681
3755
  const showCardForm = viewState === "expanding" || viewState === "card";
3682
3756
  const TRANSITION_MS = 280;
@@ -3700,18 +3774,16 @@ function SplitCardFormInner({
3700
3774
  setExpandedApmMethod(null);
3701
3775
  }, TRANSITION_MS);
3702
3776
  }, []);
3703
- useEffect5(() => {
3704
- if (layout === "buttons" && initialCardOpen) {
3777
+ useEffect4(() => {
3778
+ if (layout === "buttons" && initialCardOpen && vaultCapabilityAvailable) {
3705
3779
  setViewState("card");
3706
3780
  }
3707
- }, [layout, initialCardOpen]);
3708
- const [fullName, setFullName] = useState3("");
3709
- const [formReady, setFormReady] = useState3(false);
3781
+ }, [layout, initialCardOpen, vaultCapabilityAvailable]);
3710
3782
  const [overlayStatus, setOverlayStatus] = useState3(null);
3711
- const processingRef = useRef5(false);
3783
+ const processingRef = useRef4(false);
3712
3784
  const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
3713
- const paypalDirectRetryRef = useRef5(paypalDirectRetry);
3714
- useEffect5(() => {
3785
+ const paypalDirectRetryRef = useRef4(paypalDirectRetry);
3786
+ useEffect4(() => {
3715
3787
  paypalDirectRetryRef.current = paypalDirectRetry;
3716
3788
  }, [paypalDirectRetry]);
3717
3789
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
@@ -3759,7 +3831,6 @@ function SplitCardFormInner({
3759
3831
  }, [appearance, bStyles, layout]);
3760
3832
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
3761
3833
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
3762
- const isSelfContained = !onTokenizedBody;
3763
3834
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
3764
3835
  const resolvedAccount = useMemo3(() => mergeAccountPatch({
3765
3836
  userId,
@@ -3769,6 +3840,7 @@ function SplitCardFormInner({
3769
3840
  country: countryProp,
3770
3841
  zip: zipProp
3771
3842
  }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
3843
+ const resolvedBillingName = [resolvedAccount.firstName, resolvedAccount.lastName].filter((part) => Boolean(part?.trim())).join(" ");
3772
3844
  const stripeInstance = useMemo3(() => {
3773
3845
  if (!flopay) return null;
3774
3846
  return flopay.getRawProvider();
@@ -3825,11 +3897,11 @@ function SplitCardFormInner({
3825
3897
  },
3826
3898
  [onErrorChange]
3827
3899
  );
3828
- useEffect5(() => {
3900
+ useEffect4(() => {
3829
3901
  if (!vaultActive || !cardCapture) return;
3830
3902
  cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
3831
3903
  }, [vaultActive, cardCapture, cardFieldOrder, avsConfig]);
3832
- useEffect5(() => {
3904
+ useEffect4(() => {
3833
3905
  if (viewState === "expanding" || viewState === "collapsing" || viewState === "apm-expanding" || viewState === "apm-collapsing") {
3834
3906
  updateError(null);
3835
3907
  }
@@ -3864,7 +3936,7 @@ function SplitCardFormInner({
3864
3936
  },
3865
3937
  [layout, onError, updateError]
3866
3938
  );
3867
- useEffect5(() => {
3939
+ useEffect4(() => {
3868
3940
  if (!vaultActive || !sessionId) {
3869
3941
  setVaultMount(null);
3870
3942
  return;
@@ -3902,13 +3974,12 @@ function SplitCardFormInner({
3902
3974
  nonce,
3903
3975
  updateError
3904
3976
  ]);
3905
- const vaultOutcomeRef = useRef5({
3977
+ const vaultOutcomeRef = useRef4({
3906
3978
  onComplete,
3907
3979
  onError,
3908
3980
  updateError,
3909
3981
  emitDecline,
3910
3982
  resolvedAccount,
3911
- fullName,
3912
3983
  avsConfig,
3913
3984
  avsCheckProp,
3914
3985
  sessionId,
@@ -3921,16 +3992,15 @@ function SplitCardFormInner({
3921
3992
  updateError,
3922
3993
  emitDecline,
3923
3994
  resolvedAccount,
3924
- fullName,
3925
3995
  avsConfig,
3926
3996
  avsCheckProp,
3927
3997
  sessionId,
3928
3998
  nonce,
3929
3999
  baseUrl
3930
4000
  };
3931
- const vaultCompletedRef = useRef5(false);
4001
+ const vaultCompletedRef = useRef4(false);
3932
4002
  const buildVaultAccountSnapshot = useCallback3(() => {
3933
- const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
4003
+ const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
3934
4004
  const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
3935
4005
  const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
3936
4006
  const line1Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_1, cc) : false;
@@ -3943,8 +4013,8 @@ function SplitCardFormInner({
3943
4013
  accountData: {
3944
4014
  userId: resolvedAccount2.userId ?? "",
3945
4015
  email: resolvedAccount2.email ?? "",
3946
- firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
3947
- lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
4016
+ firstName: resolvedAccount2.firstName ?? "",
4017
+ lastName: resolvedAccount2.lastName ?? "",
3948
4018
  ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
3949
4019
  ...cityVisible && cityRef.current ? { city: cityRef.current } : {},
3950
4020
  ...stateValue2 ? { state: stateValue2 } : {},
@@ -3965,11 +4035,11 @@ function SplitCardFormInner({
3965
4035
  } : {}
3966
4036
  };
3967
4037
  }, []);
3968
- useEffect5(() => {
4038
+ useEffect4(() => {
3969
4039
  if (!vaultActive || !cardCapture) return;
3970
4040
  cardCapture.setSubmitGate?.(avsInvalid);
3971
4041
  }, [vaultActive, cardCapture, avsInvalid]);
3972
- useEffect5(() => {
4042
+ useEffect4(() => {
3973
4043
  if (!vaultActive || !cardCapture) return;
3974
4044
  vaultCompletedRef.current = false;
3975
4045
  let cancelled = false;
@@ -4072,21 +4142,25 @@ function SplitCardFormInner({
4072
4142
  const [walletLoadState, setWalletLoadState] = useState3("loading");
4073
4143
  const [paymentElementLoadState, setPaymentElementLoadState] = useState3("loading");
4074
4144
  const [directPaypalReady, setDirectPaypalReady] = useState3(false);
4145
+ const [directPaypalInitializationState, setDirectPaypalInitializationState] = useState3("loading");
4075
4146
  const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
4076
- useEffect5(() => {
4147
+ useEffect4(() => {
4077
4148
  setInAppBrowserDetected(isInAppBrowser());
4078
4149
  }, []);
4079
4150
  const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
4080
4151
  const shouldShowWallets = showStripe && showWallets;
4081
4152
  const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
4082
- const shouldRenderStripePayPal = shouldShowPayPal && !directPaypalConfigured && !!paypalStripeInstance;
4153
+ const directPaypalUnavailable = directPaypalInitializationState === "retrying" || directPaypalInitializationState === "exhausted";
4154
+ const shouldRenderStripePayPal = shouldShowPayPal && !!paypalStripeInstance && (!directPaypalConfigured || directPaypalUnavailable);
4083
4155
  const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
4084
4156
  const shouldRenderPaymentElement = showStripe && hasEnabledMethods && paymentElementMethodsForCurrency.length > 0 && !!stripeInstance;
4085
- const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4157
+ const shouldDisplayPayPalRow = shouldRenderDirectPayPal && !directPaypalUnavailable ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4086
4158
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
4087
4159
  const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
4088
- const validationFiredRef = useRef5(false);
4089
- useEffect5(() => {
4160
+ const expressSurfaceUsable = (state) => state === "loading" || state === "ready";
4161
+ const hasUsableNonCardCapability = shouldRenderDirectPayPal || shouldRenderStripePayPal && expressSurfaceUsable(paypalLoadState) || shouldRenderWallets && expressSurfaceUsable(walletLoadState) || shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
4162
+ const validationFiredRef = useRef4(false);
4163
+ useEffect4(() => {
4090
4164
  if (validationFiredRef.current) return;
4091
4165
  if (!showStripe && !showPayPal) {
4092
4166
  validationFiredRef.current = true;
@@ -4104,10 +4178,29 @@ function SplitCardFormInner({
4104
4178
  );
4105
4179
  onError?.(err);
4106
4180
  updateError(err.message);
4181
+ } else if (inAppBrowserDetected !== void 0 && !vaultCapabilityAvailable && !hasUsableNonCardCapability) {
4182
+ validationFiredRef.current = true;
4183
+ const err = new FloPayError3(
4184
+ "FloPay: this checkout session has no vault card capability and no usable supported non-card payment methods. Upgrade the billing backend integration.",
4185
+ "api_error",
4186
+ { code: "UnsupportedBackendVaultCapability" }
4187
+ );
4188
+ onError?.(err);
4189
+ updateError(err.message);
4107
4190
  }
4108
- }, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
4109
- const deprecationLoggedRef = useRef5(false);
4110
- useEffect5(() => {
4191
+ }, [
4192
+ showStripe,
4193
+ showPayPal,
4194
+ directPaypalConfigured,
4195
+ paypalStripeInstance,
4196
+ inAppBrowserDetected,
4197
+ vaultCapabilityAvailable,
4198
+ hasUsableNonCardCapability,
4199
+ onError,
4200
+ updateError
4201
+ ]);
4202
+ const deprecationLoggedRef = useRef4(false);
4203
+ useEffect4(() => {
4111
4204
  if (deprecationLoggedRef.current) return;
4112
4205
  if (!hasEnabledMethods) return;
4113
4206
  const stale = [];
@@ -4119,13 +4212,6 @@ function SplitCardFormInner({
4119
4212
  `[FloPay] ${stale.join(" / ")} ${stale.length === 1 ? "is" : "are"} deprecated: the Apple Pay / Google Pay surface is now driven by \`gateways.stripe.enabledPaymentMethods\` on the session response. Remove the legacy prop(s) to silence this warning.`
4120
4213
  );
4121
4214
  }, [hasEnabledMethods, showApplePay, showGooglePay]);
4122
- const handleNameChange = useCallback3((value) => {
4123
- setFullName(value);
4124
- onFullNameChange?.(value);
4125
- const parts = value.trim().split(/\s+/);
4126
- onFirstNameChange?.(parts[0] ?? "");
4127
- onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
4128
- }, [onFullNameChange, onFirstNameChange, onLastNameChange]);
4129
4215
  const applyInlineSessionPatch = useCallback3(
4130
4216
  (patch, method) => {
4131
4217
  if (!checkout.applyInlineSessionPatch) {
@@ -4206,8 +4292,8 @@ function SplitCardFormInner({
4206
4292
  accountData: {
4207
4293
  userId: effectiveAccount.userId ?? "",
4208
4294
  email: effectiveAccount.email ?? "",
4209
- firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
4210
- lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
4295
+ firstName: effectiveAccount.firstName ?? "",
4296
+ lastName: effectiveAccount.lastName ?? "",
4211
4297
  ...avsConfig ? (() => {
4212
4298
  const c = selectedCountryRef.current;
4213
4299
  const stateVisible = isAVSFieldVisible(avsConfig.state, c);
@@ -4255,70 +4341,6 @@ function SplitCardFormInner({
4255
4341
  return;
4256
4342
  }
4257
4343
  const json = await response.json().catch(() => null);
4258
- if (json?.type === "3ds_required") {
4259
- const secret = json["threeDSecureToken"];
4260
- if (!flopay || !secret) {
4261
- setOverlayStatus("error");
4262
- updateError("3DS authentication required but no token provided.");
4263
- return;
4264
- }
4265
- setIs3DSActive(true);
4266
- try {
4267
- const retryBillingAddress = {};
4268
- const retryCC = selectedCountryRef.current;
4269
- const retryCountry = enableAVS ? retryCC : effectiveAccount.country;
4270
- if (retryCountry) retryBillingAddress["country"] = retryCountry;
4271
- if (avsConfig && isAVSFieldVisible(avsConfig.postal_code, retryCC) && zipCodeRef.current.trim()) {
4272
- retryBillingAddress["postal_code"] = zipCodeRef.current.trim();
4273
- } else if (!avsConfig && effectiveAccount.zip) {
4274
- retryBillingAddress["postal_code"] = effectiveAccount.zip;
4275
- }
4276
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, retryCC) && addressLine1Ref.current.trim()) retryBillingAddress["line1"] = addressLine1Ref.current.trim();
4277
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_2, retryCC) && addressLine2Ref.current.trim()) retryBillingAddress["line2"] = addressLine2Ref.current.trim();
4278
- if (avsConfig && isAVSFieldVisible(avsConfig.city, retryCC) && cityRef.current.trim()) retryBillingAddress["city"] = cityRef.current.trim();
4279
- if (avsConfig && isAVSFieldVisible(avsConfig.state, retryCC) && stateRef.current.trim()) {
4280
- retryBillingAddress["state"] = stateRef.current.trim();
4281
- } else if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, retryCC) && !isAVSFieldVisible(avsConfig.state, retryCC) && isAVSFieldVisible(avsConfig.postal_code, retryCC)) {
4282
- const derivedRetryState = getStateFromPostalCode(retryCC, zipCodeRef.current.trim());
4283
- if (derivedRetryState) retryBillingAddress["state"] = derivedRetryState;
4284
- }
4285
- const result = await flopay.confirmPayment({
4286
- clientSecret: secret,
4287
- returnUrl: window.location.href,
4288
- billingDetails: {
4289
- ...effectiveAccount.email ? { email: effectiveAccount.email } : {},
4290
- ...fullName.trim() ? { name: fullName.trim() } : {},
4291
- ...Object.keys(retryBillingAddress).length > 0 ? { address: retryBillingAddress } : {}
4292
- }
4293
- });
4294
- if (result.error) {
4295
- setOverlayStatus("error");
4296
- updateError(result.error.message);
4297
- onError?.(result.error);
4298
- emitDecline("card", result.error);
4299
- return;
4300
- }
4301
- if (result.status === "succeeded" || result.status === "processing") {
4302
- processingRef.current = false;
4303
- await processPaymentInternal({
4304
- id: result.paymentIntentId,
4305
- type: "card",
4306
- threeDSecureActionResultTokenId: result.paymentIntentId
4307
- }, {
4308
- // Preserve the session/nonce/account overrides resolved
4309
- // above so the retry talks to the same (possibly
4310
- // freshly bootstrapped) session as the original attempt.
4311
- accountPatch: overrides?.accountPatch,
4312
- sessionId: effectiveSessionId,
4313
- nonce: effectiveNonce,
4314
- completionPaymentMethodId: resolvedCompletionPaymentMethodId
4315
- });
4316
- }
4317
- } finally {
4318
- setIs3DSActive(false);
4319
- }
4320
- return;
4321
- }
4322
4344
  if (json?.type === "paypal_redirect_required") {
4323
4345
  const secret = json["threeDSecureToken"];
4324
4346
  const savedPmId = json["paymentMethodId"] || tokenizedBody.id || "";
@@ -4372,6 +4394,8 @@ function SplitCardFormInner({
4372
4394
  emitDecline("paypal", "paypal_direct_required retry limit exceeded");
4373
4395
  return;
4374
4396
  }
4397
+ setDirectPaypalReady(false);
4398
+ setDirectPaypalInitializationState("loading");
4375
4399
  setPaypalDirectRetry({ orderId, attempts: prevAttempts + 1 });
4376
4400
  setOverlayStatus(null);
4377
4401
  return;
@@ -4394,7 +4418,7 @@ function SplitCardFormInner({
4394
4418
  processingRef.current = false;
4395
4419
  }
4396
4420
  },
4397
- [baseUrl, sessionId, nonce, resolvedAccount, fullName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
4421
+ [baseUrl, sessionId, nonce, resolvedAccount, resolvedBillingName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
4398
4422
  );
4399
4423
  const dispatchTokenizedBody = useCallback3(
4400
4424
  (tokenizedBody, overrides) => {
@@ -4406,35 +4430,8 @@ function SplitCardFormInner({
4406
4430
  },
4407
4431
  [onTokenizedBody, processPaymentInternal]
4408
4432
  );
4409
- useImperativeHandle(innerRef, () => ({
4410
- async handleNextAction(secret) {
4411
- if (!flopay) return;
4412
- setIs3DSActive(true);
4413
- try {
4414
- const result = await flopay.confirmPayment({
4415
- clientSecret: secret,
4416
- returnUrl: window.location.href
4417
- });
4418
- if (result.error) {
4419
- updateError(result.error.message);
4420
- onError?.(result.error);
4421
- emitDecline("card", result.error);
4422
- } else if (result.status === "succeeded" || result.status === "processing") {
4423
- dispatchTokenizedBody({
4424
- id: result.paymentIntentId,
4425
- type: "card",
4426
- threeDSecureActionResultTokenId: result.paymentIntentId
4427
- });
4428
- }
4429
- } catch (err) {
4430
- updateError(err instanceof Error ? err.message : "Payment authentication failed.");
4431
- } finally {
4432
- setIs3DSActive(false);
4433
- }
4434
- }
4435
- }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
4436
- const stripeResumeAttemptedRef = useRef5(false);
4437
- useEffect5(() => {
4433
+ const stripeResumeAttemptedRef = useRef4(false);
4434
+ useEffect4(() => {
4438
4435
  if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
4439
4436
  const params = new URLSearchParams(window.location.search);
4440
4437
  const paymentIntentId = params.get("payment_intent");
@@ -4512,166 +4509,12 @@ function SplitCardFormInner({
4512
4509
  }, persistedOverrides);
4513
4510
  })();
4514
4511
  }, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
4515
- const handleSubmit = useCallback3(
4516
- async (e) => {
4517
- e.preventDefault();
4518
- if (!flopay || !elements || isSubmitting || processingRef.current) return;
4519
- if (vaultActive) return;
4520
- if (layout !== "buttons") {
4521
- onButtonClick?.("card");
4522
- }
4523
- setProcessing(true);
4524
- setOverlayStatus("processing");
4525
- updateError(null);
4526
- let handedOff = false;
4527
- try {
4528
- if (avsConfig) {
4529
- const country = selectedCountryRef.current;
4530
- const postal = computePostalCodeState(
4531
- country,
4532
- zipCodeRef.current,
4533
- isAVSFieldVisible(avsConfig.postal_code, country)
4534
- );
4535
- if (postal.empty) {
4536
- setZipTouched(true);
4537
- updateError(getPostalCodeLabel(country) + " is required");
4538
- return;
4539
- }
4540
- if (postal.malformed) {
4541
- setZipTouched(true);
4542
- updateError(malformedPostcodeMessage(country));
4543
- return;
4544
- }
4545
- if (isAVSFieldVisible(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
4546
- updateError("Street address is required");
4547
- return;
4548
- }
4549
- if (isAVSFieldVisible(avsConfig.city, country) && !cityRef.current.trim()) {
4550
- updateError("City is required");
4551
- return;
4552
- }
4553
- if (isAVSFieldVisible(avsConfig.state, country) && !stateRef.current.trim()) {
4554
- updateError(getStateLabel(country) + " is required");
4555
- return;
4556
- }
4557
- }
4558
- if (!vaultActive) {
4559
- const submitResult = await flopay.submitElements();
4560
- if (submitResult.error) {
4561
- updateError(submitResult.error.message);
4562
- onError?.(submitResult.error);
4563
- return;
4564
- }
4565
- }
4566
- const billingAddress = {};
4567
- const cc = selectedCountryRef.current;
4568
- const avsCountry = enableAVS ? cc : resolvedAccount.country;
4569
- if (avsCountry) billingAddress["country"] = avsCountry;
4570
- if (avsConfig && isAVSFieldVisible(avsConfig.postal_code, cc) && zipCodeRef.current.trim()) {
4571
- billingAddress["postal_code"] = zipCodeRef.current.trim();
4572
- } else if (!avsConfig && resolvedAccount.zip) {
4573
- billingAddress["postal_code"] = resolvedAccount.zip;
4574
- }
4575
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, cc) && addressLine1Ref.current.trim()) billingAddress["line1"] = addressLine1Ref.current.trim();
4576
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_2, cc) && addressLine2Ref.current.trim()) billingAddress["line2"] = addressLine2Ref.current.trim();
4577
- if (avsConfig && isAVSFieldVisible(avsConfig.city, cc) && cityRef.current.trim()) billingAddress["city"] = cityRef.current.trim();
4578
- if (avsConfig && isAVSFieldVisible(avsConfig.state, cc) && stateRef.current.trim()) {
4579
- billingAddress["state"] = stateRef.current.trim();
4580
- } else if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, cc) && !isAVSFieldVisible(avsConfig.state, cc) && isAVSFieldVisible(avsConfig.postal_code, cc)) {
4581
- const derivedSubmitState = getStateFromPostalCode(cc, zipCodeRef.current.trim());
4582
- if (derivedSubmitState) billingAddress["state"] = derivedSubmitState;
4583
- }
4584
- const billingDetails = {
4585
- ...resolvedAccount.email ? { email: resolvedAccount.email } : {},
4586
- ...fullName.trim() ? { name: fullName.trim() } : {},
4587
- ...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
4588
- };
4589
- if (!sessionId || !resolvedAccount.email) {
4590
- throw new FloPayError3("Missing sessionId or email", "validation_error");
4591
- }
4592
- {
4593
- const pmResult = await flopay.createPaymentMethod(billingDetails);
4594
- if (pmResult.error || !pmResult.paymentMethodId) {
4595
- updateError(pmResult.error?.message ?? "Failed to create payment method.");
4596
- return;
4597
- }
4598
- const intentHeaders = intentRequestHeaders(nonce);
4599
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
4600
- method: "POST",
4601
- headers: intentHeaders,
4602
- body: JSON.stringify({
4603
- sessionId,
4604
- email: resolvedAccount.email,
4605
- paymentMethodType: pmResult.paymentMethodId,
4606
- isPaypal: false
4607
- })
4608
- });
4609
- if (!intentResponse.ok) {
4610
- const intentError = await buildFloPayApiErrorFromResponse(
4611
- intentResponse,
4612
- "Failed to create payment intent"
4613
- );
4614
- setOverlayStatus("error");
4615
- updateError(intentError.message);
4616
- onError?.(intentError);
4617
- emitDecline("card", intentError);
4618
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4619
- return;
4620
- }
4621
- const intentJson = await intentResponse.json();
4622
- const intentClientSecret = intentJson.data?.id;
4623
- if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
4624
- const confirmResult = await flopay.confirmCardPayment({
4625
- clientSecret: intentClientSecret,
4626
- paymentMethodId: pmResult.paymentMethodId
4627
- });
4628
- if (confirmResult.error) {
4629
- setOverlayStatus("error");
4630
- updateError(confirmResult.error.message);
4631
- onError?.(confirmResult.error);
4632
- emitDecline("card", confirmResult.error);
4633
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4634
- return;
4635
- }
4636
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
4637
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
4638
- rawProvider,
4639
- intentClientSecret
4640
- );
4641
- const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
4642
- const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
4643
- if (!paymentIntentId) {
4644
- const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
4645
- setOverlayStatus("error");
4646
- updateError(error2.message);
4647
- onError?.(error2);
4648
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4649
- return;
4650
- }
4651
- handedOff = isSelfContained;
4652
- dispatchTokenizedBody({
4653
- id: paymentMethodId,
4654
- type: "card",
4655
- threeDSecureActionResultTokenId: paymentIntentId,
4656
- originalPaymentMethodId: pmResult.paymentMethodId
4657
- });
4658
- }
4659
- } catch (err) {
4660
- setOverlayStatus("error");
4661
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
4662
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4663
- } finally {
4664
- if (!handedOff) {
4665
- setProcessing(false);
4666
- setOverlayStatus(null);
4667
- }
4668
- }
4669
- },
4670
- [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, vaultActive, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
4671
- );
4672
- const isReady = flopay !== null && (vaultActive || elements !== null);
4512
+ const handleSubmit = useCallback3((event) => {
4513
+ event.preventDefault();
4514
+ }, []);
4515
+ const isReady = flopay !== null;
4673
4516
  if (!isReady) {
4674
- return /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
4517
+ return /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
4675
4518
  }
4676
4519
  const isButtons = layout === "buttons";
4677
4520
  const appearanceVars = appearance?.variables;
@@ -4712,25 +4555,10 @@ function SplitCardFormInner({
4712
4555
  "--flopay-input-font-family": resolvedInputFontFamily,
4713
4556
  "--flopay-input-font-weight": String(resolvedInputFontWeight)
4714
4557
  };
4715
- const stripeElementStyle = {
4716
- style: {
4717
- base: {
4718
- ...sharedInputTypography,
4719
- fontSmoothing: "antialiased",
4720
- "::placeholder": {
4721
- color: resolvedPlaceholderColor,
4722
- fontSize: resolvedInputFontSize,
4723
- fontFamily: resolvedInputFontFamily,
4724
- fontWeight: resolvedInputFontWeight
4725
- }
4726
- },
4727
- invalid: { color: "#ef4444" }
4728
- }
4729
- };
4730
4558
  const containerOverrides = bStyles.cardFormContainer ?? {};
4731
4559
  const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
4732
4560
  const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
4733
- const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx8(
4561
+ const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx7(
4734
4562
  VaultCardFields,
4735
4563
  {
4736
4564
  capture: cardCapture,
@@ -4739,7 +4567,6 @@ function SplitCardFormInner({
4739
4567
  expectedOrigin: vaultMount.expectedOrigin,
4740
4568
  theme: vaultThemeColors,
4741
4569
  onReady: () => {
4742
- setFormReady(true);
4743
4570
  if (!avsConfig || typeof document === "undefined") return;
4744
4571
  const focusFirstAvs = () => {
4745
4572
  const block = document.querySelector('[data-testid="flopay-avs-fields"]');
@@ -4768,7 +4595,7 @@ function SplitCardFormInner({
4768
4595
  if (message) setOverlayStatus(null);
4769
4596
  }
4770
4597
  }
4771
- ) : /* @__PURE__ */ jsx8(
4598
+ ) : /* @__PURE__ */ jsx7(
4772
4599
  "div",
4773
4600
  {
4774
4601
  "data-testid": "flopay-vault-loading",
@@ -4796,7 +4623,7 @@ function SplitCardFormInner({
4796
4623
  display: "flex",
4797
4624
  flexDirection: "column"
4798
4625
  }, children: [
4799
- /* @__PURE__ */ jsx8("style", { children: `
4626
+ /* @__PURE__ */ jsx7("style", { children: `
4800
4627
  .flopay-shared-input::placeholder {
4801
4628
  color: var(--flopay-input-placeholder-color);
4802
4629
  opacity: 1;
@@ -4834,7 +4661,7 @@ function SplitCardFormInner({
4834
4661
  },
4835
4662
  "aria-label": "Back to payment methods",
4836
4663
  children: [
4837
- /* @__PURE__ */ jsx8("span", { style: {
4664
+ /* @__PURE__ */ jsx7("span", { style: {
4838
4665
  display: "inline-flex",
4839
4666
  alignItems: "center",
4840
4667
  justifyContent: "center",
@@ -4844,12 +4671,12 @@ function SplitCardFormInner({
4844
4671
  backgroundColor: "#f3f4f6",
4845
4672
  transition: "background-color 0.15s",
4846
4673
  ...bStyles.backButtonIcon
4847
- }, children: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "M15 18l-6-6 6-6" }) }) }),
4848
- /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
4674
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
4675
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4849
4676
  ]
4850
4677
  }
4851
4678
  ),
4852
- hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
4679
+ hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
4853
4680
  flex: 1,
4854
4681
  textAlign: "center",
4855
4682
  fontWeight: 600,
@@ -4857,9 +4684,9 @@ function SplitCardFormInner({
4857
4684
  color: "#262833",
4858
4685
  paddingRight: 80,
4859
4686
  ...bStyles.title
4860
- }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
4687
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
4861
4688
  ] }),
4862
- !isButtons && !hideTitle && /* @__PURE__ */ jsx8("div", { style: {
4689
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx7("div", { style: {
4863
4690
  textAlign: "center",
4864
4691
  fontWeight: 600,
4865
4692
  fontSize: "1.1rem",
@@ -4869,70 +4696,8 @@ function SplitCardFormInner({
4869
4696
  // (-2) and AVS block (-1) are ordered below it but above the card form.
4870
4697
  order: vaultActive ? -3 : 0,
4871
4698
  ...bStyles.title
4872
- }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) }),
4873
- !vaultActive && /* @__PURE__ */ jsxs5(Fragment2, { children: [
4874
- /* @__PURE__ */ jsx8("div", { style: {
4875
- backgroundColor: cardInputBg,
4876
- border: `1px solid ${resolvedBorder}`,
4877
- borderTopLeftRadius: resolvedBorderRadius,
4878
- borderTopRightRadius: resolvedBorderRadius,
4879
- padding: "10px"
4880
- }, children: /* @__PURE__ */ jsx8(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
4881
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex" }, children: [
4882
- /* @__PURE__ */ jsx8("div", { style: {
4883
- flex: 1,
4884
- backgroundColor: cardInputBg,
4885
- // Longhand only — mixing `border` shorthand with per-side
4886
- // overrides triggers React's "shorthand vs longhand" warning
4887
- // because render order isn't deterministic.
4888
- borderTop: "none",
4889
- borderRight: "none",
4890
- borderBottom: `1px solid ${resolvedBorder}`,
4891
- borderLeft: `1px solid ${resolvedBorder}`,
4892
- borderBottomLeftRadius: resolvedBorderRadius,
4893
- padding: "10px"
4894
- }, children: /* @__PURE__ */ jsx8(CardExpiryElement, { options: stripeElementStyle }) }),
4895
- /* @__PURE__ */ jsx8("div", { style: {
4896
- flex: 1,
4897
- backgroundColor: cardInputBg,
4898
- borderTop: "none",
4899
- borderRight: `1px solid ${resolvedBorder}`,
4900
- borderBottom: `1px solid ${resolvedBorder}`,
4901
- borderLeft: `1px solid ${resolvedBorder}`,
4902
- borderBottomRightRadius: resolvedBorderRadius,
4903
- padding: "10px"
4904
- }, children: /* @__PURE__ */ jsx8(CardCvcElement, { options: stripeElementStyle }) })
4905
- ] })
4906
- ] }),
4907
- !vaultActive && /* @__PURE__ */ jsx8("div", { style: {
4908
- backgroundColor: cardInputBg,
4909
- border: `1px solid ${resolvedBorder}`,
4910
- borderRadius: resolvedBorderRadius,
4911
- marginTop: "0.5rem",
4912
- padding: "10px"
4913
- }, children: /* @__PURE__ */ jsx8(
4914
- "input",
4915
- {
4916
- className: "flopay-shared-input",
4917
- id: "flopay-cc-name",
4918
- name: "cc-name",
4919
- placeholder: "Full Name on Card",
4920
- autoComplete: "cc-name",
4921
- value: fullName,
4922
- onChange: (e) => handleNameChange(e.target.value),
4923
- disabled: isSubmitting,
4924
- required: true,
4925
- style: {
4926
- width: "100%",
4927
- border: "none",
4928
- outline: "none",
4929
- background: "transparent",
4930
- ...sharedInputTypography,
4931
- ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
4932
- }
4933
- }
4934
- ) }),
4935
- avsConfig && /* @__PURE__ */ jsx8("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
4699
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) }),
4700
+ avsConfig && /* @__PURE__ */ jsx7("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
4936
4701
  const cc = selectedCountry;
4937
4702
  const inputWrapStyle = (invalid = false) => ({
4938
4703
  backgroundColor: cardInputBg,
@@ -4950,7 +4715,7 @@ function SplitCardFormInner({
4950
4715
  });
4951
4716
  const stateOpts = getStateOptions(cc);
4952
4717
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4953
- isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx8("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx8(
4718
+ isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx7(
4954
4719
  "input",
4955
4720
  {
4956
4721
  className: "flopay-shared-input",
@@ -4969,7 +4734,7 @@ function SplitCardFormInner({
4969
4734
  style: inputFieldStyle()
4970
4735
  }
4971
4736
  ) }),
4972
- isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx8("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx8(
4737
+ isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx7(
4973
4738
  "input",
4974
4739
  {
4975
4740
  className: "flopay-shared-input",
@@ -4992,7 +4757,7 @@ function SplitCardFormInner({
4992
4757
  gap: "0",
4993
4758
  marginTop: "0.5rem"
4994
4759
  }, children: [
4995
- isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx8("div", { style: {
4760
+ isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx7("div", { style: {
4996
4761
  flex: 1,
4997
4762
  backgroundColor: cardInputBg,
4998
4763
  borderTop: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
@@ -5003,7 +4768,7 @@ function SplitCardFormInner({
5003
4768
  borderTopLeftRadius: resolvedBorderRadius,
5004
4769
  borderBottomLeftRadius: resolvedBorderRadius,
5005
4770
  ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: resolvedBorderRadius }
5006
- }, children: /* @__PURE__ */ jsx8(
4771
+ }, children: /* @__PURE__ */ jsx7(
5007
4772
  "input",
5008
4773
  {
5009
4774
  className: "flopay-shared-input",
@@ -5022,7 +4787,7 @@ function SplitCardFormInner({
5022
4787
  style: inputFieldStyle()
5023
4788
  }
5024
4789
  ) }),
5025
- isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx8("div", { style: {
4790
+ isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx7("div", { style: {
5026
4791
  flex: 1,
5027
4792
  backgroundColor: cardInputBg,
5028
4793
  border: `1px solid ${avsBorderColor(invalidAvsFields.state)}`,
@@ -5046,11 +4811,11 @@ function SplitCardFormInner({
5046
4811
  "data-testid": "flopay-state",
5047
4812
  style: { ...inputFieldStyle(), cursor: "pointer" },
5048
4813
  children: [
5049
- /* @__PURE__ */ jsx8("option", { value: "", children: getStateLabel(cc) }),
5050
- stateOpts.map((s) => /* @__PURE__ */ jsx8("option", { value: s.code, children: s.name }, s.code))
4814
+ /* @__PURE__ */ jsx7("option", { value: "", children: getStateLabel(cc) }),
4815
+ stateOpts.map((s) => /* @__PURE__ */ jsx7("option", { value: s.code, children: s.name }, s.code))
5051
4816
  ]
5052
4817
  }
5053
- ) : /* @__PURE__ */ jsx8(
4818
+ ) : /* @__PURE__ */ jsx7(
5054
4819
  "input",
5055
4820
  {
5056
4821
  className: "flopay-shared-input",
@@ -5076,7 +4841,7 @@ function SplitCardFormInner({
5076
4841
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
5077
4842
  marginTop: "0.5rem"
5078
4843
  }, children: [
5079
- isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx8("div", { style: {
4844
+ isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx7("div", { style: {
5080
4845
  flex: avsLayoutProp === "row" ? 1 : void 0,
5081
4846
  backgroundColor: cardInputBg,
5082
4847
  borderTop: `1px solid ${resolvedBorder}`,
@@ -5085,7 +4850,7 @@ function SplitCardFormInner({
5085
4850
  borderLeft: `1px solid ${resolvedBorder}`,
5086
4851
  padding: "10px",
5087
4852
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: resolvedBorderRadius, borderBottomLeftRadius: resolvedBorderRadius, borderRight: "none" } : { borderRadius: resolvedBorderRadius }
5088
- }, children: /* @__PURE__ */ jsx8(
4853
+ }, children: /* @__PURE__ */ jsx7(
5089
4854
  "select",
5090
4855
  {
5091
4856
  id: "flopay-billing-country",
@@ -5109,13 +4874,13 @@ function SplitCardFormInner({
5109
4874
  ] }, c.code))
5110
4875
  }
5111
4876
  ) }),
5112
- isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx8("div", { style: {
4877
+ isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx7("div", { style: {
5113
4878
  flex: avsLayoutProp === "row" ? 1 : void 0,
5114
4879
  backgroundColor: cardInputBg,
5115
4880
  border: `1px solid ${zipBorderColor}`,
5116
4881
  padding: "10px",
5117
4882
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
5118
- }, children: /* @__PURE__ */ jsx8(
4883
+ }, children: /* @__PURE__ */ jsx7(
5119
4884
  "input",
5120
4885
  {
5121
4886
  className: "flopay-shared-input",
@@ -5139,7 +4904,7 @@ function SplitCardFormInner({
5139
4904
  }
5140
4905
  ) })
5141
4906
  ] }),
5142
- showPostcodeError && /* @__PURE__ */ jsx8(
4907
+ showPostcodeError && /* @__PURE__ */ jsx7(
5143
4908
  "div",
5144
4909
  {
5145
4910
  id: "flopay-billing-postal-code-error",
@@ -5156,39 +4921,9 @@ function SplitCardFormInner({
5156
4921
  )
5157
4922
  ] });
5158
4923
  })() }),
5159
- vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx8("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
4924
+ vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
5160
4925
  vaultActive && vaultCardFieldsNode,
5161
- displayError && /* @__PURE__ */ jsx8(ErrorBanner, { margin: "0.75rem 0", styleOverride: bStyles.errorBanner, children: displayError }),
5162
- !vaultActive && (children ?? /* @__PURE__ */ jsx8(
5163
- "button",
5164
- {
5165
- type: "submit",
5166
- disabled: !formReady || isSubmitting,
5167
- "data-testid": "flopay-submit",
5168
- onMouseEnter: (e) => {
5169
- if (formReady && !isSubmitting) e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
5170
- },
5171
- onMouseLeave: (e) => {
5172
- e.currentTarget.style.backgroundColor = bStyles.submitButton?.backgroundColor ?? resolvedPrimaryColor;
5173
- },
5174
- style: {
5175
- width: "100%",
5176
- padding: "0.875rem",
5177
- marginTop: "1rem",
5178
- backgroundColor: resolvedPrimaryColor,
5179
- color: "white",
5180
- border: "none",
5181
- borderRadius: resolvedBorderRadius,
5182
- fontSize: bStyles.submitButtonFontSize ?? "1rem",
5183
- fontWeight: 600,
5184
- cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
5185
- opacity: !formReady || isSubmitting ? 0.5 : 1,
5186
- transition: "background-color 0.15s",
5187
- ...bStyles.submitButton
5188
- },
5189
- children: isSubmitting ? "PROCESSING..." : submitLabel
5190
- }
5191
- ))
4926
+ displayError && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5192
4927
  ] });
5193
4928
  const gateDebugStyle = {
5194
4929
  margin: 0,
@@ -5204,9 +4939,9 @@ function SplitCardFormInner({
5204
4939
  const renderDirectPaypalGateDebug = (testId) => {
5205
4940
  if (!debug) return null;
5206
4941
  if (!showPayPal || !directPaypalConfigured) {
5207
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
4942
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
5208
4943
  }
5209
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4944
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
5210
4945
  "FloPay/DirectPayPal-debug (parent gate)",
5211
4946
  ` showPayPal=${showPayPal}`,
5212
4947
  ` directPaypalConfigured=${directPaypalConfigured}`,
@@ -5219,9 +4954,9 @@ function SplitCardFormInner({
5219
4954
  const renderStripeGateDebug = (testId) => {
5220
4955
  if (!debug) return null;
5221
4956
  if (!showStripe || !stripeInstance) {
5222
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
4957
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
5223
4958
  }
5224
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4959
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
5225
4960
  "FloPay/Stripe-debug (parent gate)",
5226
4961
  ` showStripe=${showStripe}`,
5227
4962
  ` currency=${currency}`,
@@ -5251,8 +4986,8 @@ function SplitCardFormInner({
5251
4986
  const buttonsAnim = viewState === "expanding" || viewState === "apm-expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" || viewState === "apm-collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
5252
4987
  const cardAnim = viewState === "expanding" ? `flopay-card-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-card-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : void 0;
5253
4988
  return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5254
- /* @__PURE__ */ jsx8(FloPayKeyframes, {}),
5255
- overlayStatus && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4989
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4990
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5256
4991
  /* @__PURE__ */ jsxs5("div", { style: { display: "grid" }, children: [
5257
4992
  /* @__PURE__ */ jsxs5(
5258
4993
  "div",
@@ -5272,7 +5007,7 @@ function SplitCardFormInner({
5272
5007
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
5273
5008
  renderStripeGateDebug("flopay-stripe-gate-debug"),
5274
5009
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5275
- paypalDirectRetry && /* @__PURE__ */ jsx8(
5010
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
5276
5011
  "div",
5277
5012
  {
5278
5013
  "data-testid": "flopay-paypal-direct-retry-notice",
@@ -5288,7 +5023,7 @@ function SplitCardFormInner({
5288
5023
  children: "Please confirm your PayPal payment to complete checkout."
5289
5024
  }
5290
5025
  ),
5291
- /* @__PURE__ */ jsx8(
5026
+ /* @__PURE__ */ jsx7(
5292
5027
  DirectPayPalButton,
5293
5028
  {
5294
5029
  sessionId,
@@ -5308,6 +5043,7 @@ function SplitCardFormInner({
5308
5043
  runBeforeButtonClick,
5309
5044
  isProcessing: isSubmitting,
5310
5045
  onLoadStateChange: setDirectPaypalReady,
5046
+ onInitializationStateChange: setDirectPaypalInitializationState,
5311
5047
  session: session ?? null,
5312
5048
  existingOrderId: paypalDirectRetry?.orderId,
5313
5049
  debug
@@ -5315,7 +5051,7 @@ function SplitCardFormInner({
5315
5051
  paypalDirectRetry?.orderId ?? "fresh"
5316
5052
  )
5317
5053
  ] }),
5318
- shouldRenderStripePayPal && /* @__PURE__ */ jsx8(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx8(
5054
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
5319
5055
  PayPalButtonInner,
5320
5056
  {
5321
5057
  sessionId,
@@ -5333,13 +5069,14 @@ function SplitCardFormInner({
5333
5069
  placeholderBorderRadius: buttonBorderRadius
5334
5070
  }
5335
5071
  ) }),
5336
- shouldRenderWallets ? /* @__PURE__ */ jsx8(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx8(
5072
+ shouldRenderWallets ? /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
5337
5073
  WalletButtonInner,
5338
5074
  {
5339
5075
  sessionId,
5340
5076
  nonce,
5341
5077
  email: resolvedAccount.email,
5342
5078
  billingApiUrl: resolvedBillingApiUrl,
5079
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5343
5080
  expressMethods: walletExpressMethods,
5344
5081
  onTokenizedBody: dispatchTokenizedBody,
5345
5082
  onErrorChange: updateError,
@@ -5350,8 +5087,8 @@ function SplitCardFormInner({
5350
5087
  onLoadStateChange: setWalletLoadState,
5351
5088
  placeholderBorderRadius: buttonBorderRadius
5352
5089
  }
5353
- ) }) : shouldShowWallets ? /* @__PURE__ */ jsx8("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: buttonBorderRadius, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
5354
- shouldRenderPaymentElement && /* @__PURE__ */ jsx8(
5090
+ ) }) : shouldShowWallets ? /* @__PURE__ */ jsx7("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: buttonBorderRadius, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
5091
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
5355
5092
  StripePaymentElementInner,
5356
5093
  {
5357
5094
  sessionId,
@@ -5383,7 +5120,7 @@ function SplitCardFormInner({
5383
5120
  }
5384
5121
  }
5385
5122
  ),
5386
- showStripe && /* @__PURE__ */ jsx8(
5123
+ vaultActive && /* @__PURE__ */ jsx7(
5387
5124
  "button",
5388
5125
  {
5389
5126
  type: "button",
@@ -5439,19 +5176,19 @@ function SplitCardFormInner({
5439
5176
  onMouseUp: (e) => {
5440
5177
  e.currentTarget.style.transform = "scale(1)";
5441
5178
  },
5442
- children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
5179
+ children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
5443
5180
  }
5444
5181
  ),
5445
- displayError && viewState === "buttons" && /* @__PURE__ */ jsx8(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5182
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5446
5183
  ]
5447
5184
  }
5448
5185
  ),
5449
- showStripe && isCardView && /* @__PURE__ */ jsx8("div", { style: {
5186
+ vaultActive && isCardView && /* @__PURE__ */ jsx7("div", { style: {
5450
5187
  gridArea: "1 / 1",
5451
5188
  ...cardAnim ? { animation: cardAnim } : {},
5452
5189
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
5453
5190
  }, children: cardFormBlock }),
5454
- showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx8("div", { style: {
5191
+ showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx7("div", { style: {
5455
5192
  gridArea: "1 / 1",
5456
5193
  ...apmAnim ? { animation: apmAnim } : {},
5457
5194
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
@@ -5489,7 +5226,7 @@ function SplitCardFormInner({
5489
5226
  },
5490
5227
  "aria-label": "Back to payment methods",
5491
5228
  children: [
5492
- /* @__PURE__ */ jsx8("span", { style: {
5229
+ /* @__PURE__ */ jsx7("span", { style: {
5493
5230
  display: "inline-flex",
5494
5231
  alignItems: "center",
5495
5232
  justifyContent: "center",
@@ -5499,12 +5236,12 @@ function SplitCardFormInner({
5499
5236
  backgroundColor: "#f3f4f6",
5500
5237
  transition: "background-color 0.15s",
5501
5238
  ...bStyles.backButtonIcon
5502
- }, children: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "M15 18l-6-6 6-6" }) }) }),
5503
- /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
5239
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
5240
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
5504
5241
  ]
5505
5242
  }
5506
5243
  ),
5507
- /* @__PURE__ */ jsx8("div", { style: {
5244
+ /* @__PURE__ */ jsx7("div", { style: {
5508
5245
  flex: 1,
5509
5246
  textAlign: "center",
5510
5247
  fontWeight: 600,
@@ -5514,12 +5251,12 @@ function SplitCardFormInner({
5514
5251
  ...bStyles.title
5515
5252
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
5516
5253
  ] }),
5517
- /* @__PURE__ */ jsx8(
5254
+ /* @__PURE__ */ jsx7(
5518
5255
  StripeElements,
5519
5256
  {
5520
5257
  stripe: stripeInstance,
5521
5258
  options: apmInlineOptions,
5522
- children: /* @__PURE__ */ jsx8(
5259
+ children: /* @__PURE__ */ jsx7(
5523
5260
  StripeMethodInlineForm,
5524
5261
  {
5525
5262
  method: expandedApmMethod,
@@ -5550,9 +5287,9 @@ function SplitCardFormInner({
5550
5287
  }
5551
5288
  if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
5552
5289
  return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5553
- /* @__PURE__ */ jsx8(FloPayKeyframes, {}),
5554
- overlayStatus && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5555
- /* @__PURE__ */ jsx8("div", { style: {
5290
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5291
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5292
+ /* @__PURE__ */ jsx7("div", { style: {
5556
5293
  ...apmAnim ? { animation: apmAnim } : {},
5557
5294
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
5558
5295
  }, children: /* @__PURE__ */ jsxs5("div", { style: {
@@ -5589,7 +5326,7 @@ function SplitCardFormInner({
5589
5326
  },
5590
5327
  "aria-label": "Back to payment methods",
5591
5328
  children: [
5592
- /* @__PURE__ */ jsx8("span", { style: {
5329
+ /* @__PURE__ */ jsx7("span", { style: {
5593
5330
  display: "inline-flex",
5594
5331
  alignItems: "center",
5595
5332
  justifyContent: "center",
@@ -5599,12 +5336,12 @@ function SplitCardFormInner({
5599
5336
  backgroundColor: "#f3f4f6",
5600
5337
  transition: "background-color 0.15s",
5601
5338
  ...bStyles.backButtonIcon
5602
- }, children: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "M15 18l-6-6 6-6" }) }) }),
5603
- /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
5339
+ }, children: /* @__PURE__ */ jsx7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "M15 18l-6-6 6-6" }) }) }),
5340
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
5604
5341
  ]
5605
5342
  }
5606
5343
  ),
5607
- /* @__PURE__ */ jsx8("div", { style: {
5344
+ /* @__PURE__ */ jsx7("div", { style: {
5608
5345
  flex: 1,
5609
5346
  textAlign: "center",
5610
5347
  fontWeight: 600,
@@ -5614,12 +5351,12 @@ function SplitCardFormInner({
5614
5351
  ...bStyles.title
5615
5352
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
5616
5353
  ] }),
5617
- /* @__PURE__ */ jsx8(
5354
+ /* @__PURE__ */ jsx7(
5618
5355
  StripeElements,
5619
5356
  {
5620
5357
  stripe: stripeInstance,
5621
5358
  options: apmInlineOptions,
5622
- children: /* @__PURE__ */ jsx8(
5359
+ children: /* @__PURE__ */ jsx7(
5623
5360
  StripeMethodInlineForm,
5624
5361
  {
5625
5362
  method: expandedApmMethod,
@@ -5648,13 +5385,13 @@ function SplitCardFormInner({
5648
5385
  ] });
5649
5386
  }
5650
5387
  return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5651
- /* @__PURE__ */ jsx8(FloPayKeyframes, {}),
5652
- overlayStatus && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5388
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5389
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5653
5390
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
5654
5391
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
5655
5392
  renderStripeGateDebug("flopay-stripe-gate-debug-default"),
5656
5393
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5657
- paypalDirectRetry && /* @__PURE__ */ jsx8(
5394
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
5658
5395
  "div",
5659
5396
  {
5660
5397
  "data-testid": "flopay-paypal-direct-retry-notice-default",
@@ -5670,7 +5407,7 @@ function SplitCardFormInner({
5670
5407
  children: "Please confirm your PayPal payment to complete checkout."
5671
5408
  }
5672
5409
  ),
5673
- /* @__PURE__ */ jsx8(
5410
+ /* @__PURE__ */ jsx7(
5674
5411
  DirectPayPalButton,
5675
5412
  {
5676
5413
  sessionId,
@@ -5690,6 +5427,7 @@ function SplitCardFormInner({
5690
5427
  runBeforeButtonClick,
5691
5428
  isProcessing: isSubmitting,
5692
5429
  onLoadStateChange: setDirectPaypalReady,
5430
+ onInitializationStateChange: setDirectPaypalInitializationState,
5693
5431
  session: session ?? null,
5694
5432
  existingOrderId: paypalDirectRetry?.orderId,
5695
5433
  debug
@@ -5697,7 +5435,7 @@ function SplitCardFormInner({
5697
5435
  paypalDirectRetry?.orderId ?? "fresh"
5698
5436
  )
5699
5437
  ] }),
5700
- shouldRenderStripePayPal && /* @__PURE__ */ jsx8(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx8(
5438
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
5701
5439
  PayPalButtonInner,
5702
5440
  {
5703
5441
  sessionId,
@@ -5715,13 +5453,14 @@ function SplitCardFormInner({
5715
5453
  placeholderBorderRadius: buttonBorderRadius
5716
5454
  }
5717
5455
  ) }),
5718
- shouldRenderWallets && /* @__PURE__ */ jsx8(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx8(
5456
+ shouldRenderWallets && /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
5719
5457
  WalletButtonInner,
5720
5458
  {
5721
5459
  sessionId,
5722
5460
  nonce,
5723
5461
  email: resolvedAccount.email,
5724
5462
  billingApiUrl: resolvedBillingApiUrl,
5463
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5725
5464
  expressMethods: walletExpressMethods,
5726
5465
  onTokenizedBody: dispatchTokenizedBody,
5727
5466
  onErrorChange: updateError,
@@ -5733,7 +5472,7 @@ function SplitCardFormInner({
5733
5472
  placeholderBorderRadius: buttonBorderRadius
5734
5473
  }
5735
5474
  ) }),
5736
- shouldRenderPaymentElement && /* @__PURE__ */ jsx8(
5475
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
5737
5476
  StripePaymentElementInner,
5738
5477
  {
5739
5478
  sessionId,
@@ -5766,7 +5505,7 @@ function SplitCardFormInner({
5766
5505
  }
5767
5506
  )
5768
5507
  ] }),
5769
- showStripe && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs5("div", { style: {
5508
+ vaultActive && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs5("div", { style: {
5770
5509
  display: "flex",
5771
5510
  alignItems: "center",
5772
5511
  gap: "0.75rem",
@@ -5774,11 +5513,11 @@ function SplitCardFormInner({
5774
5513
  color: "#999",
5775
5514
  fontSize: "0.85rem"
5776
5515
  }, children: [
5777
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
5778
- /* @__PURE__ */ jsx8("span", { children: "or pay with card" }),
5779
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
5516
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
5517
+ /* @__PURE__ */ jsx7("span", { children: "or pay with card" }),
5518
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
5780
5519
  ] }),
5781
- showStripe && cardFormBlock
5520
+ vaultActive && cardFormBlock
5782
5521
  ] });
5783
5522
  }
5784
5523
 
@@ -6477,7 +6216,7 @@ async function loadSavedPaymentProviders({
6477
6216
  }
6478
6217
 
6479
6218
  // src/flopay-checkout.tsx
6480
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
6219
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6481
6220
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
6482
6221
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
6483
6222
  var sessionInflightMap = /* @__PURE__ */ new Map();
@@ -6623,7 +6362,6 @@ function FloPayCheckout({
6623
6362
  onComplete,
6624
6363
  onError,
6625
6364
  onDecline,
6626
- onFullNameChange,
6627
6365
  onCountryChange,
6628
6366
  onZipChange,
6629
6367
  showPayPal = true,
@@ -6645,7 +6383,6 @@ function FloPayCheckout({
6645
6383
  cardFieldOrder,
6646
6384
  cardPreFormSlot,
6647
6385
  avsLayout,
6648
- submitLabel,
6649
6386
  className,
6650
6387
  initialErrorMessage = null,
6651
6388
  children,
@@ -6661,9 +6398,9 @@ function FloPayCheckout({
6661
6398
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
6662
6399
  const [unified, setUnified] = useState4(null);
6663
6400
  const [flopay, setFloPay] = useState4(null);
6664
- const flopayRef = useRef6(null);
6401
+ const flopayRef = useRef5(null);
6665
6402
  const [paypalFlopay, setPaypalFloPay] = useState4(null);
6666
- const paypalFlopayRef = useRef6(null);
6403
+ const paypalFlopayRef = useRef5(null);
6667
6404
  const [session, setSession] = useState4(null);
6668
6405
  const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
6669
6406
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
@@ -6678,28 +6415,53 @@ function FloPayCheckout({
6678
6415
  const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
6679
6416
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
6680
6417
  const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
6681
- const autoCheckoutAttempted = useRef6(false);
6682
- const paypalResumeAttempted = useRef6(false);
6683
- const savedPaymentKeysRef = useRef6(null);
6418
+ const [paypalInitializationState, setPaypalInitializationState] = useState4("loading");
6419
+ const [paypalManualRetryGeneration, setPaypalManualRetryGeneration] = useState4(0);
6420
+ const autoCheckoutAttempted = useRef5(false);
6421
+ const paypalResumeAttempted = useRef5(false);
6422
+ const paypalInitializationErrorEmittedRef = useRef5(false);
6423
+ const savedPaymentKeysRef = useRef5(null);
6684
6424
  const telemetryCheckoutContext = useMemo4(() => ({
6685
6425
  checkoutMode: checkoutModeProp ?? currentMode,
6686
6426
  layout: children ? "unknown" : layout === "buttons" ? "buttons" : "embedded"
6687
6427
  }), [checkoutModeProp, children, currentMode, layout]);
6688
- const onCompleteRef = useRef6(onComplete);
6428
+ const onCompleteRef = useRef5(onComplete);
6689
6429
  onCompleteRef.current = onComplete;
6690
- const onErrorRef = useRef6(onError);
6430
+ const onErrorRef = useRef5(onError);
6691
6431
  onErrorRef.current = onError;
6692
- const onDeclineRef = useRef6(onDecline);
6432
+ const onDeclineRef = useRef5(onDecline);
6693
6433
  onDeclineRef.current = onDecline;
6694
- useEffect6(() => {
6434
+ useEffect5(() => {
6695
6435
  getFloPayTelemetryBridge(flopay)?.setCheckoutContext(telemetryCheckoutContext);
6696
6436
  if (paypalFlopay && paypalFlopay !== flopay) {
6697
6437
  getFloPayTelemetryBridge(paypalFlopay)?.setCheckoutContext(telemetryCheckoutContext);
6698
6438
  }
6699
6439
  }, [flopay, paypalFlopay, telemetryCheckoutContext]);
6700
- const onSessionCompletedRef = useRef6(onSessionCompleted);
6440
+ const onSessionCompletedRef = useRef5(onSessionCompleted);
6701
6441
  onSessionCompletedRef.current = onSessionCompleted;
6702
- useEffect6(() => {
6442
+ const handlePayPalInitializationStateChange = useCallback4(
6443
+ (state) => {
6444
+ setPaypalInitializationState(state);
6445
+ if (state !== "exhausted" || paypalInitializationErrorEmittedRef.current) return;
6446
+ paypalInitializationErrorEmittedRef.current = true;
6447
+ onErrorRef.current?.(new FloPayError5(
6448
+ "PayPal is temporarily unavailable. Please try again.",
6449
+ "api_error",
6450
+ { code: "paypal_init_timeout" }
6451
+ ));
6452
+ },
6453
+ []
6454
+ );
6455
+ const retryPayPalInitialization = useCallback4(() => {
6456
+ setPaypalInitializationState("retrying");
6457
+ setPaypalManualRetryGeneration((generation) => generation + 1);
6458
+ }, []);
6459
+ useEffect5(() => {
6460
+ setPaypalInitializationState("loading");
6461
+ setPaypalManualRetryGeneration(0);
6462
+ paypalInitializationErrorEmittedRef.current = false;
6463
+ }, [activeSessionId]);
6464
+ useEffect5(() => {
6703
6465
  console.info("[FloPay] Checkout initialized", {
6704
6466
  sdk_version: SDK_VERSION2,
6705
6467
  checkout_type: checkoutType,
@@ -6727,11 +6489,11 @@ function FloPayCheckout({
6727
6489
  } : void 0,
6728
6490
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
6729
6491
  );
6730
- useEffect6(() => {
6492
+ useEffect5(() => {
6731
6493
  setCreateSessionPatch(void 0);
6732
6494
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
6733
6495
  }, [baseCreateSessionHash]);
6734
- useEffect6(() => {
6496
+ useEffect5(() => {
6735
6497
  setModeError(initialErrorMessage);
6736
6498
  }, [initialErrorMessage]);
6737
6499
  const emitDecline = useCallback4(
@@ -7008,7 +6770,7 @@ function FloPayCheckout({
7008
6770
  telemetryCheckoutContext
7009
6771
  ]
7010
6772
  );
7011
- useEffect6(() => {
6773
+ useEffect5(() => {
7012
6774
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
7013
6775
  return;
7014
6776
  }
@@ -7211,7 +6973,7 @@ function FloPayCheckout({
7211
6973
  }
7212
6974
  })();
7213
6975
  }, [emitDecline, invokeMerchantCallback, locale, normalizeSavedPaymentError, resolvedBillingUrl, telemetry]);
7214
- const initializedHashRef = useRef6(null);
6976
+ const initializedHashRef = useRef5(null);
7215
6977
  function hashCreateParams(params) {
7216
6978
  const key = JSON.stringify({
7217
6979
  c: params?.clientId,
@@ -7248,12 +7010,12 @@ function FloPayCheckout({
7248
7010
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
7249
7011
  [effectiveCreateSession]
7250
7012
  );
7251
- const createSessionParamsRef = useRef6(effectiveCreateSession);
7013
+ const createSessionParamsRef = useRef5(effectiveCreateSession);
7252
7014
  createSessionParamsRef.current = effectiveCreateSession;
7253
- useEffect6(() => {
7015
+ useEffect5(() => {
7254
7016
  setResolvedSessionId(sessionIdProp ?? "");
7255
7017
  }, [sessionIdProp]);
7256
- useEffect6(() => {
7018
+ useEffect5(() => {
7257
7019
  autoCheckoutAttempted.current = false;
7258
7020
  setModeError(initialErrorMessage);
7259
7021
  setModeOverlayError(null);
@@ -7445,12 +7207,11 @@ function FloPayCheckout({
7445
7207
  resolvedSessionId,
7446
7208
  session
7447
7209
  ]);
7448
- useEffect6(() => {
7210
+ useEffect5(() => {
7449
7211
  let cancelled = false;
7450
7212
  setLoadError(null);
7451
7213
  if (createSessionHash) {
7452
7214
  const params = createSessionParamsRef.current;
7453
- setSession(buildSyntheticSession(params, checkoutModeProp));
7454
7215
  setCurrentMode(effectiveCreateSessionMode);
7455
7216
  setIsLoading(false);
7456
7217
  if (initializedHashRef.current === createSessionHash) {
@@ -7458,6 +7219,7 @@ function FloPayCheckout({
7458
7219
  cancelled = true;
7459
7220
  };
7460
7221
  }
7222
+ setSession(buildSyntheticSession(params, checkoutModeProp));
7461
7223
  (async () => {
7462
7224
  const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
7463
7225
  const shouldAutoProcessInlineSession = effectiveCreateSessionMode === "auto" && !autoCheckoutAttempted.current && !hasPayPalRedirectParams;
@@ -7692,7 +7454,7 @@ function FloPayCheckout({
7692
7454
  ]
7693
7455
  );
7694
7456
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && !isPaypalOnlySession && (!flopay || !providerOptions);
7695
- const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx9(
7457
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx8(
7696
7458
  ProcessingOverlay,
7697
7459
  {
7698
7460
  status: modeOverlayStatus,
@@ -7708,7 +7470,7 @@ function FloPayCheckout({
7708
7470
  }
7709
7471
  if (layout === "buttons") {
7710
7472
  const bundleRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
7711
- const skeletonBar = (h) => /* @__PURE__ */ jsx9("div", { style: {
7473
+ const skeletonBar = (h) => /* @__PURE__ */ jsx8("div", { style: {
7712
7474
  height: h,
7713
7475
  borderRadius: bundleRadius,
7714
7476
  background: "#e5e7eb",
@@ -7719,14 +7481,14 @@ function FloPayCheckout({
7719
7481
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7720
7482
  showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7721
7483
  showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7722
- /* @__PURE__ */ jsx9("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7484
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7723
7485
  ] }),
7724
7486
  modeOverlay
7725
7487
  ] });
7726
7488
  }
7727
7489
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7728
7490
  /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
7729
- /* @__PURE__ */ jsx9("div", { style: {
7491
+ /* @__PURE__ */ jsx8("div", { style: {
7730
7492
  width: 24,
7731
7493
  height: 24,
7732
7494
  border: "2px solid #e5e7eb",
@@ -7734,16 +7496,16 @@ function FloPayCheckout({
7734
7496
  borderRadius: "50%",
7735
7497
  animation: "spin 0.6s linear infinite"
7736
7498
  } }),
7737
- /* @__PURE__ */ jsx9("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
7499
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
7738
7500
  ] }),
7739
7501
  modeOverlay
7740
7502
  ] });
7741
7503
  }
7742
7504
  if (loadError) {
7743
7505
  if (errorNode) {
7744
- return /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
7506
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
7745
7507
  }
7746
- return /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(
7508
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
7747
7509
  "div",
7748
7510
  {
7749
7511
  role: "alert",
@@ -7760,7 +7522,7 @@ function FloPayCheckout({
7760
7522
  }
7761
7523
  if (shouldShowInterimButtons) {
7762
7524
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7763
- /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(
7525
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
7764
7526
  InterimButtonsView,
7765
7527
  {
7766
7528
  onButtonClick,
@@ -7781,27 +7543,42 @@ function FloPayCheckout({
7781
7543
  }
7782
7544
  if (isPaypalOnlySession && session && directPaypalConfig) {
7783
7545
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7784
- /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs6("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7785
- modeError && /* @__PURE__ */ jsx9(ErrorBanner, { icon: false, children: modeError }),
7786
- /* @__PURE__ */ jsx9(
7787
- InstrumentedDirectPayPalButton,
7546
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs6("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7547
+ modeError && /* @__PURE__ */ jsx8(ErrorBanner, { icon: false, children: modeError }),
7548
+ paypalInitializationState === "retrying" && /* @__PURE__ */ jsx8("div", { role: "status", "aria-live": "polite", children: "PayPal is temporarily unavailable. Retrying\u2026" }),
7549
+ paypalInitializationState === "exhausted" && /* @__PURE__ */ jsxs6("div", { role: "alert", children: [
7550
+ /* @__PURE__ */ jsx8("div", { children: "PayPal is temporarily unavailable. Please try again." }),
7551
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: retryPayPalInitialization, children: "Retry PayPal" })
7552
+ ] }),
7553
+ paypalInitializationState !== "exhausted" && /* @__PURE__ */ jsx8(
7554
+ "div",
7788
7555
  {
7789
- sessionId: activeSessionId,
7790
- nonce: session.clientSecret || void 0,
7791
- billingApiUrl: resolvedBillingUrl,
7792
- email: session.customer?.email,
7793
- clientId: directPaypalConfig.clientId,
7794
- environment: directPaypalConfig.environment,
7795
- currency: (session.currency ?? "usd").toUpperCase(),
7796
- isSubscription: session.mode === "subscription",
7797
- onComplete,
7798
- onErrorChange: setModeError,
7799
- onDecline,
7800
- onButtonClick,
7801
- session,
7802
- telemetry,
7803
- telemetryContext: telemetryCheckoutContext,
7804
- debug
7556
+ "aria-hidden": paypalInitializationState === "retrying" ? true : void 0,
7557
+ style: paypalInitializationState === "retrying" ? { position: "relative", height: 0, opacity: 0, pointerEvents: "none" } : void 0,
7558
+ children: /* @__PURE__ */ jsx8(
7559
+ InstrumentedDirectPayPalButton,
7560
+ {
7561
+ sessionId: activeSessionId,
7562
+ nonce: session.clientSecret || void 0,
7563
+ billingApiUrl: resolvedBillingUrl,
7564
+ email: session.customer?.email,
7565
+ clientId: directPaypalConfig.clientId,
7566
+ environment: directPaypalConfig.environment,
7567
+ currency: (session.currency ?? "usd").toUpperCase(),
7568
+ isSubscription: session.mode === "subscription",
7569
+ onComplete,
7570
+ onErrorChange: setModeError,
7571
+ onDecline,
7572
+ onButtonClick,
7573
+ onInitializationStateChange: handlePayPalInitializationStateChange,
7574
+ allowAutomaticRetry: paypalManualRetryGeneration === 0,
7575
+ session,
7576
+ telemetry,
7577
+ telemetryContext: telemetryCheckoutContext,
7578
+ debug
7579
+ },
7580
+ `paypal-init-${paypalManualRetryGeneration}`
7581
+ )
7805
7582
  }
7806
7583
  )
7807
7584
  ] }) }),
@@ -7809,12 +7586,12 @@ function FloPayCheckout({
7809
7586
  ] });
7810
7587
  }
7811
7588
  if (!flopay || !providerOptions) {
7812
- return /* @__PURE__ */ jsx9(Fragment3, { children: modeOverlay });
7589
+ return /* @__PURE__ */ jsx8(Fragment3, { children: modeOverlay });
7813
7590
  }
7814
7591
  if (currentMode === "confirm") {
7815
7592
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7816
- /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs6("div", { className, children: [
7817
- modeError && /* @__PURE__ */ jsx9(
7593
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs6("div", { className, children: [
7594
+ modeError && /* @__PURE__ */ jsx8(
7818
7595
  "div",
7819
7596
  {
7820
7597
  style: {
@@ -7829,7 +7606,7 @@ function FloPayCheckout({
7829
7606
  renderConfirmButton ? renderConfirmButton({
7830
7607
  onConfirm: handleConfirmCheckout,
7831
7608
  isProcessing: confirmProcessing
7832
- }) : /* @__PURE__ */ jsx9(
7609
+ }) : /* @__PURE__ */ jsx8(
7833
7610
  "button",
7834
7611
  {
7835
7612
  type: "button",
@@ -7855,8 +7632,8 @@ function FloPayCheckout({
7855
7632
  ] });
7856
7633
  }
7857
7634
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7858
- /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
7859
- modeError && /* @__PURE__ */ jsx9(
7635
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
7636
+ modeError && /* @__PURE__ */ jsx8(
7860
7637
  "div",
7861
7638
  {
7862
7639
  style: {
@@ -7871,7 +7648,7 @@ function FloPayCheckout({
7871
7648
  children: modeError
7872
7649
  }
7873
7650
  ),
7874
- /* @__PURE__ */ jsx9(
7651
+ /* @__PURE__ */ jsx8(
7875
7652
  SessionInjector,
7876
7653
  {
7877
7654
  sessionId: activeSessionId,
@@ -7881,7 +7658,7 @@ function FloPayCheckout({
7881
7658
  children
7882
7659
  }
7883
7660
  )
7884
- ] }) : /* @__PURE__ */ jsx9(
7661
+ ] }) : /* @__PURE__ */ jsx8(
7885
7662
  SplitCardForm,
7886
7663
  {
7887
7664
  sessionId: activeSessionId,
@@ -7897,7 +7674,6 @@ function FloPayCheckout({
7897
7674
  onDecline,
7898
7675
  error: modeError,
7899
7676
  onErrorChange: setModeError,
7900
- onFullNameChange,
7901
7677
  showPayPal,
7902
7678
  showStripe,
7903
7679
  enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
@@ -7926,7 +7702,6 @@ function FloPayCheckout({
7926
7702
  avsCheck: !!enableAVS,
7927
7703
  checkoutType: createSessionParams ? "embedded_checkout" : "standard_checkout",
7928
7704
  checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout",
7929
- submitLabel,
7930
7705
  className,
7931
7706
  directPaypal: resolveDirectPaypalConfig(unified),
7932
7707
  isSubscription: session?.mode === "subscription",
@@ -7944,8 +7719,8 @@ function SessionInjector({
7944
7719
  session,
7945
7720
  children
7946
7721
  }) {
7947
- return /* @__PURE__ */ jsx9(Fragment3, { children: React9.Children.map(children, (child) => {
7948
- if (!React9.isValidElement(child)) return child;
7722
+ return /* @__PURE__ */ jsx8(Fragment3, { children: React8.Children.map(children, (child) => {
7723
+ if (!React8.isValidElement(child)) return child;
7949
7724
  const existing = child.props;
7950
7725
  const injected = {};
7951
7726
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -7960,7 +7735,7 @@ function SessionInjector({
7960
7735
  injected.lastName = session.customer.lastName;
7961
7736
  }
7962
7737
  if (Object.keys(injected).length === 0) return child;
7963
- return React9.cloneElement(child, injected);
7738
+ return React8.cloneElement(child, injected);
7964
7739
  }) });
7965
7740
  }
7966
7741
  function InterimButtonsView({
@@ -7993,7 +7768,7 @@ function InterimButtonsView({
7993
7768
  };
7994
7769
  }, [themeBundle, buttonsTheme, stylesOverride]);
7995
7770
  const skeletonRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
7996
- const skeleton = (h) => /* @__PURE__ */ jsx9("div", { style: {
7771
+ const skeleton = (h) => /* @__PURE__ */ jsx8("div", { style: {
7997
7772
  height: h,
7998
7773
  borderRadius: skeletonRadius,
7999
7774
  background: "#e5e7eb",
@@ -8035,7 +7810,7 @@ function InterimButtonsView({
8035
7810
  ...bStyles.backButton
8036
7811
  },
8037
7812
  children: [
8038
- /* @__PURE__ */ jsx9("span", { style: {
7813
+ /* @__PURE__ */ jsx8("span", { style: {
8039
7814
  display: "inline-flex",
8040
7815
  alignItems: "center",
8041
7816
  justifyContent: "center",
@@ -8044,12 +7819,12 @@ function InterimButtonsView({
8044
7819
  borderRadius: "50%",
8045
7820
  backgroundColor: "#f3f4f6",
8046
7821
  ...bStyles.backButtonIcon
8047
- }, children: /* @__PURE__ */ jsx9("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx9("path", { d: "M15 18l-6-6 6-6" }) }) }),
8048
- /* @__PURE__ */ jsx9(BackButtonContentSlot, { content: cardBackButtonContent })
7822
+ }, children: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx8("path", { d: "M15 18l-6-6 6-6" }) }) }),
7823
+ /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
8049
7824
  ]
8050
7825
  }
8051
7826
  ),
8052
- hideTitle ? /* @__PURE__ */ jsx9("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx9("div", { style: {
7827
+ hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
8053
7828
  flex: 1,
8054
7829
  textAlign: "center",
8055
7830
  fontWeight: 600,
@@ -8057,11 +7832,11 @@ function InterimButtonsView({
8057
7832
  color: "#262833",
8058
7833
  paddingRight: 80,
8059
7834
  ...bStyles.title
8060
- }, children: /* @__PURE__ */ jsx9(TitleContentSlot, { content: cardTitleContent }) })
7835
+ }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
8061
7836
  ] }),
8062
- /* @__PURE__ */ jsx9("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx9("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
7837
+ /* @__PURE__ */ jsx8("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx8("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
8063
7838
  /* @__PURE__ */ jsxs6("div", { style: { display: "flex" }, children: [
8064
- /* @__PURE__ */ jsx9("div", { style: {
7839
+ /* @__PURE__ */ jsx8("div", { style: {
8065
7840
  flex: 1,
8066
7841
  backgroundColor: inputBg,
8067
7842
  borderTop: "none",
@@ -8071,8 +7846,8 @@ function InterimButtonsView({
8071
7846
  borderBottomLeftRadius: 8,
8072
7847
  padding: 12,
8073
7848
  height: 45
8074
- }, children: /* @__PURE__ */ jsx9("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
8075
- /* @__PURE__ */ jsx9("div", { style: {
7849
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
7850
+ /* @__PURE__ */ jsx8("div", { style: {
8076
7851
  flex: 1,
8077
7852
  backgroundColor: inputBg,
8078
7853
  borderTop: "none",
@@ -8082,10 +7857,10 @@ function InterimButtonsView({
8082
7857
  borderBottomRightRadius: 8,
8083
7858
  padding: 12,
8084
7859
  height: 45
8085
- }, children: /* @__PURE__ */ jsx9("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
7860
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
8086
7861
  ] }),
8087
- /* @__PURE__ */ jsx9("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx9("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
8088
- /* @__PURE__ */ jsx9("div", { style: {
7862
+ /* @__PURE__ */ jsx8("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ jsx8("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
7863
+ /* @__PURE__ */ jsx8("div", { style: {
8089
7864
  height: 50,
8090
7865
  borderRadius: 8,
8091
7866
  marginTop: 16,
@@ -8094,7 +7869,7 @@ function InterimButtonsView({
8094
7869
  ...bStyles.submitButton,
8095
7870
  opacity: 0.5
8096
7871
  } }),
8097
- /* @__PURE__ */ jsx9("style", { children: `
7872
+ /* @__PURE__ */ jsx8("style", { children: `
8098
7873
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
8099
7874
  @keyframes flopay-interim-expand {
8100
7875
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -8107,7 +7882,7 @@ function InterimButtonsView({
8107
7882
  return /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
8108
7883
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8109
7884
  showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8110
- showStripe && /* @__PURE__ */ jsx9(
7885
+ showStripe && /* @__PURE__ */ jsx8(
8111
7886
  "button",
8112
7887
  {
8113
7888
  type: "button",
@@ -8141,362 +7916,80 @@ function InterimButtonsView({
8141
7916
  onMouseUp: (e) => {
8142
7917
  e.currentTarget.style.transform = "scale(1)";
8143
7918
  },
8144
- children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: cardButtonContent })
7919
+ children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
8145
7920
  }
8146
7921
  ),
8147
- /* @__PURE__ */ jsx9("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7922
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
8148
7923
  ] });
8149
7924
  }
8150
7925
 
8151
- // src/checkout-form.tsx
8152
- import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
8153
- import { FloPayError as FloPayError6 } from "@flopay/shared";
8154
- import { forwardRef as forwardRef2, useCallback as useCallback5, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
8155
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
8156
- var WALLET_RESUME_KEY = "flopay_wallet_resume";
8157
- var CheckoutForm = forwardRef2(
8158
- function CheckoutForm2(props, ref) {
8159
- return /* @__PURE__ */ jsx10(CheckoutFormInner, { ...props, innerRef: ref });
8160
- }
8161
- );
8162
- function CheckoutFormInner({
8163
- sessionId,
8164
- nonce,
8165
- billingApiUrl,
8166
- email,
8167
- userId,
8168
- onComplete,
8169
- onError,
8170
- onDecline,
8171
- onTokenizedBody,
8172
- layout = "auto",
8173
- submitLabel = "Pay",
8174
- showAddress = false,
8175
- className,
8176
- children,
8177
- firstName,
8178
- lastName,
8179
- chv,
8180
- isProcessing: externalProcessing,
8181
- error: externalError,
8182
- onErrorChange,
8183
- innerRef
8184
- }) {
8185
- const flopay = useFloPay();
8186
- const paypalFlopay = usePayPalFloPay();
8187
- const elements = useElements();
8188
- const contextBillingUrl = useBillingApiUrl();
8189
- const [processing, setProcessing] = useState5(false);
8190
- const [error, setError] = useState5(null);
8191
- const [is3DSActive, setIs3DSActive] = useState5(false);
8192
- const displayError = externalError ?? error;
8193
- const isSubmitting = externalProcessing ?? processing;
8194
- const isSelfContained = !onTokenizedBody;
8195
- const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
8196
- const updateError = useCallback5(
8197
- (err) => {
8198
- setError(err);
8199
- onErrorChange?.(err);
8200
- },
8201
- [onErrorChange]
8202
- );
8203
- const emitDecline = useCallback5(
8204
- (input, overrides) => {
8205
- onDecline?.(buildDeclineEvent("card", input, overrides));
8206
- },
8207
- [onDecline]
8208
- );
8209
- const processPaymentInternal = useCallback5(
8210
- async (tokenizedBody, completionPaymentMethodId) => {
8211
- setProcessing(true);
8212
- updateError(null);
8213
- const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
8214
- const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
8215
- try {
8216
- const api = new PaymentAPI5(baseUrl);
8217
- const response = await api.processPayment(userId ?? "", {
8218
- sessionId,
8219
- nonce,
8220
- tokenizedData: requestTokenizedBody,
8221
- accountData: {
8222
- userId: userId ?? "",
8223
- email: email ?? "",
8224
- firstName: firstName ?? "",
8225
- lastName: lastName ?? ""
8226
- },
8227
- chv
8228
- });
8229
- if (response.ok) {
8230
- onComplete?.({
8231
- status: "succeeded",
8232
- paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
8233
- paymentMethodId: resolvedCompletionPaymentMethodId,
8234
- checkoutMethod: tokenizedBody.isPaypal ? "paypal" : "card"
8235
- });
8236
- return;
8237
- }
8238
- const json = await response.json().catch(() => null);
8239
- if (json?.type === "3ds_required") {
8240
- const secret = json["threeDSecureToken"];
8241
- if (!flopay || !secret) {
8242
- updateError("3DS authentication required but no token provided.");
8243
- return;
8244
- }
8245
- setIs3DSActive(true);
8246
- try {
8247
- const retryFullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
8248
- const result = await flopay.confirmPayment({
8249
- clientSecret: secret,
8250
- returnUrl: window.location.href,
8251
- billingDetails: {
8252
- ...email ? { email } : {},
8253
- ...retryFullName ? { name: retryFullName } : {}
8254
- }
8255
- });
8256
- if (result.error) {
8257
- updateError(result.error.message);
8258
- onError?.(result.error);
8259
- emitDecline(result.error);
8260
- return;
8261
- }
8262
- if (result.status === "succeeded" || result.status === "processing") {
8263
- await processPaymentInternal({
8264
- id: result.paymentIntentId,
8265
- type: "card",
8266
- threeDSecureActionResultTokenId: result.paymentIntentId
8267
- }, resolvedCompletionPaymentMethodId);
8268
- }
8269
- } finally {
8270
- setIs3DSActive(false);
8271
- }
8272
- return;
8273
- }
8274
- if (json?.type === "paypal_redirect_required") {
8275
- const secret = json["threeDSecureToken"] ?? json["clientSecret"];
8276
- const pmId = json["paymentMethodId"];
8277
- const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
8278
- if (!paypalStripe || !secret) {
8279
- const message = "PayPal is not available.";
8280
- updateError(message);
8281
- onDecline?.(buildDeclineEvent("paypal", message));
8282
- return;
8283
- }
8284
- localStorage.setItem(WALLET_RESUME_KEY, JSON.stringify({
8285
- sessionId,
8286
- paymentIntentId: "",
8287
- tokenType: "card",
8288
- tokenId: pmId ?? "",
8289
- status: "pending_redirect"
8290
- }));
8291
- const confirmParams = {
8292
- return_url: window.location.href
8293
- };
8294
- if (pmId) confirmParams["payment_method"] = pmId;
8295
- const { error: confirmError } = await paypalStripe.confirmPayment({
8296
- clientSecret: secret,
8297
- confirmParams,
8298
- redirect: "if_required"
8299
- });
8300
- if (confirmError) {
8301
- const message = confirmError.message ?? "PayPal payment failed.";
8302
- updateError(message);
8303
- onDecline?.(buildDeclineEvent("paypal", message, { code: confirmError.code }));
8304
- }
8305
- return;
8306
- }
8307
- const errorMessage = json?.message ?? "Payment failed. Please try again.";
8308
- updateError(errorMessage);
8309
- emitDecline(errorMessage, {
8310
- code: json?.code,
8311
- declineCode: json?.declineCode ?? json?.gatewayDeclineReason
8312
- });
8313
- } catch (err) {
8314
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
8315
- } finally {
8316
- setProcessing(false);
8317
- }
8318
- },
8319
- [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
8320
- );
8321
- const dispatchTokenizedBody = useCallback5(
8322
- (tokenizedBody) => {
8323
- if (onTokenizedBody) {
8324
- onTokenizedBody(tokenizedBody);
8325
- } else {
8326
- processPaymentInternal(tokenizedBody);
8327
- }
8328
- },
8329
- [onTokenizedBody, processPaymentInternal]
8330
- );
8331
- useImperativeHandle2(innerRef, () => ({
8332
- async handleNextAction(secret) {
8333
- if (!flopay) return;
8334
- setIs3DSActive(true);
8335
- try {
8336
- const result = await flopay.confirmPayment({
8337
- clientSecret: secret,
8338
- returnUrl: window.location.href
8339
- });
8340
- if (result.error) {
8341
- updateError(result.error.message);
8342
- onError?.(result.error);
8343
- emitDecline(result.error);
8344
- } else if (result.status === "succeeded" || result.status === "processing") {
8345
- dispatchTokenizedBody({
8346
- id: result.paymentIntentId,
8347
- type: "card",
8348
- threeDSecureActionResultTokenId: result.paymentIntentId
8349
- });
8350
- }
8351
- } catch (err) {
8352
- updateError(err instanceof Error ? err.message : "Payment authentication failed.");
8353
- } finally {
8354
- setIs3DSActive(false);
8355
- }
8356
- }
8357
- }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
8358
- useEffect7(() => {
8359
- if (typeof window === "undefined") return;
8360
- const stored = localStorage.getItem(WALLET_RESUME_KEY);
8361
- if (!stored) return;
8362
- try {
8363
- const payload = JSON.parse(stored);
8364
- if (payload.sessionId === sessionId) {
8365
- localStorage.removeItem(WALLET_RESUME_KEY);
8366
- dispatchTokenizedBody({
8367
- id: payload.tokenId,
8368
- type: payload.tokenType,
8369
- threeDSecureActionResultTokenId: payload.paymentIntentId
8370
- });
8371
- }
8372
- } catch {
8373
- localStorage.removeItem(WALLET_RESUME_KEY);
8374
- }
8375
- }, [sessionId, dispatchTokenizedBody]);
8376
- const handleSubmit = useCallback5(
8377
- async (e) => {
8378
- e.preventDefault();
8379
- if (!flopay || !elements || isSubmitting) return;
8380
- setProcessing(true);
8381
- updateError(null);
8382
- let handedOff = false;
8383
- try {
8384
- const submitResult = await flopay.submitElements();
8385
- if (submitResult.error) {
8386
- updateError(submitResult.error.message);
8387
- onError?.(submitResult.error);
8388
- return;
8389
- }
8390
- const fullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
8391
- const pmResult = await flopay.createPaymentMethod({
8392
- ...email ? { email } : {},
8393
- ...fullName ? { name: fullName } : {}
8394
- });
8395
- if (pmResult.error || !pmResult.paymentMethodId) {
8396
- updateError(pmResult.error?.message ?? "Failed to create payment method.");
8397
- return;
8398
- }
8399
- if (!sessionId || !email) {
8400
- throw new FloPayError6("Missing sessionId or email", "validation_error");
8401
- }
8402
- const intentHeaders = intentRequestHeaders(nonce);
8403
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
8404
- method: "POST",
8405
- headers: intentHeaders,
8406
- body: JSON.stringify({
8407
- sessionId,
8408
- email,
8409
- paymentMethodType: pmResult.paymentMethodId,
8410
- isPaypal: false
8411
- })
8412
- });
8413
- if (!intentResponse.ok) {
8414
- const intentError = await buildFloPayApiErrorFromResponse(
8415
- intentResponse,
8416
- "Failed to create payment intent"
8417
- );
8418
- updateError(intentError.message);
8419
- onError?.(intentError);
8420
- emitDecline(intentError);
8421
- return;
8422
- }
8423
- const intentJson = await intentResponse.json();
8424
- const intentClientSecret = intentJson.data?.id;
8425
- if (!intentClientSecret) throw new FloPayError6("No client_secret in payment intent response", "api_error");
8426
- const confirmResult = await flopay.confirmCardPayment({
8427
- clientSecret: intentClientSecret,
8428
- paymentMethodId: pmResult.paymentMethodId
8429
- });
8430
- if (confirmResult.error) {
8431
- updateError(confirmResult.error.message);
8432
- onError?.(confirmResult.error);
8433
- emitDecline(confirmResult.error);
8434
- return;
7926
+ // src/elements.tsx
7927
+ import { useEffect as useEffect6, useRef as useRef6, useContext as useContext3 } from "react";
7928
+ import { jsx as jsx9 } from "react/jsx-runtime";
7929
+ function createElementComponent(elementType, displayName) {
7930
+ function ElementComponent({
7931
+ className,
7932
+ id,
7933
+ style,
7934
+ options,
7935
+ onChange,
7936
+ onReady,
7937
+ onFocus,
7938
+ onBlur,
7939
+ onEscape
7940
+ }) {
7941
+ const containerRef = useRef6(null);
7942
+ const elementRef = useRef6(null);
7943
+ const { elements, reportInteractive } = useContext3(FloPayContext);
7944
+ useEffect6(() => {
7945
+ if (!elements || !containerRef.current) return;
7946
+ let mounted = true;
7947
+ (async () => {
7948
+ let element = elements.getElement(elementType);
7949
+ if (!element) {
7950
+ element = await elements.create(elementType, options);
8435
7951
  }
8436
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
8437
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
8438
- rawProvider,
8439
- intentClientSecret
8440
- );
8441
- const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
8442
- const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
8443
- if (!paymentIntentId) {
8444
- const error2 = new FloPayError6("No payment intent returned after confirmation.", "api_error");
8445
- updateError(error2.message);
8446
- onError?.(error2);
7952
+ if (!mounted || !containerRef.current) {
8447
7953
  return;
8448
7954
  }
8449
- handedOff = isSelfContained;
8450
- dispatchTokenizedBody({
8451
- id: paymentMethodId,
8452
- type: "card",
8453
- threeDSecureActionResultTokenId: paymentIntentId,
8454
- originalPaymentMethodId: pmResult.paymentMethodId
7955
+ element.mount(containerRef.current);
7956
+ elementRef.current = element;
7957
+ if (onChange) element.on("change", onChange);
7958
+ element.on("ready", () => {
7959
+ reportInteractive?.();
7960
+ onReady?.();
8455
7961
  });
8456
- } catch (err) {
8457
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
8458
- } finally {
8459
- if (!handedOff) {
8460
- setProcessing(false);
8461
- }
8462
- }
8463
- },
8464
- [flopay, elements, isSubmitting, sessionId, nonce, email, firstName, lastName, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
8465
- );
8466
- const isReady = flopay !== null && elements !== null;
8467
- return /* @__PURE__ */ jsxs7("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
8468
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx10("div", { "data-testid": "flopay-overlay", style: {
8469
- position: "absolute",
8470
- inset: 0,
8471
- background: "rgba(255,255,255,0.7)",
8472
- display: "flex",
8473
- alignItems: "center",
8474
- justifyContent: "center",
8475
- zIndex: 10
8476
- }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
8477
- !isReady && /* @__PURE__ */ jsx10("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
8478
- isReady && /* @__PURE__ */ jsxs7(Fragment4, { children: [
8479
- /* @__PURE__ */ jsx10(PaymentElement, { options: { layout } }),
8480
- showAddress && /* @__PURE__ */ jsx10(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
8481
- displayError && /* @__PURE__ */ jsx10("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
8482
- children ?? /* @__PURE__ */ jsx10(
8483
- "button",
8484
- {
8485
- type: "submit",
8486
- disabled: isSubmitting || !isReady,
8487
- "data-testid": "flopay-submit",
8488
- children: isSubmitting ? "Processing..." : submitLabel
7962
+ if (onFocus) element.on("focus", onFocus);
7963
+ if (onBlur) element.on("blur", onBlur);
7964
+ if (onEscape) element.on("escape", onEscape);
7965
+ })();
7966
+ return () => {
7967
+ mounted = false;
7968
+ if (elementRef.current) {
7969
+ try {
7970
+ elementRef.current.unmount();
7971
+ } catch {
7972
+ }
7973
+ elementRef.current = null;
8489
7974
  }
8490
- )
8491
- ] })
8492
- ] });
7975
+ };
7976
+ }, [elements, reportInteractive]);
7977
+ return /* @__PURE__ */ jsx9("div", { ref: containerRef, className, id, style });
7978
+ }
7979
+ ElementComponent.displayName = displayName;
7980
+ return ElementComponent;
8493
7981
  }
7982
+ var PaymentElement2 = createElementComponent(
7983
+ "payment",
7984
+ "PaymentElement"
7985
+ );
7986
+ var AddressElement = createElementComponent("address", "AddressElement");
8494
7987
 
8495
7988
  // src/paypal-button.tsx
8496
- import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
8497
- import { FloPayError as FloPayError7 } from "@flopay/shared";
8498
- import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
8499
- import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
7989
+ import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
7990
+ import { FloPayError as FloPayError6 } from "@flopay/shared";
7991
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef7, useState as useState5 } from "react";
7992
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
8500
7993
  function PayPalButton({
8501
7994
  sessionId,
8502
7995
  nonce,
@@ -8514,14 +8007,14 @@ function PayPalButton({
8514
8007
  const flopay = useFloPay();
8515
8008
  const elements = useElements();
8516
8009
  const contextBillingUrl = useBillingApiUrl();
8517
- const [ready, setReady] = useState6(false);
8518
- const [submitting, setSubmitting] = useState6(false);
8010
+ const [ready, setReady] = useState5(false);
8011
+ const [submitting, setSubmitting] = useState5(false);
8519
8012
  const paypalResumeAttempted = useRef7(false);
8520
8013
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
8521
- const processPaymentInternal = useCallback6(
8014
+ const processPaymentInternal = useCallback5(
8522
8015
  async (tokenizedBody) => {
8523
8016
  try {
8524
- const api = new PaymentAPI6(baseUrl);
8017
+ const api = new PaymentAPI5(baseUrl);
8525
8018
  const response = await api.processPayment(userId ?? "", {
8526
8019
  sessionId,
8527
8020
  nonce,
@@ -8546,7 +8039,7 @@ function PayPalButton({
8546
8039
  },
8547
8040
  [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
8548
8041
  );
8549
- const dispatchTokenizedBody = useCallback6(
8042
+ const dispatchTokenizedBody = useCallback5(
8550
8043
  (body) => {
8551
8044
  if (onTokenizedBody) {
8552
8045
  onTokenizedBody(body);
@@ -8556,7 +8049,7 @@ function PayPalButton({
8556
8049
  },
8557
8050
  [onTokenizedBody, processPaymentInternal]
8558
8051
  );
8559
- useEffect8(() => {
8052
+ useEffect7(() => {
8560
8053
  if (!flopay || paypalResumeAttempted.current) return;
8561
8054
  const params = new URLSearchParams(window.location.search);
8562
8055
  const paymentIntentId = params.get("payment_intent");
@@ -8604,13 +8097,13 @@ function PayPalButton({
8604
8097
  }
8605
8098
  })();
8606
8099
  }, [flopay, dispatchTokenizedBody, onErrorChange]);
8607
- const handlePayPalConfirm = useCallback6(async () => {
8100
+ const handlePayPalConfirm = useCallback5(async () => {
8608
8101
  if (!flopay || !elements) return;
8609
8102
  try {
8610
8103
  setSubmitting(true);
8611
8104
  onErrorChange?.(null);
8612
8105
  if (!sessionId || !email) {
8613
- throw new FloPayError7("Missing sessionId or email for PayPal payment", "validation_error");
8106
+ throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
8614
8107
  }
8615
8108
  const result = await flopay.confirmPayPalPayment({
8616
8109
  billingApiUrl: baseUrl,
@@ -8638,11 +8131,11 @@ function PayPalButton({
8638
8131
  }
8639
8132
  }, [flopay, elements, sessionId, nonce, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
8640
8133
  if (!flopay || !elements) {
8641
- return /* @__PURE__ */ jsx11("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
8134
+ return /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
8642
8135
  }
8643
- return /* @__PURE__ */ jsxs8(Fragment5, { children: [
8644
- !ready && /* @__PURE__ */ jsx11("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
8645
- /* @__PURE__ */ jsx11("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx11(
8136
+ return /* @__PURE__ */ jsxs7(Fragment4, { children: [
8137
+ !ready && /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
8138
+ /* @__PURE__ */ jsx10("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx10(
8646
8139
  "button",
8647
8140
  {
8648
8141
  type: "button",
@@ -8664,7 +8157,7 @@ function PayPalButton({
8664
8157
  children: submitting ? "Processing..." : "PayPal"
8665
8158
  }
8666
8159
  ) }),
8667
- (submitting || isProcessing) && /* @__PURE__ */ jsx11("div", { style: {
8160
+ (submitting || isProcessing) && /* @__PURE__ */ jsx10("div", { style: {
8668
8161
  position: "fixed",
8669
8162
  inset: 0,
8670
8163
  background: "rgba(0,0,0,0.4)",
@@ -8672,7 +8165,7 @@ function PayPalButton({
8672
8165
  alignItems: "center",
8673
8166
  justifyContent: "center",
8674
8167
  zIndex: 1e3
8675
- }, children: /* @__PURE__ */ jsx11("div", { style: {
8168
+ }, children: /* @__PURE__ */ jsx10("div", { style: {
8676
8169
  background: "white",
8677
8170
  borderRadius: 8,
8678
8171
  padding: "1.5rem",
@@ -8684,19 +8177,19 @@ function PayPalButton({
8684
8177
  }
8685
8178
 
8686
8179
  // src/automatic-payment-button.tsx
8687
- import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef8, useState as useState7 } from "react";
8688
- import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
8689
- import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
8690
- import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
8180
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef8, useState as useState6 } from "react";
8181
+ import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
8182
+ import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
8183
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
8691
8184
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
8692
8185
  function sleep2(ms) {
8693
8186
  return new Promise((resolve) => setTimeout(resolve, ms));
8694
8187
  }
8695
8188
  function coerceError(err, fallbackMessage) {
8696
- if (err instanceof FloPayError8) {
8189
+ if (err instanceof FloPayError7) {
8697
8190
  return err;
8698
8191
  }
8699
- return new FloPayError8(
8192
+ return new FloPayError7(
8700
8193
  err instanceof Error ? err.message : fallbackMessage,
8701
8194
  "api_error"
8702
8195
  );
@@ -8799,36 +8292,36 @@ function FloPayAutomaticPaymentButton({
8799
8292
  utmMetadata
8800
8293
  ]
8801
8294
  );
8802
- const [isProcessing, setIsProcessing] = useState7(false);
8803
- const [overlayStatus, setOverlayStatus] = useState7(null);
8804
- const [overlayError, setOverlayError] = useState7(null);
8805
- const [fallbackSession, setFallbackSession] = useState7(null);
8295
+ const [isProcessing, setIsProcessing] = useState6(false);
8296
+ const [overlayStatus, setOverlayStatus] = useState6(null);
8297
+ const [overlayError, setOverlayError] = useState6(null);
8298
+ const [fallbackSession, setFallbackSession] = useState6(null);
8806
8299
  const isMountedRef = useRef8(true);
8807
8300
  const threeDsAbortRef = useRef8(null);
8808
8301
  const fallbackSessionRef = useRef8(fallbackSession);
8809
8302
  const onSuccessRef = useRef8(onSuccess);
8810
8303
  const onErrorRef = useRef8(onError);
8811
8304
  const onDeclineRef = useRef8(onDecline);
8812
- useEffect9(() => {
8305
+ useEffect8(() => {
8813
8306
  fallbackSessionRef.current = fallbackSession;
8814
8307
  }, [fallbackSession]);
8815
- useEffect9(() => {
8308
+ useEffect8(() => {
8816
8309
  onSuccessRef.current = onSuccess;
8817
8310
  }, [onSuccess]);
8818
- useEffect9(() => {
8311
+ useEffect8(() => {
8819
8312
  onErrorRef.current = onError;
8820
8313
  }, [onError]);
8821
- useEffect9(() => {
8314
+ useEffect8(() => {
8822
8315
  onDeclineRef.current = onDecline;
8823
8316
  }, [onDecline]);
8824
- useEffect9(() => {
8317
+ useEffect8(() => {
8825
8318
  isMountedRef.current = true;
8826
8319
  return () => {
8827
8320
  isMountedRef.current = false;
8828
8321
  threeDsAbortRef.current?.abort();
8829
8322
  };
8830
8323
  }, []);
8831
- useEffect9(() => {
8324
+ useEffect8(() => {
8832
8325
  if (typeof window === "undefined") {
8833
8326
  return;
8834
8327
  }
@@ -8842,13 +8335,13 @@ function FloPayAutomaticPaymentButton({
8842
8335
  window.removeEventListener("keydown", handleKeyDown);
8843
8336
  };
8844
8337
  }, []);
8845
- const emitDecline = useCallback7((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8338
+ const emitDecline = useCallback6((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8846
8339
  onDeclineRef.current?.(buildDeclineEvent(method, error, {
8847
8340
  code: error.code,
8848
8341
  declineCode: error.declineCode
8849
8342
  }));
8850
8343
  }, []);
8851
- const showSuccess = useCallback7(async (event) => {
8344
+ const showSuccess = useCallback6(async (event) => {
8852
8345
  if (!isMountedRef.current) return;
8853
8346
  setOverlayError(null);
8854
8347
  setOverlayStatus("success");
@@ -8856,7 +8349,7 @@ function FloPayAutomaticPaymentButton({
8856
8349
  if (!isMountedRef.current) return;
8857
8350
  onSuccessRef.current?.(event);
8858
8351
  }, []);
8859
- const showError = useCallback7(async (error, options) => {
8352
+ const showError = useCallback6(async (error, options) => {
8860
8353
  if (!isMountedRef.current) return;
8861
8354
  onErrorRef.current?.(error);
8862
8355
  if (options?.emitDecline) {
@@ -8866,10 +8359,10 @@ function FloPayAutomaticPaymentButton({
8866
8359
  setOverlayStatus("error");
8867
8360
  await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
8868
8361
  }, [emitDecline]);
8869
- const processResolvedSession = useCallback7(async (apiResult, resolvedSessionId, options) => {
8362
+ const processResolvedSession = useCallback6(async (apiResult, resolvedSessionId, options) => {
8870
8363
  const session = apiResult.data.session ?? null;
8871
8364
  if (!session) {
8872
- throw new FloPayError8("No session data returned", "api_error");
8365
+ throw new FloPayError7("No session data returned", "api_error");
8873
8366
  }
8874
8367
  const effectiveNonce = session.clientSecret || nonce;
8875
8368
  if (session.status === "complete") {
@@ -8882,19 +8375,19 @@ function FloPayAutomaticPaymentButton({
8882
8375
  return;
8883
8376
  }
8884
8377
  if (session.status === "expired") {
8885
- throw new FloPayError8("Checkout session has expired.", "api_error", {
8378
+ throw new FloPayError7("Checkout session has expired.", "api_error", {
8886
8379
  code: "checkout_session_expired"
8887
8380
  });
8888
8381
  }
8889
8382
  try {
8890
8383
  if (apiResult.autoProcessingPending) {
8891
- const api = new PaymentAPI7(resolvedBillingUrl);
8384
+ const api = new PaymentAPI6(resolvedBillingUrl);
8892
8385
  const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
8893
8386
  initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
8894
8387
  });
8895
8388
  const completedSession = completed.data.session;
8896
8389
  if (!completedSession || completedSession.status !== "complete") {
8897
- throw new FloPayError8("Automatic payment failed. Please try again.", "api_error", {
8390
+ throw new FloPayError7("Automatic payment failed. Please try again.", "api_error", {
8898
8391
  code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
8899
8392
  });
8900
8393
  }
@@ -8937,7 +8430,7 @@ function FloPayAutomaticPaymentButton({
8937
8430
  }
8938
8431
  );
8939
8432
  }
8940
- const api = new PaymentAPI7(resolvedBillingUrl);
8433
+ const api = new PaymentAPI6(resolvedBillingUrl);
8941
8434
  const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
8942
8435
  initialDelayMs: 0,
8943
8436
  nonce: effectiveNonce
@@ -9022,14 +8515,14 @@ function FloPayAutomaticPaymentButton({
9022
8515
  showError,
9023
8516
  showSuccess
9024
8517
  ]);
9025
- const handleButtonClick = useCallback7(async (event) => {
8518
+ const handleButtonClick = useCallback6(async (event) => {
9026
8519
  buttonProps.onClick?.(event);
9027
8520
  if (event.defaultPrevented || disabled || isProcessing) {
9028
8521
  return;
9029
8522
  }
9030
8523
  setFallbackSession(null);
9031
8524
  if (sessionId && createSessionDraft) {
9032
- const error = new FloPayError8(
8525
+ const error = new FloPayError7(
9033
8526
  "Provide either sessionId or create-session props, not both.",
9034
8527
  "validation_error"
9035
8528
  );
@@ -9041,7 +8534,7 @@ function FloPayAutomaticPaymentButton({
9041
8534
  return;
9042
8535
  }
9043
8536
  if (!sessionId && !createSessionDraft) {
9044
- const error = new FloPayError8(
8537
+ const error = new FloPayError7(
9045
8538
  "Provide a sessionId or the props required to create an automatic payment session.",
9046
8539
  "validation_error"
9047
8540
  );
@@ -9056,7 +8549,7 @@ function FloPayAutomaticPaymentButton({
9056
8549
  setOverlayError(null);
9057
8550
  setOverlayStatus("processing");
9058
8551
  try {
9059
- const api = new PaymentAPI7(resolvedBillingUrl);
8552
+ const api = new PaymentAPI6(resolvedBillingUrl);
9060
8553
  if (sessionId) {
9061
8554
  const result = await api.getUnifiedCheckoutSession(sessionId, nonce);
9062
8555
  await processResolvedSession(result, sessionId);
@@ -9070,7 +8563,7 @@ function FloPayAutomaticPaymentButton({
9070
8563
  fromCreateSession: true
9071
8564
  });
9072
8565
  } catch (err) {
9073
- if (err instanceof FloPayError8 && err.code === "session_auto_completed") {
8566
+ if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
9074
8567
  await showSuccess({
9075
8568
  result: { status: "succeeded" },
9076
8569
  session: null,
@@ -9104,7 +8597,7 @@ function FloPayAutomaticPaymentButton({
9104
8597
  showError,
9105
8598
  showSuccess
9106
8599
  ]);
9107
- const handleFallbackComplete = useCallback7((result) => {
8600
+ const handleFallbackComplete = useCallback6((result) => {
9108
8601
  const activeFallback = fallbackSessionRef.current;
9109
8602
  setFallbackSession(null);
9110
8603
  onSuccessRef.current?.({
@@ -9114,10 +8607,10 @@ function FloPayAutomaticPaymentButton({
9114
8607
  autoCompleted: false
9115
8608
  });
9116
8609
  }, []);
9117
- const handleFallbackError = useCallback7((error) => {
8610
+ const handleFallbackError = useCallback6((error) => {
9118
8611
  onErrorRef.current?.(error);
9119
8612
  }, []);
9120
- const handleFallbackDecline = useCallback7((decline) => {
8613
+ const handleFallbackDecline = useCallback6((decline) => {
9121
8614
  onDeclineRef.current?.(decline);
9122
8615
  }, []);
9123
8616
  const themeBundle = useMemo5(() => resolveTheme3(theme), [theme]);
@@ -9148,8 +8641,8 @@ function FloPayAutomaticPaymentButton({
9148
8641
  const resolvedPrimaryHoverColor = resolvedAppearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
9149
8642
  const resolvedButtonBorderRadius = resolvedAppearanceVars?.borderRadius ?? "8px";
9150
8643
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
9151
- return /* @__PURE__ */ jsxs9(Fragment6, { children: [
9152
- /* @__PURE__ */ jsx12(
8644
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
8645
+ /* @__PURE__ */ jsx11(
9153
8646
  "button",
9154
8647
  {
9155
8648
  ...buttonProps,
@@ -9213,17 +8706,17 @@ function FloPayAutomaticPaymentButton({
9213
8706
  e.currentTarget.style.transform = "scale(1)";
9214
8707
  }
9215
8708
  },
9216
- children: /* @__PURE__ */ jsx12(CardButtonContentSlot, { content: children })
8709
+ children: /* @__PURE__ */ jsx11(CardButtonContentSlot, { content: children })
9217
8710
  }
9218
8711
  ),
9219
- overlayStatus && /* @__PURE__ */ jsx12(
8712
+ overlayStatus && /* @__PURE__ */ jsx11(
9220
8713
  ProcessingOverlay,
9221
8714
  {
9222
8715
  status: overlayStatus,
9223
8716
  errorMessage: overlayError
9224
8717
  }
9225
8718
  ),
9226
- fallbackSession && /* @__PURE__ */ jsx12(
8719
+ fallbackSession && /* @__PURE__ */ jsx11(
9227
8720
  "div",
9228
8721
  {
9229
8722
  "data-testid": "flopay-automatic-payment-fallback",
@@ -9244,7 +8737,7 @@ function FloPayAutomaticPaymentButton({
9244
8737
  padding: "1.5rem",
9245
8738
  zIndex: 1100
9246
8739
  },
9247
- children: /* @__PURE__ */ jsx12(
8740
+ children: /* @__PURE__ */ jsx11(
9248
8741
  "div",
9249
8742
  {
9250
8743
  style: {
@@ -9260,7 +8753,7 @@ function FloPayAutomaticPaymentButton({
9260
8753
  flexDirection: "column",
9261
8754
  gap: "1rem"
9262
8755
  },
9263
- children: /* @__PURE__ */ jsx12(
8756
+ children: /* @__PURE__ */ jsx11(
9264
8757
  FloPayCheckout,
9265
8758
  {
9266
8759
  sessionId: fallbackSession.sessionId,
@@ -9287,17 +8780,12 @@ function FloPayAutomaticPaymentButton({
9287
8780
  }
9288
8781
  export {
9289
8782
  AddressElement,
9290
- CardCvcElement,
9291
- CardElement,
9292
- CardExpiryElement,
9293
- CardNumberElement,
9294
- CheckoutForm,
9295
8783
  DirectPayPalButton,
9296
8784
  FloPayAutomaticPaymentButton,
9297
8785
  FloPayCheckout,
9298
8786
  FloPayProvider,
9299
8787
  PayPalButton,
9300
- PaymentElement,
8788
+ PaymentElement2 as PaymentElement,
9301
8789
  SplitCardForm,
9302
8790
  VaultCardFields,
9303
8791
  useCheckout,