@flopay/react 1.4.1 → 1.4.2

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,22 @@ 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
+ intentKind: isSubscription ? "subscription" : "order"
1572
+ },
1573
+ { idempotencyKey: paypalIntentIdempotencyKey }
1574
+ );
1575
+ if (isInactive()) {
1576
+ throw supersededRenderError();
1575
1577
  }
1576
- const id = json?.data?.id;
1577
- if (!id) {
1578
+ if (intent.provider !== "paypal") {
1578
1579
  throw new Error(fallbackMessage);
1579
1580
  }
1580
1581
  telemetrySource.log({
@@ -1585,7 +1586,24 @@ function DirectPayPalButtonImplementation({
1585
1586
  requestCategory: "intent_create",
1586
1587
  statusClass: "2xx"
1587
1588
  });
1588
- return id;
1589
+ return intent.providerObjectId;
1590
+ };
1591
+ const reportPayPalDecline = (error, context) => {
1592
+ const currentNonce = context?.nonce ?? nonceRef.current;
1593
+ if (!currentNonce) return;
1594
+ const effectiveSessionId = context?.sessionId ?? sessionId;
1595
+ const providerDeclineReason = getProviderDeclineCode(error) ?? getProviderErrorCode(error) ?? "provider_declined";
1596
+ void new PaymentAPI(baseUrl, { telemetry: false }).reportSessionIntentDecline(
1597
+ effectiveSessionId,
1598
+ currentNonce,
1599
+ {
1600
+ provider: "paypal",
1601
+ paymentMethodCategory: "wallet",
1602
+ paymentMethodType: "paypal",
1603
+ providerDeclineReason
1604
+ }
1605
+ ).catch(() => {
1606
+ });
1589
1607
  };
1590
1608
  const effectRenderGeneration = renderGeneration;
1591
1609
  appendDebug("loadScript:scheduled (deferred 1 tick)");
@@ -1614,9 +1632,11 @@ function DirectPayPalButtonImplementation({
1614
1632
  ...paypalSdkEnv === "sandbox" ? { dataNamespace: "paypal_sandbox" } : {}
1615
1633
  });
1616
1634
  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");
1635
+ appendDebug(`loadScript:resolved inactive=${isInactive()} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
1636
+ if (isInactive()) return;
1637
+ if (!paypal?.Buttons) {
1638
+ appendDebug("FAIL: namespace missing Buttons factory");
1639
+ handlePreRenderFailure("PayPal SDK did not expose the Buttons factory.");
1620
1640
  return;
1621
1641
  }
1622
1642
  telemetrySource.log({
@@ -1626,7 +1646,7 @@ function DirectPayPalButtonImplementation({
1626
1646
  paymentMethodCategory: "paypal"
1627
1647
  });
1628
1648
  const handleApprove = async (data) => {
1629
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1649
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1630
1650
  const token = data.subscriptionID ?? data.orderID ?? "";
1631
1651
  const context = token ? approvalContextByTokenRef.current.get(token) : attemptContextBySurfaceRef.current.get(effectRenderGeneration);
1632
1652
  if (!context || !isAttemptActive(context.generation)) return;
@@ -1651,7 +1671,7 @@ function DirectPayPalButtonImplementation({
1651
1671
  } catch (err) {
1652
1672
  forwardError(err instanceof Error ? err.message : "PayPal capture failed.");
1653
1673
  } finally {
1654
- setSubmitting(false);
1674
+ if (!isInactive()) setSubmitting(false);
1655
1675
  }
1656
1676
  };
1657
1677
  const buttons = paypal.Buttons({
@@ -1662,11 +1682,20 @@ function DirectPayPalButtonImplementation({
1662
1682
  // inline-session/account patches land before the order is created,
1663
1683
  // matching the Stripe-rendered PayPal flow.
1664
1684
  onClick: async (_data, actions) => {
1685
+ if (isInactive()) {
1686
+ await actions.reject();
1687
+ return;
1688
+ }
1689
+ beginPaypalIntentAttempt();
1665
1690
  const runner = runBeforeButtonClickRef.current;
1666
1691
  let prepared = {};
1667
1692
  if (runner) {
1668
1693
  try {
1669
1694
  const beforeClick = await runner("paypal");
1695
+ if (isInactive()) {
1696
+ await actions.reject();
1697
+ return;
1698
+ }
1670
1699
  if (!beforeClick.proceed) {
1671
1700
  beforeClickRef.current = null;
1672
1701
  attemptContextBySurfaceRef.current.delete(effectRenderGeneration);
@@ -1720,10 +1749,11 @@ function DirectPayPalButtonImplementation({
1720
1749
  // prior backend round-trip (the `paypal_direct_required` retry
1721
1750
  // path), feed it straight to PayPal instead of creating a new one.
1722
1751
  // 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,
1752
+ createOrder: isSubscription ? void 0 : existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal order.").then(registerApprovalContext),
1753
+ createSubscription: isSubscription ? existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal subscription.").then(registerApprovalContext) : void 0,
1725
1754
  onApprove: handleApprove,
1726
1755
  onCancel: () => {
1756
+ if (isInactive()) return;
1727
1757
  const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
1728
1758
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1729
1759
  beforeClickRef.current = null;
@@ -1737,7 +1767,7 @@ function DirectPayPalButtonImplementation({
1737
1767
  invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
1738
1768
  },
1739
1769
  onError: (err) => {
1740
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1770
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1741
1771
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1742
1772
  appendDebug(`onError rendered=${rendered} msg=${message.slice(0, 120)}`);
1743
1773
  if (rendered) {
@@ -1757,13 +1787,16 @@ function DirectPayPalButtonImplementation({
1757
1787
  paymentMethodCategory: "paypal",
1758
1788
  requestCategory: "provider_sdk"
1759
1789
  });
1790
+ if (isProviderDecline(err)) {
1791
+ reportPayPalDecline(err, context);
1792
+ }
1760
1793
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1761
1794
  beforeClickRef.current = null;
1762
1795
  invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
1763
1796
  notifyTechnicalFailure(err, { code: "paypal_runtime_failed" });
1764
1797
  return;
1765
1798
  }
1766
- markRenderFailed(message);
1799
+ handlePreRenderFailure(message);
1767
1800
  }
1768
1801
  });
1769
1802
  const eligible = buttons.isEligible();
@@ -1776,17 +1809,19 @@ function DirectPayPalButtonImplementation({
1776
1809
  });
1777
1810
  if (!eligible) {
1778
1811
  setReady(false);
1779
- markRenderFailed(
1812
+ handlePreRenderFailure(
1780
1813
  "PayPal buttons are not eligible to render in this context (paypal_ineligible)."
1781
1814
  );
1782
1815
  return;
1783
1816
  }
1784
1817
  const typedButtons = buttons;
1818
+ ownedButtons = typedButtons;
1785
1819
  if (debug) {
1786
1820
  appendDebug(`container:dims ${formatDims(container)} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"}`);
1787
1821
  }
1788
1822
  if (debug && typeof MutationObserver !== "undefined") {
1789
1823
  containerObserver = new MutationObserver((mutations) => {
1824
+ if (isInactive()) return;
1790
1825
  for (const mutation of mutations) {
1791
1826
  if (mutation.type === "childList") {
1792
1827
  mutation.addedNodes.forEach((node) => {
@@ -1814,7 +1849,7 @@ function DirectPayPalButtonImplementation({
1814
1849
  });
1815
1850
  }
1816
1851
  const snapshotContainer = (when) => {
1817
- if (cancelled || rendered) return;
1852
+ if (isInactive() || rendered) return;
1818
1853
  const iframes = container.querySelectorAll("iframe");
1819
1854
  const hasStorageAccess = typeof document !== "undefined" && "hasStorageAccess" in document;
1820
1855
  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 +1869,25 @@ function DirectPayPalButtonImplementation({
1834
1869
  watchdogTimers.push(setTimeout(() => snapshotContainer("15s"), 15e3));
1835
1870
  }
1836
1871
  appendDebug("render:start");
1872
+ watchdogTimers.push(setTimeout(() => {
1873
+ if (isInactive() || rendered) return;
1874
+ appendDebug("watchdog:11s retryable render timeout");
1875
+ handlePreRenderFailure(
1876
+ "PayPal Buttons render did not settle within 11000ms.",
1877
+ true
1878
+ );
1879
+ }, PAYPAL_RENDER_WATCHDOG_MS));
1837
1880
  buttons.render(container).then(() => {
1838
- appendDebug(`render:resolved cancelled=${cancelled}`);
1881
+ appendDebug(`render:resolved inactive=${isInactive()}`);
1839
1882
  stopDiagnostics();
1840
- if (cancelled) {
1841
- typedButtons.close().catch(() => {
1842
- });
1883
+ if (isInactive()) {
1884
+ closeOwnedButtons();
1843
1885
  return;
1844
1886
  }
1845
- activeButtons = typedButtons;
1846
1887
  rendered = true;
1847
1888
  setReady(true);
1889
+ setRecovering(false);
1890
+ onInitializationStateChangeRef.current?.("ready");
1848
1891
  telemetrySource.log({
1849
1892
  name: "provider.ready",
1850
1893
  stage: "provider_ready",
@@ -1862,24 +1905,22 @@ function DirectPayPalButtonImplementation({
1862
1905
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1863
1906
  appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
1864
1907
  stopDiagnostics();
1865
- markRenderFailed(message);
1908
+ handlePreRenderFailure(message);
1866
1909
  });
1867
1910
  }).catch((err) => {
1868
1911
  const message = err instanceof Error ? err.message : "PayPal SDK failed to load.";
1869
1912
  appendDebug(`loadScript:rejected msg=${message.slice(0, 120)}`);
1870
- markRenderFailed(message);
1913
+ handlePreRenderFailure(message);
1871
1914
  });
1872
1915
  }, 0);
1873
1916
  return () => {
1874
1917
  cancelled = true;
1875
1918
  clearTimeout(startTimer);
1919
+ if (retryTimer) clearTimeout(retryTimer);
1876
1920
  stopDiagnostics();
1877
- if (activeButtons) {
1878
- activeButtons.close().catch(() => {
1879
- });
1880
- }
1921
+ closeOwnedButtons();
1881
1922
  };
1882
- }, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1923
+ }, [allowAutomaticRetry, baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1883
1924
  const debugPanel = debug ? /* @__PURE__ */ jsxs4(
1884
1925
  "pre",
1885
1926
  {
@@ -1904,8 +1945,8 @@ ${debugLines.join("\n")}`
1904
1945
  ]
1905
1946
  }
1906
1947
  ) : null;
1907
- if (failed) {
1908
- return debug ? /* @__PURE__ */ jsx7("div", { children: debugPanel }) : null;
1948
+ if (failed && !initializationConfigChanged) {
1949
+ return debug ? /* @__PURE__ */ jsx6("div", { children: debugPanel }) : null;
1909
1950
  }
1910
1951
  return (
1911
1952
  // Single wrapper so the parent flex container sees exactly one flex item
@@ -1914,39 +1955,52 @@ ${debugLines.join("\n")}`
1914
1955
  // intentionally has no margin/padding so the parent owns all spacing.
1915
1956
  /* @__PURE__ */ jsxs4("div", { children: [
1916
1957
  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(
1958
+ /* @__PURE__ */ jsxs4(
1959
+ "div",
1960
+ {
1961
+ "aria-hidden": recovering ? true : void 0,
1962
+ style: recovering ? {
1963
+ position: "relative",
1964
+ height: 0,
1965
+ opacity: 0,
1966
+ pointerEvents: "none"
1967
+ } : { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT },
1968
+ children: [
1969
+ !ready && !recovering && /* @__PURE__ */ jsx6(
1970
+ "div",
1971
+ {
1972
+ "data-testid": "flopay-direct-paypal-placeholder",
1973
+ style: {
1974
+ position: "absolute",
1975
+ inset: 0,
1976
+ borderRadius: 8,
1977
+ background: "#e5e7eb",
1978
+ animation: "flopay-pulse 1.5s ease-in-out infinite",
1979
+ pointerEvents: "none"
1980
+ }
1981
+ }
1982
+ ),
1983
+ /* @__PURE__ */ jsx6(
1984
+ "div",
1985
+ {
1986
+ ref: containerRef,
1987
+ "data-testid": "flopay-direct-paypal-container",
1988
+ tabIndex: -1,
1989
+ "aria-label": "PayPal payment method",
1990
+ style: {
1991
+ minHeight: DEFAULT_BUTTON_HEIGHT,
1992
+ display: "flex",
1993
+ opacity: ready ? 1 : 0
1994
+ },
1995
+ "aria-busy": submitting || isProcessing
1996
+ },
1997
+ renderGeneration
1998
+ )
1999
+ ]
2000
+ },
2001
+ renderGeneration
2002
+ ),
2003
+ showRetryFocusTarget && /* @__PURE__ */ jsx6(
1950
2004
  "button",
1951
2005
  {
1952
2006
  ref: focusTargetRef,
@@ -1961,18 +2015,34 @@ ${debugLines.join("\n")}`
1961
2015
  );
1962
2016
  }
1963
2017
  function DirectPayPalButton(props) {
1964
- return /* @__PURE__ */ jsx7(DirectPayPalButtonImplementation, { ...props });
2018
+ return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
1965
2019
  }
1966
2020
  function InstrumentedDirectPayPalButton(props) {
1967
- return /* @__PURE__ */ jsx7(DirectPayPalButtonImplementation, { ...props });
2021
+ return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
1968
2022
  }
1969
2023
 
1970
2024
  // src/split-card-form.tsx
1971
2025
  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";
2026
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1973
2027
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1974
2028
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1975
2029
  var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
2030
+ async function reportNonCardDecline({
2031
+ billingApiUrl,
2032
+ sessionId,
2033
+ nonce,
2034
+ request
2035
+ }) {
2036
+ if (!sessionId || !nonce) return;
2037
+ try {
2038
+ await new PaymentAPI2(billingApiUrl, { telemetry: false }).reportSessionIntentDecline(
2039
+ sessionId,
2040
+ nonce,
2041
+ request
2042
+ );
2043
+ } catch {
2044
+ }
2045
+ }
1976
2046
  function isAccountValidationError(err) {
1977
2047
  return err instanceof FloPayError3 && typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500;
1978
2048
  }
@@ -2084,9 +2154,9 @@ function buildExternalMethodRecoveryMessage(method, popupBlocked) {
2084
2154
  return popupBlocked ? `${base} Allow pop-ups for this site, then try again.` : base;
2085
2155
  }
2086
2156
  function useExternalAttemptReconciliation(onMissingTerminal) {
2087
- const generationRef = useRef5(0);
2088
- const invalidatedGenerationRef = useRef5(null);
2089
- const attemptRef = useRef5(null);
2157
+ const generationRef = useRef4(0);
2158
+ const invalidatedGenerationRef = useRef4(null);
2159
+ const attemptRef = useRef4(null);
2090
2160
  const clearAttemptTimer = useCallback3((targetAttempt = attemptRef.current) => {
2091
2161
  const attempt = targetAttempt;
2092
2162
  if (attempt?.timer) {
@@ -2160,7 +2230,7 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
2160
2230
  attempt.yieldedControl = true;
2161
2231
  clearAttemptTimer(attempt);
2162
2232
  }, [clearAttemptTimer]);
2163
- useEffect5(() => {
2233
+ useEffect4(() => {
2164
2234
  const handleVisibilityChange = () => {
2165
2235
  if (document.visibilityState === "visible") {
2166
2236
  scheduleRecoveryIfReturned();
@@ -2202,7 +2272,7 @@ function normalizeBeforeButtonClickError(method, err) {
2202
2272
  );
2203
2273
  }
2204
2274
  function FloPayKeyframes() {
2205
- return /* @__PURE__ */ jsx8("style", { children: FLOPAY_KEYFRAMES });
2275
+ return /* @__PURE__ */ jsx7("style", { children: FLOPAY_KEYFRAMES });
2206
2276
  }
2207
2277
  function toCssSize(value) {
2208
2278
  if (typeof value === "number") return `${value}px`;
@@ -2225,7 +2295,7 @@ function ExpressCheckoutReadySwap({
2225
2295
  }) {
2226
2296
  if (state === "unavailable" || state === "load_error") return null;
2227
2297
  return /* @__PURE__ */ jsxs5("div", { style: { position: "relative", minHeight: 44 }, children: [
2228
- /* @__PURE__ */ jsx8(
2298
+ /* @__PURE__ */ jsx7(
2229
2299
  "div",
2230
2300
  {
2231
2301
  "data-testid": placeholderTestId,
@@ -2244,7 +2314,7 @@ function ExpressCheckoutReadySwap({
2244
2314
  }
2245
2315
  }
2246
2316
  ),
2247
- /* @__PURE__ */ jsx8(
2317
+ /* @__PURE__ */ jsx7(
2248
2318
  "div",
2249
2319
  {
2250
2320
  style: {
@@ -2262,11 +2332,9 @@ function ExpressCheckoutReadySwap({
2262
2332
  function isExpressCheckoutRowVisible(state) {
2263
2333
  return state !== "unavailable" && state !== "load_error";
2264
2334
  }
2265
- var SplitCardForm = forwardRef(
2266
- function SplitCardForm2(props, ref) {
2267
- return /* @__PURE__ */ jsx8(SplitCardFormInner, { ...props, innerRef: ref });
2268
- }
2269
- );
2335
+ function SplitCardForm(props) {
2336
+ return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props });
2337
+ }
2270
2338
  function PayPalButtonInner({
2271
2339
  sessionId,
2272
2340
  nonce,
@@ -2286,18 +2354,17 @@ function PayPalButtonInner({
2286
2354
  const stripe = useStripeRaw();
2287
2355
  const elements = useStripeElements();
2288
2356
  const [loadState, setLoadState] = useState3("loading");
2289
- useEffect5(() => {
2357
+ useEffect4(() => {
2290
2358
  onLoadStateChange?.(loadState);
2291
2359
  }, [loadState, onLoadStateChange]);
2292
2360
  const [submitting, setSubmitting] = useState3(false);
2293
- const paypalResumeAttempted = useRef5(false);
2294
- const focusTargetRef = useRef5(null);
2295
- const recoveryActionRef = useRef5(null);
2361
+ const paypalResumeAttempted = useRef4(false);
2362
+ const focusTargetRef = useRef4(null);
2363
+ const recoveryActionRef = useRef4(null);
2296
2364
  const [surfaceKey, setSurfaceKey] = useState3(0);
2297
2365
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2298
- const pendingProviderFocusRef = useRef5(false);
2299
- const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
2300
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2366
+ const pendingProviderFocusRef = useRef4(false);
2367
+ const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
2301
2368
  const {
2302
2369
  startAttempt,
2303
2370
  finishAttempt,
@@ -2312,7 +2379,7 @@ function PayPalButtonInner({
2312
2379
  setShowRecoveryAction(true);
2313
2380
  setSurfaceKey((key) => key + 1);
2314
2381
  });
2315
- useEffect5(() => {
2382
+ useEffect4(() => {
2316
2383
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2317
2384
  }, [showRecoveryAction, surfaceKey]);
2318
2385
  const resetSurface = useCallback3(() => {
@@ -2330,7 +2397,7 @@ function PayPalButtonInner({
2330
2397
  (target ?? focusTargetRef.current)?.focus();
2331
2398
  }, 0);
2332
2399
  }, []);
2333
- useEffect5(() => {
2400
+ useEffect4(() => {
2334
2401
  if (!pendingProviderFocusRef.current) return;
2335
2402
  pendingProviderFocusRef.current = false;
2336
2403
  focusProviderSurface();
@@ -2340,7 +2407,7 @@ function PayPalButtonInner({
2340
2407
  onErrorChange?.(null);
2341
2408
  focusProviderSurface();
2342
2409
  }, [focusProviderSurface, onErrorChange]);
2343
- useEffect5(() => {
2410
+ useEffect4(() => {
2344
2411
  if (!stripe || paypalResumeAttempted.current) return;
2345
2412
  const params = new URLSearchParams(window.location.search);
2346
2413
  const paymentIntentId = params.get("payment_intent");
@@ -2468,32 +2535,21 @@ function PayPalButtonInner({
2468
2535
  event.paymentFailed({ reason: "fail" });
2469
2536
  return;
2470
2537
  }
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;
2538
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
2539
+ effectiveSessionId,
2540
+ effectiveNonce ?? "",
2541
+ {
2542
+ provider: "stripe",
2543
+ paymentMethodCategory: "wallet",
2544
+ paymentMethodType: "paypal",
2545
+ paymentMethodId: paymentMethod?.id ?? null,
2546
+ intentKind: "payment"
2547
+ }
2548
+ );
2549
+ if (intent.provider !== "stripe") {
2550
+ throw new Error("Invalid provider returned for PayPal intent");
2493
2551
  }
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");
2552
+ const intentClientSecret = intent.clientSecret;
2497
2553
  const confirmParams = {
2498
2554
  return_url: window.location.href
2499
2555
  };
@@ -2530,6 +2586,17 @@ function PayPalButtonInner({
2530
2586
  code: getProviderErrorCode(confirmError),
2531
2587
  declineCode: getProviderDeclineCode(confirmError)
2532
2588
  }));
2589
+ await reportNonCardDecline({
2590
+ billingApiUrl,
2591
+ sessionId: effectiveSessionId,
2592
+ nonce: effectiveNonce,
2593
+ request: {
2594
+ provider: "stripe",
2595
+ paymentMethodCategory: "wallet",
2596
+ paymentMethodType: "paypal",
2597
+ providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
2598
+ }
2599
+ });
2533
2600
  } else {
2534
2601
  recoverTechnicalFailure(confirmError, "stripe_paypal_confirm_failed", attemptContext?.generation);
2535
2602
  }
@@ -2558,9 +2625,9 @@ function PayPalButtonInner({
2558
2625
  } finally {
2559
2626
  setSubmitting(false);
2560
2627
  }
2561
- }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2628
+ }, [stripe, elements, sessionId, nonce, email, billingApiUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2562
2629
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2563
- /* @__PURE__ */ jsx8(
2630
+ /* @__PURE__ */ jsx7(
2564
2631
  ExpressCheckoutReadySwap,
2565
2632
  {
2566
2633
  state: loadState,
@@ -2575,7 +2642,7 @@ function PayPalButtonInner({
2575
2642
  "aria-label": "PayPal payment method",
2576
2643
  style: { borderRadius: 8, outlineOffset: 4 },
2577
2644
  children: [
2578
- showRecoveryAction && /* @__PURE__ */ jsx8(
2645
+ showRecoveryAction && /* @__PURE__ */ jsx7(
2579
2646
  "button",
2580
2647
  {
2581
2648
  ref: recoveryActionRef,
@@ -2586,7 +2653,7 @@ function PayPalButtonInner({
2586
2653
  children: "Try PayPal again"
2587
2654
  }
2588
2655
  ),
2589
- /* @__PURE__ */ jsx8(
2656
+ /* @__PURE__ */ jsx7(
2590
2657
  ExpressCheckoutElement,
2591
2658
  {
2592
2659
  onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
@@ -2626,7 +2693,7 @@ function PayPalButtonInner({
2626
2693
  )
2627
2694
  }
2628
2695
  ),
2629
- submitting && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: "processing" })
2696
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
2630
2697
  ] });
2631
2698
  }
2632
2699
  function WalletButtonInner({
@@ -2634,6 +2701,7 @@ function WalletButtonInner({
2634
2701
  nonce,
2635
2702
  email,
2636
2703
  billingApiUrl,
2704
+ intentKind,
2637
2705
  expressMethods,
2638
2706
  onTokenizedBody,
2639
2707
  onErrorChange,
@@ -2648,20 +2716,19 @@ function WalletButtonInner({
2648
2716
  const stripe = useStripeRaw();
2649
2717
  const elements = useStripeElements();
2650
2718
  const [loadState, setLoadState] = useState3("loading");
2651
- useEffect5(() => {
2719
+ useEffect4(() => {
2652
2720
  onLoadStateChange?.(loadState);
2653
2721
  }, [loadState, onLoadStateChange]);
2654
2722
  const [submitting, setSubmitting] = useState3(false);
2655
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2656
- const focusTargetRef = useRef5(null);
2657
- const recoveryActionRef = useRef5(null);
2723
+ const focusTargetRef = useRef4(null);
2724
+ const recoveryActionRef = useRef4(null);
2658
2725
  const [surfaceKey, setSurfaceKey] = useState3(0);
2659
2726
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2660
2727
  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());
2728
+ const pendingProviderFocusRef = useRef4(false);
2729
+ const lastWalletProviderMethodRef = useRef4("google_pay");
2730
+ const lastWalletMethodRef = useRef4("google_pay");
2731
+ const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
2665
2732
  const {
2666
2733
  startAttempt,
2667
2734
  finishAttempt,
@@ -2677,7 +2744,7 @@ function WalletButtonInner({
2677
2744
  setShowRecoveryAction(true);
2678
2745
  setSurfaceKey((key) => key + 1);
2679
2746
  });
2680
- useEffect5(() => {
2747
+ useEffect4(() => {
2681
2748
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2682
2749
  }, [showRecoveryAction, surfaceKey]);
2683
2750
  const resetSurface = useCallback3(() => {
@@ -2696,7 +2763,7 @@ function WalletButtonInner({
2696
2763
  (target ?? focusTargetRef.current)?.focus();
2697
2764
  }, 0);
2698
2765
  }, []);
2699
- useEffect5(() => {
2766
+ useEffect4(() => {
2700
2767
  if (!pendingProviderFocusRef.current) return;
2701
2768
  pendingProviderFocusRef.current = false;
2702
2769
  focusProviderSurface();
@@ -2753,30 +2820,21 @@ function WalletButtonInner({
2753
2820
  if (!effectiveSessionId || !effectiveEmail) {
2754
2821
  throw new Error("Missing sessionId or email for wallet payment");
2755
2822
  }
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;
2823
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
2824
+ effectiveSessionId,
2825
+ effectiveNonce ?? "",
2826
+ {
2827
+ provider: "stripe",
2828
+ paymentMethodCategory: "wallet",
2829
+ paymentMethodType: walletType ?? buttonMethod,
2830
+ paymentMethodId: paymentMethod.id,
2831
+ intentKind
2832
+ }
2833
+ );
2834
+ if (intent.provider !== "stripe") {
2835
+ throw new Error("Invalid provider returned for wallet intent");
2776
2836
  }
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");
2837
+ const intentClientSecret = intent.clientSecret;
2780
2838
  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
2839
  if (confirmError) {
2782
2840
  if (isProviderDecline(confirmError)) {
@@ -2786,6 +2844,17 @@ function WalletButtonInner({
2786
2844
  code: getProviderErrorCode(confirmError),
2787
2845
  declineCode: getProviderDeclineCode(confirmError)
2788
2846
  }));
2847
+ await reportNonCardDecline({
2848
+ billingApiUrl,
2849
+ sessionId: effectiveSessionId,
2850
+ nonce: effectiveNonce,
2851
+ request: {
2852
+ provider: "stripe",
2853
+ paymentMethodCategory: "wallet",
2854
+ paymentMethodType: walletType ?? buttonMethod,
2855
+ providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
2856
+ }
2857
+ });
2789
2858
  } else {
2790
2859
  recoverTechnicalFailure(walletType, confirmError, "stripe_wallet_confirm_failed", attemptContext?.generation);
2791
2860
  }
@@ -2806,7 +2875,7 @@ function WalletButtonInner({
2806
2875
  setSubmitting(false);
2807
2876
  }
2808
2877
  },
2809
- [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]
2878
+ [stripe, elements, sessionId, nonce, email, billingApiUrl, intentKind, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]
2810
2879
  );
2811
2880
  const expressMethodMap = useMemo3(() => {
2812
2881
  const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
@@ -2827,7 +2896,7 @@ function WalletButtonInner({
2827
2896
  [expressMethods]
2828
2897
  );
2829
2898
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2830
- /* @__PURE__ */ jsx8(
2899
+ /* @__PURE__ */ jsx7(
2831
2900
  ExpressCheckoutReadySwap,
2832
2901
  {
2833
2902
  state: loadState,
@@ -2857,7 +2926,7 @@ function WalletButtonInner({
2857
2926
  ]
2858
2927
  }
2859
2928
  ),
2860
- /* @__PURE__ */ jsx8(
2929
+ /* @__PURE__ */ jsx7(
2861
2930
  ExpressCheckoutElement,
2862
2931
  {
2863
2932
  onReady: (event) => {
@@ -2921,7 +2990,7 @@ function WalletButtonInner({
2921
2990
  )
2922
2991
  }
2923
2992
  ),
2924
- submitting && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: "processing" })
2993
+ submitting && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: "processing" })
2925
2994
  ] });
2926
2995
  }
2927
2996
  function StripeMethodButton({
@@ -2943,7 +3012,7 @@ function StripeMethodButton({
2943
3012
  const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
2944
3013
  const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
2945
3014
  const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
2946
- return /* @__PURE__ */ jsx8(
3015
+ return /* @__PURE__ */ jsx7(
2947
3016
  "button",
2948
3017
  {
2949
3018
  type: "button",
@@ -2990,7 +3059,7 @@ function StripeMethodButton({
2990
3059
  // Pulse-skeleton parity with the wallet ECE: no spinner, just the
2991
3060
  // status text on top of the pulsing background. Keeps the loading
2992
3061
  // affordance shape-equivalent across both kinds of tile.
2993
- /* @__PURE__ */ jsx8("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
3062
+ /* @__PURE__ */ jsx7("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
2994
3063
  ) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
2995
3064
  /* @__PURE__ */ jsxs5(
2996
3065
  "span",
@@ -3010,7 +3079,7 @@ function StripeMethodButton({
3010
3079
  // bytes ship as a sibling file, out of the JS bundle. 3:2 box
3011
3080
  // matching the datatrans logos' 120×80 viewBox; each ships its own
3012
3081
  // white rounded-rect background so it stays legible on any tile.
3013
- /* @__PURE__ */ jsx8(
3082
+ /* @__PURE__ */ jsx7(
3014
3083
  "img",
3015
3084
  {
3016
3085
  src: vendoredLogoUrl,
@@ -3030,7 +3099,7 @@ function StripeMethodButton({
3030
3099
  }
3031
3100
  }
3032
3101
  )
3033
- ) : brand?.logoSvg ? /* @__PURE__ */ jsx8(
3102
+ ) : brand?.logoSvg ? /* @__PURE__ */ jsx7(
3034
3103
  "span",
3035
3104
  {
3036
3105
  "aria-hidden": "true",
@@ -3045,11 +3114,11 @@ function StripeMethodButton({
3045
3114
  dangerouslySetInnerHTML: { __html: brand.logoSvg }
3046
3115
  }
3047
3116
  ) : null,
3048
- /* @__PURE__ */ jsx8("span", { children: getStripeMethodDisplayName(method) })
3117
+ /* @__PURE__ */ jsx7("span", { children: getStripeMethodDisplayName(method) })
3049
3118
  ]
3050
3119
  }
3051
3120
  ),
3052
- hasNextStep && /* @__PURE__ */ jsx8(
3121
+ hasNextStep && /* @__PURE__ */ jsx7(
3053
3122
  "svg",
3054
3123
  {
3055
3124
  width: "14",
@@ -3069,7 +3138,7 @@ function StripeMethodButton({
3069
3138
  opacity: 0.7
3070
3139
  },
3071
3140
  "aria-hidden": "true",
3072
- children: /* @__PURE__ */ jsx8("path", { d: "M9 18l6-6-6-6" })
3141
+ children: /* @__PURE__ */ jsx7("path", { d: "M9 18l6-6-6-6" })
3073
3142
  }
3074
3143
  )
3075
3144
  ] })
@@ -3099,10 +3168,9 @@ function StripeMethodInlineForm({
3099
3168
  const stripe = useStripeRaw();
3100
3169
  const elements = useStripeElements();
3101
3170
  const [submitting, setSubmitting] = useState3(false);
3102
- const submittingRef = useRef5(false);
3171
+ const submittingRef = useRef4(false);
3103
3172
  const [isMethodComplete, setIsMethodComplete] = useState3(false);
3104
3173
  const [loadState, setLoadState] = useState3("loading");
3105
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3106
3174
  const handlePay = useCallback3(async () => {
3107
3175
  if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
3108
3176
  submittingRef.current = true;
@@ -3133,26 +3201,22 @@ function StripeMethodInlineForm({
3133
3201
  if (!effectiveSessionId || !effectiveEmail) {
3134
3202
  throw new Error("Missing sessionId or email for payment.");
3135
3203
  }
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;
3204
+ const paymentMethodType = paymentMethod.type || method;
3205
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
3206
+ effectiveSessionId,
3207
+ effectiveNonce ?? "",
3208
+ {
3209
+ provider: "stripe",
3210
+ paymentMethodCategory: "apm",
3211
+ paymentMethodType,
3212
+ paymentMethodId: null,
3213
+ intentKind: "payment"
3214
+ }
3215
+ );
3216
+ if (intent.provider !== "stripe") {
3217
+ throw new Error("Invalid provider returned for APM intent");
3152
3218
  }
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");
3219
+ const intentClientSecret = intent.clientSecret;
3156
3220
  if (typeof window !== "undefined") {
3157
3221
  try {
3158
3222
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3185,6 +3249,17 @@ function StripeMethodInlineForm({
3185
3249
  const message = confirmError.message ?? "Payment failed.";
3186
3250
  onErrorChange?.(message);
3187
3251
  onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
3252
+ await reportNonCardDecline({
3253
+ billingApiUrl,
3254
+ sessionId: effectiveSessionId,
3255
+ nonce: effectiveNonce,
3256
+ request: {
3257
+ provider: "stripe",
3258
+ paymentMethodCategory: "apm",
3259
+ paymentMethodType: method,
3260
+ providerDeclineReason: confirmError.code ?? "provider_declined"
3261
+ }
3262
+ });
3188
3263
  return;
3189
3264
  }
3190
3265
  const piStatus = paymentIntent?.status;
@@ -3234,7 +3309,7 @@ function StripeMethodInlineForm({
3234
3309
  sessionId,
3235
3310
  nonce,
3236
3311
  email,
3237
- baseUrl,
3312
+ billingApiUrl,
3238
3313
  method,
3239
3314
  onTokenizedBody,
3240
3315
  onErrorChange,
@@ -3242,8 +3317,8 @@ function StripeMethodInlineForm({
3242
3317
  ]);
3243
3318
  void onCancel;
3244
3319
  return /* @__PURE__ */ jsxs5("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
3245
- /* @__PURE__ */ jsx8(
3246
- PaymentElement2,
3320
+ /* @__PURE__ */ jsx7(
3321
+ PaymentElement,
3247
3322
  {
3248
3323
  onReady: () => setLoadState("ready"),
3249
3324
  onLoadError: () => {
@@ -3271,7 +3346,7 @@ function StripeMethodInlineForm({
3271
3346
  }
3272
3347
  }
3273
3348
  ),
3274
- /* @__PURE__ */ jsx8(
3349
+ /* @__PURE__ */ jsx7(
3275
3350
  "button",
3276
3351
  {
3277
3352
  type: "button",
@@ -3298,7 +3373,7 @@ function StripeMethodInlineForm({
3298
3373
  children: submitting ? "Processing\u2026" : `Pay with ${getStripeMethodDisplayName(method)}`
3299
3374
  }
3300
3375
  ),
3301
- errorText && /* @__PURE__ */ jsx8(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
3376
+ errorText && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
3302
3377
  ] });
3303
3378
  }
3304
3379
  function StripePaymentElementInner({
@@ -3325,11 +3400,10 @@ function StripePaymentElementInner({
3325
3400
  onExpandApm,
3326
3401
  expandedApmMethod
3327
3402
  }) {
3328
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3329
3403
  const [expandedMethod, setExpandedMethod] = useState3(null);
3330
3404
  const [submittingMethod, setSubmittingMethod] = useState3(null);
3331
- const submittingRef = useRef5(false);
3332
- useEffect5(() => {
3405
+ const submittingRef = useRef4(false);
3406
+ useEffect4(() => {
3333
3407
  if (paymentElementMethods.length > 0 && stripeInstance) {
3334
3408
  onLoadStateChange?.("ready");
3335
3409
  } else if (!stripeInstance) {
@@ -3356,30 +3430,21 @@ function StripePaymentElementInner({
3356
3430
  if (!effectiveSessionId || !effectiveEmail) {
3357
3431
  throw new Error("Missing sessionId or email for payment.");
3358
3432
  }
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.
3433
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
3434
+ effectiveSessionId,
3435
+ effectiveNonce ?? "",
3436
+ {
3437
+ provider: "stripe",
3438
+ paymentMethodCategory: "apm",
3370
3439
  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;
3440
+ paymentMethodId: null,
3441
+ intentKind: "payment"
3442
+ }
3443
+ );
3444
+ if (intent.provider !== "stripe") {
3445
+ throw new Error("Invalid provider returned for APM intent");
3379
3446
  }
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");
3447
+ const intentClientSecret = intent.clientSecret;
3383
3448
  if (typeof window !== "undefined") {
3384
3449
  try {
3385
3450
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3420,6 +3485,17 @@ function StripePaymentElementInner({
3420
3485
  const message = confirmError.message ?? "Payment failed.";
3421
3486
  onErrorChange?.(message);
3422
3487
  onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
3488
+ await reportNonCardDecline({
3489
+ billingApiUrl,
3490
+ sessionId: effectiveSessionId,
3491
+ nonce: effectiveNonce,
3492
+ request: {
3493
+ provider: "stripe",
3494
+ paymentMethodCategory: "apm",
3495
+ paymentMethodType: method,
3496
+ providerDeclineReason: confirmError.code ?? "provider_declined"
3497
+ }
3498
+ });
3423
3499
  return;
3424
3500
  }
3425
3501
  const piStatus = paymentIntent?.status;
@@ -3467,7 +3543,7 @@ function StripePaymentElementInner({
3467
3543
  sessionId,
3468
3544
  nonce,
3469
3545
  email,
3470
- baseUrl,
3546
+ billingApiUrl,
3471
3547
  billingName,
3472
3548
  onTokenizedBody,
3473
3549
  onErrorChange,
@@ -3495,7 +3571,7 @@ function StripePaymentElementInner({
3495
3571
  paymentMethodTypes: [localExpandedMethod]
3496
3572
  };
3497
3573
  }, [localExpandedMethod, paymentElementBaseOptions]);
3498
- return /* @__PURE__ */ jsx8(
3574
+ return /* @__PURE__ */ jsx7(
3499
3575
  "div",
3500
3576
  {
3501
3577
  "data-testid": "flopay-stripe-payment-element-region",
@@ -3504,8 +3580,8 @@ function StripePaymentElementInner({
3504
3580
  const isExpanded = expandedApmMethod === method;
3505
3581
  const isSubmittingThis = submittingMethod === method;
3506
3582
  const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
3507
- return /* @__PURE__ */ jsxs5(React8.Fragment, { children: [
3508
- /* @__PURE__ */ jsx8(
3583
+ return /* @__PURE__ */ jsxs5(React7.Fragment, { children: [
3584
+ /* @__PURE__ */ jsx7(
3509
3585
  StripeMethodButton,
3510
3586
  {
3511
3587
  method,
@@ -3522,12 +3598,12 @@ function StripePaymentElementInner({
3522
3598
  fontFamily: buttonAppearance?.fontFamily
3523
3599
  }
3524
3600
  ),
3525
- !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx8(
3601
+ !onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ jsx7(
3526
3602
  StripeElements,
3527
3603
  {
3528
3604
  stripe: stripeInstance,
3529
3605
  options: localInlineElementsOptions,
3530
- children: /* @__PURE__ */ jsx8(
3606
+ children: /* @__PURE__ */ jsx7(
3531
3607
  StripeMethodInlineForm,
3532
3608
  {
3533
3609
  method,
@@ -3568,15 +3644,10 @@ function SplitCardFormInner({
3568
3644
  firstName,
3569
3645
  lastName,
3570
3646
  chv,
3571
- submitLabel = "CONFIRM PAYMENT",
3572
3647
  className,
3573
- children,
3574
3648
  isProcessing: externalProcessing,
3575
3649
  error: externalError,
3576
3650
  onErrorChange,
3577
- onFullNameChange,
3578
- onFirstNameChange,
3579
- onLastNameChange,
3580
3651
  showPayPal = true,
3581
3652
  showStripe = true,
3582
3653
  enabledPaymentMethods,
@@ -3615,17 +3686,14 @@ function SplitCardFormInner({
3615
3686
  directPaypal,
3616
3687
  isSubscription = false,
3617
3688
  session,
3618
- debug = false,
3619
- innerRef
3689
+ debug = false
3620
3690
  }) {
3621
3691
  const flopay = useFloPay();
3622
3692
  const paypalFlopay = usePayPalFloPay();
3623
- const elements = useElements();
3624
- const checkout = useContext3(CheckoutContext);
3693
+ const checkout = useContext2(CheckoutContext);
3625
3694
  const contextBillingUrl = useBillingApiUrl();
3626
3695
  const [processing, setProcessing] = useState3(false);
3627
3696
  const [error, setError] = useState3(null);
3628
- const [is3DSActive, setIs3DSActive] = useState3(false);
3629
3697
  const [selectedCountry, setSelectedCountry] = useState3(countryProp ?? "US");
3630
3698
  const [zipCode, setZipCode] = useState3(zipProp ?? "");
3631
3699
  const [addressLine1, setAddressLine1] = useState3(addressLine1Prop ?? "");
@@ -3633,18 +3701,19 @@ function SplitCardFormInner({
3633
3701
  const [city, setCity] = useState3(cityProp ?? "");
3634
3702
  const [stateValue, setStateValue] = useState3(stateProp ?? "");
3635
3703
  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 ?? "");
3704
+ const zipCodeRef = useRef4(zipProp ?? "");
3705
+ const selectedCountryRef = useRef4(countryProp ?? "US");
3706
+ const addressLine1Ref = useRef4(addressLine1Prop ?? "");
3707
+ const addressLine2Ref = useRef4(addressLine2Prop ?? "");
3708
+ const cityRef = useRef4(cityProp ?? "");
3709
+ const stateRef = useRef4(stateProp ?? "");
3642
3710
  const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
3643
3711
  const enableAVS = avsConfig !== null;
3644
3712
  const vaultBlockReady = Boolean(session?.vault?.html);
3645
3713
  const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
3714
+ const vaultCapabilityAvailable = vaultBlockReady || vaultGatewayAdvertised;
3646
3715
  const vaultActive = Boolean(
3647
- showStripe && (vaultBlockReady || vaultGatewayAdvertised) && flopay && sessionId
3716
+ showStripe && vaultCapabilityAvailable && flopay && sessionId
3648
3717
  );
3649
3718
  const cardCapture = useMemo3(() => {
3650
3719
  if (!flopay || !vaultActive || !sessionId) return null;
@@ -3676,7 +3745,9 @@ function SplitCardFormInner({
3676
3745
  const [vaultMount, setVaultMount] = useState3(
3677
3746
  () => toVaultMount(session?.vault)
3678
3747
  );
3679
- const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
3748
+ const [viewState, setViewState] = useState3(
3749
+ initialCardOpen && vaultCapabilityAvailable ? "card" : "buttons"
3750
+ );
3680
3751
  const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
3681
3752
  const showCardForm = viewState === "expanding" || viewState === "card";
3682
3753
  const TRANSITION_MS = 280;
@@ -3700,18 +3771,16 @@ function SplitCardFormInner({
3700
3771
  setExpandedApmMethod(null);
3701
3772
  }, TRANSITION_MS);
3702
3773
  }, []);
3703
- useEffect5(() => {
3704
- if (layout === "buttons" && initialCardOpen) {
3774
+ useEffect4(() => {
3775
+ if (layout === "buttons" && initialCardOpen && vaultCapabilityAvailable) {
3705
3776
  setViewState("card");
3706
3777
  }
3707
- }, [layout, initialCardOpen]);
3708
- const [fullName, setFullName] = useState3("");
3709
- const [formReady, setFormReady] = useState3(false);
3778
+ }, [layout, initialCardOpen, vaultCapabilityAvailable]);
3710
3779
  const [overlayStatus, setOverlayStatus] = useState3(null);
3711
- const processingRef = useRef5(false);
3780
+ const processingRef = useRef4(false);
3712
3781
  const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
3713
- const paypalDirectRetryRef = useRef5(paypalDirectRetry);
3714
- useEffect5(() => {
3782
+ const paypalDirectRetryRef = useRef4(paypalDirectRetry);
3783
+ useEffect4(() => {
3715
3784
  paypalDirectRetryRef.current = paypalDirectRetry;
3716
3785
  }, [paypalDirectRetry]);
3717
3786
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
@@ -3759,7 +3828,6 @@ function SplitCardFormInner({
3759
3828
  }, [appearance, bStyles, layout]);
3760
3829
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
3761
3830
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
3762
- const isSelfContained = !onTokenizedBody;
3763
3831
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
3764
3832
  const resolvedAccount = useMemo3(() => mergeAccountPatch({
3765
3833
  userId,
@@ -3769,6 +3837,7 @@ function SplitCardFormInner({
3769
3837
  country: countryProp,
3770
3838
  zip: zipProp
3771
3839
  }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
3840
+ const resolvedBillingName = [resolvedAccount.firstName, resolvedAccount.lastName].filter((part) => Boolean(part?.trim())).join(" ");
3772
3841
  const stripeInstance = useMemo3(() => {
3773
3842
  if (!flopay) return null;
3774
3843
  return flopay.getRawProvider();
@@ -3825,11 +3894,11 @@ function SplitCardFormInner({
3825
3894
  },
3826
3895
  [onErrorChange]
3827
3896
  );
3828
- useEffect5(() => {
3897
+ useEffect4(() => {
3829
3898
  if (!vaultActive || !cardCapture) return;
3830
3899
  cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
3831
3900
  }, [vaultActive, cardCapture, cardFieldOrder, avsConfig]);
3832
- useEffect5(() => {
3901
+ useEffect4(() => {
3833
3902
  if (viewState === "expanding" || viewState === "collapsing" || viewState === "apm-expanding" || viewState === "apm-collapsing") {
3834
3903
  updateError(null);
3835
3904
  }
@@ -3864,7 +3933,7 @@ function SplitCardFormInner({
3864
3933
  },
3865
3934
  [layout, onError, updateError]
3866
3935
  );
3867
- useEffect5(() => {
3936
+ useEffect4(() => {
3868
3937
  if (!vaultActive || !sessionId) {
3869
3938
  setVaultMount(null);
3870
3939
  return;
@@ -3902,13 +3971,12 @@ function SplitCardFormInner({
3902
3971
  nonce,
3903
3972
  updateError
3904
3973
  ]);
3905
- const vaultOutcomeRef = useRef5({
3974
+ const vaultOutcomeRef = useRef4({
3906
3975
  onComplete,
3907
3976
  onError,
3908
3977
  updateError,
3909
3978
  emitDecline,
3910
3979
  resolvedAccount,
3911
- fullName,
3912
3980
  avsConfig,
3913
3981
  avsCheckProp,
3914
3982
  sessionId,
@@ -3921,16 +3989,15 @@ function SplitCardFormInner({
3921
3989
  updateError,
3922
3990
  emitDecline,
3923
3991
  resolvedAccount,
3924
- fullName,
3925
3992
  avsConfig,
3926
3993
  avsCheckProp,
3927
3994
  sessionId,
3928
3995
  nonce,
3929
3996
  baseUrl
3930
3997
  };
3931
- const vaultCompletedRef = useRef5(false);
3998
+ const vaultCompletedRef = useRef4(false);
3932
3999
  const buildVaultAccountSnapshot = useCallback3(() => {
3933
- const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
4000
+ const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
3934
4001
  const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
3935
4002
  const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
3936
4003
  const line1Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_1, cc) : false;
@@ -3943,8 +4010,8 @@ function SplitCardFormInner({
3943
4010
  accountData: {
3944
4011
  userId: resolvedAccount2.userId ?? "",
3945
4012
  email: resolvedAccount2.email ?? "",
3946
- firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
3947
- lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
4013
+ firstName: resolvedAccount2.firstName ?? "",
4014
+ lastName: resolvedAccount2.lastName ?? "",
3948
4015
  ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
3949
4016
  ...cityVisible && cityRef.current ? { city: cityRef.current } : {},
3950
4017
  ...stateValue2 ? { state: stateValue2 } : {},
@@ -3965,11 +4032,11 @@ function SplitCardFormInner({
3965
4032
  } : {}
3966
4033
  };
3967
4034
  }, []);
3968
- useEffect5(() => {
4035
+ useEffect4(() => {
3969
4036
  if (!vaultActive || !cardCapture) return;
3970
4037
  cardCapture.setSubmitGate?.(avsInvalid);
3971
4038
  }, [vaultActive, cardCapture, avsInvalid]);
3972
- useEffect5(() => {
4039
+ useEffect4(() => {
3973
4040
  if (!vaultActive || !cardCapture) return;
3974
4041
  vaultCompletedRef.current = false;
3975
4042
  let cancelled = false;
@@ -4072,21 +4139,25 @@ function SplitCardFormInner({
4072
4139
  const [walletLoadState, setWalletLoadState] = useState3("loading");
4073
4140
  const [paymentElementLoadState, setPaymentElementLoadState] = useState3("loading");
4074
4141
  const [directPaypalReady, setDirectPaypalReady] = useState3(false);
4142
+ const [directPaypalInitializationState, setDirectPaypalInitializationState] = useState3("loading");
4075
4143
  const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
4076
- useEffect5(() => {
4144
+ useEffect4(() => {
4077
4145
  setInAppBrowserDetected(isInAppBrowser());
4078
4146
  }, []);
4079
4147
  const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
4080
4148
  const shouldShowWallets = showStripe && showWallets;
4081
4149
  const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
4082
- const shouldRenderStripePayPal = shouldShowPayPal && !directPaypalConfigured && !!paypalStripeInstance;
4150
+ const directPaypalUnavailable = directPaypalInitializationState === "retrying" || directPaypalInitializationState === "exhausted";
4151
+ const shouldRenderStripePayPal = shouldShowPayPal && !!paypalStripeInstance && (!directPaypalConfigured || directPaypalUnavailable);
4083
4152
  const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
4084
4153
  const shouldRenderPaymentElement = showStripe && hasEnabledMethods && paymentElementMethodsForCurrency.length > 0 && !!stripeInstance;
4085
- const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4154
+ const shouldDisplayPayPalRow = shouldRenderDirectPayPal && !directPaypalUnavailable ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4086
4155
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
4087
4156
  const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
4088
- const validationFiredRef = useRef5(false);
4089
- useEffect5(() => {
4157
+ const expressSurfaceUsable = (state) => state === "loading" || state === "ready";
4158
+ const hasUsableNonCardCapability = shouldRenderDirectPayPal || shouldRenderStripePayPal && expressSurfaceUsable(paypalLoadState) || shouldRenderWallets && expressSurfaceUsable(walletLoadState) || shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
4159
+ const validationFiredRef = useRef4(false);
4160
+ useEffect4(() => {
4090
4161
  if (validationFiredRef.current) return;
4091
4162
  if (!showStripe && !showPayPal) {
4092
4163
  validationFiredRef.current = true;
@@ -4104,10 +4175,29 @@ function SplitCardFormInner({
4104
4175
  );
4105
4176
  onError?.(err);
4106
4177
  updateError(err.message);
4178
+ } else if (inAppBrowserDetected !== void 0 && !vaultCapabilityAvailable && !hasUsableNonCardCapability) {
4179
+ validationFiredRef.current = true;
4180
+ const err = new FloPayError3(
4181
+ "FloPay: this checkout session has no vault card capability and no usable supported non-card payment methods. Upgrade the billing backend integration.",
4182
+ "api_error",
4183
+ { code: "UnsupportedBackendVaultCapability" }
4184
+ );
4185
+ onError?.(err);
4186
+ updateError(err.message);
4107
4187
  }
4108
- }, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
4109
- const deprecationLoggedRef = useRef5(false);
4110
- useEffect5(() => {
4188
+ }, [
4189
+ showStripe,
4190
+ showPayPal,
4191
+ directPaypalConfigured,
4192
+ paypalStripeInstance,
4193
+ inAppBrowserDetected,
4194
+ vaultCapabilityAvailable,
4195
+ hasUsableNonCardCapability,
4196
+ onError,
4197
+ updateError
4198
+ ]);
4199
+ const deprecationLoggedRef = useRef4(false);
4200
+ useEffect4(() => {
4111
4201
  if (deprecationLoggedRef.current) return;
4112
4202
  if (!hasEnabledMethods) return;
4113
4203
  const stale = [];
@@ -4119,13 +4209,6 @@ function SplitCardFormInner({
4119
4209
  `[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
4210
  );
4121
4211
  }, [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
4212
  const applyInlineSessionPatch = useCallback3(
4130
4213
  (patch, method) => {
4131
4214
  if (!checkout.applyInlineSessionPatch) {
@@ -4206,8 +4289,8 @@ function SplitCardFormInner({
4206
4289
  accountData: {
4207
4290
  userId: effectiveAccount.userId ?? "",
4208
4291
  email: effectiveAccount.email ?? "",
4209
- firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
4210
- lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
4292
+ firstName: effectiveAccount.firstName ?? "",
4293
+ lastName: effectiveAccount.lastName ?? "",
4211
4294
  ...avsConfig ? (() => {
4212
4295
  const c = selectedCountryRef.current;
4213
4296
  const stateVisible = isAVSFieldVisible(avsConfig.state, c);
@@ -4255,70 +4338,6 @@ function SplitCardFormInner({
4255
4338
  return;
4256
4339
  }
4257
4340
  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
4341
  if (json?.type === "paypal_redirect_required") {
4323
4342
  const secret = json["threeDSecureToken"];
4324
4343
  const savedPmId = json["paymentMethodId"] || tokenizedBody.id || "";
@@ -4372,6 +4391,8 @@ function SplitCardFormInner({
4372
4391
  emitDecline("paypal", "paypal_direct_required retry limit exceeded");
4373
4392
  return;
4374
4393
  }
4394
+ setDirectPaypalReady(false);
4395
+ setDirectPaypalInitializationState("loading");
4375
4396
  setPaypalDirectRetry({ orderId, attempts: prevAttempts + 1 });
4376
4397
  setOverlayStatus(null);
4377
4398
  return;
@@ -4394,7 +4415,7 @@ function SplitCardFormInner({
4394
4415
  processingRef.current = false;
4395
4416
  }
4396
4417
  },
4397
- [baseUrl, sessionId, nonce, resolvedAccount, fullName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
4418
+ [baseUrl, sessionId, nonce, resolvedAccount, resolvedBillingName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
4398
4419
  );
4399
4420
  const dispatchTokenizedBody = useCallback3(
4400
4421
  (tokenizedBody, overrides) => {
@@ -4406,35 +4427,8 @@ function SplitCardFormInner({
4406
4427
  },
4407
4428
  [onTokenizedBody, processPaymentInternal]
4408
4429
  );
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(() => {
4430
+ const stripeResumeAttemptedRef = useRef4(false);
4431
+ useEffect4(() => {
4438
4432
  if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
4439
4433
  const params = new URLSearchParams(window.location.search);
4440
4434
  const paymentIntentId = params.get("payment_intent");
@@ -4512,166 +4506,12 @@ function SplitCardFormInner({
4512
4506
  }, persistedOverrides);
4513
4507
  })();
4514
4508
  }, [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);
4509
+ const handleSubmit = useCallback3((event) => {
4510
+ event.preventDefault();
4511
+ }, []);
4512
+ const isReady = flopay !== null;
4673
4513
  if (!isReady) {
4674
- return /* @__PURE__ */ jsx8("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
4514
+ return /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
4675
4515
  }
4676
4516
  const isButtons = layout === "buttons";
4677
4517
  const appearanceVars = appearance?.variables;
@@ -4712,25 +4552,10 @@ function SplitCardFormInner({
4712
4552
  "--flopay-input-font-family": resolvedInputFontFamily,
4713
4553
  "--flopay-input-font-weight": String(resolvedInputFontWeight)
4714
4554
  };
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
4555
  const containerOverrides = bStyles.cardFormContainer ?? {};
4731
4556
  const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
4732
4557
  const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
4733
- const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx8(
4558
+ const vaultCardFieldsNode = cardCapture && vaultMount ? /* @__PURE__ */ jsx7(
4734
4559
  VaultCardFields,
4735
4560
  {
4736
4561
  capture: cardCapture,
@@ -4739,7 +4564,6 @@ function SplitCardFormInner({
4739
4564
  expectedOrigin: vaultMount.expectedOrigin,
4740
4565
  theme: vaultThemeColors,
4741
4566
  onReady: () => {
4742
- setFormReady(true);
4743
4567
  if (!avsConfig || typeof document === "undefined") return;
4744
4568
  const focusFirstAvs = () => {
4745
4569
  const block = document.querySelector('[data-testid="flopay-avs-fields"]');
@@ -4768,7 +4592,7 @@ function SplitCardFormInner({
4768
4592
  if (message) setOverlayStatus(null);
4769
4593
  }
4770
4594
  }
4771
- ) : /* @__PURE__ */ jsx8(
4595
+ ) : /* @__PURE__ */ jsx7(
4772
4596
  "div",
4773
4597
  {
4774
4598
  "data-testid": "flopay-vault-loading",
@@ -4796,7 +4620,7 @@ function SplitCardFormInner({
4796
4620
  display: "flex",
4797
4621
  flexDirection: "column"
4798
4622
  }, children: [
4799
- /* @__PURE__ */ jsx8("style", { children: `
4623
+ /* @__PURE__ */ jsx7("style", { children: `
4800
4624
  .flopay-shared-input::placeholder {
4801
4625
  color: var(--flopay-input-placeholder-color);
4802
4626
  opacity: 1;
@@ -4834,7 +4658,7 @@ function SplitCardFormInner({
4834
4658
  },
4835
4659
  "aria-label": "Back to payment methods",
4836
4660
  children: [
4837
- /* @__PURE__ */ jsx8("span", { style: {
4661
+ /* @__PURE__ */ jsx7("span", { style: {
4838
4662
  display: "inline-flex",
4839
4663
  alignItems: "center",
4840
4664
  justifyContent: "center",
@@ -4844,12 +4668,12 @@ function SplitCardFormInner({
4844
4668
  backgroundColor: "#f3f4f6",
4845
4669
  transition: "background-color 0.15s",
4846
4670
  ...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 })
4671
+ }, 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" }) }) }),
4672
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
4849
4673
  ]
4850
4674
  }
4851
4675
  ),
4852
- hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
4676
+ hideTitle ? /* @__PURE__ */ jsx7("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx7("div", { style: {
4853
4677
  flex: 1,
4854
4678
  textAlign: "center",
4855
4679
  fontWeight: 600,
@@ -4857,9 +4681,9 @@ function SplitCardFormInner({
4857
4681
  color: "#262833",
4858
4682
  paddingRight: 80,
4859
4683
  ...bStyles.title
4860
- }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
4684
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) })
4861
4685
  ] }),
4862
- !isButtons && !hideTitle && /* @__PURE__ */ jsx8("div", { style: {
4686
+ !isButtons && !hideTitle && /* @__PURE__ */ jsx7("div", { style: {
4863
4687
  textAlign: "center",
4864
4688
  fontWeight: 600,
4865
4689
  fontSize: "1.1rem",
@@ -4869,70 +4693,8 @@ function SplitCardFormInner({
4869
4693
  // (-2) and AVS block (-1) are ordered below it but above the card form.
4870
4694
  order: vaultActive ? -3 : 0,
4871
4695
  ...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: (() => {
4696
+ }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) }),
4697
+ avsConfig && /* @__PURE__ */ jsx7("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
4936
4698
  const cc = selectedCountry;
4937
4699
  const inputWrapStyle = (invalid = false) => ({
4938
4700
  backgroundColor: cardInputBg,
@@ -4950,7 +4712,7 @@ function SplitCardFormInner({
4950
4712
  });
4951
4713
  const stateOpts = getStateOptions(cc);
4952
4714
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4953
- isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx8("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx8(
4715
+ isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx7(
4954
4716
  "input",
4955
4717
  {
4956
4718
  className: "flopay-shared-input",
@@ -4969,7 +4731,7 @@ function SplitCardFormInner({
4969
4731
  style: inputFieldStyle()
4970
4732
  }
4971
4733
  ) }),
4972
- isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx8("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx8(
4734
+ isAVSFieldVisible(avsConfig.address_line_2, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(), children: /* @__PURE__ */ jsx7(
4973
4735
  "input",
4974
4736
  {
4975
4737
  className: "flopay-shared-input",
@@ -4992,7 +4754,7 @@ function SplitCardFormInner({
4992
4754
  gap: "0",
4993
4755
  marginTop: "0.5rem"
4994
4756
  }, children: [
4995
- isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx8("div", { style: {
4757
+ isAVSFieldVisible(avsConfig.city, cc) && /* @__PURE__ */ jsx7("div", { style: {
4996
4758
  flex: 1,
4997
4759
  backgroundColor: cardInputBg,
4998
4760
  borderTop: `1px solid ${avsBorderColor(invalidAvsFields.city)}`,
@@ -5003,7 +4765,7 @@ function SplitCardFormInner({
5003
4765
  borderTopLeftRadius: resolvedBorderRadius,
5004
4766
  borderBottomLeftRadius: resolvedBorderRadius,
5005
4767
  ...isAVSFieldVisible(avsConfig.state, cc) ? { borderRight: "none", borderTopRightRadius: 0, borderBottomRightRadius: 0 } : { borderRadius: resolvedBorderRadius }
5006
- }, children: /* @__PURE__ */ jsx8(
4768
+ }, children: /* @__PURE__ */ jsx7(
5007
4769
  "input",
5008
4770
  {
5009
4771
  className: "flopay-shared-input",
@@ -5022,7 +4784,7 @@ function SplitCardFormInner({
5022
4784
  style: inputFieldStyle()
5023
4785
  }
5024
4786
  ) }),
5025
- isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx8("div", { style: {
4787
+ isAVSFieldVisible(avsConfig.state, cc) && /* @__PURE__ */ jsx7("div", { style: {
5026
4788
  flex: 1,
5027
4789
  backgroundColor: cardInputBg,
5028
4790
  border: `1px solid ${avsBorderColor(invalidAvsFields.state)}`,
@@ -5046,11 +4808,11 @@ function SplitCardFormInner({
5046
4808
  "data-testid": "flopay-state",
5047
4809
  style: { ...inputFieldStyle(), cursor: "pointer" },
5048
4810
  children: [
5049
- /* @__PURE__ */ jsx8("option", { value: "", children: getStateLabel(cc) }),
5050
- stateOpts.map((s) => /* @__PURE__ */ jsx8("option", { value: s.code, children: s.name }, s.code))
4811
+ /* @__PURE__ */ jsx7("option", { value: "", children: getStateLabel(cc) }),
4812
+ stateOpts.map((s) => /* @__PURE__ */ jsx7("option", { value: s.code, children: s.name }, s.code))
5051
4813
  ]
5052
4814
  }
5053
- ) : /* @__PURE__ */ jsx8(
4815
+ ) : /* @__PURE__ */ jsx7(
5054
4816
  "input",
5055
4817
  {
5056
4818
  className: "flopay-shared-input",
@@ -5076,7 +4838,7 @@ function SplitCardFormInner({
5076
4838
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
5077
4839
  marginTop: "0.5rem"
5078
4840
  }, children: [
5079
- isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx8("div", { style: {
4841
+ isAVSFieldVisible(avsConfig.country, cc) && /* @__PURE__ */ jsx7("div", { style: {
5080
4842
  flex: avsLayoutProp === "row" ? 1 : void 0,
5081
4843
  backgroundColor: cardInputBg,
5082
4844
  borderTop: `1px solid ${resolvedBorder}`,
@@ -5085,7 +4847,7 @@ function SplitCardFormInner({
5085
4847
  borderLeft: `1px solid ${resolvedBorder}`,
5086
4848
  padding: "10px",
5087
4849
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: resolvedBorderRadius, borderBottomLeftRadius: resolvedBorderRadius, borderRight: "none" } : { borderRadius: resolvedBorderRadius }
5088
- }, children: /* @__PURE__ */ jsx8(
4850
+ }, children: /* @__PURE__ */ jsx7(
5089
4851
  "select",
5090
4852
  {
5091
4853
  id: "flopay-billing-country",
@@ -5109,13 +4871,13 @@ function SplitCardFormInner({
5109
4871
  ] }, c.code))
5110
4872
  }
5111
4873
  ) }),
5112
- isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx8("div", { style: {
4874
+ isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx7("div", { style: {
5113
4875
  flex: avsLayoutProp === "row" ? 1 : void 0,
5114
4876
  backgroundColor: cardInputBg,
5115
4877
  border: `1px solid ${zipBorderColor}`,
5116
4878
  padding: "10px",
5117
4879
  ...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
5118
- }, children: /* @__PURE__ */ jsx8(
4880
+ }, children: /* @__PURE__ */ jsx7(
5119
4881
  "input",
5120
4882
  {
5121
4883
  className: "flopay-shared-input",
@@ -5139,7 +4901,7 @@ function SplitCardFormInner({
5139
4901
  }
5140
4902
  ) })
5141
4903
  ] }),
5142
- showPostcodeError && /* @__PURE__ */ jsx8(
4904
+ showPostcodeError && /* @__PURE__ */ jsx7(
5143
4905
  "div",
5144
4906
  {
5145
4907
  id: "flopay-billing-postal-code-error",
@@ -5156,39 +4918,9 @@ function SplitCardFormInner({
5156
4918
  )
5157
4919
  ] });
5158
4920
  })() }),
5159
- vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx8("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
4921
+ vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
5160
4922
  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
- ))
4923
+ displayError && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5192
4924
  ] });
5193
4925
  const gateDebugStyle = {
5194
4926
  margin: 0,
@@ -5204,9 +4936,9 @@ function SplitCardFormInner({
5204
4936
  const renderDirectPaypalGateDebug = (testId) => {
5205
4937
  if (!debug) return null;
5206
4938
  if (!showPayPal || !directPaypalConfigured) {
5207
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
4939
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
5208
4940
  }
5209
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4941
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
5210
4942
  "FloPay/DirectPayPal-debug (parent gate)",
5211
4943
  ` showPayPal=${showPayPal}`,
5212
4944
  ` directPaypalConfigured=${directPaypalConfigured}`,
@@ -5219,9 +4951,9 @@ function SplitCardFormInner({
5219
4951
  const renderStripeGateDebug = (testId) => {
5220
4952
  if (!debug) return null;
5221
4953
  if (!showStripe || !stripeInstance) {
5222
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
4954
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
5223
4955
  }
5224
- return /* @__PURE__ */ jsx8("pre", { "data-testid": testId, style: gateDebugStyle, children: [
4956
+ return /* @__PURE__ */ jsx7("pre", { "data-testid": testId, style: gateDebugStyle, children: [
5225
4957
  "FloPay/Stripe-debug (parent gate)",
5226
4958
  ` showStripe=${showStripe}`,
5227
4959
  ` currency=${currency}`,
@@ -5251,8 +4983,8 @@ function SplitCardFormInner({
5251
4983
  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
4984
  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
4985
  return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5254
- /* @__PURE__ */ jsx8(FloPayKeyframes, {}),
5255
- overlayStatus && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
4986
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
4987
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5256
4988
  /* @__PURE__ */ jsxs5("div", { style: { display: "grid" }, children: [
5257
4989
  /* @__PURE__ */ jsxs5(
5258
4990
  "div",
@@ -5272,7 +5004,7 @@ function SplitCardFormInner({
5272
5004
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
5273
5005
  renderStripeGateDebug("flopay-stripe-gate-debug"),
5274
5006
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5275
- paypalDirectRetry && /* @__PURE__ */ jsx8(
5007
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
5276
5008
  "div",
5277
5009
  {
5278
5010
  "data-testid": "flopay-paypal-direct-retry-notice",
@@ -5288,7 +5020,7 @@ function SplitCardFormInner({
5288
5020
  children: "Please confirm your PayPal payment to complete checkout."
5289
5021
  }
5290
5022
  ),
5291
- /* @__PURE__ */ jsx8(
5023
+ /* @__PURE__ */ jsx7(
5292
5024
  DirectPayPalButton,
5293
5025
  {
5294
5026
  sessionId,
@@ -5308,6 +5040,7 @@ function SplitCardFormInner({
5308
5040
  runBeforeButtonClick,
5309
5041
  isProcessing: isSubmitting,
5310
5042
  onLoadStateChange: setDirectPaypalReady,
5043
+ onInitializationStateChange: setDirectPaypalInitializationState,
5311
5044
  session: session ?? null,
5312
5045
  existingOrderId: paypalDirectRetry?.orderId,
5313
5046
  debug
@@ -5315,7 +5048,7 @@ function SplitCardFormInner({
5315
5048
  paypalDirectRetry?.orderId ?? "fresh"
5316
5049
  )
5317
5050
  ] }),
5318
- shouldRenderStripePayPal && /* @__PURE__ */ jsx8(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx8(
5051
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
5319
5052
  PayPalButtonInner,
5320
5053
  {
5321
5054
  sessionId,
@@ -5333,13 +5066,14 @@ function SplitCardFormInner({
5333
5066
  placeholderBorderRadius: buttonBorderRadius
5334
5067
  }
5335
5068
  ) }),
5336
- shouldRenderWallets ? /* @__PURE__ */ jsx8(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx8(
5069
+ shouldRenderWallets ? /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
5337
5070
  WalletButtonInner,
5338
5071
  {
5339
5072
  sessionId,
5340
5073
  nonce,
5341
5074
  email: resolvedAccount.email,
5342
5075
  billingApiUrl: resolvedBillingApiUrl,
5076
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5343
5077
  expressMethods: walletExpressMethods,
5344
5078
  onTokenizedBody: dispatchTokenizedBody,
5345
5079
  onErrorChange: updateError,
@@ -5350,8 +5084,8 @@ function SplitCardFormInner({
5350
5084
  onLoadStateChange: setWalletLoadState,
5351
5085
  placeholderBorderRadius: buttonBorderRadius
5352
5086
  }
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(
5087
+ ) }) : 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,
5088
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
5355
5089
  StripePaymentElementInner,
5356
5090
  {
5357
5091
  sessionId,
@@ -5383,7 +5117,7 @@ function SplitCardFormInner({
5383
5117
  }
5384
5118
  }
5385
5119
  ),
5386
- showStripe && /* @__PURE__ */ jsx8(
5120
+ vaultActive && /* @__PURE__ */ jsx7(
5387
5121
  "button",
5388
5122
  {
5389
5123
  type: "button",
@@ -5439,19 +5173,19 @@ function SplitCardFormInner({
5439
5173
  onMouseUp: (e) => {
5440
5174
  e.currentTarget.style.transform = "scale(1)";
5441
5175
  },
5442
- children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
5176
+ children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
5443
5177
  }
5444
5178
  ),
5445
- displayError && viewState === "buttons" && /* @__PURE__ */ jsx8(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5179
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5446
5180
  ]
5447
5181
  }
5448
5182
  ),
5449
- showStripe && isCardView && /* @__PURE__ */ jsx8("div", { style: {
5183
+ vaultActive && isCardView && /* @__PURE__ */ jsx7("div", { style: {
5450
5184
  gridArea: "1 / 1",
5451
5185
  ...cardAnim ? { animation: cardAnim } : {},
5452
5186
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
5453
5187
  }, children: cardFormBlock }),
5454
- showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx8("div", { style: {
5188
+ showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ jsx7("div", { style: {
5455
5189
  gridArea: "1 / 1",
5456
5190
  ...apmAnim ? { animation: apmAnim } : {},
5457
5191
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
@@ -5489,7 +5223,7 @@ function SplitCardFormInner({
5489
5223
  },
5490
5224
  "aria-label": "Back to payment methods",
5491
5225
  children: [
5492
- /* @__PURE__ */ jsx8("span", { style: {
5226
+ /* @__PURE__ */ jsx7("span", { style: {
5493
5227
  display: "inline-flex",
5494
5228
  alignItems: "center",
5495
5229
  justifyContent: "center",
@@ -5499,12 +5233,12 @@ function SplitCardFormInner({
5499
5233
  backgroundColor: "#f3f4f6",
5500
5234
  transition: "background-color 0.15s",
5501
5235
  ...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 })
5236
+ }, 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" }) }) }),
5237
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
5504
5238
  ]
5505
5239
  }
5506
5240
  ),
5507
- /* @__PURE__ */ jsx8("div", { style: {
5241
+ /* @__PURE__ */ jsx7("div", { style: {
5508
5242
  flex: 1,
5509
5243
  textAlign: "center",
5510
5244
  fontWeight: 600,
@@ -5514,12 +5248,12 @@ function SplitCardFormInner({
5514
5248
  ...bStyles.title
5515
5249
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
5516
5250
  ] }),
5517
- /* @__PURE__ */ jsx8(
5251
+ /* @__PURE__ */ jsx7(
5518
5252
  StripeElements,
5519
5253
  {
5520
5254
  stripe: stripeInstance,
5521
5255
  options: apmInlineOptions,
5522
- children: /* @__PURE__ */ jsx8(
5256
+ children: /* @__PURE__ */ jsx7(
5523
5257
  StripeMethodInlineForm,
5524
5258
  {
5525
5259
  method: expandedApmMethod,
@@ -5550,9 +5284,9 @@ function SplitCardFormInner({
5550
5284
  }
5551
5285
  if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
5552
5286
  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: {
5287
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5288
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5289
+ /* @__PURE__ */ jsx7("div", { style: {
5556
5290
  ...apmAnim ? { animation: apmAnim } : {},
5557
5291
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
5558
5292
  }, children: /* @__PURE__ */ jsxs5("div", { style: {
@@ -5589,7 +5323,7 @@ function SplitCardFormInner({
5589
5323
  },
5590
5324
  "aria-label": "Back to payment methods",
5591
5325
  children: [
5592
- /* @__PURE__ */ jsx8("span", { style: {
5326
+ /* @__PURE__ */ jsx7("span", { style: {
5593
5327
  display: "inline-flex",
5594
5328
  alignItems: "center",
5595
5329
  justifyContent: "center",
@@ -5599,12 +5333,12 @@ function SplitCardFormInner({
5599
5333
  backgroundColor: "#f3f4f6",
5600
5334
  transition: "background-color 0.15s",
5601
5335
  ...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 })
5336
+ }, 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" }) }) }),
5337
+ /* @__PURE__ */ jsx7(BackButtonContentSlot, { content: cardBackButtonContent })
5604
5338
  ]
5605
5339
  }
5606
5340
  ),
5607
- /* @__PURE__ */ jsx8("div", { style: {
5341
+ /* @__PURE__ */ jsx7("div", { style: {
5608
5342
  flex: 1,
5609
5343
  textAlign: "center",
5610
5344
  fontWeight: 600,
@@ -5614,12 +5348,12 @@ function SplitCardFormInner({
5614
5348
  ...bStyles.title
5615
5349
  }, children: `Pay with ${getStripeMethodDisplayName(expandedApmMethod)}` })
5616
5350
  ] }),
5617
- /* @__PURE__ */ jsx8(
5351
+ /* @__PURE__ */ jsx7(
5618
5352
  StripeElements,
5619
5353
  {
5620
5354
  stripe: stripeInstance,
5621
5355
  options: apmInlineOptions,
5622
- children: /* @__PURE__ */ jsx8(
5356
+ children: /* @__PURE__ */ jsx7(
5623
5357
  StripeMethodInlineForm,
5624
5358
  {
5625
5359
  method: expandedApmMethod,
@@ -5648,13 +5382,13 @@ function SplitCardFormInner({
5648
5382
  ] });
5649
5383
  }
5650
5384
  return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5651
- /* @__PURE__ */ jsx8(FloPayKeyframes, {}),
5652
- overlayStatus && /* @__PURE__ */ jsx8(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5385
+ /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5386
+ overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5653
5387
  /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
5654
5388
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
5655
5389
  renderStripeGateDebug("flopay-stripe-gate-debug-default"),
5656
5390
  shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5657
- paypalDirectRetry && /* @__PURE__ */ jsx8(
5391
+ paypalDirectRetry && /* @__PURE__ */ jsx7(
5658
5392
  "div",
5659
5393
  {
5660
5394
  "data-testid": "flopay-paypal-direct-retry-notice-default",
@@ -5670,7 +5404,7 @@ function SplitCardFormInner({
5670
5404
  children: "Please confirm your PayPal payment to complete checkout."
5671
5405
  }
5672
5406
  ),
5673
- /* @__PURE__ */ jsx8(
5407
+ /* @__PURE__ */ jsx7(
5674
5408
  DirectPayPalButton,
5675
5409
  {
5676
5410
  sessionId,
@@ -5690,6 +5424,7 @@ function SplitCardFormInner({
5690
5424
  runBeforeButtonClick,
5691
5425
  isProcessing: isSubmitting,
5692
5426
  onLoadStateChange: setDirectPaypalReady,
5427
+ onInitializationStateChange: setDirectPaypalInitializationState,
5693
5428
  session: session ?? null,
5694
5429
  existingOrderId: paypalDirectRetry?.orderId,
5695
5430
  debug
@@ -5697,7 +5432,7 @@ function SplitCardFormInner({
5697
5432
  paypalDirectRetry?.orderId ?? "fresh"
5698
5433
  )
5699
5434
  ] }),
5700
- shouldRenderStripePayPal && /* @__PURE__ */ jsx8(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx8(
5435
+ shouldRenderStripePayPal && /* @__PURE__ */ jsx7(StripeElements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ jsx7(
5701
5436
  PayPalButtonInner,
5702
5437
  {
5703
5438
  sessionId,
@@ -5715,13 +5450,14 @@ function SplitCardFormInner({
5715
5450
  placeholderBorderRadius: buttonBorderRadius
5716
5451
  }
5717
5452
  ) }),
5718
- shouldRenderWallets && /* @__PURE__ */ jsx8(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx8(
5453
+ shouldRenderWallets && /* @__PURE__ */ jsx7(StripeElements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ jsx7(
5719
5454
  WalletButtonInner,
5720
5455
  {
5721
5456
  sessionId,
5722
5457
  nonce,
5723
5458
  email: resolvedAccount.email,
5724
5459
  billingApiUrl: resolvedBillingApiUrl,
5460
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5725
5461
  expressMethods: walletExpressMethods,
5726
5462
  onTokenizedBody: dispatchTokenizedBody,
5727
5463
  onErrorChange: updateError,
@@ -5733,7 +5469,7 @@ function SplitCardFormInner({
5733
5469
  placeholderBorderRadius: buttonBorderRadius
5734
5470
  }
5735
5471
  ) }),
5736
- shouldRenderPaymentElement && /* @__PURE__ */ jsx8(
5472
+ shouldRenderPaymentElement && /* @__PURE__ */ jsx7(
5737
5473
  StripePaymentElementInner,
5738
5474
  {
5739
5475
  sessionId,
@@ -5766,7 +5502,7 @@ function SplitCardFormInner({
5766
5502
  }
5767
5503
  )
5768
5504
  ] }),
5769
- showStripe && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs5("div", { style: {
5505
+ vaultActive && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs5("div", { style: {
5770
5506
  display: "flex",
5771
5507
  alignItems: "center",
5772
5508
  gap: "0.75rem",
@@ -5774,11 +5510,11 @@ function SplitCardFormInner({
5774
5510
  color: "#999",
5775
5511
  fontSize: "0.85rem"
5776
5512
  }, 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" } })
5513
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } }),
5514
+ /* @__PURE__ */ jsx7("span", { children: "or pay with card" }),
5515
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
5780
5516
  ] }),
5781
- showStripe && cardFormBlock
5517
+ vaultActive && cardFormBlock
5782
5518
  ] });
5783
5519
  }
5784
5520
 
@@ -6477,7 +6213,7 @@ async function loadSavedPaymentProviders({
6477
6213
  }
6478
6214
 
6479
6215
  // src/flopay-checkout.tsx
6480
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
6216
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6481
6217
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
6482
6218
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
6483
6219
  var sessionInflightMap = /* @__PURE__ */ new Map();
@@ -6623,7 +6359,6 @@ function FloPayCheckout({
6623
6359
  onComplete,
6624
6360
  onError,
6625
6361
  onDecline,
6626
- onFullNameChange,
6627
6362
  onCountryChange,
6628
6363
  onZipChange,
6629
6364
  showPayPal = true,
@@ -6645,7 +6380,6 @@ function FloPayCheckout({
6645
6380
  cardFieldOrder,
6646
6381
  cardPreFormSlot,
6647
6382
  avsLayout,
6648
- submitLabel,
6649
6383
  className,
6650
6384
  initialErrorMessage = null,
6651
6385
  children,
@@ -6661,9 +6395,9 @@ function FloPayCheckout({
6661
6395
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
6662
6396
  const [unified, setUnified] = useState4(null);
6663
6397
  const [flopay, setFloPay] = useState4(null);
6664
- const flopayRef = useRef6(null);
6398
+ const flopayRef = useRef5(null);
6665
6399
  const [paypalFlopay, setPaypalFloPay] = useState4(null);
6666
- const paypalFlopayRef = useRef6(null);
6400
+ const paypalFlopayRef = useRef5(null);
6667
6401
  const [session, setSession] = useState4(null);
6668
6402
  const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
6669
6403
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
@@ -6678,28 +6412,53 @@ function FloPayCheckout({
6678
6412
  const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
6679
6413
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
6680
6414
  const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
6681
- const autoCheckoutAttempted = useRef6(false);
6682
- const paypalResumeAttempted = useRef6(false);
6683
- const savedPaymentKeysRef = useRef6(null);
6415
+ const [paypalInitializationState, setPaypalInitializationState] = useState4("loading");
6416
+ const [paypalManualRetryGeneration, setPaypalManualRetryGeneration] = useState4(0);
6417
+ const autoCheckoutAttempted = useRef5(false);
6418
+ const paypalResumeAttempted = useRef5(false);
6419
+ const paypalInitializationErrorEmittedRef = useRef5(false);
6420
+ const savedPaymentKeysRef = useRef5(null);
6684
6421
  const telemetryCheckoutContext = useMemo4(() => ({
6685
6422
  checkoutMode: checkoutModeProp ?? currentMode,
6686
6423
  layout: children ? "unknown" : layout === "buttons" ? "buttons" : "embedded"
6687
6424
  }), [checkoutModeProp, children, currentMode, layout]);
6688
- const onCompleteRef = useRef6(onComplete);
6425
+ const onCompleteRef = useRef5(onComplete);
6689
6426
  onCompleteRef.current = onComplete;
6690
- const onErrorRef = useRef6(onError);
6427
+ const onErrorRef = useRef5(onError);
6691
6428
  onErrorRef.current = onError;
6692
- const onDeclineRef = useRef6(onDecline);
6429
+ const onDeclineRef = useRef5(onDecline);
6693
6430
  onDeclineRef.current = onDecline;
6694
- useEffect6(() => {
6431
+ useEffect5(() => {
6695
6432
  getFloPayTelemetryBridge(flopay)?.setCheckoutContext(telemetryCheckoutContext);
6696
6433
  if (paypalFlopay && paypalFlopay !== flopay) {
6697
6434
  getFloPayTelemetryBridge(paypalFlopay)?.setCheckoutContext(telemetryCheckoutContext);
6698
6435
  }
6699
6436
  }, [flopay, paypalFlopay, telemetryCheckoutContext]);
6700
- const onSessionCompletedRef = useRef6(onSessionCompleted);
6437
+ const onSessionCompletedRef = useRef5(onSessionCompleted);
6701
6438
  onSessionCompletedRef.current = onSessionCompleted;
6702
- useEffect6(() => {
6439
+ const handlePayPalInitializationStateChange = useCallback4(
6440
+ (state) => {
6441
+ setPaypalInitializationState(state);
6442
+ if (state !== "exhausted" || paypalInitializationErrorEmittedRef.current) return;
6443
+ paypalInitializationErrorEmittedRef.current = true;
6444
+ onErrorRef.current?.(new FloPayError5(
6445
+ "PayPal is temporarily unavailable. Please try again.",
6446
+ "api_error",
6447
+ { code: "paypal_init_timeout" }
6448
+ ));
6449
+ },
6450
+ []
6451
+ );
6452
+ const retryPayPalInitialization = useCallback4(() => {
6453
+ setPaypalInitializationState("retrying");
6454
+ setPaypalManualRetryGeneration((generation) => generation + 1);
6455
+ }, []);
6456
+ useEffect5(() => {
6457
+ setPaypalInitializationState("loading");
6458
+ setPaypalManualRetryGeneration(0);
6459
+ paypalInitializationErrorEmittedRef.current = false;
6460
+ }, [activeSessionId]);
6461
+ useEffect5(() => {
6703
6462
  console.info("[FloPay] Checkout initialized", {
6704
6463
  sdk_version: SDK_VERSION2,
6705
6464
  checkout_type: checkoutType,
@@ -6727,11 +6486,11 @@ function FloPayCheckout({
6727
6486
  } : void 0,
6728
6487
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
6729
6488
  );
6730
- useEffect6(() => {
6489
+ useEffect5(() => {
6731
6490
  setCreateSessionPatch(void 0);
6732
6491
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
6733
6492
  }, [baseCreateSessionHash]);
6734
- useEffect6(() => {
6493
+ useEffect5(() => {
6735
6494
  setModeError(initialErrorMessage);
6736
6495
  }, [initialErrorMessage]);
6737
6496
  const emitDecline = useCallback4(
@@ -7008,7 +6767,7 @@ function FloPayCheckout({
7008
6767
  telemetryCheckoutContext
7009
6768
  ]
7010
6769
  );
7011
- useEffect6(() => {
6770
+ useEffect5(() => {
7012
6771
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
7013
6772
  return;
7014
6773
  }
@@ -7211,7 +6970,7 @@ function FloPayCheckout({
7211
6970
  }
7212
6971
  })();
7213
6972
  }, [emitDecline, invokeMerchantCallback, locale, normalizeSavedPaymentError, resolvedBillingUrl, telemetry]);
7214
- const initializedHashRef = useRef6(null);
6973
+ const initializedHashRef = useRef5(null);
7215
6974
  function hashCreateParams(params) {
7216
6975
  const key = JSON.stringify({
7217
6976
  c: params?.clientId,
@@ -7248,12 +7007,12 @@ function FloPayCheckout({
7248
7007
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
7249
7008
  [effectiveCreateSession]
7250
7009
  );
7251
- const createSessionParamsRef = useRef6(effectiveCreateSession);
7010
+ const createSessionParamsRef = useRef5(effectiveCreateSession);
7252
7011
  createSessionParamsRef.current = effectiveCreateSession;
7253
- useEffect6(() => {
7012
+ useEffect5(() => {
7254
7013
  setResolvedSessionId(sessionIdProp ?? "");
7255
7014
  }, [sessionIdProp]);
7256
- useEffect6(() => {
7015
+ useEffect5(() => {
7257
7016
  autoCheckoutAttempted.current = false;
7258
7017
  setModeError(initialErrorMessage);
7259
7018
  setModeOverlayError(null);
@@ -7445,7 +7204,7 @@ function FloPayCheckout({
7445
7204
  resolvedSessionId,
7446
7205
  session
7447
7206
  ]);
7448
- useEffect6(() => {
7207
+ useEffect5(() => {
7449
7208
  let cancelled = false;
7450
7209
  setLoadError(null);
7451
7210
  if (createSessionHash) {
@@ -7692,7 +7451,7 @@ function FloPayCheckout({
7692
7451
  ]
7693
7452
  );
7694
7453
  const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && !isPaypalOnlySession && (!flopay || !providerOptions);
7695
- const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx9(
7454
+ const modeOverlay = modeOverlayStatus ? /* @__PURE__ */ jsx8(
7696
7455
  ProcessingOverlay,
7697
7456
  {
7698
7457
  status: modeOverlayStatus,
@@ -7708,7 +7467,7 @@ function FloPayCheckout({
7708
7467
  }
7709
7468
  if (layout === "buttons") {
7710
7469
  const bundleRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
7711
- const skeletonBar = (h) => /* @__PURE__ */ jsx9("div", { style: {
7470
+ const skeletonBar = (h) => /* @__PURE__ */ jsx8("div", { style: {
7712
7471
  height: h,
7713
7472
  borderRadius: bundleRadius,
7714
7473
  background: "#e5e7eb",
@@ -7719,14 +7478,14 @@ function FloPayCheckout({
7719
7478
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7720
7479
  showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7721
7480
  showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7722
- /* @__PURE__ */ jsx9("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7481
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7723
7482
  ] }),
7724
7483
  modeOverlay
7725
7484
  ] });
7726
7485
  }
7727
7486
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7728
7487
  /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
7729
- /* @__PURE__ */ jsx9("div", { style: {
7488
+ /* @__PURE__ */ jsx8("div", { style: {
7730
7489
  width: 24,
7731
7490
  height: 24,
7732
7491
  border: "2px solid #e5e7eb",
@@ -7734,16 +7493,16 @@ function FloPayCheckout({
7734
7493
  borderRadius: "50%",
7735
7494
  animation: "spin 0.6s linear infinite"
7736
7495
  } }),
7737
- /* @__PURE__ */ jsx9("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
7496
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
7738
7497
  ] }),
7739
7498
  modeOverlay
7740
7499
  ] });
7741
7500
  }
7742
7501
  if (loadError) {
7743
7502
  if (errorNode) {
7744
- return /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
7503
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: errorNode(loadError) });
7745
7504
  }
7746
- return /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(
7505
+ return /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
7747
7506
  "div",
7748
7507
  {
7749
7508
  role: "alert",
@@ -7760,7 +7519,7 @@ function FloPayCheckout({
7760
7519
  }
7761
7520
  if (shouldShowInterimButtons) {
7762
7521
  return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7763
- /* @__PURE__ */ jsx9(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx9(
7522
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
7764
7523
  InterimButtonsView,
7765
7524
  {
7766
7525
  onButtonClick,
@@ -7781,27 +7540,42 @@ function FloPayCheckout({
7781
7540
  }
7782
7541
  if (isPaypalOnlySession && session && directPaypalConfig) {
7783
7542
  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,
7543
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs6("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7544
+ modeError && /* @__PURE__ */ jsx8(ErrorBanner, { icon: false, children: modeError }),
7545
+ paypalInitializationState === "retrying" && /* @__PURE__ */ jsx8("div", { role: "status", "aria-live": "polite", children: "PayPal is temporarily unavailable. Retrying\u2026" }),
7546
+ paypalInitializationState === "exhausted" && /* @__PURE__ */ jsxs6("div", { role: "alert", children: [
7547
+ /* @__PURE__ */ jsx8("div", { children: "PayPal is temporarily unavailable. Please try again." }),
7548
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: retryPayPalInitialization, children: "Retry PayPal" })
7549
+ ] }),
7550
+ paypalInitializationState !== "exhausted" && /* @__PURE__ */ jsx8(
7551
+ "div",
7788
7552
  {
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
7553
+ "aria-hidden": paypalInitializationState === "retrying" ? true : void 0,
7554
+ style: paypalInitializationState === "retrying" ? { position: "relative", height: 0, opacity: 0, pointerEvents: "none" } : void 0,
7555
+ children: /* @__PURE__ */ jsx8(
7556
+ InstrumentedDirectPayPalButton,
7557
+ {
7558
+ sessionId: activeSessionId,
7559
+ nonce: session.clientSecret || void 0,
7560
+ billingApiUrl: resolvedBillingUrl,
7561
+ email: session.customer?.email,
7562
+ clientId: directPaypalConfig.clientId,
7563
+ environment: directPaypalConfig.environment,
7564
+ currency: (session.currency ?? "usd").toUpperCase(),
7565
+ isSubscription: session.mode === "subscription",
7566
+ onComplete,
7567
+ onErrorChange: setModeError,
7568
+ onDecline,
7569
+ onButtonClick,
7570
+ onInitializationStateChange: handlePayPalInitializationStateChange,
7571
+ allowAutomaticRetry: paypalManualRetryGeneration === 0,
7572
+ session,
7573
+ telemetry,
7574
+ telemetryContext: telemetryCheckoutContext,
7575
+ debug
7576
+ },
7577
+ `paypal-init-${paypalManualRetryGeneration}`
7578
+ )
7805
7579
  }
7806
7580
  )
7807
7581
  ] }) }),
@@ -7809,12 +7583,12 @@ function FloPayCheckout({
7809
7583
  ] });
7810
7584
  }
7811
7585
  if (!flopay || !providerOptions) {
7812
- return /* @__PURE__ */ jsx9(Fragment3, { children: modeOverlay });
7586
+ return /* @__PURE__ */ jsx8(Fragment3, { children: modeOverlay });
7813
7587
  }
7814
7588
  if (currentMode === "confirm") {
7815
7589
  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(
7590
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs6("div", { className, children: [
7591
+ modeError && /* @__PURE__ */ jsx8(
7818
7592
  "div",
7819
7593
  {
7820
7594
  style: {
@@ -7829,7 +7603,7 @@ function FloPayCheckout({
7829
7603
  renderConfirmButton ? renderConfirmButton({
7830
7604
  onConfirm: handleConfirmCheckout,
7831
7605
  isProcessing: confirmProcessing
7832
- }) : /* @__PURE__ */ jsx9(
7606
+ }) : /* @__PURE__ */ jsx8(
7833
7607
  "button",
7834
7608
  {
7835
7609
  type: "button",
@@ -7855,8 +7629,8 @@ function FloPayCheckout({
7855
7629
  ] });
7856
7630
  }
7857
7631
  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(
7632
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
7633
+ modeError && /* @__PURE__ */ jsx8(
7860
7634
  "div",
7861
7635
  {
7862
7636
  style: {
@@ -7871,7 +7645,7 @@ function FloPayCheckout({
7871
7645
  children: modeError
7872
7646
  }
7873
7647
  ),
7874
- /* @__PURE__ */ jsx9(
7648
+ /* @__PURE__ */ jsx8(
7875
7649
  SessionInjector,
7876
7650
  {
7877
7651
  sessionId: activeSessionId,
@@ -7881,7 +7655,7 @@ function FloPayCheckout({
7881
7655
  children
7882
7656
  }
7883
7657
  )
7884
- ] }) : /* @__PURE__ */ jsx9(
7658
+ ] }) : /* @__PURE__ */ jsx8(
7885
7659
  SplitCardForm,
7886
7660
  {
7887
7661
  sessionId: activeSessionId,
@@ -7897,7 +7671,6 @@ function FloPayCheckout({
7897
7671
  onDecline,
7898
7672
  error: modeError,
7899
7673
  onErrorChange: setModeError,
7900
- onFullNameChange,
7901
7674
  showPayPal,
7902
7675
  showStripe,
7903
7676
  enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
@@ -7926,7 +7699,6 @@ function FloPayCheckout({
7926
7699
  avsCheck: !!enableAVS,
7927
7700
  checkoutType: createSessionParams ? "embedded_checkout" : "standard_checkout",
7928
7701
  checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout",
7929
- submitLabel,
7930
7702
  className,
7931
7703
  directPaypal: resolveDirectPaypalConfig(unified),
7932
7704
  isSubscription: session?.mode === "subscription",
@@ -7944,8 +7716,8 @@ function SessionInjector({
7944
7716
  session,
7945
7717
  children
7946
7718
  }) {
7947
- return /* @__PURE__ */ jsx9(Fragment3, { children: React9.Children.map(children, (child) => {
7948
- if (!React9.isValidElement(child)) return child;
7719
+ return /* @__PURE__ */ jsx8(Fragment3, { children: React8.Children.map(children, (child) => {
7720
+ if (!React8.isValidElement(child)) return child;
7949
7721
  const existing = child.props;
7950
7722
  const injected = {};
7951
7723
  if (!existing.sessionId) injected.sessionId = sessionId;
@@ -7960,7 +7732,7 @@ function SessionInjector({
7960
7732
  injected.lastName = session.customer.lastName;
7961
7733
  }
7962
7734
  if (Object.keys(injected).length === 0) return child;
7963
- return React9.cloneElement(child, injected);
7735
+ return React8.cloneElement(child, injected);
7964
7736
  }) });
7965
7737
  }
7966
7738
  function InterimButtonsView({
@@ -7993,7 +7765,7 @@ function InterimButtonsView({
7993
7765
  };
7994
7766
  }, [themeBundle, buttonsTheme, stylesOverride]);
7995
7767
  const skeletonRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
7996
- const skeleton = (h) => /* @__PURE__ */ jsx9("div", { style: {
7768
+ const skeleton = (h) => /* @__PURE__ */ jsx8("div", { style: {
7997
7769
  height: h,
7998
7770
  borderRadius: skeletonRadius,
7999
7771
  background: "#e5e7eb",
@@ -8035,7 +7807,7 @@ function InterimButtonsView({
8035
7807
  ...bStyles.backButton
8036
7808
  },
8037
7809
  children: [
8038
- /* @__PURE__ */ jsx9("span", { style: {
7810
+ /* @__PURE__ */ jsx8("span", { style: {
8039
7811
  display: "inline-flex",
8040
7812
  alignItems: "center",
8041
7813
  justifyContent: "center",
@@ -8044,12 +7816,12 @@ function InterimButtonsView({
8044
7816
  borderRadius: "50%",
8045
7817
  backgroundColor: "#f3f4f6",
8046
7818
  ...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 })
7819
+ }, 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" }) }) }),
7820
+ /* @__PURE__ */ jsx8(BackButtonContentSlot, { content: cardBackButtonContent })
8049
7821
  ]
8050
7822
  }
8051
7823
  ),
8052
- hideTitle ? /* @__PURE__ */ jsx9("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx9("div", { style: {
7824
+ hideTitle ? /* @__PURE__ */ jsx8("div", { style: { flex: 1 } }) : /* @__PURE__ */ jsx8("div", { style: {
8053
7825
  flex: 1,
8054
7826
  textAlign: "center",
8055
7827
  fontWeight: 600,
@@ -8057,11 +7829,11 @@ function InterimButtonsView({
8057
7829
  color: "#262833",
8058
7830
  paddingRight: 80,
8059
7831
  ...bStyles.title
8060
- }, children: /* @__PURE__ */ jsx9(TitleContentSlot, { content: cardTitleContent }) })
7832
+ }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
8061
7833
  ] }),
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" } }) }),
7834
+ /* @__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
7835
  /* @__PURE__ */ jsxs6("div", { style: { display: "flex" }, children: [
8064
- /* @__PURE__ */ jsx9("div", { style: {
7836
+ /* @__PURE__ */ jsx8("div", { style: {
8065
7837
  flex: 1,
8066
7838
  backgroundColor: inputBg,
8067
7839
  borderTop: "none",
@@ -8071,8 +7843,8 @@ function InterimButtonsView({
8071
7843
  borderBottomLeftRadius: 8,
8072
7844
  padding: 12,
8073
7845
  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: {
7846
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
7847
+ /* @__PURE__ */ jsx8("div", { style: {
8076
7848
  flex: 1,
8077
7849
  backgroundColor: inputBg,
8078
7850
  borderTop: "none",
@@ -8082,10 +7854,10 @@ function InterimButtonsView({
8082
7854
  borderBottomRightRadius: 8,
8083
7855
  padding: 12,
8084
7856
  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" } }) })
7857
+ }, children: /* @__PURE__ */ jsx8("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
8086
7858
  ] }),
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: {
7859
+ /* @__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" } }) }),
7860
+ /* @__PURE__ */ jsx8("div", { style: {
8089
7861
  height: 50,
8090
7862
  borderRadius: 8,
8091
7863
  marginTop: 16,
@@ -8094,7 +7866,7 @@ function InterimButtonsView({
8094
7866
  ...bStyles.submitButton,
8095
7867
  opacity: 0.5
8096
7868
  } }),
8097
- /* @__PURE__ */ jsx9("style", { children: `
7869
+ /* @__PURE__ */ jsx8("style", { children: `
8098
7870
  @keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
8099
7871
  @keyframes flopay-interim-expand {
8100
7872
  0% { opacity: 0; max-height: 0; transform: translateY(-12px); }
@@ -8107,7 +7879,7 @@ function InterimButtonsView({
8107
7879
  return /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
8108
7880
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8109
7881
  showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8110
- showStripe && /* @__PURE__ */ jsx9(
7882
+ showStripe && /* @__PURE__ */ jsx8(
8111
7883
  "button",
8112
7884
  {
8113
7885
  type: "button",
@@ -8141,362 +7913,80 @@ function InterimButtonsView({
8141
7913
  onMouseUp: (e) => {
8142
7914
  e.currentTarget.style.transform = "scale(1)";
8143
7915
  },
8144
- children: /* @__PURE__ */ jsx9(CardButtonContentSlot, { content: cardButtonContent })
7916
+ children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
8145
7917
  }
8146
7918
  ),
8147
- /* @__PURE__ */ jsx9("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
7919
+ /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
8148
7920
  ] });
8149
7921
  }
8150
7922
 
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;
7923
+ // src/elements.tsx
7924
+ import { useEffect as useEffect6, useRef as useRef6, useContext as useContext3 } from "react";
7925
+ import { jsx as jsx9 } from "react/jsx-runtime";
7926
+ function createElementComponent(elementType, displayName) {
7927
+ function ElementComponent({
7928
+ className,
7929
+ id,
7930
+ style,
7931
+ options,
7932
+ onChange,
7933
+ onReady,
7934
+ onFocus,
7935
+ onBlur,
7936
+ onEscape
7937
+ }) {
7938
+ const containerRef = useRef6(null);
7939
+ const elementRef = useRef6(null);
7940
+ const { elements, reportInteractive } = useContext3(FloPayContext);
7941
+ useEffect6(() => {
7942
+ if (!elements || !containerRef.current) return;
7943
+ let mounted = true;
7944
+ (async () => {
7945
+ let element = elements.getElement(elementType);
7946
+ if (!element) {
7947
+ element = await elements.create(elementType, options);
8435
7948
  }
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);
7949
+ if (!mounted || !containerRef.current) {
8447
7950
  return;
8448
7951
  }
8449
- handedOff = isSelfContained;
8450
- dispatchTokenizedBody({
8451
- id: paymentMethodId,
8452
- type: "card",
8453
- threeDSecureActionResultTokenId: paymentIntentId,
8454
- originalPaymentMethodId: pmResult.paymentMethodId
7952
+ element.mount(containerRef.current);
7953
+ elementRef.current = element;
7954
+ if (onChange) element.on("change", onChange);
7955
+ element.on("ready", () => {
7956
+ reportInteractive?.();
7957
+ onReady?.();
8455
7958
  });
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
7959
+ if (onFocus) element.on("focus", onFocus);
7960
+ if (onBlur) element.on("blur", onBlur);
7961
+ if (onEscape) element.on("escape", onEscape);
7962
+ })();
7963
+ return () => {
7964
+ mounted = false;
7965
+ if (elementRef.current) {
7966
+ try {
7967
+ elementRef.current.unmount();
7968
+ } catch {
7969
+ }
7970
+ elementRef.current = null;
8489
7971
  }
8490
- )
8491
- ] })
8492
- ] });
7972
+ };
7973
+ }, [elements, reportInteractive]);
7974
+ return /* @__PURE__ */ jsx9("div", { ref: containerRef, className, id, style });
7975
+ }
7976
+ ElementComponent.displayName = displayName;
7977
+ return ElementComponent;
8493
7978
  }
7979
+ var PaymentElement2 = createElementComponent(
7980
+ "payment",
7981
+ "PaymentElement"
7982
+ );
7983
+ var AddressElement = createElementComponent("address", "AddressElement");
8494
7984
 
8495
7985
  // 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";
7986
+ import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
7987
+ import { FloPayError as FloPayError6 } from "@flopay/shared";
7988
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef7, useState as useState5 } from "react";
7989
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
8500
7990
  function PayPalButton({
8501
7991
  sessionId,
8502
7992
  nonce,
@@ -8514,14 +8004,14 @@ function PayPalButton({
8514
8004
  const flopay = useFloPay();
8515
8005
  const elements = useElements();
8516
8006
  const contextBillingUrl = useBillingApiUrl();
8517
- const [ready, setReady] = useState6(false);
8518
- const [submitting, setSubmitting] = useState6(false);
8007
+ const [ready, setReady] = useState5(false);
8008
+ const [submitting, setSubmitting] = useState5(false);
8519
8009
  const paypalResumeAttempted = useRef7(false);
8520
8010
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
8521
- const processPaymentInternal = useCallback6(
8011
+ const processPaymentInternal = useCallback5(
8522
8012
  async (tokenizedBody) => {
8523
8013
  try {
8524
- const api = new PaymentAPI6(baseUrl);
8014
+ const api = new PaymentAPI5(baseUrl);
8525
8015
  const response = await api.processPayment(userId ?? "", {
8526
8016
  sessionId,
8527
8017
  nonce,
@@ -8546,7 +8036,7 @@ function PayPalButton({
8546
8036
  },
8547
8037
  [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
8548
8038
  );
8549
- const dispatchTokenizedBody = useCallback6(
8039
+ const dispatchTokenizedBody = useCallback5(
8550
8040
  (body) => {
8551
8041
  if (onTokenizedBody) {
8552
8042
  onTokenizedBody(body);
@@ -8556,7 +8046,7 @@ function PayPalButton({
8556
8046
  },
8557
8047
  [onTokenizedBody, processPaymentInternal]
8558
8048
  );
8559
- useEffect8(() => {
8049
+ useEffect7(() => {
8560
8050
  if (!flopay || paypalResumeAttempted.current) return;
8561
8051
  const params = new URLSearchParams(window.location.search);
8562
8052
  const paymentIntentId = params.get("payment_intent");
@@ -8604,13 +8094,13 @@ function PayPalButton({
8604
8094
  }
8605
8095
  })();
8606
8096
  }, [flopay, dispatchTokenizedBody, onErrorChange]);
8607
- const handlePayPalConfirm = useCallback6(async () => {
8097
+ const handlePayPalConfirm = useCallback5(async () => {
8608
8098
  if (!flopay || !elements) return;
8609
8099
  try {
8610
8100
  setSubmitting(true);
8611
8101
  onErrorChange?.(null);
8612
8102
  if (!sessionId || !email) {
8613
- throw new FloPayError7("Missing sessionId or email for PayPal payment", "validation_error");
8103
+ throw new FloPayError6("Missing sessionId or email for PayPal payment", "validation_error");
8614
8104
  }
8615
8105
  const result = await flopay.confirmPayPalPayment({
8616
8106
  billingApiUrl: baseUrl,
@@ -8638,11 +8128,11 @@ function PayPalButton({
8638
8128
  }
8639
8129
  }, [flopay, elements, sessionId, nonce, email, baseUrl, dispatchTokenizedBody, onErrorChange]);
8640
8130
  if (!flopay || !elements) {
8641
- return /* @__PURE__ */ jsx11("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
8131
+ return /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
8642
8132
  }
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(
8133
+ return /* @__PURE__ */ jsxs7(Fragment4, { children: [
8134
+ !ready && /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
8135
+ /* @__PURE__ */ jsx10("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx10(
8646
8136
  "button",
8647
8137
  {
8648
8138
  type: "button",
@@ -8664,7 +8154,7 @@ function PayPalButton({
8664
8154
  children: submitting ? "Processing..." : "PayPal"
8665
8155
  }
8666
8156
  ) }),
8667
- (submitting || isProcessing) && /* @__PURE__ */ jsx11("div", { style: {
8157
+ (submitting || isProcessing) && /* @__PURE__ */ jsx10("div", { style: {
8668
8158
  position: "fixed",
8669
8159
  inset: 0,
8670
8160
  background: "rgba(0,0,0,0.4)",
@@ -8672,7 +8162,7 @@ function PayPalButton({
8672
8162
  alignItems: "center",
8673
8163
  justifyContent: "center",
8674
8164
  zIndex: 1e3
8675
- }, children: /* @__PURE__ */ jsx11("div", { style: {
8165
+ }, children: /* @__PURE__ */ jsx10("div", { style: {
8676
8166
  background: "white",
8677
8167
  borderRadius: 8,
8678
8168
  padding: "1.5rem",
@@ -8684,19 +8174,19 @@ function PayPalButton({
8684
8174
  }
8685
8175
 
8686
8176
  // 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";
8177
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef8, useState as useState6 } from "react";
8178
+ import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
8179
+ import { FloPayError as FloPayError7, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
8180
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
8691
8181
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
8692
8182
  function sleep2(ms) {
8693
8183
  return new Promise((resolve) => setTimeout(resolve, ms));
8694
8184
  }
8695
8185
  function coerceError(err, fallbackMessage) {
8696
- if (err instanceof FloPayError8) {
8186
+ if (err instanceof FloPayError7) {
8697
8187
  return err;
8698
8188
  }
8699
- return new FloPayError8(
8189
+ return new FloPayError7(
8700
8190
  err instanceof Error ? err.message : fallbackMessage,
8701
8191
  "api_error"
8702
8192
  );
@@ -8799,36 +8289,36 @@ function FloPayAutomaticPaymentButton({
8799
8289
  utmMetadata
8800
8290
  ]
8801
8291
  );
8802
- const [isProcessing, setIsProcessing] = useState7(false);
8803
- const [overlayStatus, setOverlayStatus] = useState7(null);
8804
- const [overlayError, setOverlayError] = useState7(null);
8805
- const [fallbackSession, setFallbackSession] = useState7(null);
8292
+ const [isProcessing, setIsProcessing] = useState6(false);
8293
+ const [overlayStatus, setOverlayStatus] = useState6(null);
8294
+ const [overlayError, setOverlayError] = useState6(null);
8295
+ const [fallbackSession, setFallbackSession] = useState6(null);
8806
8296
  const isMountedRef = useRef8(true);
8807
8297
  const threeDsAbortRef = useRef8(null);
8808
8298
  const fallbackSessionRef = useRef8(fallbackSession);
8809
8299
  const onSuccessRef = useRef8(onSuccess);
8810
8300
  const onErrorRef = useRef8(onError);
8811
8301
  const onDeclineRef = useRef8(onDecline);
8812
- useEffect9(() => {
8302
+ useEffect8(() => {
8813
8303
  fallbackSessionRef.current = fallbackSession;
8814
8304
  }, [fallbackSession]);
8815
- useEffect9(() => {
8305
+ useEffect8(() => {
8816
8306
  onSuccessRef.current = onSuccess;
8817
8307
  }, [onSuccess]);
8818
- useEffect9(() => {
8308
+ useEffect8(() => {
8819
8309
  onErrorRef.current = onError;
8820
8310
  }, [onError]);
8821
- useEffect9(() => {
8311
+ useEffect8(() => {
8822
8312
  onDeclineRef.current = onDecline;
8823
8313
  }, [onDecline]);
8824
- useEffect9(() => {
8314
+ useEffect8(() => {
8825
8315
  isMountedRef.current = true;
8826
8316
  return () => {
8827
8317
  isMountedRef.current = false;
8828
8318
  threeDsAbortRef.current?.abort();
8829
8319
  };
8830
8320
  }, []);
8831
- useEffect9(() => {
8321
+ useEffect8(() => {
8832
8322
  if (typeof window === "undefined") {
8833
8323
  return;
8834
8324
  }
@@ -8842,13 +8332,13 @@ function FloPayAutomaticPaymentButton({
8842
8332
  window.removeEventListener("keydown", handleKeyDown);
8843
8333
  };
8844
8334
  }, []);
8845
- const emitDecline = useCallback7((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8335
+ const emitDecline = useCallback6((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8846
8336
  onDeclineRef.current?.(buildDeclineEvent(method, error, {
8847
8337
  code: error.code,
8848
8338
  declineCode: error.declineCode
8849
8339
  }));
8850
8340
  }, []);
8851
- const showSuccess = useCallback7(async (event) => {
8341
+ const showSuccess = useCallback6(async (event) => {
8852
8342
  if (!isMountedRef.current) return;
8853
8343
  setOverlayError(null);
8854
8344
  setOverlayStatus("success");
@@ -8856,7 +8346,7 @@ function FloPayAutomaticPaymentButton({
8856
8346
  if (!isMountedRef.current) return;
8857
8347
  onSuccessRef.current?.(event);
8858
8348
  }, []);
8859
- const showError = useCallback7(async (error, options) => {
8349
+ const showError = useCallback6(async (error, options) => {
8860
8350
  if (!isMountedRef.current) return;
8861
8351
  onErrorRef.current?.(error);
8862
8352
  if (options?.emitDecline) {
@@ -8866,10 +8356,10 @@ function FloPayAutomaticPaymentButton({
8866
8356
  setOverlayStatus("error");
8867
8357
  await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
8868
8358
  }, [emitDecline]);
8869
- const processResolvedSession = useCallback7(async (apiResult, resolvedSessionId, options) => {
8359
+ const processResolvedSession = useCallback6(async (apiResult, resolvedSessionId, options) => {
8870
8360
  const session = apiResult.data.session ?? null;
8871
8361
  if (!session) {
8872
- throw new FloPayError8("No session data returned", "api_error");
8362
+ throw new FloPayError7("No session data returned", "api_error");
8873
8363
  }
8874
8364
  const effectiveNonce = session.clientSecret || nonce;
8875
8365
  if (session.status === "complete") {
@@ -8882,19 +8372,19 @@ function FloPayAutomaticPaymentButton({
8882
8372
  return;
8883
8373
  }
8884
8374
  if (session.status === "expired") {
8885
- throw new FloPayError8("Checkout session has expired.", "api_error", {
8375
+ throw new FloPayError7("Checkout session has expired.", "api_error", {
8886
8376
  code: "checkout_session_expired"
8887
8377
  });
8888
8378
  }
8889
8379
  try {
8890
8380
  if (apiResult.autoProcessingPending) {
8891
- const api = new PaymentAPI7(resolvedBillingUrl);
8381
+ const api = new PaymentAPI6(resolvedBillingUrl);
8892
8382
  const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
8893
8383
  initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
8894
8384
  });
8895
8385
  const completedSession = completed.data.session;
8896
8386
  if (!completedSession || completedSession.status !== "complete") {
8897
- throw new FloPayError8("Automatic payment failed. Please try again.", "api_error", {
8387
+ throw new FloPayError7("Automatic payment failed. Please try again.", "api_error", {
8898
8388
  code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
8899
8389
  });
8900
8390
  }
@@ -8937,7 +8427,7 @@ function FloPayAutomaticPaymentButton({
8937
8427
  }
8938
8428
  );
8939
8429
  }
8940
- const api = new PaymentAPI7(resolvedBillingUrl);
8430
+ const api = new PaymentAPI6(resolvedBillingUrl);
8941
8431
  const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
8942
8432
  initialDelayMs: 0,
8943
8433
  nonce: effectiveNonce
@@ -9022,14 +8512,14 @@ function FloPayAutomaticPaymentButton({
9022
8512
  showError,
9023
8513
  showSuccess
9024
8514
  ]);
9025
- const handleButtonClick = useCallback7(async (event) => {
8515
+ const handleButtonClick = useCallback6(async (event) => {
9026
8516
  buttonProps.onClick?.(event);
9027
8517
  if (event.defaultPrevented || disabled || isProcessing) {
9028
8518
  return;
9029
8519
  }
9030
8520
  setFallbackSession(null);
9031
8521
  if (sessionId && createSessionDraft) {
9032
- const error = new FloPayError8(
8522
+ const error = new FloPayError7(
9033
8523
  "Provide either sessionId or create-session props, not both.",
9034
8524
  "validation_error"
9035
8525
  );
@@ -9041,7 +8531,7 @@ function FloPayAutomaticPaymentButton({
9041
8531
  return;
9042
8532
  }
9043
8533
  if (!sessionId && !createSessionDraft) {
9044
- const error = new FloPayError8(
8534
+ const error = new FloPayError7(
9045
8535
  "Provide a sessionId or the props required to create an automatic payment session.",
9046
8536
  "validation_error"
9047
8537
  );
@@ -9056,7 +8546,7 @@ function FloPayAutomaticPaymentButton({
9056
8546
  setOverlayError(null);
9057
8547
  setOverlayStatus("processing");
9058
8548
  try {
9059
- const api = new PaymentAPI7(resolvedBillingUrl);
8549
+ const api = new PaymentAPI6(resolvedBillingUrl);
9060
8550
  if (sessionId) {
9061
8551
  const result = await api.getUnifiedCheckoutSession(sessionId, nonce);
9062
8552
  await processResolvedSession(result, sessionId);
@@ -9070,7 +8560,7 @@ function FloPayAutomaticPaymentButton({
9070
8560
  fromCreateSession: true
9071
8561
  });
9072
8562
  } catch (err) {
9073
- if (err instanceof FloPayError8 && err.code === "session_auto_completed") {
8563
+ if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
9074
8564
  await showSuccess({
9075
8565
  result: { status: "succeeded" },
9076
8566
  session: null,
@@ -9104,7 +8594,7 @@ function FloPayAutomaticPaymentButton({
9104
8594
  showError,
9105
8595
  showSuccess
9106
8596
  ]);
9107
- const handleFallbackComplete = useCallback7((result) => {
8597
+ const handleFallbackComplete = useCallback6((result) => {
9108
8598
  const activeFallback = fallbackSessionRef.current;
9109
8599
  setFallbackSession(null);
9110
8600
  onSuccessRef.current?.({
@@ -9114,10 +8604,10 @@ function FloPayAutomaticPaymentButton({
9114
8604
  autoCompleted: false
9115
8605
  });
9116
8606
  }, []);
9117
- const handleFallbackError = useCallback7((error) => {
8607
+ const handleFallbackError = useCallback6((error) => {
9118
8608
  onErrorRef.current?.(error);
9119
8609
  }, []);
9120
- const handleFallbackDecline = useCallback7((decline) => {
8610
+ const handleFallbackDecline = useCallback6((decline) => {
9121
8611
  onDeclineRef.current?.(decline);
9122
8612
  }, []);
9123
8613
  const themeBundle = useMemo5(() => resolveTheme3(theme), [theme]);
@@ -9148,8 +8638,8 @@ function FloPayAutomaticPaymentButton({
9148
8638
  const resolvedPrimaryHoverColor = resolvedAppearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
9149
8639
  const resolvedButtonBorderRadius = resolvedAppearanceVars?.borderRadius ?? "8px";
9150
8640
  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(
8641
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
8642
+ /* @__PURE__ */ jsx11(
9153
8643
  "button",
9154
8644
  {
9155
8645
  ...buttonProps,
@@ -9213,17 +8703,17 @@ function FloPayAutomaticPaymentButton({
9213
8703
  e.currentTarget.style.transform = "scale(1)";
9214
8704
  }
9215
8705
  },
9216
- children: /* @__PURE__ */ jsx12(CardButtonContentSlot, { content: children })
8706
+ children: /* @__PURE__ */ jsx11(CardButtonContentSlot, { content: children })
9217
8707
  }
9218
8708
  ),
9219
- overlayStatus && /* @__PURE__ */ jsx12(
8709
+ overlayStatus && /* @__PURE__ */ jsx11(
9220
8710
  ProcessingOverlay,
9221
8711
  {
9222
8712
  status: overlayStatus,
9223
8713
  errorMessage: overlayError
9224
8714
  }
9225
8715
  ),
9226
- fallbackSession && /* @__PURE__ */ jsx12(
8716
+ fallbackSession && /* @__PURE__ */ jsx11(
9227
8717
  "div",
9228
8718
  {
9229
8719
  "data-testid": "flopay-automatic-payment-fallback",
@@ -9244,7 +8734,7 @@ function FloPayAutomaticPaymentButton({
9244
8734
  padding: "1.5rem",
9245
8735
  zIndex: 1100
9246
8736
  },
9247
- children: /* @__PURE__ */ jsx12(
8737
+ children: /* @__PURE__ */ jsx11(
9248
8738
  "div",
9249
8739
  {
9250
8740
  style: {
@@ -9260,7 +8750,7 @@ function FloPayAutomaticPaymentButton({
9260
8750
  flexDirection: "column",
9261
8751
  gap: "1rem"
9262
8752
  },
9263
- children: /* @__PURE__ */ jsx12(
8753
+ children: /* @__PURE__ */ jsx11(
9264
8754
  FloPayCheckout,
9265
8755
  {
9266
8756
  sessionId: fallbackSession.sessionId,
@@ -9287,17 +8777,12 @@ function FloPayAutomaticPaymentButton({
9287
8777
  }
9288
8778
  export {
9289
8779
  AddressElement,
9290
- CardCvcElement,
9291
- CardElement,
9292
- CardExpiryElement,
9293
- CardNumberElement,
9294
- CheckoutForm,
9295
8780
  DirectPayPalButton,
9296
8781
  FloPayAutomaticPaymentButton,
9297
8782
  FloPayCheckout,
9298
8783
  FloPayProvider,
9299
8784
  PayPalButton,
9300
- PaymentElement,
8785
+ PaymentElement2 as PaymentElement,
9301
8786
  SplitCardForm,
9302
8787
  VaultCardFields,
9303
8788
  useCheckout,