@flopay/react 1.4.0 → 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 React8, { 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,41 +359,98 @@ 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 });
366
+ }
367
+
368
+ // src/error-banner.tsx
369
+ import "react";
370
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
371
+ function ErrorBanner({
372
+ children,
373
+ margin,
374
+ icon = true,
375
+ styleOverride
376
+ }) {
377
+ return /* @__PURE__ */ jsxs2(
378
+ "div",
379
+ {
380
+ role: "alert",
381
+ "data-testid": "flopay-error",
382
+ style: {
383
+ margin,
384
+ padding: "0.625rem 0.875rem",
385
+ background: "#FEF2F2",
386
+ border: "1px solid #FECACA",
387
+ borderRadius: "8px",
388
+ color: "#991B1B",
389
+ fontSize: "0.85rem",
390
+ fontWeight: 600,
391
+ ...icon ? { display: "flex", alignItems: "center", gap: "0.5rem" } : {},
392
+ ...styleOverride ?? {}
393
+ },
394
+ children: [
395
+ icon && /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx4(
396
+ "path",
397
+ {
398
+ d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
399
+ stroke: "#DC2626",
400
+ strokeWidth: "2",
401
+ strokeLinecap: "round",
402
+ strokeLinejoin: "round"
403
+ }
404
+ ) }),
405
+ children
406
+ ]
407
+ }
408
+ );
429
409
  }
430
410
 
411
+ // src/payment-logos.generated.ts
412
+ var PAYMENT_METHOD_LOGO_URLS = {
413
+ "alipay": new URL("./payment-logos/alipay.svg", import.meta.url).href,
414
+ "bancontact": new URL("./payment-logos/bancontact.svg", import.meta.url).href,
415
+ "blik": new URL("./payment-logos/blik.svg", import.meta.url).href,
416
+ "eps": new URL("./payment-logos/eps.svg", import.meta.url).href,
417
+ "giropay": new URL("./payment-logos/giropay.svg", import.meta.url).href,
418
+ "ideal": new URL("./payment-logos/ideal.svg", import.meta.url).href,
419
+ "klarna": new URL("./payment-logos/klarna.svg", import.meta.url).href,
420
+ "p24": new URL("./payment-logos/p24.svg", import.meta.url).href,
421
+ "sepa_debit": new URL("./payment-logos/sepa_debit.svg", import.meta.url).href,
422
+ "wechat_pay": new URL("./payment-logos/wechat_pay.svg", import.meta.url).href
423
+ };
424
+
431
425
  // src/split-card-form.tsx
432
- import React7, { 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";
433
427
 
434
428
  // src/hooks.ts
435
- import { useContext as useContext2 } from "react";
429
+ import { useContext } from "react";
436
430
  import { resolveBillingApiUrl as resolveBillingApiUrl2 } from "@flopay/shared";
437
431
  function useFloPay() {
438
- const ctx = useContext2(FloPayContext);
432
+ const ctx = useContext(FloPayContext);
439
433
  return ctx.flopay;
440
434
  }
441
435
  function usePayPalFloPay() {
442
- const ctx = useContext2(FloPayContext);
436
+ const ctx = useContext(FloPayContext);
443
437
  return ctx.paypalFlopay ?? null;
444
438
  }
445
439
  function useElements() {
446
- const ctx = useContext2(FloPayContext);
440
+ const ctx = useContext(FloPayContext);
447
441
  return ctx.elements;
448
442
  }
449
443
  function useCheckout() {
450
- return useContext2(CheckoutContext);
444
+ return useContext(CheckoutContext);
451
445
  }
452
446
  function useBillingApiUrl() {
453
- const ctx = useContext2(FloPayContext);
447
+ const ctx = useContext(FloPayContext);
454
448
  return ctx.billingApiUrl || resolveBillingApiUrl2();
455
449
  }
456
450
 
457
451
  // src/processing-overlay.tsx
458
452
  import "react";
459
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
453
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
460
454
  var PROCESSING_OVERLAY_SUCCESS_DELAY_MS = 1200;
461
455
  var PROCESSING_OVERLAY_ERROR_DELAY_MS = 1500;
462
456
  function ProcessingOverlay({
@@ -480,7 +474,7 @@ function ProcessingOverlay({
480
474
  zIndex: 1e3,
481
475
  backdropFilter: "blur(2px)"
482
476
  },
483
- children: /* @__PURE__ */ jsxs2(
477
+ children: /* @__PURE__ */ jsxs3(
484
478
  "div",
485
479
  {
486
480
  style: {
@@ -496,8 +490,8 @@ function ProcessingOverlay({
496
490
  gap: 16
497
491
  },
498
492
  children: [
499
- /* @__PURE__ */ jsxs2("div", { style: { width: 48, height: 48, position: "relative" }, children: [
500
- status === "processing" && /* @__PURE__ */ jsxs2(
493
+ /* @__PURE__ */ jsxs3("div", { style: { width: 48, height: 48, position: "relative" }, children: [
494
+ status === "processing" && /* @__PURE__ */ jsxs3(
501
495
  "svg",
502
496
  {
503
497
  width: "48",
@@ -512,7 +506,7 @@ function ProcessingOverlay({
512
506
  ]
513
507
  }
514
508
  ),
515
- status === "success" && /* @__PURE__ */ jsx5("div", { style: { animation: "flopay-pop 0.4s cubic-bezier(0.34,1.56,0.64,1)" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
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: [
516
510
  /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#22c55e" }),
517
511
  /* @__PURE__ */ jsx5(
518
512
  "path",
@@ -530,7 +524,7 @@ function ProcessingOverlay({
530
524
  }
531
525
  )
532
526
  ] }) }),
533
- status === "error" && /* @__PURE__ */ jsx5("div", { style: { animation: "flopay-shake 0.4s ease" }, children: /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
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: [
534
528
  /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "11", fill: "#ef4444" }),
535
529
  /* @__PURE__ */ jsx5(
536
530
  "path",
@@ -548,7 +542,7 @@ function ProcessingOverlay({
548
542
  )
549
543
  ] }) })
550
544
  ] }),
551
- /* @__PURE__ */ jsxs2(
545
+ /* @__PURE__ */ jsxs3(
552
546
  "span",
553
547
  {
554
548
  style: {
@@ -717,13 +711,6 @@ function buildDeclineEvent(method, input, overrides) {
717
711
  ...declineCode ? { declineCode } : {}
718
712
  };
719
713
  }
720
- function isRecord(value) {
721
- return typeof value === "object" && value !== null;
722
- }
723
- function readString(payload, key) {
724
- const value = payload?.[key];
725
- return typeof value === "string" && value.trim() ? value : void 0;
726
- }
727
714
  var FRIENDLY_MESSAGE_OVERRIDES = [
728
715
  {
729
716
  match: /^paypal authorization required\.?$/i,
@@ -739,20 +726,8 @@ function applyFriendlyMessageOverride(message) {
739
726
  }
740
727
  return message;
741
728
  }
742
- function buildFloPayApiError(payload, fallbackMessage) {
743
- const nestedError = isRecord(payload?.error) ? payload.error : null;
744
- const rawMessage = readString(payload, "message") ?? readString(nestedError, "message") ?? fallbackMessage;
745
- const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
746
- const code = readString(payload, "code") ?? readString(payload, "gatewayErrorCode") ?? readString(nestedError, "code");
747
- const declineCode = readString(payload, "declineCode") ?? readString(payload, "gatewayDeclineReason") ?? readString(payload, "decline_code") ?? readString(nestedError, "decline_code");
748
- return new FloPayError(message, "api_error", {
749
- ...code ? { code } : {},
750
- ...declineCode ? { declineCode } : {}
751
- });
752
- }
753
- async function buildFloPayApiErrorFromResponse(response, fallbackMessage) {
754
- const payload = await response.json().catch(() => null);
755
- return buildFloPayApiError(payload, fallbackMessage);
729
+ function isSuccessfulPaymentIntentStatus(status) {
730
+ return status === "succeeded" || status === "requires_capture" || status === "processing";
756
731
  }
757
732
  function mapPayPalIntentStatusToPaymentResult(status) {
758
733
  if (status === "succeeded") {
@@ -763,41 +738,6 @@ function mapPayPalIntentStatusToPaymentResult(status) {
763
738
  }
764
739
  return "failed";
765
740
  }
766
- function resolvePaymentIntentPaymentMethodId(paymentIntent) {
767
- const paymentMethod = paymentIntent?.payment_method;
768
- if (typeof paymentMethod === "string" && paymentMethod.startsWith("pm_")) {
769
- return paymentMethod;
770
- }
771
- if (paymentMethod && typeof paymentMethod === "object" && typeof paymentMethod.id === "string") {
772
- return paymentMethod.id;
773
- }
774
- return void 0;
775
- }
776
- async function retrievePaymentIntentFromProvider(provider, clientSecret) {
777
- const retriever = provider;
778
- if (isSetupIntentClientSecret(clientSecret)) {
779
- if (!retriever?.retrieveSetupIntent) {
780
- return null;
781
- }
782
- const { setupIntent, error: error2 } = await retriever.retrieveSetupIntent(clientSecret);
783
- if (error2) {
784
- throw new FloPayError(error2.message ?? "Failed to retrieve setup intent.", "api_error", {
785
- ...error2.code ? { code: error2.code } : {}
786
- });
787
- }
788
- return setupIntent ?? null;
789
- }
790
- if (!retriever?.retrievePaymentIntent) {
791
- return null;
792
- }
793
- const { paymentIntent, error } = await retriever.retrievePaymentIntent(clientSecret);
794
- if (error) {
795
- throw new FloPayError(error.message ?? "Failed to retrieve payment intent.", "api_error", {
796
- ...error.code ? { code: error.code } : {}
797
- });
798
- }
799
- return paymentIntent ?? null;
800
- }
801
741
  function resolveWalletElementsMode(amountInMinorUnits) {
802
742
  if (typeof amountInMinorUnits !== "number" || !Number.isFinite(amountInMinorUnits) || amountInMinorUnits <= 0) {
803
743
  return { mode: "setup" };
@@ -866,10 +806,15 @@ function isInAppBrowser(userAgent) {
866
806
  }
867
807
 
868
808
  // src/direct-paypal-button.tsx
869
- 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";
870
810
  import { loadScript } from "@paypal/paypal-js";
871
811
  import { PaymentAPI } from "@flopay/js";
872
- 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";
873
818
 
874
819
  // src/merchant-callback.ts
875
820
  function invokeMerchantCallback(callback) {
@@ -921,8 +866,14 @@ function isPopupBlockedError(err) {
921
866
  }
922
867
 
923
868
  // src/direct-paypal-button.tsx
924
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
869
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
925
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
+ ];
926
877
  var DIRECT_PAYPAL_RECOVERY_MESSAGE = "We couldn't open PayPal. Try again or choose another payment method.";
927
878
  var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
928
879
  width: "100%",
@@ -937,6 +888,9 @@ var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
937
888
  fontWeight: 700,
938
889
  cursor: "pointer"
939
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
+ }
940
894
  function buildDirectPayPalRecoveryMessage(popupBlocked) {
941
895
  return popupBlocked ? `${DIRECT_PAYPAL_RECOVERY_MESSAGE} Allow pop-ups for this site, then try again.` : DIRECT_PAYPAL_RECOVERY_MESSAGE;
942
896
  }
@@ -956,6 +910,8 @@ function DirectPayPalButtonImplementation({
956
910
  onTechnicalFailure,
957
911
  isProcessing = false,
958
912
  onLoadStateChange,
913
+ onInitializationStateChange,
914
+ allowAutomaticRetry = true,
959
915
  onButtonClick,
960
916
  runBeforeButtonClick,
961
917
  session,
@@ -984,7 +940,7 @@ function DirectPayPalButtonImplementation({
984
940
  telemetryContext?.layout
985
941
  ]);
986
942
  const floPayTelemetry = useMemo2(() => getFloPayTelemetryBridge(flopay), [flopay]);
987
- useEffect4(() => () => {
943
+ useEffect3(() => () => {
988
944
  if (!standaloneTelemetry) return;
989
945
  void standaloneTelemetry.flush().catch(() => {
990
946
  }).finally(() => standaloneTelemetry.destroy());
@@ -1009,76 +965,104 @@ function DirectPayPalButtonImplementation({
1009
965
  startTiming: () => floPayTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
1010
966
  elapsed: (startedAt) => floPayTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
1011
967
  }), [floPayTelemetry, standaloneTelemetry]);
1012
- const containerRef = useRef4(null);
1013
- const providerStartedAt = useRef4(0);
1014
- const focusTargetRef = useRef4(null);
1015
- const pendingProviderFocusRef = useRef4(false);
968
+ const containerRef = useRef3(null);
969
+ const providerStartedAt = useRef3(0);
970
+ const focusTargetRef = useRef3(null);
971
+ const pendingProviderFocusRef = useRef3(false);
1016
972
  const [ready, setReady] = useState2(false);
973
+ const [recovering, setRecovering] = useState2(false);
1017
974
  const [renderGeneration, setRenderGeneration] = useState2(0);
1018
- const activeRenderGenerationRef = useRef4(0);
975
+ const activeRenderGenerationRef = useRef3(0);
1019
976
  const [showRetryFocusTarget, setShowRetryFocusTarget] = useState2(false);
977
+ const automaticRetryUsedRef = useRef3(false);
978
+ const initializationFailureLoggedRef = useRef3(false);
1020
979
  const [failed, setFailed] = useState2(false);
1021
980
  const [submitting, setSubmitting] = useState2(false);
1022
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;
1023
996
  const [debugLines, setDebugLines] = useState2([]);
1024
997
  const appendDebug = (line) => {
1025
998
  if (!debug) return;
1026
999
  setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
1027
1000
  };
1028
- const onTokenizedBodyRef = useRef4(onTokenizedBody);
1029
- const onCompleteRef = useRef4(onComplete);
1030
- const onErrorChangeRef = useRef4(onErrorChange);
1031
- const onDeclineRef = useRef4(onDecline);
1032
- const onTechnicalFailureRef = useRef4(onTechnicalFailure);
1033
- const onButtonClickRef = useRef4(onButtonClick);
1034
- const onLoadStateChangeRef = useRef4(onLoadStateChange);
1035
- const runBeforeButtonClickRef = useRef4(runBeforeButtonClick);
1036
- const sessionRef = useRef4(session);
1037
- const emailRef = useRef4(email);
1038
- const nonceRef = useRef4(nonce);
1039
- const beforeClickRef = useRef4(null);
1040
- const attemptGenerationRef = useRef4(0);
1041
- const attemptRef = useRef4(null);
1042
- const invalidatedAttemptGenerationRef = useRef4(null);
1043
- const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
1044
- const approvalContextByTokenRef = useRef4(/* @__PURE__ */ new Map());
1045
- 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(() => {
1046
1027
  onTokenizedBodyRef.current = onTokenizedBody;
1047
1028
  }, [onTokenizedBody]);
1048
- useEffect4(() => {
1029
+ useEffect3(() => {
1049
1030
  onCompleteRef.current = onComplete;
1050
1031
  }, [onComplete]);
1051
- useEffect4(() => {
1032
+ useEffect3(() => {
1052
1033
  onErrorChangeRef.current = onErrorChange;
1053
1034
  }, [onErrorChange]);
1054
- useEffect4(() => {
1035
+ useEffect3(() => {
1055
1036
  onDeclineRef.current = onDecline;
1056
1037
  }, [onDecline]);
1057
- useEffect4(() => {
1038
+ useEffect3(() => {
1058
1039
  onTechnicalFailureRef.current = onTechnicalFailure;
1059
1040
  }, [onTechnicalFailure]);
1060
- useEffect4(() => {
1041
+ useEffect3(() => {
1061
1042
  onButtonClickRef.current = onButtonClick;
1062
1043
  }, [onButtonClick]);
1063
- useEffect4(() => {
1044
+ useEffect3(() => {
1064
1045
  onLoadStateChangeRef.current = onLoadStateChange;
1065
1046
  }, [onLoadStateChange]);
1066
- useEffect4(() => {
1047
+ useEffect3(() => {
1048
+ onInitializationStateChangeRef.current = onInitializationStateChange;
1049
+ }, [onInitializationStateChange]);
1050
+ useEffect3(() => {
1067
1051
  runBeforeButtonClickRef.current = runBeforeButtonClick;
1068
1052
  }, [runBeforeButtonClick]);
1069
- useEffect4(() => {
1053
+ useEffect3(() => {
1070
1054
  sessionRef.current = session;
1071
1055
  }, [session]);
1072
- useEffect4(() => {
1056
+ useEffect3(() => {
1073
1057
  emailRef.current = email;
1074
1058
  }, [email]);
1075
- useEffect4(() => {
1059
+ useEffect3(() => {
1076
1060
  nonceRef.current = nonce;
1077
1061
  }, [nonce]);
1078
- useEffect4(() => {
1062
+ useEffect3(() => {
1079
1063
  activeRenderGenerationRef.current = renderGeneration;
1080
1064
  }, [renderGeneration]);
1081
- useEffect4(() => {
1065
+ useEffect3(() => {
1082
1066
  if (showRetryFocusTarget) focusTargetRef.current?.focus();
1083
1067
  }, [showRetryFocusTarget, renderGeneration]);
1084
1068
  const focusRetryTarget = useCallback2(() => {
@@ -1097,7 +1081,7 @@ function DirectPayPalButtonImplementation({
1097
1081
  );
1098
1082
  (target ?? containerRef.current)?.focus?.();
1099
1083
  }, []);
1100
- useEffect4(() => {
1084
+ useEffect3(() => {
1101
1085
  if (!ready || !pendingProviderFocusRef.current) return;
1102
1086
  pendingProviderFocusRef.current = false;
1103
1087
  focusPayPalSurface();
@@ -1180,7 +1164,7 @@ function DirectPayPalButtonImplementation({
1180
1164
  }
1181
1165
  return token;
1182
1166
  };
1183
- useEffect4(() => {
1167
+ useEffect3(() => {
1184
1168
  const handleVisibilityChange = () => {
1185
1169
  if (document.visibilityState === "visible") {
1186
1170
  scheduleMissingCallbackRecovery();
@@ -1198,11 +1182,21 @@ function DirectPayPalButtonImplementation({
1198
1182
  clearAttemptTimer();
1199
1183
  };
1200
1184
  }, []);
1201
- 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(() => {
1202
1196
  invokeMerchantCallback(() => onLoadStateChangeRef.current?.(ready && !failed));
1203
1197
  }, [ready, failed]);
1204
1198
  const normalizedEnv = normalizeGatewayEnvironment(environment);
1205
- useEffect4(() => {
1199
+ useEffect3(() => {
1206
1200
  const maskedClient = clientId ? `${clientId.slice(0, 6)}\u2026(len ${clientId.length})` : "(empty)";
1207
1201
  const ua = typeof navigator !== "undefined" ? navigator.userAgent : "(no navigator)";
1208
1202
  appendDebug(`mount clientId=${maskedClient} env=${environment ?? "(unset)"}\u2192${normalizedEnv ?? "live"} ccy=${currency} sub=${isSubscription}`);
@@ -1232,7 +1226,29 @@ function DirectPayPalButtonImplementation({
1232
1226
  paymentMethodCategory: "paypal"
1233
1227
  });
1234
1228
  let cancelled = false;
1235
- 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
+ };
1236
1252
  let overlayStartedAt = null;
1237
1253
  const finishOverlay = () => {
1238
1254
  if (overlayStartedAt === null) return;
@@ -1302,6 +1318,7 @@ function DirectPayPalButtonImplementation({
1302
1318
  };
1303
1319
  const errorMessages = [];
1304
1320
  const onWindowError = (ev) => {
1321
+ if (isInactive()) return;
1305
1322
  const msg = ev.message ?? String(ev.error ?? "(no message)");
1306
1323
  if (msg && (msg.toLowerCase().includes("paypal") || msg.toLowerCase().includes("zoid") || msg.toLowerCase().includes("postrobot") || msg.toLowerCase().includes("storage"))) {
1307
1324
  appendDebug(`window:error ${msg.slice(0, 140)}`);
@@ -1309,6 +1326,7 @@ function DirectPayPalButtonImplementation({
1309
1326
  }
1310
1327
  };
1311
1328
  const onUnhandledRejection = (ev) => {
1329
+ if (isInactive()) return;
1312
1330
  const reason = ev.reason instanceof Error ? ev.reason.message : String(ev.reason ?? "(no reason)");
1313
1331
  if (reason && (reason.toLowerCase().includes("paypal") || reason.toLowerCase().includes("zoid") || reason.toLowerCase().includes("postrobot") || reason.toLowerCase().includes("storage"))) {
1314
1332
  appendDebug(`window:rejection ${reason.slice(0, 140)}`);
@@ -1323,6 +1341,7 @@ function DirectPayPalButtonImplementation({
1323
1341
  if (debug && typeof PerformanceObserver !== "undefined") {
1324
1342
  try {
1325
1343
  perfObserver = new PerformanceObserver((list) => {
1344
+ if (isInactive()) return;
1326
1345
  for (const entry of list.getEntries()) {
1327
1346
  if (!entry.name.toLowerCase().includes("paypal")) continue;
1328
1347
  paypalRequestCount.value += 1;
@@ -1351,17 +1370,13 @@ function DirectPayPalButtonImplementation({
1351
1370
  return lower.includes("zoid destroyed") || lower.includes("destroyed all components") || lower.includes("window closed") || lower.includes("detected container element removed");
1352
1371
  };
1353
1372
  const forwardError = (message) => {
1354
- if (cancelled) return;
1373
+ if (isInactive()) return;
1355
1374
  if (isZoidLifecycleMessage(message)) return;
1356
1375
  const friendly = applyFriendlyMessageOverride(message) ?? message;
1357
1376
  invokeMerchantCallback(() => onErrorChangeRef.current?.(friendly));
1358
1377
  };
1359
1378
  const markRenderFailed = (message) => {
1360
1379
  if (cancelled) return;
1361
- if (isZoidLifecycleMessage(message)) {
1362
- appendDebug(`markRenderFailed:skip-zoid msg=${message.slice(0, 80)}`);
1363
- return;
1364
- }
1365
1380
  setFailed(true);
1366
1381
  if (!message.includes("paypal_ineligible")) {
1367
1382
  telemetrySource.error({
@@ -1374,8 +1389,48 @@ function DirectPayPalButtonImplementation({
1374
1389
  }
1375
1390
  console.error("[FloPay] DirectPayPal load/render failure:", message);
1376
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
+ };
1377
1431
  setFailed(false);
1378
1432
  const dispatchTokenizedBody = async (body, prepared = beforeClickRef.current) => {
1433
+ if (isInactive()) return;
1379
1434
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1380
1435
  if (onTokenizedBodyRef.current) {
1381
1436
  await onTokenizedBodyRef.current(body, {
@@ -1429,41 +1484,45 @@ function DirectPayPalButtonImplementation({
1429
1484
  }
1430
1485
  );
1431
1486
  if (response.ok) {
1487
+ if (superseded || cancelled && componentMountedRef.current) return;
1432
1488
  finishProcessing();
1433
1489
  telemetrySource.terminal({
1434
1490
  outcome: "payment_succeeded",
1435
1491
  provider: "paypal",
1436
1492
  paymentMethodCategory: "paypal"
1437
1493
  });
1438
- } else {
1439
- const json = await response.json().catch(() => null);
1440
- const rawMessage = json?.["message"] ?? "PayPal payment failed.";
1441
- const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
1442
- finishProcessing();
1443
- if (typeof json?.["declineCode"] === "string") {
1444
- telemetrySource.terminal({
1445
- outcome: "payment_declined",
1446
- provider: "paypal",
1447
- paymentMethodCategory: "paypal"
1448
- });
1449
- } else {
1450
- telemetrySource.error({
1451
- errorCode: "PAYMENT_PROCESSING_FAILED",
1452
- stage: "processing",
1453
- provider: "paypal",
1454
- paymentMethodCategory: "paypal",
1455
- requestCategory: "process_payment",
1456
- statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
1457
- });
1458
- }
1459
- forwardError(message);
1460
- invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
1461
- code: json?.["code"],
1462
- declineCode: json?.["declineCode"]
1463
- })));
1494
+ invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
1464
1495
  return;
1465
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;
1466
1524
  } catch (err) {
1525
+ if (isInactive()) return;
1467
1526
  finishProcessing();
1468
1527
  telemetrySource.error({
1469
1528
  errorCode: "NETWORK_REQUEST_FAILED",
@@ -1479,15 +1538,21 @@ function DirectPayPalButtonImplementation({
1479
1538
  invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message)));
1480
1539
  return;
1481
1540
  }
1482
- invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
1483
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);
1484
1548
  const createPaypalIntent = async (fallbackMessage) => {
1549
+ if (isInactive()) {
1550
+ throw supersededRenderError();
1551
+ }
1552
+ if (!paypalIntentAttemptActive) beginPaypalIntentAttempt();
1485
1553
  const prepared = beforeClickRef.current;
1486
1554
  const effectiveSessionId = prepared?.sessionId ?? sessionId;
1487
- const effectiveEmail = prepared?.accountPatch?.email ?? emailRef.current;
1488
- const intentHeaders = { "Content-Type": "application/json" };
1489
1555
  const currentNonce = prepared?.nonce ?? nonceRef.current;
1490
- if (currentNonce) intentHeaders["x-checkout-session-token"] = currentNonce;
1491
1556
  telemetrySource.log({
1492
1557
  name: "payment.intent.started",
1493
1558
  stage: "processing",
@@ -1495,22 +1560,22 @@ function DirectPayPalButtonImplementation({
1495
1560
  paymentMethodCategory: "paypal",
1496
1561
  requestCategory: "intent_create"
1497
1562
  });
1498
- const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
1499
- method: "POST",
1500
- headers: intentHeaders,
1501
- body: JSON.stringify({
1502
- sessionId: effectiveSessionId,
1503
- email: effectiveEmail,
1563
+ const intent = await new PaymentAPI(baseUrl, { telemetry: false }).createSessionIntent(
1564
+ effectiveSessionId,
1565
+ currentNonce ?? "",
1566
+ {
1567
+ provider: "paypal",
1568
+ paymentMethodCategory: "wallet",
1504
1569
  paymentMethodType: "paypal",
1505
- isPaypal: "true"
1506
- })
1507
- });
1508
- const json = await response.json().catch(() => null);
1509
- if (!response.ok) {
1510
- 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();
1511
1577
  }
1512
- const id = json?.data?.id;
1513
- if (!id) {
1578
+ if (intent.provider !== "paypal") {
1514
1579
  throw new Error(fallbackMessage);
1515
1580
  }
1516
1581
  telemetrySource.log({
@@ -1521,7 +1586,24 @@ function DirectPayPalButtonImplementation({
1521
1586
  requestCategory: "intent_create",
1522
1587
  statusClass: "2xx"
1523
1588
  });
1524
- 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
+ });
1525
1607
  };
1526
1608
  const effectRenderGeneration = renderGeneration;
1527
1609
  appendDebug("loadScript:scheduled (deferred 1 tick)");
@@ -1550,9 +1632,11 @@ function DirectPayPalButtonImplementation({
1550
1632
  ...paypalSdkEnv === "sandbox" ? { dataNamespace: "paypal_sandbox" } : {}
1551
1633
  });
1552
1634
  loadPromise.then((paypal) => {
1553
- appendDebug(`loadScript:resolved cancelled=${cancelled} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
1554
- if (cancelled || !paypal?.Buttons) {
1555
- 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.");
1556
1640
  return;
1557
1641
  }
1558
1642
  telemetrySource.log({
@@ -1562,7 +1646,7 @@ function DirectPayPalButtonImplementation({
1562
1646
  paymentMethodCategory: "paypal"
1563
1647
  });
1564
1648
  const handleApprove = async (data) => {
1565
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1649
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1566
1650
  const token = data.subscriptionID ?? data.orderID ?? "";
1567
1651
  const context = token ? approvalContextByTokenRef.current.get(token) : attemptContextBySurfaceRef.current.get(effectRenderGeneration);
1568
1652
  if (!context || !isAttemptActive(context.generation)) return;
@@ -1587,7 +1671,7 @@ function DirectPayPalButtonImplementation({
1587
1671
  } catch (err) {
1588
1672
  forwardError(err instanceof Error ? err.message : "PayPal capture failed.");
1589
1673
  } finally {
1590
- setSubmitting(false);
1674
+ if (!isInactive()) setSubmitting(false);
1591
1675
  }
1592
1676
  };
1593
1677
  const buttons = paypal.Buttons({
@@ -1598,11 +1682,20 @@ function DirectPayPalButtonImplementation({
1598
1682
  // inline-session/account patches land before the order is created,
1599
1683
  // matching the Stripe-rendered PayPal flow.
1600
1684
  onClick: async (_data, actions) => {
1685
+ if (isInactive()) {
1686
+ await actions.reject();
1687
+ return;
1688
+ }
1689
+ beginPaypalIntentAttempt();
1601
1690
  const runner = runBeforeButtonClickRef.current;
1602
1691
  let prepared = {};
1603
1692
  if (runner) {
1604
1693
  try {
1605
1694
  const beforeClick = await runner("paypal");
1695
+ if (isInactive()) {
1696
+ await actions.reject();
1697
+ return;
1698
+ }
1606
1699
  if (!beforeClick.proceed) {
1607
1700
  beforeClickRef.current = null;
1608
1701
  attemptContextBySurfaceRef.current.delete(effectRenderGeneration);
@@ -1656,10 +1749,11 @@ function DirectPayPalButtonImplementation({
1656
1749
  // prior backend round-trip (the `paypal_direct_required` retry
1657
1750
  // path), feed it straight to PayPal instead of creating a new one.
1658
1751
  // Otherwise fall back to the normal create-intent call.
1659
- createOrder: isSubscription ? void 0 : existingOrderId ? () => Promise.resolve(registerApprovalContext(existingOrderId)) : () => createPaypalIntent("Failed to create PayPal order.").then(registerApprovalContext),
1660
- 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,
1661
1754
  onApprove: handleApprove,
1662
1755
  onCancel: () => {
1756
+ if (isInactive()) return;
1663
1757
  const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
1664
1758
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1665
1759
  beforeClickRef.current = null;
@@ -1673,7 +1767,7 @@ function DirectPayPalButtonImplementation({
1673
1767
  invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
1674
1768
  },
1675
1769
  onError: (err) => {
1676
- if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1770
+ if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
1677
1771
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1678
1772
  appendDebug(`onError rendered=${rendered} msg=${message.slice(0, 120)}`);
1679
1773
  if (rendered) {
@@ -1693,13 +1787,16 @@ function DirectPayPalButtonImplementation({
1693
1787
  paymentMethodCategory: "paypal",
1694
1788
  requestCategory: "provider_sdk"
1695
1789
  });
1790
+ if (isProviderDecline(err)) {
1791
+ reportPayPalDecline(err, context);
1792
+ }
1696
1793
  if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
1697
1794
  beforeClickRef.current = null;
1698
1795
  invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
1699
1796
  notifyTechnicalFailure(err, { code: "paypal_runtime_failed" });
1700
1797
  return;
1701
1798
  }
1702
- markRenderFailed(message);
1799
+ handlePreRenderFailure(message);
1703
1800
  }
1704
1801
  });
1705
1802
  const eligible = buttons.isEligible();
@@ -1712,17 +1809,19 @@ function DirectPayPalButtonImplementation({
1712
1809
  });
1713
1810
  if (!eligible) {
1714
1811
  setReady(false);
1715
- markRenderFailed(
1812
+ handlePreRenderFailure(
1716
1813
  "PayPal buttons are not eligible to render in this context (paypal_ineligible)."
1717
1814
  );
1718
1815
  return;
1719
1816
  }
1720
1817
  const typedButtons = buttons;
1818
+ ownedButtons = typedButtons;
1721
1819
  if (debug) {
1722
1820
  appendDebug(`container:dims ${formatDims(container)} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"}`);
1723
1821
  }
1724
1822
  if (debug && typeof MutationObserver !== "undefined") {
1725
1823
  containerObserver = new MutationObserver((mutations) => {
1824
+ if (isInactive()) return;
1726
1825
  for (const mutation of mutations) {
1727
1826
  if (mutation.type === "childList") {
1728
1827
  mutation.addedNodes.forEach((node) => {
@@ -1750,7 +1849,7 @@ function DirectPayPalButtonImplementation({
1750
1849
  });
1751
1850
  }
1752
1851
  const snapshotContainer = (when) => {
1753
- if (cancelled || rendered) return;
1852
+ if (isInactive() || rendered) return;
1754
1853
  const iframes = container.querySelectorAll("iframe");
1755
1854
  const hasStorageAccess = typeof document !== "undefined" && "hasStorageAccess" in document;
1756
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}`);
@@ -1770,17 +1869,25 @@ function DirectPayPalButtonImplementation({
1770
1869
  watchdogTimers.push(setTimeout(() => snapshotContainer("15s"), 15e3));
1771
1870
  }
1772
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));
1773
1880
  buttons.render(container).then(() => {
1774
- appendDebug(`render:resolved cancelled=${cancelled}`);
1881
+ appendDebug(`render:resolved inactive=${isInactive()}`);
1775
1882
  stopDiagnostics();
1776
- if (cancelled) {
1777
- typedButtons.close().catch(() => {
1778
- });
1883
+ if (isInactive()) {
1884
+ closeOwnedButtons();
1779
1885
  return;
1780
1886
  }
1781
- activeButtons = typedButtons;
1782
1887
  rendered = true;
1783
1888
  setReady(true);
1889
+ setRecovering(false);
1890
+ onInitializationStateChangeRef.current?.("ready");
1784
1891
  telemetrySource.log({
1785
1892
  name: "provider.ready",
1786
1893
  stage: "provider_ready",
@@ -1798,25 +1905,23 @@ function DirectPayPalButtonImplementation({
1798
1905
  const message = err instanceof Error ? err.message : "PayPal failed to render.";
1799
1906
  appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
1800
1907
  stopDiagnostics();
1801
- markRenderFailed(message);
1908
+ handlePreRenderFailure(message);
1802
1909
  });
1803
1910
  }).catch((err) => {
1804
1911
  const message = err instanceof Error ? err.message : "PayPal SDK failed to load.";
1805
1912
  appendDebug(`loadScript:rejected msg=${message.slice(0, 120)}`);
1806
- markRenderFailed(message);
1913
+ handlePreRenderFailure(message);
1807
1914
  });
1808
1915
  }, 0);
1809
1916
  return () => {
1810
1917
  cancelled = true;
1811
1918
  clearTimeout(startTimer);
1919
+ if (retryTimer) clearTimeout(retryTimer);
1812
1920
  stopDiagnostics();
1813
- if (activeButtons) {
1814
- activeButtons.close().catch(() => {
1815
- });
1816
- }
1921
+ closeOwnedButtons();
1817
1922
  };
1818
- }, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1819
- const debugPanel = debug ? /* @__PURE__ */ jsxs3(
1923
+ }, [allowAutomaticRetry, baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
1924
+ const debugPanel = debug ? /* @__PURE__ */ jsxs4(
1820
1925
  "pre",
1821
1926
  {
1822
1927
  "data-testid": "flopay-direct-paypal-debug",
@@ -1840,7 +1945,7 @@ ${debugLines.join("\n")}`
1840
1945
  ]
1841
1946
  }
1842
1947
  ) : null;
1843
- if (failed) {
1948
+ if (failed && !initializationConfigChanged) {
1844
1949
  return debug ? /* @__PURE__ */ jsx6("div", { children: debugPanel }) : null;
1845
1950
  }
1846
1951
  return (
@@ -1848,51 +1953,64 @@ ${debugLines.join("\n")}`
1848
1953
  // (otherwise the fragment's placeholder + container become two siblings
1849
1954
  // and any spacing-sensitive layout has to reason about both). The wrapper
1850
1955
  // intentionally has no margin/padding so the parent owns all spacing.
1851
- /* @__PURE__ */ jsxs3("div", { children: [
1956
+ /* @__PURE__ */ jsxs4("div", { children: [
1852
1957
  debugPanel,
1853
- /* @__PURE__ */ jsxs3("div", { style: { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT }, children: [
1854
- !ready && /* @__PURE__ */ jsx6(
1855
- "div",
1856
- {
1857
- "data-testid": "flopay-direct-paypal-placeholder",
1858
- style: {
1859
- position: "absolute",
1860
- inset: 0,
1861
- borderRadius: 8,
1862
- background: "#e5e7eb",
1863
- animation: "flopay-pulse 1.5s ease-in-out infinite",
1864
- pointerEvents: "none"
1865
- }
1866
- }
1867
- ),
1868
- /* @__PURE__ */ jsx6(
1869
- "div",
1870
- {
1871
- ref: containerRef,
1872
- "data-testid": "flopay-direct-paypal-container",
1873
- tabIndex: -1,
1874
- "aria-label": "PayPal payment method",
1875
- style: {
1876
- minHeight: DEFAULT_BUTTON_HEIGHT,
1877
- display: "flex",
1878
- opacity: ready ? 1 : 0
1879
- },
1880
- "aria-busy": submitting || isProcessing
1881
- },
1882
- renderGeneration
1883
- )
1884
- ] }),
1885
- showRetryFocusTarget && /* @__PURE__ */ jsx6(
1886
- "button",
1958
+ /* @__PURE__ */ jsxs4(
1959
+ "div",
1887
1960
  {
1888
- ref: focusTargetRef,
1889
- type: "button",
1890
- "data-testid": "flopay-direct-paypal-focus-target",
1891
- onClick: focusPayPalSurface,
1892
- style: DIRECT_PAYPAL_RECOVERY_ACTION_STYLE,
1893
- children: "Try PayPal again"
1894
- }
1895
- )
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(
2004
+ "button",
2005
+ {
2006
+ ref: focusTargetRef,
2007
+ type: "button",
2008
+ "data-testid": "flopay-direct-paypal-focus-target",
2009
+ onClick: focusPayPalSurface,
2010
+ style: DIRECT_PAYPAL_RECOVERY_ACTION_STYLE,
2011
+ children: "Try PayPal again"
2012
+ }
2013
+ )
1896
2014
  ] })
1897
2015
  );
1898
2016
  }
@@ -1905,10 +2023,26 @@ function InstrumentedDirectPayPalButton(props) {
1905
2023
 
1906
2024
  // src/split-card-form.tsx
1907
2025
  import { FloPayError as FloPayError3, isSetupIntentClientSecret as isSetupIntentClientSecret2, resolveTheme } from "@flopay/shared";
1908
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2026
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1909
2027
  var STRIPE_RESUME_KEY = "flopay_stripe_resume";
1910
2028
  var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
1911
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
+ }
1912
2046
  function isAccountValidationError(err) {
1913
2047
  return err instanceof FloPayError3 && typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500;
1914
2048
  }
@@ -2020,9 +2154,9 @@ function buildExternalMethodRecoveryMessage(method, popupBlocked) {
2020
2154
  return popupBlocked ? `${base} Allow pop-ups for this site, then try again.` : base;
2021
2155
  }
2022
2156
  function useExternalAttemptReconciliation(onMissingTerminal) {
2023
- const generationRef = useRef5(0);
2024
- const invalidatedGenerationRef = useRef5(null);
2025
- const attemptRef = useRef5(null);
2157
+ const generationRef = useRef4(0);
2158
+ const invalidatedGenerationRef = useRef4(null);
2159
+ const attemptRef = useRef4(null);
2026
2160
  const clearAttemptTimer = useCallback3((targetAttempt = attemptRef.current) => {
2027
2161
  const attempt = targetAttempt;
2028
2162
  if (attempt?.timer) {
@@ -2096,7 +2230,7 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
2096
2230
  attempt.yieldedControl = true;
2097
2231
  clearAttemptTimer(attempt);
2098
2232
  }, [clearAttemptTimer]);
2099
- useEffect5(() => {
2233
+ useEffect4(() => {
2100
2234
  const handleVisibilityChange = () => {
2101
2235
  if (document.visibilityState === "visible") {
2102
2236
  scheduleRecoveryIfReturned();
@@ -2160,7 +2294,7 @@ function ExpressCheckoutReadySwap({
2160
2294
  children
2161
2295
  }) {
2162
2296
  if (state === "unavailable" || state === "load_error") return null;
2163
- return /* @__PURE__ */ jsxs4("div", { style: { position: "relative", minHeight: 44 }, children: [
2297
+ return /* @__PURE__ */ jsxs5("div", { style: { position: "relative", minHeight: 44 }, children: [
2164
2298
  /* @__PURE__ */ jsx7(
2165
2299
  "div",
2166
2300
  {
@@ -2198,11 +2332,9 @@ function ExpressCheckoutReadySwap({
2198
2332
  function isExpressCheckoutRowVisible(state) {
2199
2333
  return state !== "unavailable" && state !== "load_error";
2200
2334
  }
2201
- var SplitCardForm = forwardRef(
2202
- function SplitCardForm2(props, ref) {
2203
- return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props, innerRef: ref });
2204
- }
2205
- );
2335
+ function SplitCardForm(props) {
2336
+ return /* @__PURE__ */ jsx7(SplitCardFormInner, { ...props });
2337
+ }
2206
2338
  function PayPalButtonInner({
2207
2339
  sessionId,
2208
2340
  nonce,
@@ -2222,18 +2354,17 @@ function PayPalButtonInner({
2222
2354
  const stripe = useStripeRaw();
2223
2355
  const elements = useStripeElements();
2224
2356
  const [loadState, setLoadState] = useState3("loading");
2225
- useEffect5(() => {
2357
+ useEffect4(() => {
2226
2358
  onLoadStateChange?.(loadState);
2227
2359
  }, [loadState, onLoadStateChange]);
2228
2360
  const [submitting, setSubmitting] = useState3(false);
2229
- const paypalResumeAttempted = useRef5(false);
2230
- const focusTargetRef = useRef5(null);
2231
- const recoveryActionRef = useRef5(null);
2361
+ const paypalResumeAttempted = useRef4(false);
2362
+ const focusTargetRef = useRef4(null);
2363
+ const recoveryActionRef = useRef4(null);
2232
2364
  const [surfaceKey, setSurfaceKey] = useState3(0);
2233
2365
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2234
- const pendingProviderFocusRef = useRef5(false);
2235
- const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
2236
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2366
+ const pendingProviderFocusRef = useRef4(false);
2367
+ const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
2237
2368
  const {
2238
2369
  startAttempt,
2239
2370
  finishAttempt,
@@ -2248,7 +2379,7 @@ function PayPalButtonInner({
2248
2379
  setShowRecoveryAction(true);
2249
2380
  setSurfaceKey((key) => key + 1);
2250
2381
  });
2251
- useEffect5(() => {
2382
+ useEffect4(() => {
2252
2383
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2253
2384
  }, [showRecoveryAction, surfaceKey]);
2254
2385
  const resetSurface = useCallback3(() => {
@@ -2266,7 +2397,7 @@ function PayPalButtonInner({
2266
2397
  (target ?? focusTargetRef.current)?.focus();
2267
2398
  }, 0);
2268
2399
  }, []);
2269
- useEffect5(() => {
2400
+ useEffect4(() => {
2270
2401
  if (!pendingProviderFocusRef.current) return;
2271
2402
  pendingProviderFocusRef.current = false;
2272
2403
  focusProviderSurface();
@@ -2276,7 +2407,7 @@ function PayPalButtonInner({
2276
2407
  onErrorChange?.(null);
2277
2408
  focusProviderSurface();
2278
2409
  }, [focusProviderSurface, onErrorChange]);
2279
- useEffect5(() => {
2410
+ useEffect4(() => {
2280
2411
  if (!stripe || paypalResumeAttempted.current) return;
2281
2412
  const params = new URLSearchParams(window.location.search);
2282
2413
  const paymentIntentId = params.get("payment_intent");
@@ -2404,33 +2535,21 @@ function PayPalButtonInner({
2404
2535
  event.paymentFailed({ reason: "fail" });
2405
2536
  return;
2406
2537
  }
2407
- const intentHeaders = { "Content-Type": "application/json" };
2408
- if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
2409
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2410
- method: "POST",
2411
- headers: intentHeaders,
2412
- body: JSON.stringify({
2413
- sessionId: effectiveSessionId,
2414
- email: effectiveEmail,
2415
- paymentMethodType: paymentMethod?.id ?? "paypal",
2416
- isPaypal: "true",
2417
- setupFutureUsage: "off_session",
2418
- setup_future_usage: "off_session"
2419
- })
2420
- });
2421
- if (!intentResponse.ok) {
2422
- const intentError = await buildFloPayApiErrorFromResponse(
2423
- intentResponse,
2424
- "Failed to create payment intent"
2425
- );
2426
- onErrorChange?.(intentError.message);
2427
- onDecline?.(buildDeclineEvent("paypal", intentError));
2428
- event.paymentFailed({ reason: "fail", message: intentError.message });
2429
- 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");
2430
2551
  }
2431
- const intentJson = await intentResponse.json();
2432
- const intentClientSecret = intentJson.data?.id;
2433
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
2552
+ const intentClientSecret = intent.clientSecret;
2434
2553
  const confirmParams = {
2435
2554
  return_url: window.location.href
2436
2555
  };
@@ -2467,6 +2586,17 @@ function PayPalButtonInner({
2467
2586
  code: getProviderErrorCode(confirmError),
2468
2587
  declineCode: getProviderDeclineCode(confirmError)
2469
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
+ });
2470
2600
  } else {
2471
2601
  recoverTechnicalFailure(confirmError, "stripe_paypal_confirm_failed", attemptContext?.generation);
2472
2602
  }
@@ -2495,15 +2625,15 @@ function PayPalButtonInner({
2495
2625
  } finally {
2496
2626
  setSubmitting(false);
2497
2627
  }
2498
- }, [stripe, elements, sessionId, nonce, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2499
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2628
+ }, [stripe, elements, sessionId, nonce, email, billingApiUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
2629
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2500
2630
  /* @__PURE__ */ jsx7(
2501
2631
  ExpressCheckoutReadySwap,
2502
2632
  {
2503
2633
  state: loadState,
2504
2634
  placeholderTestId: "flopay-paypal-placeholder",
2505
2635
  borderRadius: placeholderBorderRadius,
2506
- children: /* @__PURE__ */ jsxs4(
2636
+ children: /* @__PURE__ */ jsxs5(
2507
2637
  "div",
2508
2638
  {
2509
2639
  ref: focusTargetRef,
@@ -2571,6 +2701,7 @@ function WalletButtonInner({
2571
2701
  nonce,
2572
2702
  email,
2573
2703
  billingApiUrl,
2704
+ intentKind,
2574
2705
  expressMethods,
2575
2706
  onTokenizedBody,
2576
2707
  onErrorChange,
@@ -2585,20 +2716,19 @@ function WalletButtonInner({
2585
2716
  const stripe = useStripeRaw();
2586
2717
  const elements = useStripeElements();
2587
2718
  const [loadState, setLoadState] = useState3("loading");
2588
- useEffect5(() => {
2719
+ useEffect4(() => {
2589
2720
  onLoadStateChange?.(loadState);
2590
2721
  }, [loadState, onLoadStateChange]);
2591
2722
  const [submitting, setSubmitting] = useState3(false);
2592
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
2593
- const focusTargetRef = useRef5(null);
2594
- const recoveryActionRef = useRef5(null);
2723
+ const focusTargetRef = useRef4(null);
2724
+ const recoveryActionRef = useRef4(null);
2595
2725
  const [surfaceKey, setSurfaceKey] = useState3(0);
2596
2726
  const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
2597
2727
  const [recoveryActionMethod, setRecoveryActionMethod] = useState3("google_pay");
2598
- const pendingProviderFocusRef = useRef5(false);
2599
- const lastWalletProviderMethodRef = useRef5("google_pay");
2600
- const lastWalletMethodRef = useRef5("google_pay");
2601
- 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());
2602
2732
  const {
2603
2733
  startAttempt,
2604
2734
  finishAttempt,
@@ -2614,7 +2744,7 @@ function WalletButtonInner({
2614
2744
  setShowRecoveryAction(true);
2615
2745
  setSurfaceKey((key) => key + 1);
2616
2746
  });
2617
- useEffect5(() => {
2747
+ useEffect4(() => {
2618
2748
  if (showRecoveryAction) recoveryActionRef.current?.focus();
2619
2749
  }, [showRecoveryAction, surfaceKey]);
2620
2750
  const resetSurface = useCallback3(() => {
@@ -2633,7 +2763,7 @@ function WalletButtonInner({
2633
2763
  (target ?? focusTargetRef.current)?.focus();
2634
2764
  }, 0);
2635
2765
  }, []);
2636
- useEffect5(() => {
2766
+ useEffect4(() => {
2637
2767
  if (!pendingProviderFocusRef.current) return;
2638
2768
  pendingProviderFocusRef.current = false;
2639
2769
  focusProviderSurface();
@@ -2690,31 +2820,21 @@ function WalletButtonInner({
2690
2820
  if (!effectiveSessionId || !effectiveEmail) {
2691
2821
  throw new Error("Missing sessionId or email for wallet payment");
2692
2822
  }
2693
- const intentHeaders = { "Content-Type": "application/json" };
2694
- if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
2695
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
2696
- method: "POST",
2697
- headers: intentHeaders,
2698
- body: JSON.stringify({
2699
- sessionId: effectiveSessionId,
2700
- email: effectiveEmail,
2701
- paymentMethodType: paymentMethod.id,
2702
- isPaypal: false
2703
- })
2704
- });
2705
- if (!intentResponse.ok) {
2706
- const intentError = await buildFloPayApiErrorFromResponse(
2707
- intentResponse,
2708
- "Failed to create payment intent"
2709
- );
2710
- onErrorChange?.(intentError.message);
2711
- onDecline?.(buildDeclineEvent(method, intentError));
2712
- event.paymentFailed({ reason: "fail", message: intentError.message });
2713
- 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");
2714
2836
  }
2715
- const intentJson = await intentResponse.json();
2716
- const intentClientSecret = intentJson.data?.id;
2717
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
2837
+ const intentClientSecret = intent.clientSecret;
2718
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 }));
2719
2839
  if (confirmError) {
2720
2840
  if (isProviderDecline(confirmError)) {
@@ -2724,6 +2844,17 @@ function WalletButtonInner({
2724
2844
  code: getProviderErrorCode(confirmError),
2725
2845
  declineCode: getProviderDeclineCode(confirmError)
2726
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
+ });
2727
2858
  } else {
2728
2859
  recoverTechnicalFailure(walletType, confirmError, "stripe_wallet_confirm_failed", attemptContext?.generation);
2729
2860
  }
@@ -2744,7 +2875,7 @@ function WalletButtonInner({
2744
2875
  setSubmitting(false);
2745
2876
  }
2746
2877
  },
2747
- [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]
2748
2879
  );
2749
2880
  const expressMethodMap = useMemo3(() => {
2750
2881
  const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
@@ -2764,14 +2895,14 @@ function WalletButtonInner({
2764
2895
  () => expressMethods.map(stripeExpressMethodToOptionKey),
2765
2896
  [expressMethods]
2766
2897
  );
2767
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
2898
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2768
2899
  /* @__PURE__ */ jsx7(
2769
2900
  ExpressCheckoutReadySwap,
2770
2901
  {
2771
2902
  state: loadState,
2772
2903
  placeholderTestId: "flopay-wallet-placeholder",
2773
2904
  borderRadius: placeholderBorderRadius,
2774
- children: /* @__PURE__ */ jsxs4(
2905
+ children: /* @__PURE__ */ jsxs5(
2775
2906
  "div",
2776
2907
  {
2777
2908
  ref: focusTargetRef,
@@ -2780,7 +2911,7 @@ function WalletButtonInner({
2780
2911
  "aria-label": "Wallet payment methods",
2781
2912
  style: { borderRadius: 8, outlineOffset: 4 },
2782
2913
  children: [
2783
- showRecoveryAction && /* @__PURE__ */ jsxs4(
2914
+ showRecoveryAction && /* @__PURE__ */ jsxs5(
2784
2915
  "button",
2785
2916
  {
2786
2917
  ref: recoveryActionRef,
@@ -2877,6 +3008,7 @@ function StripeMethodButton({
2877
3008
  fontFamily
2878
3009
  }) {
2879
3010
  const brand = resolveStripeMethodBrandVariant(method, themeId);
3011
+ const vendoredLogoUrl = PAYMENT_METHOD_LOGO_URLS[method];
2880
3012
  const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
2881
3013
  const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
2882
3014
  const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
@@ -2928,8 +3060,8 @@ function StripeMethodButton({
2928
3060
  // status text on top of the pulsing background. Keeps the loading
2929
3061
  // affordance shape-equivalent across both kinds of tile.
2930
3062
  /* @__PURE__ */ jsx7("span", { style: { margin: "0 auto" }, children: `Connecting to ${getStripeMethodDisplayName(method)}\u2026` })
2931
- ) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
2932
- /* @__PURE__ */ jsxs4(
3063
+ ) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
3064
+ /* @__PURE__ */ jsxs5(
2933
3065
  "span",
2934
3066
  {
2935
3067
  style: {
@@ -2942,15 +3074,36 @@ function StripeMethodButton({
2942
3074
  gap: hasVendoredStripeMethodLogo(method) ? 8 : 0
2943
3075
  },
2944
3076
  children: [
2945
- brand?.logoSvg && /* @__PURE__ */ jsx7(
3077
+ vendoredLogoUrl ? (
3078
+ // Vendored brand logo — an <img> asset (not inline SVG) so its
3079
+ // bytes ship as a sibling file, out of the JS bundle. 3:2 box
3080
+ // matching the datatrans logos' 120×80 viewBox; each ships its own
3081
+ // white rounded-rect background so it stays legible on any tile.
3082
+ /* @__PURE__ */ jsx7(
3083
+ "img",
3084
+ {
3085
+ src: vendoredLogoUrl,
3086
+ alt: "",
3087
+ "aria-hidden": "true",
3088
+ loading: "lazy",
3089
+ decoding: "async",
3090
+ width: 30,
3091
+ height: 20,
3092
+ style: {
3093
+ width: 30,
3094
+ height: 20,
3095
+ flexShrink: 0,
3096
+ borderRadius: 3,
3097
+ objectFit: "contain",
3098
+ display: "inline-flex"
3099
+ }
3100
+ }
3101
+ )
3102
+ ) : brand?.logoSvg ? /* @__PURE__ */ jsx7(
2946
3103
  "span",
2947
3104
  {
2948
3105
  "aria-hidden": "true",
2949
3106
  style: {
2950
- // 3:2 box matching the vendored datatrans logos' 120×80
2951
- // viewBox. Each logo ships with its own white rounded-rect
2952
- // background baked in, so the mark stays legible on any
2953
- // brand-coloured tile without an extra chip wrapper here.
2954
3107
  width: 30,
2955
3108
  height: 20,
2956
3109
  flexShrink: 0,
@@ -2960,7 +3113,7 @@ function StripeMethodButton({
2960
3113
  },
2961
3114
  dangerouslySetInnerHTML: { __html: brand.logoSvg }
2962
3115
  }
2963
- ),
3116
+ ) : null,
2964
3117
  /* @__PURE__ */ jsx7("span", { children: getStripeMethodDisplayName(method) })
2965
3118
  ]
2966
3119
  }
@@ -3015,10 +3168,9 @@ function StripeMethodInlineForm({
3015
3168
  const stripe = useStripeRaw();
3016
3169
  const elements = useStripeElements();
3017
3170
  const [submitting, setSubmitting] = useState3(false);
3018
- const submittingRef = useRef5(false);
3171
+ const submittingRef = useRef4(false);
3019
3172
  const [isMethodComplete, setIsMethodComplete] = useState3(false);
3020
3173
  const [loadState, setLoadState] = useState3("loading");
3021
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3022
3174
  const handlePay = useCallback3(async () => {
3023
3175
  if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
3024
3176
  submittingRef.current = true;
@@ -3049,27 +3201,22 @@ function StripeMethodInlineForm({
3049
3201
  if (!effectiveSessionId || !effectiveEmail) {
3050
3202
  throw new Error("Missing sessionId or email for payment.");
3051
3203
  }
3052
- const intentHeaders = { "Content-Type": "application/json" };
3053
- if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
3054
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3055
- method: "POST",
3056
- headers: intentHeaders,
3057
- body: JSON.stringify({
3058
- sessionId: effectiveSessionId,
3059
- email: effectiveEmail,
3060
- paymentMethodType: paymentMethod.type || method,
3061
- isPaypal: false
3062
- })
3063
- });
3064
- if (!intentResponse.ok) {
3065
- const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
3066
- onErrorChange?.(intentError.message);
3067
- onDecline?.(buildDeclineEvent("card", intentError));
3068
- 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");
3069
3218
  }
3070
- const intentJson = await intentResponse.json();
3071
- const intentClientSecret = intentJson.data?.id;
3072
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
3219
+ const intentClientSecret = intent.clientSecret;
3073
3220
  if (typeof window !== "undefined") {
3074
3221
  try {
3075
3222
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3102,11 +3249,21 @@ function StripeMethodInlineForm({
3102
3249
  const message = confirmError.message ?? "Payment failed.";
3103
3250
  onErrorChange?.(message);
3104
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
+ });
3105
3263
  return;
3106
3264
  }
3107
- const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
3108
3265
  const piStatus = paymentIntent?.status;
3109
- if (!piStatus || !SUCCESSFUL_PI_STATUSES.has(piStatus)) {
3266
+ if (!isSuccessfulPaymentIntentStatus(piStatus)) {
3110
3267
  if (typeof window !== "undefined") {
3111
3268
  try {
3112
3269
  localStorage.removeItem(STRIPE_RESUME_KEY);
@@ -3152,16 +3309,16 @@ function StripeMethodInlineForm({
3152
3309
  sessionId,
3153
3310
  nonce,
3154
3311
  email,
3155
- baseUrl,
3312
+ billingApiUrl,
3156
3313
  method,
3157
3314
  onTokenizedBody,
3158
3315
  onErrorChange,
3159
3316
  onDecline
3160
3317
  ]);
3161
3318
  void onCancel;
3162
- return /* @__PURE__ */ jsxs4("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
3319
+ return /* @__PURE__ */ jsxs5("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
3163
3320
  /* @__PURE__ */ jsx7(
3164
- PaymentElement2,
3321
+ PaymentElement,
3165
3322
  {
3166
3323
  onReady: () => setLoadState("ready"),
3167
3324
  onLoadError: () => {
@@ -3216,22 +3373,7 @@ function StripeMethodInlineForm({
3216
3373
  children: submitting ? "Processing\u2026" : `Pay with ${getStripeMethodDisplayName(method)}`
3217
3374
  }
3218
3375
  ),
3219
- errorText && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
3220
- margin: "0.75rem 0 0",
3221
- padding: "0.625rem 0.875rem",
3222
- background: "#FEF2F2",
3223
- border: "1px solid #FECACA",
3224
- borderRadius: "8px",
3225
- color: "#991B1B",
3226
- fontSize: "0.85rem",
3227
- fontWeight: 600,
3228
- display: "flex",
3229
- alignItems: "center",
3230
- gap: "0.5rem"
3231
- }, children: [
3232
- /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
3233
- errorText
3234
- ] })
3376
+ errorText && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
3235
3377
  ] });
3236
3378
  }
3237
3379
  function StripePaymentElementInner({
@@ -3258,11 +3400,10 @@ function StripePaymentElementInner({
3258
3400
  onExpandApm,
3259
3401
  expandedApmMethod
3260
3402
  }) {
3261
- const baseUrl = billingApiUrl.replace(/\/+$/, "");
3262
3403
  const [expandedMethod, setExpandedMethod] = useState3(null);
3263
3404
  const [submittingMethod, setSubmittingMethod] = useState3(null);
3264
- const submittingRef = useRef5(false);
3265
- useEffect5(() => {
3405
+ const submittingRef = useRef4(false);
3406
+ useEffect4(() => {
3266
3407
  if (paymentElementMethods.length > 0 && stripeInstance) {
3267
3408
  onLoadStateChange?.("ready");
3268
3409
  } else if (!stripeInstance) {
@@ -3289,31 +3430,21 @@ function StripePaymentElementInner({
3289
3430
  if (!effectiveSessionId || !effectiveEmail) {
3290
3431
  throw new Error("Missing sessionId or email for payment.");
3291
3432
  }
3292
- const intentHeaders = { "Content-Type": "application/json" };
3293
- if (effectiveNonce) intentHeaders["x-checkout-session-token"] = effectiveNonce;
3294
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
3295
- method: "POST",
3296
- headers: intentHeaders,
3297
- body: JSON.stringify({
3298
- sessionId: effectiveSessionId,
3299
- email: effectiveEmail,
3300
- // Wire type, not pm_xxx — backend scopes `payment_method_types`
3301
- // to this single method so unrelated account-wide methods
3302
- // (apple_pay without domain verification, …) don't poison PI
3303
- // creation.
3433
+ const intent = await new PaymentAPI2(billingApiUrl).createSessionIntent(
3434
+ effectiveSessionId,
3435
+ effectiveNonce ?? "",
3436
+ {
3437
+ provider: "stripe",
3438
+ paymentMethodCategory: "apm",
3304
3439
  paymentMethodType: method,
3305
- isPaypal: false
3306
- })
3307
- });
3308
- if (!intentResponse.ok) {
3309
- const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
3310
- onErrorChange?.(intentError.message);
3311
- onDecline?.(buildDeclineEvent("card", intentError));
3312
- return;
3440
+ paymentMethodId: null,
3441
+ intentKind: "payment"
3442
+ }
3443
+ );
3444
+ if (intent.provider !== "stripe") {
3445
+ throw new Error("Invalid provider returned for APM intent");
3313
3446
  }
3314
- const intentJson = await intentResponse.json();
3315
- const intentClientSecret = intentJson.data?.id;
3316
- if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
3447
+ const intentClientSecret = intent.clientSecret;
3317
3448
  if (typeof window !== "undefined") {
3318
3449
  try {
3319
3450
  localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
@@ -3354,11 +3485,21 @@ function StripePaymentElementInner({
3354
3485
  const message = confirmError.message ?? "Payment failed.";
3355
3486
  onErrorChange?.(message);
3356
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
+ });
3357
3499
  return;
3358
3500
  }
3359
- const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
3360
3501
  const piStatus = paymentIntent?.status;
3361
- if (!piStatus || !SUCCESSFUL_PI_STATUSES.has(piStatus)) {
3502
+ if (!isSuccessfulPaymentIntentStatus(piStatus)) {
3362
3503
  if (typeof window !== "undefined") {
3363
3504
  try {
3364
3505
  localStorage.removeItem(STRIPE_RESUME_KEY);
@@ -3402,7 +3543,7 @@ function StripePaymentElementInner({
3402
3543
  sessionId,
3403
3544
  nonce,
3404
3545
  email,
3405
- baseUrl,
3546
+ billingApiUrl,
3406
3547
  billingName,
3407
3548
  onTokenizedBody,
3408
3549
  onErrorChange,
@@ -3439,7 +3580,7 @@ function StripePaymentElementInner({
3439
3580
  const isExpanded = expandedApmMethod === method;
3440
3581
  const isSubmittingThis = submittingMethod === method;
3441
3582
  const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
3442
- return /* @__PURE__ */ jsxs4(React7.Fragment, { children: [
3583
+ return /* @__PURE__ */ jsxs5(React7.Fragment, { children: [
3443
3584
  /* @__PURE__ */ jsx7(
3444
3585
  StripeMethodButton,
3445
3586
  {
@@ -3503,15 +3644,10 @@ function SplitCardFormInner({
3503
3644
  firstName,
3504
3645
  lastName,
3505
3646
  chv,
3506
- submitLabel = "CONFIRM PAYMENT",
3507
3647
  className,
3508
- children,
3509
3648
  isProcessing: externalProcessing,
3510
3649
  error: externalError,
3511
3650
  onErrorChange,
3512
- onFullNameChange,
3513
- onFirstNameChange,
3514
- onLastNameChange,
3515
3651
  showPayPal = true,
3516
3652
  showStripe = true,
3517
3653
  enabledPaymentMethods,
@@ -3550,17 +3686,14 @@ function SplitCardFormInner({
3550
3686
  directPaypal,
3551
3687
  isSubscription = false,
3552
3688
  session,
3553
- debug = false,
3554
- innerRef
3689
+ debug = false
3555
3690
  }) {
3556
3691
  const flopay = useFloPay();
3557
3692
  const paypalFlopay = usePayPalFloPay();
3558
- const elements = useElements();
3559
- const checkout = useContext3(CheckoutContext);
3693
+ const checkout = useContext2(CheckoutContext);
3560
3694
  const contextBillingUrl = useBillingApiUrl();
3561
3695
  const [processing, setProcessing] = useState3(false);
3562
3696
  const [error, setError] = useState3(null);
3563
- const [is3DSActive, setIs3DSActive] = useState3(false);
3564
3697
  const [selectedCountry, setSelectedCountry] = useState3(countryProp ?? "US");
3565
3698
  const [zipCode, setZipCode] = useState3(zipProp ?? "");
3566
3699
  const [addressLine1, setAddressLine1] = useState3(addressLine1Prop ?? "");
@@ -3568,18 +3701,19 @@ function SplitCardFormInner({
3568
3701
  const [city, setCity] = useState3(cityProp ?? "");
3569
3702
  const [stateValue, setStateValue] = useState3(stateProp ?? "");
3570
3703
  const [accountPatch, setAccountPatch] = useState3({});
3571
- const zipCodeRef = useRef5(zipProp ?? "");
3572
- const selectedCountryRef = useRef5(countryProp ?? "US");
3573
- const addressLine1Ref = useRef5(addressLine1Prop ?? "");
3574
- const addressLine2Ref = useRef5(addressLine2Prop ?? "");
3575
- const cityRef = useRef5(cityProp ?? "");
3576
- 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 ?? "");
3577
3710
  const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
3578
3711
  const enableAVS = avsConfig !== null;
3579
3712
  const vaultBlockReady = Boolean(session?.vault?.html);
3580
3713
  const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
3714
+ const vaultCapabilityAvailable = vaultBlockReady || vaultGatewayAdvertised;
3581
3715
  const vaultActive = Boolean(
3582
- showStripe && (vaultBlockReady || vaultGatewayAdvertised) && flopay && sessionId
3716
+ showStripe && vaultCapabilityAvailable && flopay && sessionId
3583
3717
  );
3584
3718
  const cardCapture = useMemo3(() => {
3585
3719
  if (!flopay || !vaultActive || !sessionId) return null;
@@ -3611,7 +3745,9 @@ function SplitCardFormInner({
3611
3745
  const [vaultMount, setVaultMount] = useState3(
3612
3746
  () => toVaultMount(session?.vault)
3613
3747
  );
3614
- const [viewState, setViewState] = useState3(initialCardOpen ? "card" : "buttons");
3748
+ const [viewState, setViewState] = useState3(
3749
+ initialCardOpen && vaultCapabilityAvailable ? "card" : "buttons"
3750
+ );
3615
3751
  const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
3616
3752
  const showCardForm = viewState === "expanding" || viewState === "card";
3617
3753
  const TRANSITION_MS = 280;
@@ -3635,18 +3771,16 @@ function SplitCardFormInner({
3635
3771
  setExpandedApmMethod(null);
3636
3772
  }, TRANSITION_MS);
3637
3773
  }, []);
3638
- useEffect5(() => {
3639
- if (layout === "buttons" && initialCardOpen) {
3774
+ useEffect4(() => {
3775
+ if (layout === "buttons" && initialCardOpen && vaultCapabilityAvailable) {
3640
3776
  setViewState("card");
3641
3777
  }
3642
- }, [layout, initialCardOpen]);
3643
- const [fullName, setFullName] = useState3("");
3644
- const [formReady, setFormReady] = useState3(false);
3778
+ }, [layout, initialCardOpen, vaultCapabilityAvailable]);
3645
3779
  const [overlayStatus, setOverlayStatus] = useState3(null);
3646
- const processingRef = useRef5(false);
3780
+ const processingRef = useRef4(false);
3647
3781
  const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
3648
- const paypalDirectRetryRef = useRef5(paypalDirectRetry);
3649
- useEffect5(() => {
3782
+ const paypalDirectRetryRef = useRef4(paypalDirectRetry);
3783
+ useEffect4(() => {
3650
3784
  paypalDirectRetryRef.current = paypalDirectRetry;
3651
3785
  }, [paypalDirectRetry]);
3652
3786
  const resolvedBillingApiUrl = billingApiUrl || contextBillingUrl;
@@ -3694,7 +3828,6 @@ function SplitCardFormInner({
3694
3828
  }, [appearance, bStyles, layout]);
3695
3829
  const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
3696
3830
  const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
3697
- const isSelfContained = !onTokenizedBody;
3698
3831
  const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
3699
3832
  const resolvedAccount = useMemo3(() => mergeAccountPatch({
3700
3833
  userId,
@@ -3704,6 +3837,7 @@ function SplitCardFormInner({
3704
3837
  country: countryProp,
3705
3838
  zip: zipProp
3706
3839
  }, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
3840
+ const resolvedBillingName = [resolvedAccount.firstName, resolvedAccount.lastName].filter((part) => Boolean(part?.trim())).join(" ");
3707
3841
  const stripeInstance = useMemo3(() => {
3708
3842
  if (!flopay) return null;
3709
3843
  return flopay.getRawProvider();
@@ -3760,11 +3894,11 @@ function SplitCardFormInner({
3760
3894
  },
3761
3895
  [onErrorChange]
3762
3896
  );
3763
- useEffect5(() => {
3897
+ useEffect4(() => {
3764
3898
  if (!vaultActive || !cardCapture) return;
3765
3899
  cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
3766
3900
  }, [vaultActive, cardCapture, cardFieldOrder, avsConfig]);
3767
- useEffect5(() => {
3901
+ useEffect4(() => {
3768
3902
  if (viewState === "expanding" || viewState === "collapsing" || viewState === "apm-expanding" || viewState === "apm-collapsing") {
3769
3903
  updateError(null);
3770
3904
  }
@@ -3799,7 +3933,7 @@ function SplitCardFormInner({
3799
3933
  },
3800
3934
  [layout, onError, updateError]
3801
3935
  );
3802
- useEffect5(() => {
3936
+ useEffect4(() => {
3803
3937
  if (!vaultActive || !sessionId) {
3804
3938
  setVaultMount(null);
3805
3939
  return;
@@ -3837,13 +3971,12 @@ function SplitCardFormInner({
3837
3971
  nonce,
3838
3972
  updateError
3839
3973
  ]);
3840
- const vaultOutcomeRef = useRef5({
3974
+ const vaultOutcomeRef = useRef4({
3841
3975
  onComplete,
3842
3976
  onError,
3843
3977
  updateError,
3844
3978
  emitDecline,
3845
3979
  resolvedAccount,
3846
- fullName,
3847
3980
  avsConfig,
3848
3981
  avsCheckProp,
3849
3982
  sessionId,
@@ -3856,16 +3989,15 @@ function SplitCardFormInner({
3856
3989
  updateError,
3857
3990
  emitDecline,
3858
3991
  resolvedAccount,
3859
- fullName,
3860
3992
  avsConfig,
3861
3993
  avsCheckProp,
3862
3994
  sessionId,
3863
3995
  nonce,
3864
3996
  baseUrl
3865
3997
  };
3866
- const vaultCompletedRef = useRef5(false);
3998
+ const vaultCompletedRef = useRef4(false);
3867
3999
  const buildVaultAccountSnapshot = useCallback3(() => {
3868
- const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
4000
+ const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
3869
4001
  const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
3870
4002
  const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
3871
4003
  const line1Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_1, cc) : false;
@@ -3878,8 +4010,8 @@ function SplitCardFormInner({
3878
4010
  accountData: {
3879
4011
  userId: resolvedAccount2.userId ?? "",
3880
4012
  email: resolvedAccount2.email ?? "",
3881
- firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
3882
- lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
4013
+ firstName: resolvedAccount2.firstName ?? "",
4014
+ lastName: resolvedAccount2.lastName ?? "",
3883
4015
  ...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
3884
4016
  ...cityVisible && cityRef.current ? { city: cityRef.current } : {},
3885
4017
  ...stateValue2 ? { state: stateValue2 } : {},
@@ -3900,11 +4032,11 @@ function SplitCardFormInner({
3900
4032
  } : {}
3901
4033
  };
3902
4034
  }, []);
3903
- useEffect5(() => {
4035
+ useEffect4(() => {
3904
4036
  if (!vaultActive || !cardCapture) return;
3905
4037
  cardCapture.setSubmitGate?.(avsInvalid);
3906
4038
  }, [vaultActive, cardCapture, avsInvalid]);
3907
- useEffect5(() => {
4039
+ useEffect4(() => {
3908
4040
  if (!vaultActive || !cardCapture) return;
3909
4041
  vaultCompletedRef.current = false;
3910
4042
  let cancelled = false;
@@ -4007,21 +4139,25 @@ function SplitCardFormInner({
4007
4139
  const [walletLoadState, setWalletLoadState] = useState3("loading");
4008
4140
  const [paymentElementLoadState, setPaymentElementLoadState] = useState3("loading");
4009
4141
  const [directPaypalReady, setDirectPaypalReady] = useState3(false);
4142
+ const [directPaypalInitializationState, setDirectPaypalInitializationState] = useState3("loading");
4010
4143
  const [inAppBrowserDetected, setInAppBrowserDetected] = useState3();
4011
- useEffect5(() => {
4144
+ useEffect4(() => {
4012
4145
  setInAppBrowserDetected(isInAppBrowser());
4013
4146
  }, []);
4014
4147
  const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
4015
4148
  const shouldShowWallets = showStripe && showWallets;
4016
4149
  const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
4017
- const shouldRenderStripePayPal = shouldShowPayPal && !directPaypalConfigured && !!paypalStripeInstance;
4150
+ const directPaypalUnavailable = directPaypalInitializationState === "retrying" || directPaypalInitializationState === "exhausted";
4151
+ const shouldRenderStripePayPal = shouldShowPayPal && !!paypalStripeInstance && (!directPaypalConfigured || directPaypalUnavailable);
4018
4152
  const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
4019
4153
  const shouldRenderPaymentElement = showStripe && hasEnabledMethods && paymentElementMethodsForCurrency.length > 0 && !!stripeInstance;
4020
- const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4154
+ const shouldDisplayPayPalRow = shouldRenderDirectPayPal && !directPaypalUnavailable ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
4021
4155
  const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
4022
4156
  const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
4023
- const validationFiredRef = useRef5(false);
4024
- 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(() => {
4025
4161
  if (validationFiredRef.current) return;
4026
4162
  if (!showStripe && !showPayPal) {
4027
4163
  validationFiredRef.current = true;
@@ -4039,10 +4175,29 @@ function SplitCardFormInner({
4039
4175
  );
4040
4176
  onError?.(err);
4041
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);
4042
4187
  }
4043
- }, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
4044
- const deprecationLoggedRef = useRef5(false);
4045
- 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(() => {
4046
4201
  if (deprecationLoggedRef.current) return;
4047
4202
  if (!hasEnabledMethods) return;
4048
4203
  const stale = [];
@@ -4054,13 +4209,6 @@ function SplitCardFormInner({
4054
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.`
4055
4210
  );
4056
4211
  }, [hasEnabledMethods, showApplePay, showGooglePay]);
4057
- const handleNameChange = useCallback3((value) => {
4058
- setFullName(value);
4059
- onFullNameChange?.(value);
4060
- const parts = value.trim().split(/\s+/);
4061
- onFirstNameChange?.(parts[0] ?? "");
4062
- onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
4063
- }, [onFullNameChange, onFirstNameChange, onLastNameChange]);
4064
4212
  const applyInlineSessionPatch = useCallback3(
4065
4213
  (patch, method) => {
4066
4214
  if (!checkout.applyInlineSessionPatch) {
@@ -4141,8 +4289,8 @@ function SplitCardFormInner({
4141
4289
  accountData: {
4142
4290
  userId: effectiveAccount.userId ?? "",
4143
4291
  email: effectiveAccount.email ?? "",
4144
- firstName: effectiveAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
4145
- lastName: effectiveAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
4292
+ firstName: effectiveAccount.firstName ?? "",
4293
+ lastName: effectiveAccount.lastName ?? "",
4146
4294
  ...avsConfig ? (() => {
4147
4295
  const c = selectedCountryRef.current;
4148
4296
  const stateVisible = isAVSFieldVisible(avsConfig.state, c);
@@ -4190,70 +4338,6 @@ function SplitCardFormInner({
4190
4338
  return;
4191
4339
  }
4192
4340
  const json = await response.json().catch(() => null);
4193
- if (json?.type === "3ds_required") {
4194
- const secret = json["threeDSecureToken"];
4195
- if (!flopay || !secret) {
4196
- setOverlayStatus("error");
4197
- updateError("3DS authentication required but no token provided.");
4198
- return;
4199
- }
4200
- setIs3DSActive(true);
4201
- try {
4202
- const retryBillingAddress = {};
4203
- const retryCC = selectedCountryRef.current;
4204
- const retryCountry = enableAVS ? retryCC : effectiveAccount.country;
4205
- if (retryCountry) retryBillingAddress["country"] = retryCountry;
4206
- if (avsConfig && isAVSFieldVisible(avsConfig.postal_code, retryCC) && zipCodeRef.current.trim()) {
4207
- retryBillingAddress["postal_code"] = zipCodeRef.current.trim();
4208
- } else if (!avsConfig && effectiveAccount.zip) {
4209
- retryBillingAddress["postal_code"] = effectiveAccount.zip;
4210
- }
4211
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, retryCC) && addressLine1Ref.current.trim()) retryBillingAddress["line1"] = addressLine1Ref.current.trim();
4212
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_2, retryCC) && addressLine2Ref.current.trim()) retryBillingAddress["line2"] = addressLine2Ref.current.trim();
4213
- if (avsConfig && isAVSFieldVisible(avsConfig.city, retryCC) && cityRef.current.trim()) retryBillingAddress["city"] = cityRef.current.trim();
4214
- if (avsConfig && isAVSFieldVisible(avsConfig.state, retryCC) && stateRef.current.trim()) {
4215
- retryBillingAddress["state"] = stateRef.current.trim();
4216
- } else if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, retryCC) && !isAVSFieldVisible(avsConfig.state, retryCC) && isAVSFieldVisible(avsConfig.postal_code, retryCC)) {
4217
- const derivedRetryState = getStateFromPostalCode(retryCC, zipCodeRef.current.trim());
4218
- if (derivedRetryState) retryBillingAddress["state"] = derivedRetryState;
4219
- }
4220
- const result = await flopay.confirmPayment({
4221
- clientSecret: secret,
4222
- returnUrl: window.location.href,
4223
- billingDetails: {
4224
- ...effectiveAccount.email ? { email: effectiveAccount.email } : {},
4225
- ...fullName.trim() ? { name: fullName.trim() } : {},
4226
- ...Object.keys(retryBillingAddress).length > 0 ? { address: retryBillingAddress } : {}
4227
- }
4228
- });
4229
- if (result.error) {
4230
- setOverlayStatus("error");
4231
- updateError(result.error.message);
4232
- onError?.(result.error);
4233
- emitDecline("card", result.error);
4234
- return;
4235
- }
4236
- if (result.status === "succeeded" || result.status === "processing") {
4237
- processingRef.current = false;
4238
- await processPaymentInternal({
4239
- id: result.paymentIntentId,
4240
- type: "card",
4241
- threeDSecureActionResultTokenId: result.paymentIntentId
4242
- }, {
4243
- // Preserve the session/nonce/account overrides resolved
4244
- // above so the retry talks to the same (possibly
4245
- // freshly bootstrapped) session as the original attempt.
4246
- accountPatch: overrides?.accountPatch,
4247
- sessionId: effectiveSessionId,
4248
- nonce: effectiveNonce,
4249
- completionPaymentMethodId: resolvedCompletionPaymentMethodId
4250
- });
4251
- }
4252
- } finally {
4253
- setIs3DSActive(false);
4254
- }
4255
- return;
4256
- }
4257
4341
  if (json?.type === "paypal_redirect_required") {
4258
4342
  const secret = json["threeDSecureToken"];
4259
4343
  const savedPmId = json["paymentMethodId"] || tokenizedBody.id || "";
@@ -4307,6 +4391,8 @@ function SplitCardFormInner({
4307
4391
  emitDecline("paypal", "paypal_direct_required retry limit exceeded");
4308
4392
  return;
4309
4393
  }
4394
+ setDirectPaypalReady(false);
4395
+ setDirectPaypalInitializationState("loading");
4310
4396
  setPaypalDirectRetry({ orderId, attempts: prevAttempts + 1 });
4311
4397
  setOverlayStatus(null);
4312
4398
  return;
@@ -4329,7 +4415,7 @@ function SplitCardFormInner({
4329
4415
  processingRef.current = false;
4330
4416
  }
4331
4417
  },
4332
- [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]
4333
4419
  );
4334
4420
  const dispatchTokenizedBody = useCallback3(
4335
4421
  (tokenizedBody, overrides) => {
@@ -4341,35 +4427,8 @@ function SplitCardFormInner({
4341
4427
  },
4342
4428
  [onTokenizedBody, processPaymentInternal]
4343
4429
  );
4344
- useImperativeHandle(innerRef, () => ({
4345
- async handleNextAction(secret) {
4346
- if (!flopay) return;
4347
- setIs3DSActive(true);
4348
- try {
4349
- const result = await flopay.confirmPayment({
4350
- clientSecret: secret,
4351
- returnUrl: window.location.href
4352
- });
4353
- if (result.error) {
4354
- updateError(result.error.message);
4355
- onError?.(result.error);
4356
- emitDecline("card", result.error);
4357
- } else if (result.status === "succeeded" || result.status === "processing") {
4358
- dispatchTokenizedBody({
4359
- id: result.paymentIntentId,
4360
- type: "card",
4361
- threeDSecureActionResultTokenId: result.paymentIntentId
4362
- });
4363
- }
4364
- } catch (err) {
4365
- updateError(err instanceof Error ? err.message : "Payment authentication failed.");
4366
- } finally {
4367
- setIs3DSActive(false);
4368
- }
4369
- }
4370
- }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
4371
- const stripeResumeAttemptedRef = useRef5(false);
4372
- useEffect5(() => {
4430
+ const stripeResumeAttemptedRef = useRef4(false);
4431
+ useEffect4(() => {
4373
4432
  if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
4374
4433
  const params = new URLSearchParams(window.location.search);
4375
4434
  const paymentIntentId = params.get("payment_intent");
@@ -4430,7 +4489,7 @@ function SplitCardFormInner({
4430
4489
  return;
4431
4490
  }
4432
4491
  const piStatus = paymentIntent?.status;
4433
- const successful = piStatus === "succeeded" || piStatus === "requires_capture" || piStatus === "processing";
4492
+ const successful = isSuccessfulPaymentIntentStatus(piStatus);
4434
4493
  if (!paymentIntent || !successful) {
4435
4494
  const message = piStatus === "canceled" ? "Payment was canceled." : "Payment was not completed. Please try again.";
4436
4495
  updateError(message);
@@ -4447,165 +4506,10 @@ function SplitCardFormInner({
4447
4506
  }, persistedOverrides);
4448
4507
  })();
4449
4508
  }, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
4450
- const handleSubmit = useCallback3(
4451
- async (e) => {
4452
- e.preventDefault();
4453
- if (!flopay || !elements || isSubmitting || processingRef.current) return;
4454
- if (vaultActive) return;
4455
- if (layout !== "buttons") {
4456
- onButtonClick?.("card");
4457
- }
4458
- setProcessing(true);
4459
- setOverlayStatus("processing");
4460
- updateError(null);
4461
- let handedOff = false;
4462
- try {
4463
- if (avsConfig) {
4464
- const country = selectedCountryRef.current;
4465
- const postal = computePostalCodeState(
4466
- country,
4467
- zipCodeRef.current,
4468
- isAVSFieldVisible(avsConfig.postal_code, country)
4469
- );
4470
- if (postal.empty) {
4471
- setZipTouched(true);
4472
- updateError(getPostalCodeLabel(country) + " is required");
4473
- return;
4474
- }
4475
- if (postal.malformed) {
4476
- setZipTouched(true);
4477
- updateError(malformedPostcodeMessage(country));
4478
- return;
4479
- }
4480
- if (isAVSFieldVisible(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
4481
- updateError("Street address is required");
4482
- return;
4483
- }
4484
- if (isAVSFieldVisible(avsConfig.city, country) && !cityRef.current.trim()) {
4485
- updateError("City is required");
4486
- return;
4487
- }
4488
- if (isAVSFieldVisible(avsConfig.state, country) && !stateRef.current.trim()) {
4489
- updateError(getStateLabel(country) + " is required");
4490
- return;
4491
- }
4492
- }
4493
- if (!vaultActive) {
4494
- const submitResult = await flopay.submitElements();
4495
- if (submitResult.error) {
4496
- updateError(submitResult.error.message);
4497
- onError?.(submitResult.error);
4498
- return;
4499
- }
4500
- }
4501
- const billingAddress = {};
4502
- const cc = selectedCountryRef.current;
4503
- const avsCountry = enableAVS ? cc : resolvedAccount.country;
4504
- if (avsCountry) billingAddress["country"] = avsCountry;
4505
- if (avsConfig && isAVSFieldVisible(avsConfig.postal_code, cc) && zipCodeRef.current.trim()) {
4506
- billingAddress["postal_code"] = zipCodeRef.current.trim();
4507
- } else if (!avsConfig && resolvedAccount.zip) {
4508
- billingAddress["postal_code"] = resolvedAccount.zip;
4509
- }
4510
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, cc) && addressLine1Ref.current.trim()) billingAddress["line1"] = addressLine1Ref.current.trim();
4511
- if (avsConfig && isAVSFieldVisible(avsConfig.address_line_2, cc) && addressLine2Ref.current.trim()) billingAddress["line2"] = addressLine2Ref.current.trim();
4512
- if (avsConfig && isAVSFieldVisible(avsConfig.city, cc) && cityRef.current.trim()) billingAddress["city"] = cityRef.current.trim();
4513
- if (avsConfig && isAVSFieldVisible(avsConfig.state, cc) && stateRef.current.trim()) {
4514
- billingAddress["state"] = stateRef.current.trim();
4515
- } else if (avsConfig && isAVSFieldVisible(avsConfig.address_line_1, cc) && !isAVSFieldVisible(avsConfig.state, cc) && isAVSFieldVisible(avsConfig.postal_code, cc)) {
4516
- const derivedSubmitState = getStateFromPostalCode(cc, zipCodeRef.current.trim());
4517
- if (derivedSubmitState) billingAddress["state"] = derivedSubmitState;
4518
- }
4519
- const billingDetails = {
4520
- ...resolvedAccount.email ? { email: resolvedAccount.email } : {},
4521
- ...fullName.trim() ? { name: fullName.trim() } : {},
4522
- ...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
4523
- };
4524
- if (!sessionId || !resolvedAccount.email) {
4525
- throw new FloPayError3("Missing sessionId or email", "validation_error");
4526
- }
4527
- {
4528
- const pmResult = await flopay.createPaymentMethod(billingDetails);
4529
- if (pmResult.error || !pmResult.paymentMethodId) {
4530
- updateError(pmResult.error?.message ?? "Failed to create payment method.");
4531
- return;
4532
- }
4533
- const intentHeaders = { "Content-Type": "application/json" };
4534
- if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
4535
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
4536
- method: "POST",
4537
- headers: intentHeaders,
4538
- body: JSON.stringify({
4539
- sessionId,
4540
- email: resolvedAccount.email,
4541
- paymentMethodType: pmResult.paymentMethodId,
4542
- isPaypal: false
4543
- })
4544
- });
4545
- if (!intentResponse.ok) {
4546
- const intentError = await buildFloPayApiErrorFromResponse(
4547
- intentResponse,
4548
- "Failed to create payment intent"
4549
- );
4550
- setOverlayStatus("error");
4551
- updateError(intentError.message);
4552
- onError?.(intentError);
4553
- emitDecline("card", intentError);
4554
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4555
- return;
4556
- }
4557
- const intentJson = await intentResponse.json();
4558
- const intentClientSecret = intentJson.data?.id;
4559
- if (!intentClientSecret) throw new FloPayError3("No client_secret in payment intent response", "api_error");
4560
- const confirmResult = await flopay.confirmCardPayment({
4561
- clientSecret: intentClientSecret,
4562
- paymentMethodId: pmResult.paymentMethodId
4563
- });
4564
- if (confirmResult.error) {
4565
- setOverlayStatus("error");
4566
- updateError(confirmResult.error.message);
4567
- onError?.(confirmResult.error);
4568
- emitDecline("card", confirmResult.error);
4569
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4570
- return;
4571
- }
4572
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
4573
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
4574
- rawProvider,
4575
- intentClientSecret
4576
- );
4577
- const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
4578
- const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
4579
- if (!paymentIntentId) {
4580
- const error2 = new FloPayError3("No payment intent returned after confirmation.", "api_error");
4581
- setOverlayStatus("error");
4582
- updateError(error2.message);
4583
- onError?.(error2);
4584
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4585
- return;
4586
- }
4587
- handedOff = isSelfContained;
4588
- dispatchTokenizedBody({
4589
- id: paymentMethodId,
4590
- type: "card",
4591
- threeDSecureActionResultTokenId: paymentIntentId,
4592
- originalPaymentMethodId: pmResult.paymentMethodId
4593
- });
4594
- }
4595
- } catch (err) {
4596
- setOverlayStatus("error");
4597
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
4598
- await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
4599
- } finally {
4600
- if (!handedOff) {
4601
- setProcessing(false);
4602
- setOverlayStatus(null);
4603
- }
4604
- }
4605
- },
4606
- [flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, vaultActive, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
4607
- );
4608
- const isReady = flopay !== null && (vaultActive || elements !== null);
4509
+ const handleSubmit = useCallback3((event) => {
4510
+ event.preventDefault();
4511
+ }, []);
4512
+ const isReady = flopay !== null;
4609
4513
  if (!isReady) {
4610
4514
  return /* @__PURE__ */ jsx7("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
4611
4515
  }
@@ -4648,21 +4552,6 @@ function SplitCardFormInner({
4648
4552
  "--flopay-input-font-family": resolvedInputFontFamily,
4649
4553
  "--flopay-input-font-weight": String(resolvedInputFontWeight)
4650
4554
  };
4651
- const stripeElementStyle = {
4652
- style: {
4653
- base: {
4654
- ...sharedInputTypography,
4655
- fontSmoothing: "antialiased",
4656
- "::placeholder": {
4657
- color: resolvedPlaceholderColor,
4658
- fontSize: resolvedInputFontSize,
4659
- fontFamily: resolvedInputFontFamily,
4660
- fontWeight: resolvedInputFontWeight
4661
- }
4662
- },
4663
- invalid: { color: "#ef4444" }
4664
- }
4665
- };
4666
4555
  const containerOverrides = bStyles.cardFormContainer ?? {};
4667
4556
  const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
4668
4557
  const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
@@ -4675,7 +4564,6 @@ function SplitCardFormInner({
4675
4564
  expectedOrigin: vaultMount.expectedOrigin,
4676
4565
  theme: vaultThemeColors,
4677
4566
  onReady: () => {
4678
- setFormReady(true);
4679
4567
  if (!avsConfig || typeof document === "undefined") return;
4680
4568
  const focusFirstAvs = () => {
4681
4569
  const block = document.querySelector('[data-testid="flopay-avs-fields"]');
@@ -4719,7 +4607,7 @@ function SplitCardFormInner({
4719
4607
  children: "Loading secure card form\u2026"
4720
4608
  }
4721
4609
  );
4722
- const cardFormBlock = /* @__PURE__ */ jsxs4("div", { style: {
4610
+ const cardFormBlock = /* @__PURE__ */ jsxs5("div", { style: {
4723
4611
  backgroundColor: cardBg,
4724
4612
  borderRadius: containerRadius,
4725
4613
  ...containerOverrides,
@@ -4741,14 +4629,14 @@ function SplitCardFormInner({
4741
4629
  font-weight: var(--flopay-input-font-weight);
4742
4630
  }
4743
4631
  ` }),
4744
- isButtons && showCardForm && /* @__PURE__ */ jsxs4("div", { style: {
4632
+ isButtons && showCardForm && /* @__PURE__ */ jsxs5("div", { style: {
4745
4633
  display: "flex",
4746
4634
  alignItems: "center",
4747
4635
  padding: "0.75rem 0 0.625rem",
4748
4636
  // Keep the header on top when AVS is ordered above the card on vault.
4749
4637
  order: vaultActive ? -2 : 0
4750
4638
  }, children: [
4751
- /* @__PURE__ */ jsxs4(
4639
+ /* @__PURE__ */ jsxs5(
4752
4640
  "button",
4753
4641
  {
4754
4642
  type: "button",
@@ -4806,68 +4694,6 @@ function SplitCardFormInner({
4806
4694
  order: vaultActive ? -3 : 0,
4807
4695
  ...bStyles.title
4808
4696
  }, children: /* @__PURE__ */ jsx7(TitleContentSlot, { content: cardTitleContent }) }),
4809
- !vaultActive && /* @__PURE__ */ jsxs4(Fragment2, { children: [
4810
- /* @__PURE__ */ jsx7("div", { style: {
4811
- backgroundColor: cardInputBg,
4812
- border: `1px solid ${resolvedBorder}`,
4813
- borderTopLeftRadius: resolvedBorderRadius,
4814
- borderTopRightRadius: resolvedBorderRadius,
4815
- padding: "10px"
4816
- }, children: /* @__PURE__ */ jsx7(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
4817
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex" }, children: [
4818
- /* @__PURE__ */ jsx7("div", { style: {
4819
- flex: 1,
4820
- backgroundColor: cardInputBg,
4821
- // Longhand only — mixing `border` shorthand with per-side
4822
- // overrides triggers React's "shorthand vs longhand" warning
4823
- // because render order isn't deterministic.
4824
- borderTop: "none",
4825
- borderRight: "none",
4826
- borderBottom: `1px solid ${resolvedBorder}`,
4827
- borderLeft: `1px solid ${resolvedBorder}`,
4828
- borderBottomLeftRadius: resolvedBorderRadius,
4829
- padding: "10px"
4830
- }, children: /* @__PURE__ */ jsx7(CardExpiryElement, { options: stripeElementStyle }) }),
4831
- /* @__PURE__ */ jsx7("div", { style: {
4832
- flex: 1,
4833
- backgroundColor: cardInputBg,
4834
- borderTop: "none",
4835
- borderRight: `1px solid ${resolvedBorder}`,
4836
- borderBottom: `1px solid ${resolvedBorder}`,
4837
- borderLeft: `1px solid ${resolvedBorder}`,
4838
- borderBottomRightRadius: resolvedBorderRadius,
4839
- padding: "10px"
4840
- }, children: /* @__PURE__ */ jsx7(CardCvcElement, { options: stripeElementStyle }) })
4841
- ] })
4842
- ] }),
4843
- !vaultActive && /* @__PURE__ */ jsx7("div", { style: {
4844
- backgroundColor: cardInputBg,
4845
- border: `1px solid ${resolvedBorder}`,
4846
- borderRadius: resolvedBorderRadius,
4847
- marginTop: "0.5rem",
4848
- padding: "10px"
4849
- }, children: /* @__PURE__ */ jsx7(
4850
- "input",
4851
- {
4852
- className: "flopay-shared-input",
4853
- id: "flopay-cc-name",
4854
- name: "cc-name",
4855
- placeholder: "Full Name on Card",
4856
- autoComplete: "cc-name",
4857
- value: fullName,
4858
- onChange: (e) => handleNameChange(e.target.value),
4859
- disabled: isSubmitting,
4860
- required: true,
4861
- style: {
4862
- width: "100%",
4863
- border: "none",
4864
- outline: "none",
4865
- background: "transparent",
4866
- ...sharedInputTypography,
4867
- ...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
4868
- }
4869
- }
4870
- ) }),
4871
4697
  avsConfig && /* @__PURE__ */ jsx7("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
4872
4698
  const cc = selectedCountry;
4873
4699
  const inputWrapStyle = (invalid = false) => ({
@@ -4885,7 +4711,7 @@ function SplitCardFormInner({
4885
4711
  ...sharedInputTypography
4886
4712
  });
4887
4713
  const stateOpts = getStateOptions(cc);
4888
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
4714
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
4889
4715
  isAVSFieldVisible(avsConfig.address_line_1, cc) && /* @__PURE__ */ jsx7("div", { style: inputWrapStyle(invalidAvsFields.line1), children: /* @__PURE__ */ jsx7(
4890
4716
  "input",
4891
4717
  {
@@ -4923,7 +4749,7 @@ function SplitCardFormInner({
4923
4749
  style: inputFieldStyle()
4924
4750
  }
4925
4751
  ) }),
4926
- (isAVSFieldVisible(avsConfig.city, cc) || isAVSFieldVisible(avsConfig.state, cc)) && /* @__PURE__ */ jsxs4("div", { style: {
4752
+ (isAVSFieldVisible(avsConfig.city, cc) || isAVSFieldVisible(avsConfig.state, cc)) && /* @__PURE__ */ jsxs5("div", { style: {
4927
4753
  display: "flex",
4928
4754
  gap: "0",
4929
4755
  marginTop: "0.5rem"
@@ -4966,7 +4792,7 @@ function SplitCardFormInner({
4966
4792
  borderTopRightRadius: resolvedBorderRadius,
4967
4793
  borderBottomRightRadius: resolvedBorderRadius,
4968
4794
  ...isAVSFieldVisible(avsConfig.city, cc) ? { borderTopLeftRadius: 0, borderBottomLeftRadius: 0 } : { borderRadius: resolvedBorderRadius }
4969
- }, children: stateOpts ? /* @__PURE__ */ jsxs4(
4795
+ }, children: stateOpts ? /* @__PURE__ */ jsxs5(
4970
4796
  "select",
4971
4797
  {
4972
4798
  id: "flopay-billing-state",
@@ -5006,7 +4832,7 @@ function SplitCardFormInner({
5006
4832
  }
5007
4833
  ) })
5008
4834
  ] }),
5009
- (isAVSFieldVisible(avsConfig.country, cc) || isAVSFieldVisible(avsConfig.postal_code, cc)) && /* @__PURE__ */ jsxs4("div", { style: {
4835
+ (isAVSFieldVisible(avsConfig.country, cc) || isAVSFieldVisible(avsConfig.postal_code, cc)) && /* @__PURE__ */ jsxs5("div", { style: {
5010
4836
  display: "flex",
5011
4837
  flexDirection: avsLayoutProp === "column" ? "column" : "row",
5012
4838
  gap: avsLayoutProp === "column" ? "0.5rem" : "0",
@@ -5038,7 +4864,7 @@ function SplitCardFormInner({
5038
4864
  autoComplete: "billing country",
5039
4865
  "data-testid": "flopay-country",
5040
4866
  style: { ...inputFieldStyle(), cursor: "pointer" },
5041
- children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs4("option", { value: c.code, children: [
4867
+ children: COUNTRY_OPTIONS.map((c) => /* @__PURE__ */ jsxs5("option", { value: c.code, children: [
5042
4868
  c.flag,
5043
4869
  " ",
5044
4870
  c.name
@@ -5094,53 +4920,7 @@ function SplitCardFormInner({
5094
4920
  })() }),
5095
4921
  vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
5096
4922
  vaultActive && vaultCardFieldsNode,
5097
- displayError && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
5098
- margin: "0.75rem 0",
5099
- padding: "0.625rem 0.875rem",
5100
- background: "#FEF2F2",
5101
- border: "1px solid #FECACA",
5102
- borderRadius: "8px",
5103
- color: "#991B1B",
5104
- fontSize: "0.85rem",
5105
- fontWeight: 600,
5106
- display: "flex",
5107
- alignItems: "center",
5108
- gap: "0.5rem",
5109
- ...bStyles.errorBanner ? bStyles.errorBanner : {}
5110
- }, children: [
5111
- /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
5112
- displayError
5113
- ] }),
5114
- !vaultActive && (children ?? /* @__PURE__ */ jsx7(
5115
- "button",
5116
- {
5117
- type: "submit",
5118
- disabled: !formReady || isSubmitting,
5119
- "data-testid": "flopay-submit",
5120
- onMouseEnter: (e) => {
5121
- if (formReady && !isSubmitting) e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
5122
- },
5123
- onMouseLeave: (e) => {
5124
- e.currentTarget.style.backgroundColor = bStyles.submitButton?.backgroundColor ?? resolvedPrimaryColor;
5125
- },
5126
- style: {
5127
- width: "100%",
5128
- padding: "0.875rem",
5129
- marginTop: "1rem",
5130
- backgroundColor: resolvedPrimaryColor,
5131
- color: "white",
5132
- border: "none",
5133
- borderRadius: resolvedBorderRadius,
5134
- fontSize: bStyles.submitButtonFontSize ?? "1rem",
5135
- fontWeight: 600,
5136
- cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
5137
- opacity: !formReady || isSubmitting ? 0.5 : 1,
5138
- transition: "background-color 0.15s",
5139
- ...bStyles.submitButton
5140
- },
5141
- children: isSubmitting ? "PROCESSING..." : submitLabel
5142
- }
5143
- ))
4923
+ displayError && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.75rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5144
4924
  ] });
5145
4925
  const gateDebugStyle = {
5146
4926
  margin: 0,
@@ -5202,11 +4982,11 @@ function SplitCardFormInner({
5202
4982
  const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
5203
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;
5204
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;
5205
- return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
4985
+ return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5206
4986
  /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5207
4987
  overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5208
- /* @__PURE__ */ jsxs4("div", { style: { display: "grid" }, children: [
5209
- /* @__PURE__ */ jsxs4(
4988
+ /* @__PURE__ */ jsxs5("div", { style: { display: "grid" }, children: [
4989
+ /* @__PURE__ */ jsxs5(
5210
4990
  "div",
5211
4991
  {
5212
4992
  "data-testid": "flopay-buttons-panel",
@@ -5223,7 +5003,7 @@ function SplitCardFormInner({
5223
5003
  children: [
5224
5004
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
5225
5005
  renderStripeGateDebug("flopay-stripe-gate-debug"),
5226
- shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
5006
+ shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5227
5007
  paypalDirectRetry && /* @__PURE__ */ jsx7(
5228
5008
  "div",
5229
5009
  {
@@ -5260,6 +5040,7 @@ function SplitCardFormInner({
5260
5040
  runBeforeButtonClick,
5261
5041
  isProcessing: isSubmitting,
5262
5042
  onLoadStateChange: setDirectPaypalReady,
5043
+ onInitializationStateChange: setDirectPaypalInitializationState,
5263
5044
  session: session ?? null,
5264
5045
  existingOrderId: paypalDirectRetry?.orderId,
5265
5046
  debug
@@ -5292,6 +5073,7 @@ function SplitCardFormInner({
5292
5073
  nonce,
5293
5074
  email: resolvedAccount.email,
5294
5075
  billingApiUrl: resolvedBillingApiUrl,
5076
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5295
5077
  expressMethods: walletExpressMethods,
5296
5078
  onTokenizedBody: dispatchTokenizedBody,
5297
5079
  onErrorChange: updateError,
@@ -5335,7 +5117,7 @@ function SplitCardFormInner({
5335
5117
  }
5336
5118
  }
5337
5119
  ),
5338
- showStripe && /* @__PURE__ */ jsx7(
5120
+ vaultActive && /* @__PURE__ */ jsx7(
5339
5121
  "button",
5340
5122
  {
5341
5123
  type: "button",
@@ -5394,27 +5176,11 @@ function SplitCardFormInner({
5394
5176
  children: /* @__PURE__ */ jsx7(CardButtonContentSlot, { content: cardButtonContent })
5395
5177
  }
5396
5178
  ),
5397
- displayError && viewState === "buttons" && /* @__PURE__ */ jsxs4("div", { role: "alert", "data-testid": "flopay-error", style: {
5398
- margin: "0.25rem 0",
5399
- padding: "0.625rem 0.875rem",
5400
- background: "#FEF2F2",
5401
- border: "1px solid #FECACA",
5402
- borderRadius: "8px",
5403
- color: "#991B1B",
5404
- fontSize: "0.85rem",
5405
- fontWeight: 600,
5406
- display: "flex",
5407
- alignItems: "center",
5408
- gap: "0.5rem",
5409
- ...bStyles.errorBanner ? bStyles.errorBanner : {}
5410
- }, children: [
5411
- /* @__PURE__ */ jsx7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx7("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
5412
- displayError
5413
- ] })
5179
+ displayError && viewState === "buttons" && /* @__PURE__ */ jsx7(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
5414
5180
  ]
5415
5181
  }
5416
5182
  ),
5417
- showStripe && isCardView && /* @__PURE__ */ jsx7("div", { style: {
5183
+ vaultActive && isCardView && /* @__PURE__ */ jsx7("div", { style: {
5418
5184
  gridArea: "1 / 1",
5419
5185
  ...cardAnim ? { animation: cardAnim } : {},
5420
5186
  ...viewState === "collapsing" ? { pointerEvents: "none" } : {}
@@ -5423,18 +5189,18 @@ function SplitCardFormInner({
5423
5189
  gridArea: "1 / 1",
5424
5190
  ...apmAnim ? { animation: apmAnim } : {},
5425
5191
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
5426
- }, children: /* @__PURE__ */ jsxs4("div", { style: {
5192
+ }, children: /* @__PURE__ */ jsxs5("div", { style: {
5427
5193
  backgroundColor: cardBg,
5428
5194
  borderRadius: containerRadius,
5429
5195
  ...containerOverrides,
5430
5196
  padding: containerPadding
5431
5197
  }, children: [
5432
- /* @__PURE__ */ jsxs4("div", { style: {
5198
+ /* @__PURE__ */ jsxs5("div", { style: {
5433
5199
  display: "flex",
5434
5200
  alignItems: "center",
5435
5201
  padding: "0.75rem 0 0.625rem"
5436
5202
  }, children: [
5437
- /* @__PURE__ */ jsxs4(
5203
+ /* @__PURE__ */ jsxs5(
5438
5204
  "button",
5439
5205
  {
5440
5206
  type: "button",
@@ -5517,24 +5283,24 @@ function SplitCardFormInner({
5517
5283
  ] });
5518
5284
  }
5519
5285
  if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
5520
- return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5286
+ return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5521
5287
  /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5522
5288
  overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5523
5289
  /* @__PURE__ */ jsx7("div", { style: {
5524
5290
  ...apmAnim ? { animation: apmAnim } : {},
5525
5291
  ...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
5526
- }, children: /* @__PURE__ */ jsxs4("div", { style: {
5292
+ }, children: /* @__PURE__ */ jsxs5("div", { style: {
5527
5293
  backgroundColor: cardBg,
5528
5294
  borderRadius: containerRadius,
5529
5295
  ...containerOverrides,
5530
5296
  padding: containerPadding
5531
5297
  }, children: [
5532
- /* @__PURE__ */ jsxs4("div", { style: {
5298
+ /* @__PURE__ */ jsxs5("div", { style: {
5533
5299
  display: "flex",
5534
5300
  alignItems: "center",
5535
5301
  padding: "0.75rem 0 0.625rem"
5536
5302
  }, children: [
5537
- /* @__PURE__ */ jsxs4(
5303
+ /* @__PURE__ */ jsxs5(
5538
5304
  "button",
5539
5305
  {
5540
5306
  type: "button",
@@ -5615,13 +5381,13 @@ function SplitCardFormInner({
5615
5381
  ] }) })
5616
5382
  ] });
5617
5383
  }
5618
- return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5384
+ return /* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
5619
5385
  /* @__PURE__ */ jsx7(FloPayKeyframes, {}),
5620
5386
  overlayStatus && /* @__PURE__ */ jsx7(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
5621
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
5387
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
5622
5388
  renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
5623
5389
  renderStripeGateDebug("flopay-stripe-gate-debug-default"),
5624
- shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs4(Fragment2, { children: [
5390
+ shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ jsxs5(Fragment2, { children: [
5625
5391
  paypalDirectRetry && /* @__PURE__ */ jsx7(
5626
5392
  "div",
5627
5393
  {
@@ -5658,6 +5424,7 @@ function SplitCardFormInner({
5658
5424
  runBeforeButtonClick,
5659
5425
  isProcessing: isSubmitting,
5660
5426
  onLoadStateChange: setDirectPaypalReady,
5427
+ onInitializationStateChange: setDirectPaypalInitializationState,
5661
5428
  session: session ?? null,
5662
5429
  existingOrderId: paypalDirectRetry?.orderId,
5663
5430
  debug
@@ -5690,6 +5457,7 @@ function SplitCardFormInner({
5690
5457
  nonce,
5691
5458
  email: resolvedAccount.email,
5692
5459
  billingApiUrl: resolvedBillingApiUrl,
5460
+ intentKind: totalAmount > 0 ? "payment" : "setup",
5693
5461
  expressMethods: walletExpressMethods,
5694
5462
  onTokenizedBody: dispatchTokenizedBody,
5695
5463
  onErrorChange: updateError,
@@ -5734,7 +5502,7 @@ function SplitCardFormInner({
5734
5502
  }
5735
5503
  )
5736
5504
  ] }),
5737
- showStripe && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs4("div", { style: {
5505
+ vaultActive && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ jsxs5("div", { style: {
5738
5506
  display: "flex",
5739
5507
  alignItems: "center",
5740
5508
  gap: "0.75rem",
@@ -5746,7 +5514,7 @@ function SplitCardFormInner({
5746
5514
  /* @__PURE__ */ jsx7("span", { children: "or pay with card" }),
5747
5515
  /* @__PURE__ */ jsx7("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
5748
5516
  ] }),
5749
- showStripe && cardFormBlock
5517
+ vaultActive && cardFormBlock
5750
5518
  ] });
5751
5519
  }
5752
5520
 
@@ -5774,6 +5542,9 @@ async function confirmResumeIntent(stripe, clientSecret, data, isSetupIntent) {
5774
5542
  const { paymentIntent, error } = await stripe.confirmCardPayment(clientSecret, data);
5775
5543
  return { error, intent: paymentIntent ?? null };
5776
5544
  }
5545
+ function withCheckoutMethod(error, checkoutMethod) {
5546
+ return Object.assign(error, { checkoutMethod });
5547
+ }
5777
5548
  function getRedirectResultFromCheckoutProcessError(error) {
5778
5549
  if (!error?.type || !error.threeDSecureToken) {
5779
5550
  return null;
@@ -5995,7 +5766,7 @@ function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment fai
5995
5766
  const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
5996
5767
  const rawMessage = error?.message ?? fallbackMessage;
5997
5768
  const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
5998
- return Object.assign(
5769
+ return withCheckoutMethod(
5999
5770
  new FloPayError4(
6000
5771
  message,
6001
5772
  "api_error",
@@ -6003,7 +5774,7 @@ function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment fai
6003
5774
  code: error?.gatewayErrorCode
6004
5775
  }
6005
5776
  ),
6006
- { checkoutMethod }
5777
+ checkoutMethod
6007
5778
  );
6008
5779
  }
6009
5780
  function resolveSavedPaymentReturnUrl(session) {
@@ -6175,13 +5946,13 @@ async function processSavedPaymentForMode({
6175
5946
  if (recoveredRedirect) {
6176
5947
  return recoveredRedirect;
6177
5948
  }
6178
- throw Object.assign(
5949
+ throw withCheckoutMethod(
6179
5950
  new FloPayError4(
6180
5951
  "Your card requires authentication. Please enter your payment details below.",
6181
5952
  "api_error",
6182
5953
  { code: "authentication_required" }
6183
5954
  ),
6184
- { checkoutMethod: "card" }
5955
+ "card"
6185
5956
  );
6186
5957
  }
6187
5958
  throw new FloPayError4(
@@ -6209,13 +5980,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6209
5980
  }
6210
5981
  if (redirectResult.type === "3ds_required") {
6211
5982
  if (!attempt3DS || !redirectResult.threeDSecureToken) {
6212
- throw Object.assign(
5983
+ throw withCheckoutMethod(
6213
5984
  new FloPayError4(
6214
5985
  "Your card requires authentication. Please enter your payment details below.",
6215
5986
  "api_error",
6216
5987
  { code: "authentication_required" }
6217
5988
  ),
6218
- { checkoutMethod: "card" }
5989
+ "card"
6219
5990
  );
6220
5991
  }
6221
5992
  const isSetupIntent = isSetupIntentClientSecret3(redirectResult.threeDSecureToken);
@@ -6224,13 +5995,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6224
5995
  const retrieved = await retrieveResumeIntent(stripe, redirectResult.threeDSecureToken, isSetupIntent);
6225
5996
  if (retrieved) {
6226
5997
  if (retrieved.error) {
6227
- throw Object.assign(
5998
+ throw withCheckoutMethod(
6228
5999
  new FloPayError4(
6229
6000
  retrieved.error.message ?? `Failed to retrieve 3DS ${isSetupIntent ? "setup" : "payment"} status.`,
6230
6001
  "api_error",
6231
6002
  { code: retrieved.error.code }
6232
6003
  ),
6233
- { checkoutMethod: "card" }
6004
+ "card"
6234
6005
  );
6235
6006
  }
6236
6007
  const existingIntent = retrieved.intent;
@@ -6253,13 +6024,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6253
6024
  ) : null;
6254
6025
  if (confirmed) {
6255
6026
  if (confirmed.error) {
6256
- throw Object.assign(
6027
+ throw withCheckoutMethod(
6257
6028
  new FloPayError4(
6258
6029
  confirmed.error.message ?? "3DS authentication failed.",
6259
6030
  "api_error",
6260
6031
  { code: confirmed.error.code }
6261
6032
  ),
6262
- { checkoutMethod: "card" }
6033
+ "card"
6263
6034
  );
6264
6035
  }
6265
6036
  paymentIntent = confirmed.intent;
@@ -6268,13 +6039,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6268
6039
  clientSecret: redirectResult.threeDSecureToken
6269
6040
  });
6270
6041
  if (nextAction.error) {
6271
- throw Object.assign(
6042
+ throw withCheckoutMethod(
6272
6043
  new FloPayError4(
6273
6044
  nextAction.error.message ?? "3DS authentication failed.",
6274
6045
  "api_error",
6275
6046
  { code: nextAction.error.code }
6276
6047
  ),
6277
- { checkoutMethod: "card" }
6048
+ "card"
6278
6049
  );
6279
6050
  }
6280
6051
  paymentIntent = (isSetupIntent ? nextAction.setupIntent : nextAction.paymentIntent) ?? null;
@@ -6310,17 +6081,17 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6310
6081
  telemetry
6311
6082
  });
6312
6083
  }
6313
- throw Object.assign(
6084
+ throw withCheckoutMethod(
6314
6085
  new FloPayError4("3DS authentication did not complete successfully.", "api_error"),
6315
- { checkoutMethod: "card" }
6086
+ "card"
6316
6087
  );
6317
6088
  }
6318
6089
  if (redirectResult.type === "paypal_redirect_required") {
6319
6090
  const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
6320
6091
  if (!paypalStripe) {
6321
- throw Object.assign(
6092
+ throw withCheckoutMethod(
6322
6093
  new FloPayError4("PayPal is not available.", "api_error"),
6323
- { checkoutMethod: "paypal" }
6094
+ "paypal"
6324
6095
  );
6325
6096
  }
6326
6097
  if (redirectResult.paymentMethodId) {
@@ -6328,13 +6099,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6328
6099
  clientSecret: redirectResult.threeDSecureToken
6329
6100
  });
6330
6101
  if (error2) {
6331
- throw Object.assign(
6102
+ throw withCheckoutMethod(
6332
6103
  new FloPayError4(
6333
6104
  error2.message ?? "PayPal authorization failed.",
6334
6105
  "api_error",
6335
6106
  { code: error2.code }
6336
6107
  ),
6337
- { checkoutMethod: "paypal" }
6108
+ "paypal"
6338
6109
  );
6339
6110
  }
6340
6111
  return {
@@ -6351,13 +6122,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
6351
6122
  redirect: "if_required"
6352
6123
  });
6353
6124
  if (error) {
6354
- throw Object.assign(
6125
+ throw withCheckoutMethod(
6355
6126
  new FloPayError4(
6356
6127
  error.message ?? "PayPal authorization failed.",
6357
6128
  "api_error",
6358
6129
  { code: error.code }
6359
6130
  ),
6360
- { checkoutMethod: "paypal" }
6131
+ "paypal"
6361
6132
  );
6362
6133
  }
6363
6134
  return {
@@ -6442,7 +6213,7 @@ async function loadSavedPaymentProviders({
6442
6213
  }
6443
6214
 
6444
6215
  // src/flopay-checkout.tsx
6445
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
6216
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
6446
6217
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2 = 44;
6447
6218
  var PAYPAL_RESUME_STORAGE_KEY = "flopay_checkout_saved_payment_resume";
6448
6219
  var sessionInflightMap = /* @__PURE__ */ new Map();
@@ -6588,7 +6359,6 @@ function FloPayCheckout({
6588
6359
  onComplete,
6589
6360
  onError,
6590
6361
  onDecline,
6591
- onFullNameChange,
6592
6362
  onCountryChange,
6593
6363
  onZipChange,
6594
6364
  showPayPal = true,
@@ -6610,7 +6380,6 @@ function FloPayCheckout({
6610
6380
  cardFieldOrder,
6611
6381
  cardPreFormSlot,
6612
6382
  avsLayout,
6613
- submitLabel,
6614
6383
  className,
6615
6384
  initialErrorMessage = null,
6616
6385
  children,
@@ -6626,9 +6395,9 @@ function FloPayCheckout({
6626
6395
  const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
6627
6396
  const [unified, setUnified] = useState4(null);
6628
6397
  const [flopay, setFloPay] = useState4(null);
6629
- const flopayRef = useRef6(null);
6398
+ const flopayRef = useRef5(null);
6630
6399
  const [paypalFlopay, setPaypalFloPay] = useState4(null);
6631
- const paypalFlopayRef = useRef6(null);
6400
+ const paypalFlopayRef = useRef5(null);
6632
6401
  const [session, setSession] = useState4(null);
6633
6402
  const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
6634
6403
  const activeSessionId = sessionIdProp ?? resolvedSessionId;
@@ -6643,28 +6412,53 @@ function FloPayCheckout({
6643
6412
  const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
6644
6413
  const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
6645
6414
  const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
6646
- const autoCheckoutAttempted = useRef6(false);
6647
- const paypalResumeAttempted = useRef6(false);
6648
- 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);
6649
6421
  const telemetryCheckoutContext = useMemo4(() => ({
6650
6422
  checkoutMode: checkoutModeProp ?? currentMode,
6651
6423
  layout: children ? "unknown" : layout === "buttons" ? "buttons" : "embedded"
6652
6424
  }), [checkoutModeProp, children, currentMode, layout]);
6653
- const onCompleteRef = useRef6(onComplete);
6425
+ const onCompleteRef = useRef5(onComplete);
6654
6426
  onCompleteRef.current = onComplete;
6655
- const onErrorRef = useRef6(onError);
6427
+ const onErrorRef = useRef5(onError);
6656
6428
  onErrorRef.current = onError;
6657
- const onDeclineRef = useRef6(onDecline);
6429
+ const onDeclineRef = useRef5(onDecline);
6658
6430
  onDeclineRef.current = onDecline;
6659
- useEffect6(() => {
6431
+ useEffect5(() => {
6660
6432
  getFloPayTelemetryBridge(flopay)?.setCheckoutContext(telemetryCheckoutContext);
6661
6433
  if (paypalFlopay && paypalFlopay !== flopay) {
6662
6434
  getFloPayTelemetryBridge(paypalFlopay)?.setCheckoutContext(telemetryCheckoutContext);
6663
6435
  }
6664
6436
  }, [flopay, paypalFlopay, telemetryCheckoutContext]);
6665
- const onSessionCompletedRef = useRef6(onSessionCompleted);
6437
+ const onSessionCompletedRef = useRef5(onSessionCompleted);
6666
6438
  onSessionCompletedRef.current = onSessionCompleted;
6667
- 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(() => {
6668
6462
  console.info("[FloPay] Checkout initialized", {
6669
6463
  sdk_version: SDK_VERSION2,
6670
6464
  checkout_type: checkoutType,
@@ -6692,11 +6486,11 @@ function FloPayCheckout({
6692
6486
  } : void 0,
6693
6487
  [effectiveCreateSessionBase, effectiveCreateSessionMode]
6694
6488
  );
6695
- useEffect6(() => {
6489
+ useEffect5(() => {
6696
6490
  setCreateSessionPatch(void 0);
6697
6491
  setCreateSessionPatchBaseHash(baseCreateSessionHash);
6698
6492
  }, [baseCreateSessionHash]);
6699
- useEffect6(() => {
6493
+ useEffect5(() => {
6700
6494
  setModeError(initialErrorMessage);
6701
6495
  }, [initialErrorMessage]);
6702
6496
  const emitDecline = useCallback4(
@@ -6973,7 +6767,7 @@ function FloPayCheckout({
6973
6767
  telemetryCheckoutContext
6974
6768
  ]
6975
6769
  );
6976
- useEffect6(() => {
6770
+ useEffect5(() => {
6977
6771
  if (typeof window === "undefined" || paypalResumeAttempted.current) {
6978
6772
  return;
6979
6773
  }
@@ -7176,7 +6970,7 @@ function FloPayCheckout({
7176
6970
  }
7177
6971
  })();
7178
6972
  }, [emitDecline, invokeMerchantCallback, locale, normalizeSavedPaymentError, resolvedBillingUrl, telemetry]);
7179
- const initializedHashRef = useRef6(null);
6973
+ const initializedHashRef = useRef5(null);
7180
6974
  function hashCreateParams(params) {
7181
6975
  const key = JSON.stringify({
7182
6976
  c: params?.clientId,
@@ -7213,12 +7007,12 @@ function FloPayCheckout({
7213
7007
  () => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
7214
7008
  [effectiveCreateSession]
7215
7009
  );
7216
- const createSessionParamsRef = useRef6(effectiveCreateSession);
7010
+ const createSessionParamsRef = useRef5(effectiveCreateSession);
7217
7011
  createSessionParamsRef.current = effectiveCreateSession;
7218
- useEffect6(() => {
7012
+ useEffect5(() => {
7219
7013
  setResolvedSessionId(sessionIdProp ?? "");
7220
7014
  }, [sessionIdProp]);
7221
- useEffect6(() => {
7015
+ useEffect5(() => {
7222
7016
  autoCheckoutAttempted.current = false;
7223
7017
  setModeError(initialErrorMessage);
7224
7018
  setModeOverlayError(null);
@@ -7410,7 +7204,7 @@ function FloPayCheckout({
7410
7204
  resolvedSessionId,
7411
7205
  session
7412
7206
  ]);
7413
- useEffect6(() => {
7207
+ useEffect5(() => {
7414
7208
  let cancelled = false;
7415
7209
  setLoadError(null);
7416
7210
  if (createSessionHash) {
@@ -7666,7 +7460,7 @@ function FloPayCheckout({
7666
7460
  ) : null;
7667
7461
  if (isLoading) {
7668
7462
  if (loadingNode) {
7669
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7463
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7670
7464
  loadingNode,
7671
7465
  modeOverlay
7672
7466
  ] });
@@ -7679,8 +7473,8 @@ function FloPayCheckout({
7679
7473
  background: "#e5e7eb",
7680
7474
  animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
7681
7475
  } });
7682
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7683
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7476
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7477
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7684
7478
  showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7685
7479
  showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
7686
7480
  showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
@@ -7689,8 +7483,8 @@ function FloPayCheckout({
7689
7483
  modeOverlay
7690
7484
  ] });
7691
7485
  }
7692
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7693
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
7486
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7487
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", justifyContent: "center", padding: 32 }, children: [
7694
7488
  /* @__PURE__ */ jsx8("div", { style: {
7695
7489
  width: 24,
7696
7490
  height: 24,
@@ -7724,7 +7518,7 @@ function FloPayCheckout({
7724
7518
  ) });
7725
7519
  }
7726
7520
  if (shouldShowInterimButtons) {
7727
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7521
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7728
7522
  /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(
7729
7523
  InterimButtonsView,
7730
7524
  {
@@ -7745,44 +7539,43 @@ function FloPayCheckout({
7745
7539
  ] });
7746
7540
  }
7747
7541
  if (isPaypalOnlySession && session && directPaypalConfig) {
7748
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7749
- /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsxs5("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7750
- modeError && /* @__PURE__ */ jsx8(
7542
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
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(
7751
7551
  "div",
7752
7552
  {
7753
- role: "alert",
7754
- "data-testid": "flopay-error",
7755
- style: {
7756
- padding: "0.625rem 0.875rem",
7757
- background: "#FEF2F2",
7758
- border: "1px solid #FECACA",
7759
- borderRadius: "8px",
7760
- color: "#991B1B",
7761
- fontSize: "0.85rem",
7762
- fontWeight: 600
7763
- },
7764
- children: modeError
7765
- }
7766
- ),
7767
- /* @__PURE__ */ jsx8(
7768
- InstrumentedDirectPayPalButton,
7769
- {
7770
- sessionId: activeSessionId,
7771
- nonce: session.clientSecret || void 0,
7772
- billingApiUrl: resolvedBillingUrl,
7773
- email: session.customer?.email,
7774
- clientId: directPaypalConfig.clientId,
7775
- environment: directPaypalConfig.environment,
7776
- currency: (session.currency ?? "usd").toUpperCase(),
7777
- isSubscription: session.mode === "subscription",
7778
- onComplete,
7779
- onErrorChange: setModeError,
7780
- onDecline,
7781
- onButtonClick,
7782
- session,
7783
- telemetry,
7784
- telemetryContext: telemetryCheckoutContext,
7785
- 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
+ )
7786
7579
  }
7787
7580
  )
7788
7581
  ] }) }),
@@ -7793,8 +7586,8 @@ function FloPayCheckout({
7793
7586
  return /* @__PURE__ */ jsx8(Fragment3, { children: modeOverlay });
7794
7587
  }
7795
7588
  if (currentMode === "confirm") {
7796
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7797
- /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs5("div", { className, children: [
7589
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7590
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: /* @__PURE__ */ jsxs6("div", { className, children: [
7798
7591
  modeError && /* @__PURE__ */ jsx8(
7799
7592
  "div",
7800
7593
  {
@@ -7835,8 +7628,8 @@ function FloPayCheckout({
7835
7628
  modeOverlay
7836
7629
  ] });
7837
7630
  }
7838
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
7839
- /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
7631
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
7632
+ /* @__PURE__ */ jsx8(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ jsx8(FloPayProvider, { flopay, paypalFlopay, options: providerOptions, children: children ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
7840
7633
  modeError && /* @__PURE__ */ jsx8(
7841
7634
  "div",
7842
7635
  {
@@ -7878,7 +7671,6 @@ function FloPayCheckout({
7878
7671
  onDecline,
7879
7672
  error: modeError,
7880
7673
  onErrorChange: setModeError,
7881
- onFullNameChange,
7882
7674
  showPayPal,
7883
7675
  showStripe,
7884
7676
  enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
@@ -7907,7 +7699,6 @@ function FloPayCheckout({
7907
7699
  avsCheck: !!enableAVS,
7908
7700
  checkoutType: createSessionParams ? "embedded_checkout" : "standard_checkout",
7909
7701
  checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout",
7910
- submitLabel,
7911
7702
  className,
7912
7703
  directPaypal: resolveDirectPaypalConfig(unified),
7913
7704
  isSubscription: session?.mode === "subscription",
@@ -7946,10 +7737,6 @@ function SessionInjector({
7946
7737
  }
7947
7738
  function InterimButtonsView({
7948
7739
  onButtonClick,
7949
- onCardButtonClick,
7950
- cardLoading = false,
7951
- cardOpen,
7952
- errorMessage,
7953
7740
  showPayPal,
7954
7741
  showStripe = true,
7955
7742
  showApplePay,
@@ -7962,12 +7749,6 @@ function InterimButtonsView({
7962
7749
  cardTitleContent
7963
7750
  }) {
7964
7751
  const [showCardForm, setShowCardForm] = useState4(false);
7965
- const isCardOpenControlled = typeof cardOpen === "boolean";
7966
- useEffect6(() => {
7967
- if (isCardOpenControlled) {
7968
- setShowCardForm(cardOpen);
7969
- }
7970
- }, [cardOpen, isCardOpenControlled]);
7971
7752
  const themeBundle = useMemo4(() => resolveTheme2(theme), [theme]);
7972
7753
  const bStyles = useMemo4(() => {
7973
7754
  const base = themeBundle?.buttonsLayout ?? resolveButtonsLayoutTheme2(buttonsTheme);
@@ -7996,25 +7777,20 @@ function InterimButtonsView({
7996
7777
  const inputBg = bStyles.cardInputBackground ?? "white";
7997
7778
  const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
7998
7779
  const hideTitle = isEmptySlotContent(cardTitleContent);
7999
- return /* @__PURE__ */ jsxs5("div", { style: {
7780
+ return /* @__PURE__ */ jsxs6("div", { style: {
8000
7781
  backgroundColor: bStyles.cardFormContainer?.backgroundColor ?? "white",
8001
7782
  borderRadius: "8px",
8002
7783
  animation: "flopay-interim-expand 0.35s cubic-bezier(0.4, 0, 0.2, 1) both",
8003
7784
  overflow: "hidden",
8004
7785
  ...bStyles.cardFormContainer
8005
7786
  }, children: [
8006
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
8007
- /* @__PURE__ */ jsxs5(
7787
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", padding: "0.75rem 0 0.625rem" }, children: [
7788
+ /* @__PURE__ */ jsxs6(
8008
7789
  "button",
8009
7790
  {
8010
7791
  type: "button",
8011
- onClick: () => {
8012
- if (!isCardOpenControlled) {
8013
- setShowCardForm(false);
8014
- }
8015
- },
7792
+ onClick: () => setShowCardForm(false),
8016
7793
  "aria-label": "Back to payment methods",
8017
- disabled: isCardOpenControlled || cardLoading,
8018
7794
  style: {
8019
7795
  display: "inline-flex",
8020
7796
  alignItems: "center",
@@ -8026,8 +7802,8 @@ function InterimButtonsView({
8026
7802
  fontWeight: 500,
8027
7803
  padding: 0,
8028
7804
  flexShrink: 0,
8029
- opacity: isCardOpenControlled || cardLoading ? 0.6 : 1,
8030
- cursor: isCardOpenControlled || cardLoading ? "not-allowed" : "pointer",
7805
+ opacity: 1,
7806
+ cursor: "pointer",
8031
7807
  ...bStyles.backButton
8032
7808
  },
8033
7809
  children: [
@@ -8056,7 +7832,7 @@ function InterimButtonsView({
8056
7832
  }, children: /* @__PURE__ */ jsx8(TitleContentSlot, { content: cardTitleContent }) })
8057
7833
  ] }),
8058
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" } }) }),
8059
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex" }, children: [
7835
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex" }, children: [
8060
7836
  /* @__PURE__ */ jsx8("div", { style: {
8061
7837
  flex: 1,
8062
7838
  backgroundColor: inputBg,
@@ -8100,23 +7876,17 @@ function InterimButtonsView({
8100
7876
  ` })
8101
7877
  ] });
8102
7878
  }
8103
- return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
7879
+ return /* @__PURE__ */ jsxs6("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
8104
7880
  showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8105
7881
  showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
8106
7882
  showStripe && /* @__PURE__ */ jsx8(
8107
7883
  "button",
8108
7884
  {
8109
7885
  type: "button",
8110
- onClick: async () => {
8111
- if (cardLoading) return;
8112
- if (onCardButtonClick) {
8113
- await onCardButtonClick();
8114
- return;
8115
- }
7886
+ onClick: () => {
8116
7887
  onButtonClick?.("card");
8117
7888
  setShowCardForm(true);
8118
7889
  },
8119
- disabled: cardLoading,
8120
7890
  style: {
8121
7891
  width: "100%",
8122
7892
  ...cardButtonSizing,
@@ -8126,7 +7896,7 @@ function InterimButtonsView({
8126
7896
  borderRadius: "8px",
8127
7897
  fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
8128
7898
  fontWeight: 600,
8129
- cursor: cardLoading ? "not-allowed" : "pointer",
7899
+ cursor: "pointer",
8130
7900
  display: "flex",
8131
7901
  alignItems: "center",
8132
7902
  justifyContent: "center",
@@ -8134,7 +7904,7 @@ function InterimButtonsView({
8134
7904
  boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
8135
7905
  transition: "transform 0.1s",
8136
7906
  position: "relative",
8137
- opacity: cardLoading ? 0.6 : 1,
7907
+ opacity: 1,
8138
7908
  ...bStyles.cardButton
8139
7909
  },
8140
7910
  onMouseDown: (e) => {
@@ -8146,377 +7916,77 @@ function InterimButtonsView({
8146
7916
  children: /* @__PURE__ */ jsx8(CardButtonContentSlot, { content: cardButtonContent })
8147
7917
  }
8148
7918
  ),
8149
- errorMessage && /* @__PURE__ */ jsxs5("div", { style: {
8150
- margin: "0.25rem 0",
8151
- padding: "0.625rem 0.875rem",
8152
- background: "#FEF2F2",
8153
- border: "1px solid #FECACA",
8154
- borderRadius: "8px",
8155
- color: "#991B1B",
8156
- fontSize: "0.85rem",
8157
- fontWeight: 600,
8158
- display: "flex",
8159
- alignItems: "center",
8160
- gap: "0.5rem",
8161
- ...bStyles.errorBanner
8162
- }, children: [
8163
- /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx8("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
8164
- errorMessage
8165
- ] }),
8166
7919
  /* @__PURE__ */ jsx8("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
8167
7920
  ] });
8168
7921
  }
8169
7922
 
8170
- // src/checkout-form.tsx
8171
- import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
8172
- import { FloPayError as FloPayError6 } from "@flopay/shared";
8173
- import { forwardRef as forwardRef2, useCallback as useCallback5, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
8174
- import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
8175
- var WALLET_RESUME_KEY = "flopay_wallet_resume";
8176
- var CheckoutForm = forwardRef2(
8177
- function CheckoutForm2(props, ref) {
8178
- return /* @__PURE__ */ jsx9(CheckoutFormInner, { ...props, innerRef: ref });
8179
- }
8180
- );
8181
- function CheckoutFormInner({
8182
- sessionId,
8183
- nonce,
8184
- billingApiUrl,
8185
- email,
8186
- userId,
8187
- onComplete,
8188
- onError,
8189
- onDecline,
8190
- onTokenizedBody,
8191
- layout = "auto",
8192
- submitLabel = "Pay",
8193
- showAddress = false,
8194
- className,
8195
- children,
8196
- firstName,
8197
- lastName,
8198
- chv,
8199
- isProcessing: externalProcessing,
8200
- error: externalError,
8201
- onErrorChange,
8202
- innerRef
8203
- }) {
8204
- const flopay = useFloPay();
8205
- const paypalFlopay = usePayPalFloPay();
8206
- const elements = useElements();
8207
- const contextBillingUrl = useBillingApiUrl();
8208
- const [processing, setProcessing] = useState5(false);
8209
- const [error, setError] = useState5(null);
8210
- const [is3DSActive, setIs3DSActive] = useState5(false);
8211
- const displayError = externalError ?? error;
8212
- const isSubmitting = externalProcessing ?? processing;
8213
- const isSelfContained = !onTokenizedBody;
8214
- const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
8215
- const updateError = useCallback5(
8216
- (err) => {
8217
- setError(err);
8218
- onErrorChange?.(err);
8219
- },
8220
- [onErrorChange]
8221
- );
8222
- const emitDecline = useCallback5(
8223
- (input, overrides) => {
8224
- onDecline?.(buildDeclineEvent("card", input, overrides));
8225
- },
8226
- [onDecline]
8227
- );
8228
- const processPaymentInternal = useCallback5(
8229
- async (tokenizedBody, completionPaymentMethodId) => {
8230
- setProcessing(true);
8231
- updateError(null);
8232
- const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
8233
- const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
8234
- try {
8235
- const api = new PaymentAPI5(baseUrl);
8236
- const response = await api.processPayment(userId ?? "", {
8237
- sessionId,
8238
- nonce,
8239
- tokenizedData: requestTokenizedBody,
8240
- accountData: {
8241
- userId: userId ?? "",
8242
- email: email ?? "",
8243
- firstName: firstName ?? "",
8244
- lastName: lastName ?? ""
8245
- },
8246
- chv
8247
- });
8248
- if (response.ok) {
8249
- onComplete?.({
8250
- status: "succeeded",
8251
- paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
8252
- paymentMethodId: resolvedCompletionPaymentMethodId,
8253
- checkoutMethod: tokenizedBody.isPaypal ? "paypal" : "card"
8254
- });
8255
- return;
8256
- }
8257
- const json = await response.json().catch(() => null);
8258
- if (json?.type === "3ds_required") {
8259
- const secret = json["threeDSecureToken"];
8260
- if (!flopay || !secret) {
8261
- updateError("3DS authentication required but no token provided.");
8262
- return;
8263
- }
8264
- setIs3DSActive(true);
8265
- try {
8266
- const retryFullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
8267
- const result = await flopay.confirmPayment({
8268
- clientSecret: secret,
8269
- returnUrl: window.location.href,
8270
- billingDetails: {
8271
- ...email ? { email } : {},
8272
- ...retryFullName ? { name: retryFullName } : {}
8273
- }
8274
- });
8275
- if (result.error) {
8276
- updateError(result.error.message);
8277
- onError?.(result.error);
8278
- emitDecline(result.error);
8279
- return;
8280
- }
8281
- if (result.status === "succeeded" || result.status === "processing") {
8282
- await processPaymentInternal({
8283
- id: result.paymentIntentId,
8284
- type: "card",
8285
- threeDSecureActionResultTokenId: result.paymentIntentId
8286
- }, resolvedCompletionPaymentMethodId);
8287
- }
8288
- } finally {
8289
- setIs3DSActive(false);
8290
- }
8291
- return;
8292
- }
8293
- if (json?.type === "paypal_redirect_required") {
8294
- const secret = json["threeDSecureToken"] ?? json["clientSecret"];
8295
- const pmId = json["paymentMethodId"];
8296
- const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
8297
- if (!paypalStripe || !secret) {
8298
- const message = "PayPal is not available.";
8299
- updateError(message);
8300
- onDecline?.(buildDeclineEvent("paypal", message));
8301
- return;
8302
- }
8303
- localStorage.setItem(WALLET_RESUME_KEY, JSON.stringify({
8304
- sessionId,
8305
- paymentIntentId: "",
8306
- tokenType: "card",
8307
- tokenId: pmId ?? "",
8308
- status: "pending_redirect"
8309
- }));
8310
- const confirmParams = {
8311
- return_url: window.location.href
8312
- };
8313
- if (pmId) confirmParams["payment_method"] = pmId;
8314
- const { error: confirmError } = await paypalStripe.confirmPayment({
8315
- clientSecret: secret,
8316
- confirmParams,
8317
- redirect: "if_required"
8318
- });
8319
- if (confirmError) {
8320
- const message = confirmError.message ?? "PayPal payment failed.";
8321
- updateError(message);
8322
- onDecline?.(buildDeclineEvent("paypal", message, { code: confirmError.code }));
8323
- }
8324
- return;
8325
- }
8326
- const errorMessage = json?.message ?? "Payment failed. Please try again.";
8327
- updateError(errorMessage);
8328
- emitDecline(errorMessage, {
8329
- code: json?.code,
8330
- declineCode: json?.declineCode ?? json?.gatewayDeclineReason
8331
- });
8332
- } catch (err) {
8333
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
8334
- } finally {
8335
- setProcessing(false);
8336
- }
8337
- },
8338
- [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
8339
- );
8340
- const dispatchTokenizedBody = useCallback5(
8341
- (tokenizedBody) => {
8342
- if (onTokenizedBody) {
8343
- onTokenizedBody(tokenizedBody);
8344
- } else {
8345
- processPaymentInternal(tokenizedBody);
8346
- }
8347
- },
8348
- [onTokenizedBody, processPaymentInternal]
8349
- );
8350
- useImperativeHandle2(innerRef, () => ({
8351
- async handleNextAction(secret) {
8352
- if (!flopay) return;
8353
- setIs3DSActive(true);
8354
- try {
8355
- const result = await flopay.confirmPayment({
8356
- clientSecret: secret,
8357
- returnUrl: window.location.href
8358
- });
8359
- if (result.error) {
8360
- updateError(result.error.message);
8361
- onError?.(result.error);
8362
- emitDecline(result.error);
8363
- } else if (result.status === "succeeded" || result.status === "processing") {
8364
- dispatchTokenizedBody({
8365
- id: result.paymentIntentId,
8366
- type: "card",
8367
- threeDSecureActionResultTokenId: result.paymentIntentId
8368
- });
8369
- }
8370
- } catch (err) {
8371
- updateError(err instanceof Error ? err.message : "Payment authentication failed.");
8372
- } finally {
8373
- setIs3DSActive(false);
8374
- }
8375
- }
8376
- }), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
8377
- useEffect7(() => {
8378
- if (typeof window === "undefined") return;
8379
- const stored = localStorage.getItem(WALLET_RESUME_KEY);
8380
- if (!stored) return;
8381
- try {
8382
- const payload = JSON.parse(stored);
8383
- if (payload.sessionId === sessionId) {
8384
- localStorage.removeItem(WALLET_RESUME_KEY);
8385
- dispatchTokenizedBody({
8386
- id: payload.tokenId,
8387
- type: payload.tokenType,
8388
- threeDSecureActionResultTokenId: payload.paymentIntentId
8389
- });
8390
- }
8391
- } catch {
8392
- localStorage.removeItem(WALLET_RESUME_KEY);
8393
- }
8394
- }, [sessionId, dispatchTokenizedBody]);
8395
- const handleSubmit = useCallback5(
8396
- async (e) => {
8397
- e.preventDefault();
8398
- if (!flopay || !elements || isSubmitting) return;
8399
- setProcessing(true);
8400
- updateError(null);
8401
- let handedOff = false;
8402
- try {
8403
- const submitResult = await flopay.submitElements();
8404
- if (submitResult.error) {
8405
- updateError(submitResult.error.message);
8406
- onError?.(submitResult.error);
8407
- return;
8408
- }
8409
- const fullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
8410
- const pmResult = await flopay.createPaymentMethod({
8411
- ...email ? { email } : {},
8412
- ...fullName ? { name: fullName } : {}
8413
- });
8414
- if (pmResult.error || !pmResult.paymentMethodId) {
8415
- updateError(pmResult.error?.message ?? "Failed to create payment method.");
8416
- return;
8417
- }
8418
- if (!sessionId || !email) {
8419
- throw new FloPayError6("Missing sessionId or email", "validation_error");
8420
- }
8421
- const intentHeaders = { "Content-Type": "application/json" };
8422
- if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
8423
- const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
8424
- method: "POST",
8425
- headers: intentHeaders,
8426
- body: JSON.stringify({
8427
- sessionId,
8428
- email,
8429
- paymentMethodType: pmResult.paymentMethodId,
8430
- isPaypal: false
8431
- })
8432
- });
8433
- if (!intentResponse.ok) {
8434
- const intentError = await buildFloPayApiErrorFromResponse(
8435
- intentResponse,
8436
- "Failed to create payment intent"
8437
- );
8438
- updateError(intentError.message);
8439
- onError?.(intentError);
8440
- emitDecline(intentError);
8441
- return;
8442
- }
8443
- const intentJson = await intentResponse.json();
8444
- const intentClientSecret = intentJson.data?.id;
8445
- if (!intentClientSecret) throw new FloPayError6("No client_secret in payment intent response", "api_error");
8446
- const confirmResult = await flopay.confirmCardPayment({
8447
- clientSecret: intentClientSecret,
8448
- paymentMethodId: pmResult.paymentMethodId
8449
- });
8450
- if (confirmResult.error) {
8451
- updateError(confirmResult.error.message);
8452
- onError?.(confirmResult.error);
8453
- emitDecline(confirmResult.error);
8454
- 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);
8455
7948
  }
8456
- const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
8457
- const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
8458
- rawProvider,
8459
- intentClientSecret
8460
- );
8461
- const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
8462
- const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
8463
- if (!paymentIntentId) {
8464
- const error2 = new FloPayError6("No payment intent returned after confirmation.", "api_error");
8465
- updateError(error2.message);
8466
- onError?.(error2);
7949
+ if (!mounted || !containerRef.current) {
8467
7950
  return;
8468
7951
  }
8469
- handedOff = isSelfContained;
8470
- dispatchTokenizedBody({
8471
- id: paymentMethodId,
8472
- type: "card",
8473
- threeDSecureActionResultTokenId: paymentIntentId,
8474
- 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?.();
8475
7958
  });
8476
- } catch (err) {
8477
- updateError(err instanceof Error ? err.message : "An unexpected error occurred");
8478
- } finally {
8479
- if (!handedOff) {
8480
- setProcessing(false);
8481
- }
8482
- }
8483
- },
8484
- [flopay, elements, isSubmitting, sessionId, nonce, email, firstName, lastName, baseUrl, isSelfContained, dispatchTokenizedBody, onError, updateError, emitDecline]
8485
- );
8486
- const isReady = flopay !== null && elements !== null;
8487
- return /* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
8488
- (is3DSActive || isSubmitting) && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-overlay", style: {
8489
- position: "absolute",
8490
- inset: 0,
8491
- background: "rgba(255,255,255,0.7)",
8492
- display: "flex",
8493
- alignItems: "center",
8494
- justifyContent: "center",
8495
- zIndex: 10
8496
- }, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
8497
- !isReady && /* @__PURE__ */ jsx9("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
8498
- isReady && /* @__PURE__ */ jsxs6(Fragment4, { children: [
8499
- /* @__PURE__ */ jsx9(PaymentElement, { options: { layout } }),
8500
- showAddress && /* @__PURE__ */ jsx9(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
8501
- displayError && /* @__PURE__ */ jsx9("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
8502
- children ?? /* @__PURE__ */ jsx9(
8503
- "button",
8504
- {
8505
- type: "submit",
8506
- disabled: isSubmitting || !isReady,
8507
- "data-testid": "flopay-submit",
8508
- 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;
8509
7971
  }
8510
- )
8511
- ] })
8512
- ] });
7972
+ };
7973
+ }, [elements, reportInteractive]);
7974
+ return /* @__PURE__ */ jsx9("div", { ref: containerRef, className, id, style });
7975
+ }
7976
+ ElementComponent.displayName = displayName;
7977
+ return ElementComponent;
8513
7978
  }
7979
+ var PaymentElement2 = createElementComponent(
7980
+ "payment",
7981
+ "PaymentElement"
7982
+ );
7983
+ var AddressElement = createElementComponent("address", "AddressElement");
8514
7984
 
8515
7985
  // src/paypal-button.tsx
8516
- import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
8517
- import { FloPayError as FloPayError7 } from "@flopay/shared";
8518
- import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
8519
- import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } 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";
8520
7990
  function PayPalButton({
8521
7991
  sessionId,
8522
7992
  nonce,
@@ -8534,14 +8004,14 @@ function PayPalButton({
8534
8004
  const flopay = useFloPay();
8535
8005
  const elements = useElements();
8536
8006
  const contextBillingUrl = useBillingApiUrl();
8537
- const [ready, setReady] = useState6(false);
8538
- const [submitting, setSubmitting] = useState6(false);
8007
+ const [ready, setReady] = useState5(false);
8008
+ const [submitting, setSubmitting] = useState5(false);
8539
8009
  const paypalResumeAttempted = useRef7(false);
8540
8010
  const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
8541
- const processPaymentInternal = useCallback6(
8011
+ const processPaymentInternal = useCallback5(
8542
8012
  async (tokenizedBody) => {
8543
8013
  try {
8544
- const api = new PaymentAPI6(baseUrl);
8014
+ const api = new PaymentAPI5(baseUrl);
8545
8015
  const response = await api.processPayment(userId ?? "", {
8546
8016
  sessionId,
8547
8017
  nonce,
@@ -8566,7 +8036,7 @@ function PayPalButton({
8566
8036
  },
8567
8037
  [baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
8568
8038
  );
8569
- const dispatchTokenizedBody = useCallback6(
8039
+ const dispatchTokenizedBody = useCallback5(
8570
8040
  (body) => {
8571
8041
  if (onTokenizedBody) {
8572
8042
  onTokenizedBody(body);
@@ -8576,7 +8046,7 @@ function PayPalButton({
8576
8046
  },
8577
8047
  [onTokenizedBody, processPaymentInternal]
8578
8048
  );
8579
- useEffect8(() => {
8049
+ useEffect7(() => {
8580
8050
  if (!flopay || paypalResumeAttempted.current) return;
8581
8051
  const params = new URLSearchParams(window.location.search);
8582
8052
  const paymentIntentId = params.get("payment_intent");
@@ -8624,13 +8094,13 @@ function PayPalButton({
8624
8094
  }
8625
8095
  })();
8626
8096
  }, [flopay, dispatchTokenizedBody, onErrorChange]);
8627
- const handlePayPalConfirm = useCallback6(async () => {
8097
+ const handlePayPalConfirm = useCallback5(async () => {
8628
8098
  if (!flopay || !elements) return;
8629
8099
  try {
8630
8100
  setSubmitting(true);
8631
8101
  onErrorChange?.(null);
8632
8102
  if (!sessionId || !email) {
8633
- 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");
8634
8104
  }
8635
8105
  const result = await flopay.confirmPayPalPayment({
8636
8106
  billingApiUrl: baseUrl,
@@ -8660,7 +8130,7 @@ function PayPalButton({
8660
8130
  if (!flopay || !elements) {
8661
8131
  return /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6, animation: "pulse 1.5s infinite" } });
8662
8132
  }
8663
- return /* @__PURE__ */ jsxs7(Fragment5, { children: [
8133
+ return /* @__PURE__ */ jsxs7(Fragment4, { children: [
8664
8134
  !ready && /* @__PURE__ */ jsx10("div", { style: { height: 45, background: "#f0f0f0", borderRadius: 6 } }),
8665
8135
  /* @__PURE__ */ jsx10("div", { style: ready ? {} : { display: "none" }, children: /* @__PURE__ */ jsx10(
8666
8136
  "button",
@@ -8704,19 +8174,19 @@ function PayPalButton({
8704
8174
  }
8705
8175
 
8706
8176
  // src/automatic-payment-button.tsx
8707
- import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef8, useState as useState7 } from "react";
8708
- import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
8709
- import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
8710
- import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } 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";
8711
8181
  var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
8712
8182
  function sleep2(ms) {
8713
8183
  return new Promise((resolve) => setTimeout(resolve, ms));
8714
8184
  }
8715
8185
  function coerceError(err, fallbackMessage) {
8716
- if (err instanceof FloPayError8) {
8186
+ if (err instanceof FloPayError7) {
8717
8187
  return err;
8718
8188
  }
8719
- return new FloPayError8(
8189
+ return new FloPayError7(
8720
8190
  err instanceof Error ? err.message : fallbackMessage,
8721
8191
  "api_error"
8722
8192
  );
@@ -8819,36 +8289,36 @@ function FloPayAutomaticPaymentButton({
8819
8289
  utmMetadata
8820
8290
  ]
8821
8291
  );
8822
- const [isProcessing, setIsProcessing] = useState7(false);
8823
- const [overlayStatus, setOverlayStatus] = useState7(null);
8824
- const [overlayError, setOverlayError] = useState7(null);
8825
- 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);
8826
8296
  const isMountedRef = useRef8(true);
8827
8297
  const threeDsAbortRef = useRef8(null);
8828
8298
  const fallbackSessionRef = useRef8(fallbackSession);
8829
8299
  const onSuccessRef = useRef8(onSuccess);
8830
8300
  const onErrorRef = useRef8(onError);
8831
8301
  const onDeclineRef = useRef8(onDecline);
8832
- useEffect9(() => {
8302
+ useEffect8(() => {
8833
8303
  fallbackSessionRef.current = fallbackSession;
8834
8304
  }, [fallbackSession]);
8835
- useEffect9(() => {
8305
+ useEffect8(() => {
8836
8306
  onSuccessRef.current = onSuccess;
8837
8307
  }, [onSuccess]);
8838
- useEffect9(() => {
8308
+ useEffect8(() => {
8839
8309
  onErrorRef.current = onError;
8840
8310
  }, [onError]);
8841
- useEffect9(() => {
8311
+ useEffect8(() => {
8842
8312
  onDeclineRef.current = onDecline;
8843
8313
  }, [onDecline]);
8844
- useEffect9(() => {
8314
+ useEffect8(() => {
8845
8315
  isMountedRef.current = true;
8846
8316
  return () => {
8847
8317
  isMountedRef.current = false;
8848
8318
  threeDsAbortRef.current?.abort();
8849
8319
  };
8850
8320
  }, []);
8851
- useEffect9(() => {
8321
+ useEffect8(() => {
8852
8322
  if (typeof window === "undefined") {
8853
8323
  return;
8854
8324
  }
@@ -8862,13 +8332,13 @@ function FloPayAutomaticPaymentButton({
8862
8332
  window.removeEventListener("keydown", handleKeyDown);
8863
8333
  };
8864
8334
  }, []);
8865
- const emitDecline = useCallback7((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8335
+ const emitDecline = useCallback6((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
8866
8336
  onDeclineRef.current?.(buildDeclineEvent(method, error, {
8867
8337
  code: error.code,
8868
8338
  declineCode: error.declineCode
8869
8339
  }));
8870
8340
  }, []);
8871
- const showSuccess = useCallback7(async (event) => {
8341
+ const showSuccess = useCallback6(async (event) => {
8872
8342
  if (!isMountedRef.current) return;
8873
8343
  setOverlayError(null);
8874
8344
  setOverlayStatus("success");
@@ -8876,7 +8346,7 @@ function FloPayAutomaticPaymentButton({
8876
8346
  if (!isMountedRef.current) return;
8877
8347
  onSuccessRef.current?.(event);
8878
8348
  }, []);
8879
- const showError = useCallback7(async (error, options) => {
8349
+ const showError = useCallback6(async (error, options) => {
8880
8350
  if (!isMountedRef.current) return;
8881
8351
  onErrorRef.current?.(error);
8882
8352
  if (options?.emitDecline) {
@@ -8886,10 +8356,10 @@ function FloPayAutomaticPaymentButton({
8886
8356
  setOverlayStatus("error");
8887
8357
  await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
8888
8358
  }, [emitDecline]);
8889
- const processResolvedSession = useCallback7(async (apiResult, resolvedSessionId, options) => {
8359
+ const processResolvedSession = useCallback6(async (apiResult, resolvedSessionId, options) => {
8890
8360
  const session = apiResult.data.session ?? null;
8891
8361
  if (!session) {
8892
- throw new FloPayError8("No session data returned", "api_error");
8362
+ throw new FloPayError7("No session data returned", "api_error");
8893
8363
  }
8894
8364
  const effectiveNonce = session.clientSecret || nonce;
8895
8365
  if (session.status === "complete") {
@@ -8902,19 +8372,19 @@ function FloPayAutomaticPaymentButton({
8902
8372
  return;
8903
8373
  }
8904
8374
  if (session.status === "expired") {
8905
- throw new FloPayError8("Checkout session has expired.", "api_error", {
8375
+ throw new FloPayError7("Checkout session has expired.", "api_error", {
8906
8376
  code: "checkout_session_expired"
8907
8377
  });
8908
8378
  }
8909
8379
  try {
8910
8380
  if (apiResult.autoProcessingPending) {
8911
- const api = new PaymentAPI7(resolvedBillingUrl);
8381
+ const api = new PaymentAPI6(resolvedBillingUrl);
8912
8382
  const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
8913
8383
  initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
8914
8384
  });
8915
8385
  const completedSession = completed.data.session;
8916
8386
  if (!completedSession || completedSession.status !== "complete") {
8917
- throw new FloPayError8("Automatic payment failed. Please try again.", "api_error", {
8387
+ throw new FloPayError7("Automatic payment failed. Please try again.", "api_error", {
8918
8388
  code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
8919
8389
  });
8920
8390
  }
@@ -8957,7 +8427,7 @@ function FloPayAutomaticPaymentButton({
8957
8427
  }
8958
8428
  );
8959
8429
  }
8960
- const api = new PaymentAPI7(resolvedBillingUrl);
8430
+ const api = new PaymentAPI6(resolvedBillingUrl);
8961
8431
  const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
8962
8432
  initialDelayMs: 0,
8963
8433
  nonce: effectiveNonce
@@ -9042,14 +8512,14 @@ function FloPayAutomaticPaymentButton({
9042
8512
  showError,
9043
8513
  showSuccess
9044
8514
  ]);
9045
- const handleButtonClick = useCallback7(async (event) => {
8515
+ const handleButtonClick = useCallback6(async (event) => {
9046
8516
  buttonProps.onClick?.(event);
9047
8517
  if (event.defaultPrevented || disabled || isProcessing) {
9048
8518
  return;
9049
8519
  }
9050
8520
  setFallbackSession(null);
9051
8521
  if (sessionId && createSessionDraft) {
9052
- const error = new FloPayError8(
8522
+ const error = new FloPayError7(
9053
8523
  "Provide either sessionId or create-session props, not both.",
9054
8524
  "validation_error"
9055
8525
  );
@@ -9061,7 +8531,7 @@ function FloPayAutomaticPaymentButton({
9061
8531
  return;
9062
8532
  }
9063
8533
  if (!sessionId && !createSessionDraft) {
9064
- const error = new FloPayError8(
8534
+ const error = new FloPayError7(
9065
8535
  "Provide a sessionId or the props required to create an automatic payment session.",
9066
8536
  "validation_error"
9067
8537
  );
@@ -9076,7 +8546,7 @@ function FloPayAutomaticPaymentButton({
9076
8546
  setOverlayError(null);
9077
8547
  setOverlayStatus("processing");
9078
8548
  try {
9079
- const api = new PaymentAPI7(resolvedBillingUrl);
8549
+ const api = new PaymentAPI6(resolvedBillingUrl);
9080
8550
  if (sessionId) {
9081
8551
  const result = await api.getUnifiedCheckoutSession(sessionId, nonce);
9082
8552
  await processResolvedSession(result, sessionId);
@@ -9090,7 +8560,7 @@ function FloPayAutomaticPaymentButton({
9090
8560
  fromCreateSession: true
9091
8561
  });
9092
8562
  } catch (err) {
9093
- if (err instanceof FloPayError8 && err.code === "session_auto_completed") {
8563
+ if (err instanceof FloPayError7 && err.code === "session_auto_completed") {
9094
8564
  await showSuccess({
9095
8565
  result: { status: "succeeded" },
9096
8566
  session: null,
@@ -9124,7 +8594,7 @@ function FloPayAutomaticPaymentButton({
9124
8594
  showError,
9125
8595
  showSuccess
9126
8596
  ]);
9127
- const handleFallbackComplete = useCallback7((result) => {
8597
+ const handleFallbackComplete = useCallback6((result) => {
9128
8598
  const activeFallback = fallbackSessionRef.current;
9129
8599
  setFallbackSession(null);
9130
8600
  onSuccessRef.current?.({
@@ -9134,10 +8604,10 @@ function FloPayAutomaticPaymentButton({
9134
8604
  autoCompleted: false
9135
8605
  });
9136
8606
  }, []);
9137
- const handleFallbackError = useCallback7((error) => {
8607
+ const handleFallbackError = useCallback6((error) => {
9138
8608
  onErrorRef.current?.(error);
9139
8609
  }, []);
9140
- const handleFallbackDecline = useCallback7((decline) => {
8610
+ const handleFallbackDecline = useCallback6((decline) => {
9141
8611
  onDeclineRef.current?.(decline);
9142
8612
  }, []);
9143
8613
  const themeBundle = useMemo5(() => resolveTheme3(theme), [theme]);
@@ -9168,7 +8638,7 @@ function FloPayAutomaticPaymentButton({
9168
8638
  const resolvedPrimaryHoverColor = resolvedAppearanceVars?.colorPrimaryHover ?? darkenHex(resolvedPrimaryColor, 0.12);
9169
8639
  const resolvedButtonBorderRadius = resolvedAppearanceVars?.borderRadius ?? "8px";
9170
8640
  const cardButtonSizing = children === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
9171
- return /* @__PURE__ */ jsxs8(Fragment6, { children: [
8641
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
9172
8642
  /* @__PURE__ */ jsx11(
9173
8643
  "button",
9174
8644
  {
@@ -9307,17 +8777,12 @@ function FloPayAutomaticPaymentButton({
9307
8777
  }
9308
8778
  export {
9309
8779
  AddressElement,
9310
- CardCvcElement,
9311
- CardElement,
9312
- CardExpiryElement,
9313
- CardNumberElement,
9314
- CheckoutForm,
9315
8780
  DirectPayPalButton,
9316
8781
  FloPayAutomaticPaymentButton,
9317
8782
  FloPayCheckout,
9318
8783
  FloPayProvider,
9319
8784
  PayPalButton,
9320
- PaymentElement,
8785
+ PaymentElement2 as PaymentElement,
9321
8786
  SplitCardForm,
9322
8787
  VaultCardFields,
9323
8788
  useCheckout,