@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/README.md +100 -165
- package/dist/index.cjs +729 -1270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -180
- package/dist/index.d.ts +71 -180
- package/dist/index.mjs +949 -1484
- package/dist/index.mjs.map +1 -1
- package/dist/payment-logos/alipay.svg +1 -0
- package/dist/payment-logos/bancontact.svg +1 -0
- package/dist/payment-logos/blik.svg +1 -0
- package/dist/payment-logos/eps.svg +1 -0
- package/dist/payment-logos/giropay.svg +1 -0
- package/dist/payment-logos/ideal.svg +1 -0
- package/dist/payment-logos/klarna.svg +1 -0
- package/dist/payment-logos/p24.svg +1 -0
- package/dist/payment-logos/sepa_debit.svg +1 -0
- package/dist/payment-logos/wechat_pay.svg +1 -0
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -31,17 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AddressElement: () => AddressElement,
|
|
34
|
-
CardCvcElement: () => CardCvcElement,
|
|
35
|
-
CardElement: () => CardElement,
|
|
36
|
-
CardExpiryElement: () => CardExpiryElement,
|
|
37
|
-
CardNumberElement: () => CardNumberElement,
|
|
38
|
-
CheckoutForm: () => CheckoutForm,
|
|
39
34
|
DirectPayPalButton: () => DirectPayPalButton,
|
|
40
35
|
FloPayAutomaticPaymentButton: () => FloPayAutomaticPaymentButton,
|
|
41
36
|
FloPayCheckout: () => FloPayCheckout,
|
|
42
37
|
FloPayProvider: () => FloPayProvider,
|
|
43
38
|
PayPalButton: () => PayPalButton,
|
|
44
|
-
PaymentElement: () =>
|
|
39
|
+
PaymentElement: () => PaymentElement2,
|
|
45
40
|
SplitCardForm: () => SplitCardForm,
|
|
46
41
|
VaultCardFields: () => VaultCardFields,
|
|
47
42
|
useCheckout: () => useCheckout,
|
|
@@ -51,6 +46,10 @@ __export(index_exports, {
|
|
|
51
46
|
});
|
|
52
47
|
module.exports = __toCommonJS(index_exports);
|
|
53
48
|
|
|
49
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.18_typescript@6.0.3_yaml@2.9.0/node_modules/tsup/assets/cjs_shims.js
|
|
50
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
51
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
52
|
+
|
|
54
53
|
// src/provider.tsx
|
|
55
54
|
var import_react2 = require("react");
|
|
56
55
|
var import_shared = require("@flopay/shared");
|
|
@@ -317,77 +316,14 @@ function TitleContentSlot({
|
|
|
317
316
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: content === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DefaultTitleContent, {}) : content });
|
|
318
317
|
}
|
|
319
318
|
|
|
320
|
-
// src/elements.tsx
|
|
321
|
-
var import_react4 = require("react");
|
|
322
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
323
|
-
function createElementComponent(elementType, displayName) {
|
|
324
|
-
function ElementComponent({
|
|
325
|
-
className,
|
|
326
|
-
id,
|
|
327
|
-
style,
|
|
328
|
-
options,
|
|
329
|
-
onChange,
|
|
330
|
-
onReady,
|
|
331
|
-
onFocus,
|
|
332
|
-
onBlur,
|
|
333
|
-
onEscape
|
|
334
|
-
}) {
|
|
335
|
-
const containerRef = (0, import_react4.useRef)(null);
|
|
336
|
-
const elementRef = (0, import_react4.useRef)(null);
|
|
337
|
-
const { elements, reportInteractive } = (0, import_react4.useContext)(FloPayContext);
|
|
338
|
-
(0, import_react4.useEffect)(() => {
|
|
339
|
-
if (!elements || !containerRef.current) return;
|
|
340
|
-
let mounted = true;
|
|
341
|
-
(async () => {
|
|
342
|
-
let element = elements.getElement(elementType);
|
|
343
|
-
if (!element) {
|
|
344
|
-
element = await elements.create(elementType, options);
|
|
345
|
-
}
|
|
346
|
-
if (!mounted || !containerRef.current) {
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
element.mount(containerRef.current);
|
|
350
|
-
elementRef.current = element;
|
|
351
|
-
if (onChange) element.on("change", onChange);
|
|
352
|
-
element.on("ready", () => {
|
|
353
|
-
reportInteractive?.();
|
|
354
|
-
onReady?.();
|
|
355
|
-
});
|
|
356
|
-
if (onFocus) element.on("focus", onFocus);
|
|
357
|
-
if (onBlur) element.on("blur", onBlur);
|
|
358
|
-
if (onEscape) element.on("escape", onEscape);
|
|
359
|
-
})();
|
|
360
|
-
return () => {
|
|
361
|
-
mounted = false;
|
|
362
|
-
if (elementRef.current) {
|
|
363
|
-
try {
|
|
364
|
-
elementRef.current.unmount();
|
|
365
|
-
} catch {
|
|
366
|
-
}
|
|
367
|
-
elementRef.current = null;
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
}, [elements, reportInteractive]);
|
|
371
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: containerRef, className, id, style });
|
|
372
|
-
}
|
|
373
|
-
ElementComponent.displayName = displayName;
|
|
374
|
-
return ElementComponent;
|
|
375
|
-
}
|
|
376
|
-
var PaymentElement = createElementComponent("payment", "PaymentElement");
|
|
377
|
-
var CardElement = createElementComponent("card", "CardElement");
|
|
378
|
-
var CardNumberElement = createElementComponent("cardNumber", "CardNumberElement");
|
|
379
|
-
var CardExpiryElement = createElementComponent("cardExpiry", "CardExpiryElement");
|
|
380
|
-
var CardCvcElement = createElementComponent("cardCvc", "CardCvcElement");
|
|
381
|
-
var AddressElement = createElementComponent("address", "AddressElement");
|
|
382
|
-
|
|
383
319
|
// src/split-card-form.tsx
|
|
384
320
|
var import_react_stripe_js = require("@stripe/react-stripe-js");
|
|
385
321
|
var import_shared5 = require("@flopay/shared");
|
|
386
322
|
var import_js2 = require("@flopay/js");
|
|
387
323
|
|
|
388
324
|
// src/vault-card-fields.tsx
|
|
389
|
-
var
|
|
390
|
-
var
|
|
325
|
+
var import_react4 = require("react");
|
|
326
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
391
327
|
function VaultCardFields({
|
|
392
328
|
capture,
|
|
393
329
|
html,
|
|
@@ -399,16 +335,16 @@ function VaultCardFields({
|
|
|
399
335
|
onError,
|
|
400
336
|
onValidation
|
|
401
337
|
}) {
|
|
402
|
-
const containerRef = (0,
|
|
403
|
-
const onReadyRef = (0,
|
|
404
|
-
const onErrorRef = (0,
|
|
405
|
-
const onValidationRef = (0,
|
|
338
|
+
const containerRef = (0, import_react4.useRef)(null);
|
|
339
|
+
const onReadyRef = (0, import_react4.useRef)(onReady);
|
|
340
|
+
const onErrorRef = (0, import_react4.useRef)(onError);
|
|
341
|
+
const onValidationRef = (0, import_react4.useRef)(onValidation);
|
|
406
342
|
onReadyRef.current = onReady;
|
|
407
343
|
onErrorRef.current = onError;
|
|
408
344
|
onValidationRef.current = onValidation;
|
|
409
|
-
const themeRef = (0,
|
|
345
|
+
const themeRef = (0, import_react4.useRef)(theme);
|
|
410
346
|
themeRef.current = theme;
|
|
411
|
-
(0,
|
|
347
|
+
(0, import_react4.useEffect)(() => {
|
|
412
348
|
const el = containerRef.current;
|
|
413
349
|
if (!el) return;
|
|
414
350
|
let active = true;
|
|
@@ -450,12 +386,69 @@ function VaultCardFields({
|
|
|
450
386
|
capture.unmount();
|
|
451
387
|
};
|
|
452
388
|
}, [capture, html, messageToken, expectedOrigin]);
|
|
453
|
-
(0,
|
|
389
|
+
(0, import_react4.useEffect)(() => {
|
|
454
390
|
if (theme) capture.applyTheme?.(theme);
|
|
455
391
|
}, [capture, theme]);
|
|
456
|
-
return /* @__PURE__ */ (0,
|
|
392
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: containerRef, "data-testid": "flopay-vault-card-fields", style: containerStyle });
|
|
457
393
|
}
|
|
458
394
|
|
|
395
|
+
// src/error-banner.tsx
|
|
396
|
+
var import_react5 = require("react");
|
|
397
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
398
|
+
function ErrorBanner({
|
|
399
|
+
children,
|
|
400
|
+
margin,
|
|
401
|
+
icon = true,
|
|
402
|
+
styleOverride
|
|
403
|
+
}) {
|
|
404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
405
|
+
"div",
|
|
406
|
+
{
|
|
407
|
+
role: "alert",
|
|
408
|
+
"data-testid": "flopay-error",
|
|
409
|
+
style: {
|
|
410
|
+
margin,
|
|
411
|
+
padding: "0.625rem 0.875rem",
|
|
412
|
+
background: "#FEF2F2",
|
|
413
|
+
border: "1px solid #FECACA",
|
|
414
|
+
borderRadius: "8px",
|
|
415
|
+
color: "#991B1B",
|
|
416
|
+
fontSize: "0.85rem",
|
|
417
|
+
fontWeight: 600,
|
|
418
|
+
...icon ? { display: "flex", alignItems: "center", gap: "0.5rem" } : {},
|
|
419
|
+
...styleOverride ?? {}
|
|
420
|
+
},
|
|
421
|
+
children: [
|
|
422
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
423
|
+
"path",
|
|
424
|
+
{
|
|
425
|
+
d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
|
|
426
|
+
stroke: "#DC2626",
|
|
427
|
+
strokeWidth: "2",
|
|
428
|
+
strokeLinecap: "round",
|
|
429
|
+
strokeLinejoin: "round"
|
|
430
|
+
}
|
|
431
|
+
) }),
|
|
432
|
+
children
|
|
433
|
+
]
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/payment-logos.generated.ts
|
|
439
|
+
var PAYMENT_METHOD_LOGO_URLS = {
|
|
440
|
+
"alipay": new URL("./payment-logos/alipay.svg", importMetaUrl).href,
|
|
441
|
+
"bancontact": new URL("./payment-logos/bancontact.svg", importMetaUrl).href,
|
|
442
|
+
"blik": new URL("./payment-logos/blik.svg", importMetaUrl).href,
|
|
443
|
+
"eps": new URL("./payment-logos/eps.svg", importMetaUrl).href,
|
|
444
|
+
"giropay": new URL("./payment-logos/giropay.svg", importMetaUrl).href,
|
|
445
|
+
"ideal": new URL("./payment-logos/ideal.svg", importMetaUrl).href,
|
|
446
|
+
"klarna": new URL("./payment-logos/klarna.svg", importMetaUrl).href,
|
|
447
|
+
"p24": new URL("./payment-logos/p24.svg", importMetaUrl).href,
|
|
448
|
+
"sepa_debit": new URL("./payment-logos/sepa_debit.svg", importMetaUrl).href,
|
|
449
|
+
"wechat_pay": new URL("./payment-logos/wechat_pay.svg", importMetaUrl).href
|
|
450
|
+
};
|
|
451
|
+
|
|
459
452
|
// src/split-card-form.tsx
|
|
460
453
|
var import_react9 = __toESM(require("react"), 1);
|
|
461
454
|
|
|
@@ -745,13 +738,6 @@ function buildDeclineEvent(method, input, overrides) {
|
|
|
745
738
|
...declineCode ? { declineCode } : {}
|
|
746
739
|
};
|
|
747
740
|
}
|
|
748
|
-
function isRecord(value) {
|
|
749
|
-
return typeof value === "object" && value !== null;
|
|
750
|
-
}
|
|
751
|
-
function readString(payload, key) {
|
|
752
|
-
const value = payload?.[key];
|
|
753
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
754
|
-
}
|
|
755
741
|
var FRIENDLY_MESSAGE_OVERRIDES = [
|
|
756
742
|
{
|
|
757
743
|
match: /^paypal authorization required\.?$/i,
|
|
@@ -767,20 +753,8 @@ function applyFriendlyMessageOverride(message) {
|
|
|
767
753
|
}
|
|
768
754
|
return message;
|
|
769
755
|
}
|
|
770
|
-
function
|
|
771
|
-
|
|
772
|
-
const rawMessage = readString(payload, "message") ?? readString(nestedError, "message") ?? fallbackMessage;
|
|
773
|
-
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
774
|
-
const code = readString(payload, "code") ?? readString(payload, "gatewayErrorCode") ?? readString(nestedError, "code");
|
|
775
|
-
const declineCode = readString(payload, "declineCode") ?? readString(payload, "gatewayDeclineReason") ?? readString(payload, "decline_code") ?? readString(nestedError, "decline_code");
|
|
776
|
-
return new import_shared3.FloPayError(message, "api_error", {
|
|
777
|
-
...code ? { code } : {},
|
|
778
|
-
...declineCode ? { declineCode } : {}
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
async function buildFloPayApiErrorFromResponse(response, fallbackMessage) {
|
|
782
|
-
const payload = await response.json().catch(() => null);
|
|
783
|
-
return buildFloPayApiError(payload, fallbackMessage);
|
|
756
|
+
function isSuccessfulPaymentIntentStatus(status) {
|
|
757
|
+
return status === "succeeded" || status === "requires_capture" || status === "processing";
|
|
784
758
|
}
|
|
785
759
|
function mapPayPalIntentStatusToPaymentResult(status) {
|
|
786
760
|
if (status === "succeeded") {
|
|
@@ -791,41 +765,6 @@ function mapPayPalIntentStatusToPaymentResult(status) {
|
|
|
791
765
|
}
|
|
792
766
|
return "failed";
|
|
793
767
|
}
|
|
794
|
-
function resolvePaymentIntentPaymentMethodId(paymentIntent) {
|
|
795
|
-
const paymentMethod = paymentIntent?.payment_method;
|
|
796
|
-
if (typeof paymentMethod === "string" && paymentMethod.startsWith("pm_")) {
|
|
797
|
-
return paymentMethod;
|
|
798
|
-
}
|
|
799
|
-
if (paymentMethod && typeof paymentMethod === "object" && typeof paymentMethod.id === "string") {
|
|
800
|
-
return paymentMethod.id;
|
|
801
|
-
}
|
|
802
|
-
return void 0;
|
|
803
|
-
}
|
|
804
|
-
async function retrievePaymentIntentFromProvider(provider, clientSecret) {
|
|
805
|
-
const retriever = provider;
|
|
806
|
-
if ((0, import_shared3.isSetupIntentClientSecret)(clientSecret)) {
|
|
807
|
-
if (!retriever?.retrieveSetupIntent) {
|
|
808
|
-
return null;
|
|
809
|
-
}
|
|
810
|
-
const { setupIntent, error: error2 } = await retriever.retrieveSetupIntent(clientSecret);
|
|
811
|
-
if (error2) {
|
|
812
|
-
throw new import_shared3.FloPayError(error2.message ?? "Failed to retrieve setup intent.", "api_error", {
|
|
813
|
-
...error2.code ? { code: error2.code } : {}
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
|
-
return setupIntent ?? null;
|
|
817
|
-
}
|
|
818
|
-
if (!retriever?.retrievePaymentIntent) {
|
|
819
|
-
return null;
|
|
820
|
-
}
|
|
821
|
-
const { paymentIntent, error } = await retriever.retrievePaymentIntent(clientSecret);
|
|
822
|
-
if (error) {
|
|
823
|
-
throw new import_shared3.FloPayError(error.message ?? "Failed to retrieve payment intent.", "api_error", {
|
|
824
|
-
...error.code ? { code: error.code } : {}
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
return paymentIntent ?? null;
|
|
828
|
-
}
|
|
829
768
|
function resolveWalletElementsMode(amountInMinorUnits) {
|
|
830
769
|
if (typeof amountInMinorUnits !== "number" || !Number.isFinite(amountInMinorUnits) || amountInMinorUnits <= 0) {
|
|
831
770
|
return { mode: "setup" };
|
|
@@ -951,6 +890,12 @@ function isPopupBlockedError(err) {
|
|
|
951
890
|
// src/direct-paypal-button.tsx
|
|
952
891
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
953
892
|
var DEFAULT_BUTTON_HEIGHT = 45;
|
|
893
|
+
var PAYPAL_INIT_RETRY_DELAY_MS = 1e3;
|
|
894
|
+
var PAYPAL_RENDER_WATCHDOG_MS = 11e3;
|
|
895
|
+
var PAYPAL_INIT_ACKNOWLEDGEMENT_HOSTS = [
|
|
896
|
+
"https://www.paypal.com",
|
|
897
|
+
"https://www.sandbox.paypal.com"
|
|
898
|
+
];
|
|
954
899
|
var DIRECT_PAYPAL_RECOVERY_MESSAGE = "We couldn't open PayPal. Try again or choose another payment method.";
|
|
955
900
|
var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
|
|
956
901
|
width: "100%",
|
|
@@ -965,6 +910,9 @@ var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
|
|
|
965
910
|
fontWeight: 700,
|
|
966
911
|
cursor: "pointer"
|
|
967
912
|
};
|
|
913
|
+
function isPayPalInitAcknowledgementTimeout(message) {
|
|
914
|
+
return message.includes("No ack for postMessage init()") && PAYPAL_INIT_ACKNOWLEDGEMENT_HOSTS.some((host) => message.includes(host)) && message.includes("10000ms");
|
|
915
|
+
}
|
|
968
916
|
function buildDirectPayPalRecoveryMessage(popupBlocked) {
|
|
969
917
|
return popupBlocked ? `${DIRECT_PAYPAL_RECOVERY_MESSAGE} Allow pop-ups for this site, then try again.` : DIRECT_PAYPAL_RECOVERY_MESSAGE;
|
|
970
918
|
}
|
|
@@ -984,6 +932,8 @@ function DirectPayPalButtonImplementation({
|
|
|
984
932
|
onTechnicalFailure,
|
|
985
933
|
isProcessing = false,
|
|
986
934
|
onLoadStateChange,
|
|
935
|
+
onInitializationStateChange,
|
|
936
|
+
allowAutomaticRetry = true,
|
|
987
937
|
onButtonClick,
|
|
988
938
|
runBeforeButtonClick,
|
|
989
939
|
session,
|
|
@@ -1042,12 +992,29 @@ function DirectPayPalButtonImplementation({
|
|
|
1042
992
|
const focusTargetRef = (0, import_react8.useRef)(null);
|
|
1043
993
|
const pendingProviderFocusRef = (0, import_react8.useRef)(false);
|
|
1044
994
|
const [ready, setReady] = (0, import_react8.useState)(false);
|
|
995
|
+
const [recovering, setRecovering] = (0, import_react8.useState)(false);
|
|
1045
996
|
const [renderGeneration, setRenderGeneration] = (0, import_react8.useState)(0);
|
|
1046
997
|
const activeRenderGenerationRef = (0, import_react8.useRef)(0);
|
|
1047
998
|
const [showRetryFocusTarget, setShowRetryFocusTarget] = (0, import_react8.useState)(false);
|
|
999
|
+
const automaticRetryUsedRef = (0, import_react8.useRef)(false);
|
|
1000
|
+
const initializationFailureLoggedRef = (0, import_react8.useRef)(false);
|
|
1048
1001
|
const [failed, setFailed] = (0, import_react8.useState)(false);
|
|
1049
1002
|
const [submitting, setSubmitting] = (0, import_react8.useState)(false);
|
|
1050
1003
|
const baseUrl = (0, import_react8.useMemo)(() => billingApiUrl.replace(/\/+$/, ""), [billingApiUrl]);
|
|
1004
|
+
const initializationConfigKey = (0, import_react8.useMemo)(
|
|
1005
|
+
() => JSON.stringify([
|
|
1006
|
+
baseUrl,
|
|
1007
|
+
clientId,
|
|
1008
|
+
currency,
|
|
1009
|
+
environment ?? "",
|
|
1010
|
+
isSubscription,
|
|
1011
|
+
sessionId,
|
|
1012
|
+
existingOrderId ?? ""
|
|
1013
|
+
]),
|
|
1014
|
+
[baseUrl, clientId, currency, environment, existingOrderId, isSubscription, sessionId]
|
|
1015
|
+
);
|
|
1016
|
+
const initializationConfigKeyRef = (0, import_react8.useRef)(initializationConfigKey);
|
|
1017
|
+
const initializationConfigChanged = initializationConfigKeyRef.current !== initializationConfigKey;
|
|
1051
1018
|
const [debugLines, setDebugLines] = (0, import_react8.useState)([]);
|
|
1052
1019
|
const appendDebug = (line) => {
|
|
1053
1020
|
if (!debug) return;
|
|
@@ -1060,6 +1027,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1060
1027
|
const onTechnicalFailureRef = (0, import_react8.useRef)(onTechnicalFailure);
|
|
1061
1028
|
const onButtonClickRef = (0, import_react8.useRef)(onButtonClick);
|
|
1062
1029
|
const onLoadStateChangeRef = (0, import_react8.useRef)(onLoadStateChange);
|
|
1030
|
+
const onInitializationStateChangeRef = (0, import_react8.useRef)(onInitializationStateChange);
|
|
1063
1031
|
const runBeforeButtonClickRef = (0, import_react8.useRef)(runBeforeButtonClick);
|
|
1064
1032
|
const sessionRef = (0, import_react8.useRef)(session);
|
|
1065
1033
|
const emailRef = (0, import_react8.useRef)(email);
|
|
@@ -1070,6 +1038,13 @@ function DirectPayPalButtonImplementation({
|
|
|
1070
1038
|
const invalidatedAttemptGenerationRef = (0, import_react8.useRef)(null);
|
|
1071
1039
|
const attemptContextBySurfaceRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
1072
1040
|
const approvalContextByTokenRef = (0, import_react8.useRef)(/* @__PURE__ */ new Map());
|
|
1041
|
+
const componentMountedRef = (0, import_react8.useRef)(false);
|
|
1042
|
+
(0, import_react8.useEffect)(() => {
|
|
1043
|
+
componentMountedRef.current = true;
|
|
1044
|
+
return () => {
|
|
1045
|
+
componentMountedRef.current = false;
|
|
1046
|
+
};
|
|
1047
|
+
}, []);
|
|
1073
1048
|
(0, import_react8.useEffect)(() => {
|
|
1074
1049
|
onTokenizedBodyRef.current = onTokenizedBody;
|
|
1075
1050
|
}, [onTokenizedBody]);
|
|
@@ -1091,6 +1066,9 @@ function DirectPayPalButtonImplementation({
|
|
|
1091
1066
|
(0, import_react8.useEffect)(() => {
|
|
1092
1067
|
onLoadStateChangeRef.current = onLoadStateChange;
|
|
1093
1068
|
}, [onLoadStateChange]);
|
|
1069
|
+
(0, import_react8.useEffect)(() => {
|
|
1070
|
+
onInitializationStateChangeRef.current = onInitializationStateChange;
|
|
1071
|
+
}, [onInitializationStateChange]);
|
|
1094
1072
|
(0, import_react8.useEffect)(() => {
|
|
1095
1073
|
runBeforeButtonClickRef.current = runBeforeButtonClick;
|
|
1096
1074
|
}, [runBeforeButtonClick]);
|
|
@@ -1226,6 +1204,16 @@ function DirectPayPalButtonImplementation({
|
|
|
1226
1204
|
clearAttemptTimer();
|
|
1227
1205
|
};
|
|
1228
1206
|
}, []);
|
|
1207
|
+
(0, import_react8.useEffect)(() => {
|
|
1208
|
+
if (initializationConfigKeyRef.current === initializationConfigKey) return;
|
|
1209
|
+
initializationConfigKeyRef.current = initializationConfigKey;
|
|
1210
|
+
automaticRetryUsedRef.current = false;
|
|
1211
|
+
initializationFailureLoggedRef.current = false;
|
|
1212
|
+
setReady(false);
|
|
1213
|
+
setRecovering(false);
|
|
1214
|
+
setFailed(false);
|
|
1215
|
+
onInitializationStateChangeRef.current?.("loading");
|
|
1216
|
+
}, [initializationConfigKey]);
|
|
1229
1217
|
(0, import_react8.useEffect)(() => {
|
|
1230
1218
|
invokeMerchantCallback(() => onLoadStateChangeRef.current?.(ready && !failed));
|
|
1231
1219
|
}, [ready, failed]);
|
|
@@ -1260,7 +1248,29 @@ function DirectPayPalButtonImplementation({
|
|
|
1260
1248
|
paymentMethodCategory: "paypal"
|
|
1261
1249
|
});
|
|
1262
1250
|
let cancelled = false;
|
|
1263
|
-
let
|
|
1251
|
+
let superseded = false;
|
|
1252
|
+
const isInactive = () => cancelled || superseded;
|
|
1253
|
+
let paypalIntentIdempotencyKey;
|
|
1254
|
+
let paypalIntentAttemptActive = false;
|
|
1255
|
+
const beginPaypalIntentAttempt = () => {
|
|
1256
|
+
paypalIntentIdempotencyKey = (0, import_shared4.generateIdempotencyKey)();
|
|
1257
|
+
if (!paypalIntentIdempotencyKey) {
|
|
1258
|
+
console.warn(
|
|
1259
|
+
"[FloPay] DirectPayPal: secure idempotency key unavailable; retries may not be deduplicated."
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
paypalIntentAttemptActive = true;
|
|
1263
|
+
};
|
|
1264
|
+
let retryTimer = null;
|
|
1265
|
+
let preRenderFailureHandled = false;
|
|
1266
|
+
let ownedButtons = null;
|
|
1267
|
+
let ownedButtonsClosed = false;
|
|
1268
|
+
const closeOwnedButtons = () => {
|
|
1269
|
+
if (!ownedButtons || ownedButtonsClosed) return;
|
|
1270
|
+
ownedButtonsClosed = true;
|
|
1271
|
+
ownedButtons.close().catch(() => {
|
|
1272
|
+
});
|
|
1273
|
+
};
|
|
1264
1274
|
let overlayStartedAt = null;
|
|
1265
1275
|
const finishOverlay = () => {
|
|
1266
1276
|
if (overlayStartedAt === null) return;
|
|
@@ -1330,6 +1340,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1330
1340
|
};
|
|
1331
1341
|
const errorMessages = [];
|
|
1332
1342
|
const onWindowError = (ev) => {
|
|
1343
|
+
if (isInactive()) return;
|
|
1333
1344
|
const msg = ev.message ?? String(ev.error ?? "(no message)");
|
|
1334
1345
|
if (msg && (msg.toLowerCase().includes("paypal") || msg.toLowerCase().includes("zoid") || msg.toLowerCase().includes("postrobot") || msg.toLowerCase().includes("storage"))) {
|
|
1335
1346
|
appendDebug(`window:error ${msg.slice(0, 140)}`);
|
|
@@ -1337,6 +1348,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1337
1348
|
}
|
|
1338
1349
|
};
|
|
1339
1350
|
const onUnhandledRejection = (ev) => {
|
|
1351
|
+
if (isInactive()) return;
|
|
1340
1352
|
const reason = ev.reason instanceof Error ? ev.reason.message : String(ev.reason ?? "(no reason)");
|
|
1341
1353
|
if (reason && (reason.toLowerCase().includes("paypal") || reason.toLowerCase().includes("zoid") || reason.toLowerCase().includes("postrobot") || reason.toLowerCase().includes("storage"))) {
|
|
1342
1354
|
appendDebug(`window:rejection ${reason.slice(0, 140)}`);
|
|
@@ -1351,6 +1363,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1351
1363
|
if (debug && typeof PerformanceObserver !== "undefined") {
|
|
1352
1364
|
try {
|
|
1353
1365
|
perfObserver = new PerformanceObserver((list) => {
|
|
1366
|
+
if (isInactive()) return;
|
|
1354
1367
|
for (const entry of list.getEntries()) {
|
|
1355
1368
|
if (!entry.name.toLowerCase().includes("paypal")) continue;
|
|
1356
1369
|
paypalRequestCount.value += 1;
|
|
@@ -1379,17 +1392,13 @@ function DirectPayPalButtonImplementation({
|
|
|
1379
1392
|
return lower.includes("zoid destroyed") || lower.includes("destroyed all components") || lower.includes("window closed") || lower.includes("detected container element removed");
|
|
1380
1393
|
};
|
|
1381
1394
|
const forwardError = (message) => {
|
|
1382
|
-
if (
|
|
1395
|
+
if (isInactive()) return;
|
|
1383
1396
|
if (isZoidLifecycleMessage(message)) return;
|
|
1384
1397
|
const friendly = applyFriendlyMessageOverride(message) ?? message;
|
|
1385
1398
|
invokeMerchantCallback(() => onErrorChangeRef.current?.(friendly));
|
|
1386
1399
|
};
|
|
1387
1400
|
const markRenderFailed = (message) => {
|
|
1388
1401
|
if (cancelled) return;
|
|
1389
|
-
if (isZoidLifecycleMessage(message)) {
|
|
1390
|
-
appendDebug(`markRenderFailed:skip-zoid msg=${message.slice(0, 80)}`);
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
1402
|
setFailed(true);
|
|
1394
1403
|
if (!message.includes("paypal_ineligible")) {
|
|
1395
1404
|
telemetrySource.error({
|
|
@@ -1402,8 +1411,48 @@ function DirectPayPalButtonImplementation({
|
|
|
1402
1411
|
}
|
|
1403
1412
|
console.error("[FloPay] DirectPayPal load/render failure:", message);
|
|
1404
1413
|
};
|
|
1414
|
+
const handlePreRenderFailure = (message, retryable = false) => {
|
|
1415
|
+
if (cancelled) return;
|
|
1416
|
+
const isRecoveryAttempt = automaticRetryUsedRef.current || !allowAutomaticRetry;
|
|
1417
|
+
if (preRenderFailureHandled) return;
|
|
1418
|
+
preRenderFailureHandled = true;
|
|
1419
|
+
superseded = true;
|
|
1420
|
+
stopDiagnostics();
|
|
1421
|
+
closeOwnedButtons();
|
|
1422
|
+
const isRetryableInitializationFailure = retryable || isPayPalInitAcknowledgementTimeout(message);
|
|
1423
|
+
if (isRetryableInitializationFailure && allowAutomaticRetry && !automaticRetryUsedRef.current) {
|
|
1424
|
+
automaticRetryUsedRef.current = true;
|
|
1425
|
+
setReady(false);
|
|
1426
|
+
setSubmitting(false);
|
|
1427
|
+
setRecovering(true);
|
|
1428
|
+
onInitializationStateChangeRef.current?.("retrying");
|
|
1429
|
+
retryTimer = setTimeout(() => {
|
|
1430
|
+
if (cancelled) return;
|
|
1431
|
+
setRenderGeneration((generation) => generation + 1);
|
|
1432
|
+
}, PAYPAL_INIT_RETRY_DELAY_MS);
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
if (isRetryableInitializationFailure || isRecoveryAttempt) {
|
|
1436
|
+
setReady(false);
|
|
1437
|
+
setSubmitting(false);
|
|
1438
|
+
setRecovering(false);
|
|
1439
|
+
setFailed(true);
|
|
1440
|
+
onInitializationStateChangeRef.current?.("exhausted");
|
|
1441
|
+
if (!initializationFailureLoggedRef.current) {
|
|
1442
|
+
initializationFailureLoggedRef.current = true;
|
|
1443
|
+
const failureCode = isPayPalInitAcknowledgementTimeout(message) ? "paypal_init_timeout" : retryable ? "paypal_render_timeout" : "paypal_init_failure";
|
|
1444
|
+
console.error(
|
|
1445
|
+
"[FloPay] Direct PayPal initialization failed:",
|
|
1446
|
+
failureCode
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
markRenderFailed(message);
|
|
1452
|
+
};
|
|
1405
1453
|
setFailed(false);
|
|
1406
1454
|
const dispatchTokenizedBody = async (body, prepared = beforeClickRef.current) => {
|
|
1455
|
+
if (isInactive()) return;
|
|
1407
1456
|
const effectiveSessionId = prepared?.sessionId ?? sessionId;
|
|
1408
1457
|
if (onTokenizedBodyRef.current) {
|
|
1409
1458
|
await onTokenizedBodyRef.current(body, {
|
|
@@ -1457,41 +1506,45 @@ function DirectPayPalButtonImplementation({
|
|
|
1457
1506
|
}
|
|
1458
1507
|
);
|
|
1459
1508
|
if (response.ok) {
|
|
1509
|
+
if (superseded || cancelled && componentMountedRef.current) return;
|
|
1460
1510
|
finishProcessing();
|
|
1461
1511
|
telemetrySource.terminal({
|
|
1462
1512
|
outcome: "payment_succeeded",
|
|
1463
1513
|
provider: "paypal",
|
|
1464
1514
|
paymentMethodCategory: "paypal"
|
|
1465
1515
|
});
|
|
1466
|
-
|
|
1467
|
-
const json = await response.json().catch(() => null);
|
|
1468
|
-
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1469
|
-
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1470
|
-
finishProcessing();
|
|
1471
|
-
if (typeof json?.["declineCode"] === "string") {
|
|
1472
|
-
telemetrySource.terminal({
|
|
1473
|
-
outcome: "payment_declined",
|
|
1474
|
-
provider: "paypal",
|
|
1475
|
-
paymentMethodCategory: "paypal"
|
|
1476
|
-
});
|
|
1477
|
-
} else {
|
|
1478
|
-
telemetrySource.error({
|
|
1479
|
-
errorCode: "PAYMENT_PROCESSING_FAILED",
|
|
1480
|
-
stage: "processing",
|
|
1481
|
-
provider: "paypal",
|
|
1482
|
-
paymentMethodCategory: "paypal",
|
|
1483
|
-
requestCategory: "process_payment",
|
|
1484
|
-
statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
|
|
1485
|
-
});
|
|
1486
|
-
}
|
|
1487
|
-
forwardError(message);
|
|
1488
|
-
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1489
|
-
code: json?.["code"],
|
|
1490
|
-
declineCode: json?.["declineCode"]
|
|
1491
|
-
})));
|
|
1516
|
+
invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
|
|
1492
1517
|
return;
|
|
1493
1518
|
}
|
|
1519
|
+
if (isInactive()) return;
|
|
1520
|
+
const json = await response.json().catch(() => null);
|
|
1521
|
+
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1522
|
+
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1523
|
+
finishProcessing();
|
|
1524
|
+
if (typeof json?.["declineCode"] === "string") {
|
|
1525
|
+
telemetrySource.terminal({
|
|
1526
|
+
outcome: "payment_declined",
|
|
1527
|
+
provider: "paypal",
|
|
1528
|
+
paymentMethodCategory: "paypal"
|
|
1529
|
+
});
|
|
1530
|
+
} else {
|
|
1531
|
+
telemetrySource.error({
|
|
1532
|
+
errorCode: "PAYMENT_PROCESSING_FAILED",
|
|
1533
|
+
stage: "processing",
|
|
1534
|
+
provider: "paypal",
|
|
1535
|
+
paymentMethodCategory: "paypal",
|
|
1536
|
+
requestCategory: "process_payment",
|
|
1537
|
+
statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
forwardError(message);
|
|
1541
|
+
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1542
|
+
code: json?.["code"],
|
|
1543
|
+
declineCode: json?.["declineCode"]
|
|
1544
|
+
})));
|
|
1545
|
+
return;
|
|
1494
1546
|
} catch (err) {
|
|
1547
|
+
if (isInactive()) return;
|
|
1495
1548
|
finishProcessing();
|
|
1496
1549
|
telemetrySource.error({
|
|
1497
1550
|
errorCode: "NETWORK_REQUEST_FAILED",
|
|
@@ -1507,15 +1560,21 @@ function DirectPayPalButtonImplementation({
|
|
|
1507
1560
|
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message)));
|
|
1508
1561
|
return;
|
|
1509
1562
|
}
|
|
1510
|
-
invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
|
|
1511
1563
|
};
|
|
1564
|
+
const supersededRenderError = () => new import_shared4.FloPayError(
|
|
1565
|
+
"PayPal render was superseded.",
|
|
1566
|
+
"api_error",
|
|
1567
|
+
{ code: "paypal_render_superseded" }
|
|
1568
|
+
);
|
|
1569
|
+
const resolveExistingOrder = (orderId) => isInactive() ? Promise.reject(supersededRenderError()) : Promise.resolve(orderId);
|
|
1512
1570
|
const createPaypalIntent = async (fallbackMessage) => {
|
|
1571
|
+
if (isInactive()) {
|
|
1572
|
+
throw supersededRenderError();
|
|
1573
|
+
}
|
|
1574
|
+
if (!paypalIntentAttemptActive) beginPaypalIntentAttempt();
|
|
1513
1575
|
const prepared = beforeClickRef.current;
|
|
1514
1576
|
const effectiveSessionId = prepared?.sessionId ?? sessionId;
|
|
1515
|
-
const effectiveEmail = prepared?.accountPatch?.email ?? emailRef.current;
|
|
1516
|
-
const intentHeaders = { "Content-Type": "application/json" };
|
|
1517
1577
|
const currentNonce = prepared?.nonce ?? nonceRef.current;
|
|
1518
|
-
if (currentNonce) intentHeaders["x-checkout-session-token"] = currentNonce;
|
|
1519
1578
|
telemetrySource.log({
|
|
1520
1579
|
name: "payment.intent.started",
|
|
1521
1580
|
stage: "processing",
|
|
@@ -1523,22 +1582,22 @@ function DirectPayPalButtonImplementation({
|
|
|
1523
1582
|
paymentMethodCategory: "paypal",
|
|
1524
1583
|
requestCategory: "intent_create"
|
|
1525
1584
|
});
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1585
|
+
const intent = await new import_js.PaymentAPI(baseUrl, { telemetry: false }).createSessionIntent(
|
|
1586
|
+
effectiveSessionId,
|
|
1587
|
+
currentNonce ?? "",
|
|
1588
|
+
{
|
|
1589
|
+
provider: "paypal",
|
|
1590
|
+
paymentMethodCategory: "wallet",
|
|
1532
1591
|
paymentMethodType: "paypal",
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1592
|
+
paymentMethodId: null,
|
|
1593
|
+
intentKind: isSubscription ? "subscription" : "order"
|
|
1594
|
+
},
|
|
1595
|
+
{ idempotencyKey: paypalIntentIdempotencyKey }
|
|
1596
|
+
);
|
|
1597
|
+
if (isInactive()) {
|
|
1598
|
+
throw supersededRenderError();
|
|
1539
1599
|
}
|
|
1540
|
-
|
|
1541
|
-
if (!id) {
|
|
1600
|
+
if (intent.provider !== "paypal") {
|
|
1542
1601
|
throw new Error(fallbackMessage);
|
|
1543
1602
|
}
|
|
1544
1603
|
telemetrySource.log({
|
|
@@ -1549,7 +1608,24 @@ function DirectPayPalButtonImplementation({
|
|
|
1549
1608
|
requestCategory: "intent_create",
|
|
1550
1609
|
statusClass: "2xx"
|
|
1551
1610
|
});
|
|
1552
|
-
return
|
|
1611
|
+
return intent.providerObjectId;
|
|
1612
|
+
};
|
|
1613
|
+
const reportPayPalDecline = (error, context) => {
|
|
1614
|
+
const currentNonce = context?.nonce ?? nonceRef.current;
|
|
1615
|
+
if (!currentNonce) return;
|
|
1616
|
+
const effectiveSessionId = context?.sessionId ?? sessionId;
|
|
1617
|
+
const providerDeclineReason = getProviderDeclineCode(error) ?? getProviderErrorCode(error) ?? "provider_declined";
|
|
1618
|
+
void new import_js.PaymentAPI(baseUrl, { telemetry: false }).reportSessionIntentDecline(
|
|
1619
|
+
effectiveSessionId,
|
|
1620
|
+
currentNonce,
|
|
1621
|
+
{
|
|
1622
|
+
provider: "paypal",
|
|
1623
|
+
paymentMethodCategory: "wallet",
|
|
1624
|
+
paymentMethodType: "paypal",
|
|
1625
|
+
providerDeclineReason
|
|
1626
|
+
}
|
|
1627
|
+
).catch(() => {
|
|
1628
|
+
});
|
|
1553
1629
|
};
|
|
1554
1630
|
const effectRenderGeneration = renderGeneration;
|
|
1555
1631
|
appendDebug("loadScript:scheduled (deferred 1 tick)");
|
|
@@ -1578,9 +1654,11 @@ function DirectPayPalButtonImplementation({
|
|
|
1578
1654
|
...paypalSdkEnv === "sandbox" ? { dataNamespace: "paypal_sandbox" } : {}
|
|
1579
1655
|
});
|
|
1580
1656
|
loadPromise.then((paypal) => {
|
|
1581
|
-
appendDebug(`loadScript:resolved
|
|
1582
|
-
if (
|
|
1583
|
-
|
|
1657
|
+
appendDebug(`loadScript:resolved inactive=${isInactive()} ns=${!!paypal} buttons=${!!paypal?.Buttons}`);
|
|
1658
|
+
if (isInactive()) return;
|
|
1659
|
+
if (!paypal?.Buttons) {
|
|
1660
|
+
appendDebug("FAIL: namespace missing Buttons factory");
|
|
1661
|
+
handlePreRenderFailure("PayPal SDK did not expose the Buttons factory.");
|
|
1584
1662
|
return;
|
|
1585
1663
|
}
|
|
1586
1664
|
telemetrySource.log({
|
|
@@ -1590,7 +1668,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1590
1668
|
paymentMethodCategory: "paypal"
|
|
1591
1669
|
});
|
|
1592
1670
|
const handleApprove = async (data) => {
|
|
1593
|
-
if (
|
|
1671
|
+
if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
|
|
1594
1672
|
const token = data.subscriptionID ?? data.orderID ?? "";
|
|
1595
1673
|
const context = token ? approvalContextByTokenRef.current.get(token) : attemptContextBySurfaceRef.current.get(effectRenderGeneration);
|
|
1596
1674
|
if (!context || !isAttemptActive(context.generation)) return;
|
|
@@ -1615,7 +1693,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1615
1693
|
} catch (err) {
|
|
1616
1694
|
forwardError(err instanceof Error ? err.message : "PayPal capture failed.");
|
|
1617
1695
|
} finally {
|
|
1618
|
-
setSubmitting(false);
|
|
1696
|
+
if (!isInactive()) setSubmitting(false);
|
|
1619
1697
|
}
|
|
1620
1698
|
};
|
|
1621
1699
|
const buttons = paypal.Buttons({
|
|
@@ -1626,11 +1704,20 @@ function DirectPayPalButtonImplementation({
|
|
|
1626
1704
|
// inline-session/account patches land before the order is created,
|
|
1627
1705
|
// matching the Stripe-rendered PayPal flow.
|
|
1628
1706
|
onClick: async (_data, actions) => {
|
|
1707
|
+
if (isInactive()) {
|
|
1708
|
+
await actions.reject();
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
beginPaypalIntentAttempt();
|
|
1629
1712
|
const runner = runBeforeButtonClickRef.current;
|
|
1630
1713
|
let prepared = {};
|
|
1631
1714
|
if (runner) {
|
|
1632
1715
|
try {
|
|
1633
1716
|
const beforeClick = await runner("paypal");
|
|
1717
|
+
if (isInactive()) {
|
|
1718
|
+
await actions.reject();
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1634
1721
|
if (!beforeClick.proceed) {
|
|
1635
1722
|
beforeClickRef.current = null;
|
|
1636
1723
|
attemptContextBySurfaceRef.current.delete(effectRenderGeneration);
|
|
@@ -1684,10 +1771,11 @@ function DirectPayPalButtonImplementation({
|
|
|
1684
1771
|
// prior backend round-trip (the `paypal_direct_required` retry
|
|
1685
1772
|
// path), feed it straight to PayPal instead of creating a new one.
|
|
1686
1773
|
// Otherwise fall back to the normal create-intent call.
|
|
1687
|
-
createOrder: isSubscription ? void 0 : existingOrderId ? () =>
|
|
1688
|
-
createSubscription: isSubscription ? existingOrderId ? () =>
|
|
1774
|
+
createOrder: isSubscription ? void 0 : existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal order.").then(registerApprovalContext),
|
|
1775
|
+
createSubscription: isSubscription ? existingOrderId ? () => resolveExistingOrder(existingOrderId).then(registerApprovalContext) : () => createPaypalIntent("Failed to create PayPal subscription.").then(registerApprovalContext) : void 0,
|
|
1689
1776
|
onApprove: handleApprove,
|
|
1690
1777
|
onCancel: () => {
|
|
1778
|
+
if (isInactive()) return;
|
|
1691
1779
|
const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
|
|
1692
1780
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1693
1781
|
beforeClickRef.current = null;
|
|
@@ -1701,7 +1789,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1701
1789
|
invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
|
|
1702
1790
|
},
|
|
1703
1791
|
onError: (err) => {
|
|
1704
|
-
if (
|
|
1792
|
+
if (isInactive() || activeRenderGenerationRef.current !== effectRenderGeneration) return;
|
|
1705
1793
|
const message = err instanceof Error ? err.message : "PayPal failed to render.";
|
|
1706
1794
|
appendDebug(`onError rendered=${rendered} msg=${message.slice(0, 120)}`);
|
|
1707
1795
|
if (rendered) {
|
|
@@ -1721,13 +1809,16 @@ function DirectPayPalButtonImplementation({
|
|
|
1721
1809
|
paymentMethodCategory: "paypal",
|
|
1722
1810
|
requestCategory: "provider_sdk"
|
|
1723
1811
|
});
|
|
1812
|
+
if (isProviderDecline(err)) {
|
|
1813
|
+
reportPayPalDecline(err, context);
|
|
1814
|
+
}
|
|
1724
1815
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1725
1816
|
beforeClickRef.current = null;
|
|
1726
1817
|
invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
|
|
1727
1818
|
notifyTechnicalFailure(err, { code: "paypal_runtime_failed" });
|
|
1728
1819
|
return;
|
|
1729
1820
|
}
|
|
1730
|
-
|
|
1821
|
+
handlePreRenderFailure(message);
|
|
1731
1822
|
}
|
|
1732
1823
|
});
|
|
1733
1824
|
const eligible = buttons.isEligible();
|
|
@@ -1740,17 +1831,19 @@ function DirectPayPalButtonImplementation({
|
|
|
1740
1831
|
});
|
|
1741
1832
|
if (!eligible) {
|
|
1742
1833
|
setReady(false);
|
|
1743
|
-
|
|
1834
|
+
handlePreRenderFailure(
|
|
1744
1835
|
"PayPal buttons are not eligible to render in this context (paypal_ineligible)."
|
|
1745
1836
|
);
|
|
1746
1837
|
return;
|
|
1747
1838
|
}
|
|
1748
1839
|
const typedButtons = buttons;
|
|
1840
|
+
ownedButtons = typedButtons;
|
|
1749
1841
|
if (debug) {
|
|
1750
1842
|
appendDebug(`container:dims ${formatDims(container)} visibility=${typeof document !== "undefined" ? document.visibilityState : "(no document)"}`);
|
|
1751
1843
|
}
|
|
1752
1844
|
if (debug && typeof MutationObserver !== "undefined") {
|
|
1753
1845
|
containerObserver = new MutationObserver((mutations) => {
|
|
1846
|
+
if (isInactive()) return;
|
|
1754
1847
|
for (const mutation of mutations) {
|
|
1755
1848
|
if (mutation.type === "childList") {
|
|
1756
1849
|
mutation.addedNodes.forEach((node) => {
|
|
@@ -1778,7 +1871,7 @@ function DirectPayPalButtonImplementation({
|
|
|
1778
1871
|
});
|
|
1779
1872
|
}
|
|
1780
1873
|
const snapshotContainer = (when) => {
|
|
1781
|
-
if (
|
|
1874
|
+
if (isInactive() || rendered) return;
|
|
1782
1875
|
const iframes = container.querySelectorAll("iframe");
|
|
1783
1876
|
const hasStorageAccess = typeof document !== "undefined" && "hasStorageAccess" in document;
|
|
1784
1877
|
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}`);
|
|
@@ -1798,17 +1891,25 @@ function DirectPayPalButtonImplementation({
|
|
|
1798
1891
|
watchdogTimers.push(setTimeout(() => snapshotContainer("15s"), 15e3));
|
|
1799
1892
|
}
|
|
1800
1893
|
appendDebug("render:start");
|
|
1894
|
+
watchdogTimers.push(setTimeout(() => {
|
|
1895
|
+
if (isInactive() || rendered) return;
|
|
1896
|
+
appendDebug("watchdog:11s retryable render timeout");
|
|
1897
|
+
handlePreRenderFailure(
|
|
1898
|
+
"PayPal Buttons render did not settle within 11000ms.",
|
|
1899
|
+
true
|
|
1900
|
+
);
|
|
1901
|
+
}, PAYPAL_RENDER_WATCHDOG_MS));
|
|
1801
1902
|
buttons.render(container).then(() => {
|
|
1802
|
-
appendDebug(`render:resolved
|
|
1903
|
+
appendDebug(`render:resolved inactive=${isInactive()}`);
|
|
1803
1904
|
stopDiagnostics();
|
|
1804
|
-
if (
|
|
1805
|
-
|
|
1806
|
-
});
|
|
1905
|
+
if (isInactive()) {
|
|
1906
|
+
closeOwnedButtons();
|
|
1807
1907
|
return;
|
|
1808
1908
|
}
|
|
1809
|
-
activeButtons = typedButtons;
|
|
1810
1909
|
rendered = true;
|
|
1811
1910
|
setReady(true);
|
|
1911
|
+
setRecovering(false);
|
|
1912
|
+
onInitializationStateChangeRef.current?.("ready");
|
|
1812
1913
|
telemetrySource.log({
|
|
1813
1914
|
name: "provider.ready",
|
|
1814
1915
|
stage: "provider_ready",
|
|
@@ -1826,24 +1927,22 @@ function DirectPayPalButtonImplementation({
|
|
|
1826
1927
|
const message = err instanceof Error ? err.message : "PayPal failed to render.";
|
|
1827
1928
|
appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
|
|
1828
1929
|
stopDiagnostics();
|
|
1829
|
-
|
|
1930
|
+
handlePreRenderFailure(message);
|
|
1830
1931
|
});
|
|
1831
1932
|
}).catch((err) => {
|
|
1832
1933
|
const message = err instanceof Error ? err.message : "PayPal SDK failed to load.";
|
|
1833
1934
|
appendDebug(`loadScript:rejected msg=${message.slice(0, 120)}`);
|
|
1834
|
-
|
|
1935
|
+
handlePreRenderFailure(message);
|
|
1835
1936
|
});
|
|
1836
1937
|
}, 0);
|
|
1837
1938
|
return () => {
|
|
1838
1939
|
cancelled = true;
|
|
1839
1940
|
clearTimeout(startTimer);
|
|
1941
|
+
if (retryTimer) clearTimeout(retryTimer);
|
|
1840
1942
|
stopDiagnostics();
|
|
1841
|
-
|
|
1842
|
-
activeButtons.close().catch(() => {
|
|
1843
|
-
});
|
|
1844
|
-
}
|
|
1943
|
+
closeOwnedButtons();
|
|
1845
1944
|
};
|
|
1846
|
-
}, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
|
|
1945
|
+
}, [allowAutomaticRetry, baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
|
|
1847
1946
|
const debugPanel = debug ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1848
1947
|
"pre",
|
|
1849
1948
|
{
|
|
@@ -1868,7 +1967,7 @@ ${debugLines.join("\n")}`
|
|
|
1868
1967
|
]
|
|
1869
1968
|
}
|
|
1870
1969
|
) : null;
|
|
1871
|
-
if (failed) {
|
|
1970
|
+
if (failed && !initializationConfigChanged) {
|
|
1872
1971
|
return debug ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: debugPanel }) : null;
|
|
1873
1972
|
}
|
|
1874
1973
|
return (
|
|
@@ -1878,38 +1977,51 @@ ${debugLines.join("\n")}`
|
|
|
1878
1977
|
// intentionally has no margin/padding so the parent owns all spacing.
|
|
1879
1978
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
|
|
1880
1979
|
debugPanel,
|
|
1881
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1981
|
+
"div",
|
|
1982
|
+
{
|
|
1983
|
+
"aria-hidden": recovering ? true : void 0,
|
|
1984
|
+
style: recovering ? {
|
|
1985
|
+
position: "relative",
|
|
1986
|
+
height: 0,
|
|
1987
|
+
opacity: 0,
|
|
1988
|
+
pointerEvents: "none"
|
|
1989
|
+
} : { position: "relative", minHeight: DEFAULT_BUTTON_HEIGHT },
|
|
1990
|
+
children: [
|
|
1991
|
+
!ready && !recovering && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1992
|
+
"div",
|
|
1993
|
+
{
|
|
1994
|
+
"data-testid": "flopay-direct-paypal-placeholder",
|
|
1995
|
+
style: {
|
|
1996
|
+
position: "absolute",
|
|
1997
|
+
inset: 0,
|
|
1998
|
+
borderRadius: 8,
|
|
1999
|
+
background: "#e5e7eb",
|
|
2000
|
+
animation: "flopay-pulse 1.5s ease-in-out infinite",
|
|
2001
|
+
pointerEvents: "none"
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
),
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2006
|
+
"div",
|
|
2007
|
+
{
|
|
2008
|
+
ref: containerRef,
|
|
2009
|
+
"data-testid": "flopay-direct-paypal-container",
|
|
2010
|
+
tabIndex: -1,
|
|
2011
|
+
"aria-label": "PayPal payment method",
|
|
2012
|
+
style: {
|
|
2013
|
+
minHeight: DEFAULT_BUTTON_HEIGHT,
|
|
2014
|
+
display: "flex",
|
|
2015
|
+
opacity: ready ? 1 : 0
|
|
2016
|
+
},
|
|
2017
|
+
"aria-busy": submitting || isProcessing
|
|
2018
|
+
},
|
|
2019
|
+
renderGeneration
|
|
2020
|
+
)
|
|
2021
|
+
]
|
|
2022
|
+
},
|
|
2023
|
+
renderGeneration
|
|
2024
|
+
),
|
|
1913
2025
|
showRetryFocusTarget && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1914
2026
|
"button",
|
|
1915
2027
|
{
|
|
@@ -1937,6 +2049,22 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
|
1937
2049
|
var STRIPE_RESUME_KEY = "flopay_stripe_resume";
|
|
1938
2050
|
var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
1939
2051
|
var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
|
|
2052
|
+
async function reportNonCardDecline({
|
|
2053
|
+
billingApiUrl,
|
|
2054
|
+
sessionId,
|
|
2055
|
+
nonce,
|
|
2056
|
+
request
|
|
2057
|
+
}) {
|
|
2058
|
+
if (!sessionId || !nonce) return;
|
|
2059
|
+
try {
|
|
2060
|
+
await new import_js2.PaymentAPI(billingApiUrl, { telemetry: false }).reportSessionIntentDecline(
|
|
2061
|
+
sessionId,
|
|
2062
|
+
nonce,
|
|
2063
|
+
request
|
|
2064
|
+
);
|
|
2065
|
+
} catch {
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1940
2068
|
function isAccountValidationError(err) {
|
|
1941
2069
|
return err instanceof import_shared6.FloPayError && typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500;
|
|
1942
2070
|
}
|
|
@@ -2226,11 +2354,9 @@ function ExpressCheckoutReadySwap({
|
|
|
2226
2354
|
function isExpressCheckoutRowVisible(state) {
|
|
2227
2355
|
return state !== "unavailable" && state !== "load_error";
|
|
2228
2356
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
}
|
|
2233
|
-
);
|
|
2357
|
+
function SplitCardForm(props) {
|
|
2358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SplitCardFormInner, { ...props });
|
|
2359
|
+
}
|
|
2234
2360
|
function PayPalButtonInner({
|
|
2235
2361
|
sessionId,
|
|
2236
2362
|
nonce,
|
|
@@ -2261,7 +2387,6 @@ function PayPalButtonInner({
|
|
|
2261
2387
|
const [showRecoveryAction, setShowRecoveryAction] = (0, import_react9.useState)(false);
|
|
2262
2388
|
const pendingProviderFocusRef = (0, import_react9.useRef)(false);
|
|
2263
2389
|
const attemptContextBySurfaceRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
|
|
2264
|
-
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2265
2390
|
const {
|
|
2266
2391
|
startAttempt,
|
|
2267
2392
|
finishAttempt,
|
|
@@ -2432,33 +2557,21 @@ function PayPalButtonInner({
|
|
|
2432
2557
|
event.paymentFailed({ reason: "fail" });
|
|
2433
2558
|
return;
|
|
2434
2559
|
}
|
|
2435
|
-
const
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
});
|
|
2449
|
-
if (!intentResponse.ok) {
|
|
2450
|
-
const intentError = await buildFloPayApiErrorFromResponse(
|
|
2451
|
-
intentResponse,
|
|
2452
|
-
"Failed to create payment intent"
|
|
2453
|
-
);
|
|
2454
|
-
onErrorChange?.(intentError.message);
|
|
2455
|
-
onDecline?.(buildDeclineEvent("paypal", intentError));
|
|
2456
|
-
event.paymentFailed({ reason: "fail", message: intentError.message });
|
|
2457
|
-
return;
|
|
2560
|
+
const intent = await new import_js2.PaymentAPI(billingApiUrl).createSessionIntent(
|
|
2561
|
+
effectiveSessionId,
|
|
2562
|
+
effectiveNonce ?? "",
|
|
2563
|
+
{
|
|
2564
|
+
provider: "stripe",
|
|
2565
|
+
paymentMethodCategory: "wallet",
|
|
2566
|
+
paymentMethodType: "paypal",
|
|
2567
|
+
paymentMethodId: paymentMethod?.id ?? null,
|
|
2568
|
+
intentKind: "payment"
|
|
2569
|
+
}
|
|
2570
|
+
);
|
|
2571
|
+
if (intent.provider !== "stripe") {
|
|
2572
|
+
throw new Error("Invalid provider returned for PayPal intent");
|
|
2458
2573
|
}
|
|
2459
|
-
const
|
|
2460
|
-
const intentClientSecret = intentJson.data?.id;
|
|
2461
|
-
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
2574
|
+
const intentClientSecret = intent.clientSecret;
|
|
2462
2575
|
const confirmParams = {
|
|
2463
2576
|
return_url: window.location.href
|
|
2464
2577
|
};
|
|
@@ -2495,6 +2608,17 @@ function PayPalButtonInner({
|
|
|
2495
2608
|
code: getProviderErrorCode(confirmError),
|
|
2496
2609
|
declineCode: getProviderDeclineCode(confirmError)
|
|
2497
2610
|
}));
|
|
2611
|
+
await reportNonCardDecline({
|
|
2612
|
+
billingApiUrl,
|
|
2613
|
+
sessionId: effectiveSessionId,
|
|
2614
|
+
nonce: effectiveNonce,
|
|
2615
|
+
request: {
|
|
2616
|
+
provider: "stripe",
|
|
2617
|
+
paymentMethodCategory: "wallet",
|
|
2618
|
+
paymentMethodType: "paypal",
|
|
2619
|
+
providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
|
|
2620
|
+
}
|
|
2621
|
+
});
|
|
2498
2622
|
} else {
|
|
2499
2623
|
recoverTechnicalFailure(confirmError, "stripe_paypal_confirm_failed", attemptContext?.generation);
|
|
2500
2624
|
}
|
|
@@ -2523,7 +2647,7 @@ function PayPalButtonInner({
|
|
|
2523
2647
|
} finally {
|
|
2524
2648
|
setSubmitting(false);
|
|
2525
2649
|
}
|
|
2526
|
-
}, [stripe, elements, sessionId, nonce, email,
|
|
2650
|
+
}, [stripe, elements, sessionId, nonce, email, billingApiUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]);
|
|
2527
2651
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
2528
2652
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2529
2653
|
ExpressCheckoutReadySwap,
|
|
@@ -2599,6 +2723,7 @@ function WalletButtonInner({
|
|
|
2599
2723
|
nonce,
|
|
2600
2724
|
email,
|
|
2601
2725
|
billingApiUrl,
|
|
2726
|
+
intentKind,
|
|
2602
2727
|
expressMethods,
|
|
2603
2728
|
onTokenizedBody,
|
|
2604
2729
|
onErrorChange,
|
|
@@ -2617,7 +2742,6 @@ function WalletButtonInner({
|
|
|
2617
2742
|
onLoadStateChange?.(loadState);
|
|
2618
2743
|
}, [loadState, onLoadStateChange]);
|
|
2619
2744
|
const [submitting, setSubmitting] = (0, import_react9.useState)(false);
|
|
2620
|
-
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2621
2745
|
const focusTargetRef = (0, import_react9.useRef)(null);
|
|
2622
2746
|
const recoveryActionRef = (0, import_react9.useRef)(null);
|
|
2623
2747
|
const [surfaceKey, setSurfaceKey] = (0, import_react9.useState)(0);
|
|
@@ -2718,31 +2842,21 @@ function WalletButtonInner({
|
|
|
2718
2842
|
if (!effectiveSessionId || !effectiveEmail) {
|
|
2719
2843
|
throw new Error("Missing sessionId or email for wallet payment");
|
|
2720
2844
|
}
|
|
2721
|
-
const
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
const intentError = await buildFloPayApiErrorFromResponse(
|
|
2735
|
-
intentResponse,
|
|
2736
|
-
"Failed to create payment intent"
|
|
2737
|
-
);
|
|
2738
|
-
onErrorChange?.(intentError.message);
|
|
2739
|
-
onDecline?.(buildDeclineEvent(method, intentError));
|
|
2740
|
-
event.paymentFailed({ reason: "fail", message: intentError.message });
|
|
2741
|
-
return;
|
|
2845
|
+
const intent = await new import_js2.PaymentAPI(billingApiUrl).createSessionIntent(
|
|
2846
|
+
effectiveSessionId,
|
|
2847
|
+
effectiveNonce ?? "",
|
|
2848
|
+
{
|
|
2849
|
+
provider: "stripe",
|
|
2850
|
+
paymentMethodCategory: "wallet",
|
|
2851
|
+
paymentMethodType: walletType ?? buttonMethod,
|
|
2852
|
+
paymentMethodId: paymentMethod.id,
|
|
2853
|
+
intentKind
|
|
2854
|
+
}
|
|
2855
|
+
);
|
|
2856
|
+
if (intent.provider !== "stripe") {
|
|
2857
|
+
throw new Error("Invalid provider returned for wallet intent");
|
|
2742
2858
|
}
|
|
2743
|
-
const
|
|
2744
|
-
const intentClientSecret = intentJson.data?.id;
|
|
2745
|
-
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
2859
|
+
const intentClientSecret = intent.clientSecret;
|
|
2746
2860
|
const { error: confirmError, intentId } = (0, import_shared6.isSetupIntentClientSecret)(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 }));
|
|
2747
2861
|
if (confirmError) {
|
|
2748
2862
|
if (isProviderDecline(confirmError)) {
|
|
@@ -2752,6 +2866,17 @@ function WalletButtonInner({
|
|
|
2752
2866
|
code: getProviderErrorCode(confirmError),
|
|
2753
2867
|
declineCode: getProviderDeclineCode(confirmError)
|
|
2754
2868
|
}));
|
|
2869
|
+
await reportNonCardDecline({
|
|
2870
|
+
billingApiUrl,
|
|
2871
|
+
sessionId: effectiveSessionId,
|
|
2872
|
+
nonce: effectiveNonce,
|
|
2873
|
+
request: {
|
|
2874
|
+
provider: "stripe",
|
|
2875
|
+
paymentMethodCategory: "wallet",
|
|
2876
|
+
paymentMethodType: walletType ?? buttonMethod,
|
|
2877
|
+
providerDeclineReason: getProviderErrorCode(confirmError) ?? "provider_declined"
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2755
2880
|
} else {
|
|
2756
2881
|
recoverTechnicalFailure(walletType, confirmError, "stripe_wallet_confirm_failed", attemptContext?.generation);
|
|
2757
2882
|
}
|
|
@@ -2772,7 +2897,7 @@ function WalletButtonInner({
|
|
|
2772
2897
|
setSubmitting(false);
|
|
2773
2898
|
}
|
|
2774
2899
|
},
|
|
2775
|
-
[stripe, elements, sessionId, nonce, email,
|
|
2900
|
+
[stripe, elements, sessionId, nonce, email, billingApiUrl, intentKind, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick, recoverTechnicalFailure, finishAttempt, isAttemptCurrent, isAttemptInvalidated, surfaceKey]
|
|
2776
2901
|
);
|
|
2777
2902
|
const expressMethodMap = (0, import_react9.useMemo)(() => {
|
|
2778
2903
|
const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
|
|
@@ -2905,6 +3030,7 @@ function StripeMethodButton({
|
|
|
2905
3030
|
fontFamily
|
|
2906
3031
|
}) {
|
|
2907
3032
|
const brand = (0, import_shared5.resolveStripeMethodBrandVariant)(method, themeId);
|
|
3033
|
+
const vendoredLogoUrl = PAYMENT_METHOD_LOGO_URLS[method];
|
|
2908
3034
|
const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
|
|
2909
3035
|
const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
|
|
2910
3036
|
const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
|
|
@@ -2970,15 +3096,36 @@ function StripeMethodButton({
|
|
|
2970
3096
|
gap: (0, import_shared5.hasVendoredStripeMethodLogo)(method) ? 8 : 0
|
|
2971
3097
|
},
|
|
2972
3098
|
children: [
|
|
2973
|
-
|
|
3099
|
+
vendoredLogoUrl ? (
|
|
3100
|
+
// Vendored brand logo — an <img> asset (not inline SVG) so its
|
|
3101
|
+
// bytes ship as a sibling file, out of the JS bundle. 3:2 box
|
|
3102
|
+
// matching the datatrans logos' 120×80 viewBox; each ships its own
|
|
3103
|
+
// white rounded-rect background so it stays legible on any tile.
|
|
3104
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3105
|
+
"img",
|
|
3106
|
+
{
|
|
3107
|
+
src: vendoredLogoUrl,
|
|
3108
|
+
alt: "",
|
|
3109
|
+
"aria-hidden": "true",
|
|
3110
|
+
loading: "lazy",
|
|
3111
|
+
decoding: "async",
|
|
3112
|
+
width: 30,
|
|
3113
|
+
height: 20,
|
|
3114
|
+
style: {
|
|
3115
|
+
width: 30,
|
|
3116
|
+
height: 20,
|
|
3117
|
+
flexShrink: 0,
|
|
3118
|
+
borderRadius: 3,
|
|
3119
|
+
objectFit: "contain",
|
|
3120
|
+
display: "inline-flex"
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
)
|
|
3124
|
+
) : brand?.logoSvg ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2974
3125
|
"span",
|
|
2975
3126
|
{
|
|
2976
3127
|
"aria-hidden": "true",
|
|
2977
3128
|
style: {
|
|
2978
|
-
// 3:2 box matching the vendored datatrans logos' 120×80
|
|
2979
|
-
// viewBox. Each logo ships with its own white rounded-rect
|
|
2980
|
-
// background baked in, so the mark stays legible on any
|
|
2981
|
-
// brand-coloured tile without an extra chip wrapper here.
|
|
2982
3129
|
width: 30,
|
|
2983
3130
|
height: 20,
|
|
2984
3131
|
flexShrink: 0,
|
|
@@ -2988,7 +3135,7 @@ function StripeMethodButton({
|
|
|
2988
3135
|
},
|
|
2989
3136
|
dangerouslySetInnerHTML: { __html: brand.logoSvg }
|
|
2990
3137
|
}
|
|
2991
|
-
),
|
|
3138
|
+
) : null,
|
|
2992
3139
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: (0, import_shared5.getStripeMethodDisplayName)(method) })
|
|
2993
3140
|
]
|
|
2994
3141
|
}
|
|
@@ -3046,7 +3193,6 @@ function StripeMethodInlineForm({
|
|
|
3046
3193
|
const submittingRef = (0, import_react9.useRef)(false);
|
|
3047
3194
|
const [isMethodComplete, setIsMethodComplete] = (0, import_react9.useState)(false);
|
|
3048
3195
|
const [loadState, setLoadState] = (0, import_react9.useState)("loading");
|
|
3049
|
-
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
3050
3196
|
const handlePay = (0, import_react9.useCallback)(async () => {
|
|
3051
3197
|
if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
|
|
3052
3198
|
submittingRef.current = true;
|
|
@@ -3077,27 +3223,22 @@ function StripeMethodInlineForm({
|
|
|
3077
3223
|
if (!effectiveSessionId || !effectiveEmail) {
|
|
3078
3224
|
throw new Error("Missing sessionId or email for payment.");
|
|
3079
3225
|
}
|
|
3080
|
-
const
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
|
-
if (
|
|
3093
|
-
|
|
3094
|
-
onErrorChange?.(intentError.message);
|
|
3095
|
-
onDecline?.(buildDeclineEvent("card", intentError));
|
|
3096
|
-
return;
|
|
3226
|
+
const paymentMethodType = paymentMethod.type || method;
|
|
3227
|
+
const intent = await new import_js2.PaymentAPI(billingApiUrl).createSessionIntent(
|
|
3228
|
+
effectiveSessionId,
|
|
3229
|
+
effectiveNonce ?? "",
|
|
3230
|
+
{
|
|
3231
|
+
provider: "stripe",
|
|
3232
|
+
paymentMethodCategory: "apm",
|
|
3233
|
+
paymentMethodType,
|
|
3234
|
+
paymentMethodId: null,
|
|
3235
|
+
intentKind: "payment"
|
|
3236
|
+
}
|
|
3237
|
+
);
|
|
3238
|
+
if (intent.provider !== "stripe") {
|
|
3239
|
+
throw new Error("Invalid provider returned for APM intent");
|
|
3097
3240
|
}
|
|
3098
|
-
const
|
|
3099
|
-
const intentClientSecret = intentJson.data?.id;
|
|
3100
|
-
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
3241
|
+
const intentClientSecret = intent.clientSecret;
|
|
3101
3242
|
if (typeof window !== "undefined") {
|
|
3102
3243
|
try {
|
|
3103
3244
|
localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
|
|
@@ -3130,11 +3271,21 @@ function StripeMethodInlineForm({
|
|
|
3130
3271
|
const message = confirmError.message ?? "Payment failed.";
|
|
3131
3272
|
onErrorChange?.(message);
|
|
3132
3273
|
onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
|
|
3274
|
+
await reportNonCardDecline({
|
|
3275
|
+
billingApiUrl,
|
|
3276
|
+
sessionId: effectiveSessionId,
|
|
3277
|
+
nonce: effectiveNonce,
|
|
3278
|
+
request: {
|
|
3279
|
+
provider: "stripe",
|
|
3280
|
+
paymentMethodCategory: "apm",
|
|
3281
|
+
paymentMethodType: method,
|
|
3282
|
+
providerDeclineReason: confirmError.code ?? "provider_declined"
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3133
3285
|
return;
|
|
3134
3286
|
}
|
|
3135
|
-
const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
|
|
3136
3287
|
const piStatus = paymentIntent?.status;
|
|
3137
|
-
if (!
|
|
3288
|
+
if (!isSuccessfulPaymentIntentStatus(piStatus)) {
|
|
3138
3289
|
if (typeof window !== "undefined") {
|
|
3139
3290
|
try {
|
|
3140
3291
|
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
@@ -3180,7 +3331,7 @@ function StripeMethodInlineForm({
|
|
|
3180
3331
|
sessionId,
|
|
3181
3332
|
nonce,
|
|
3182
3333
|
email,
|
|
3183
|
-
|
|
3334
|
+
billingApiUrl,
|
|
3184
3335
|
method,
|
|
3185
3336
|
onTokenizedBody,
|
|
3186
3337
|
onErrorChange,
|
|
@@ -3244,22 +3395,7 @@ function StripeMethodInlineForm({
|
|
|
3244
3395
|
children: submitting ? "Processing\u2026" : `Pay with ${(0, import_shared5.getStripeMethodDisplayName)(method)}`
|
|
3245
3396
|
}
|
|
3246
3397
|
),
|
|
3247
|
-
errorText && /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
3248
|
-
margin: "0.75rem 0 0",
|
|
3249
|
-
padding: "0.625rem 0.875rem",
|
|
3250
|
-
background: "#FEF2F2",
|
|
3251
|
-
border: "1px solid #FECACA",
|
|
3252
|
-
borderRadius: "8px",
|
|
3253
|
-
color: "#991B1B",
|
|
3254
|
-
fontSize: "0.85rem",
|
|
3255
|
-
fontWeight: 600,
|
|
3256
|
-
display: "flex",
|
|
3257
|
-
alignItems: "center",
|
|
3258
|
-
gap: "0.5rem"
|
|
3259
|
-
}, children: [
|
|
3260
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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" }) }),
|
|
3261
|
-
errorText
|
|
3262
|
-
] })
|
|
3398
|
+
errorText && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBanner, { margin: "0.75rem 0 0", children: errorText })
|
|
3263
3399
|
] });
|
|
3264
3400
|
}
|
|
3265
3401
|
function StripePaymentElementInner({
|
|
@@ -3286,7 +3422,6 @@ function StripePaymentElementInner({
|
|
|
3286
3422
|
onExpandApm,
|
|
3287
3423
|
expandedApmMethod
|
|
3288
3424
|
}) {
|
|
3289
|
-
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
3290
3425
|
const [expandedMethod, setExpandedMethod] = (0, import_react9.useState)(null);
|
|
3291
3426
|
const [submittingMethod, setSubmittingMethod] = (0, import_react9.useState)(null);
|
|
3292
3427
|
const submittingRef = (0, import_react9.useRef)(false);
|
|
@@ -3317,31 +3452,21 @@ function StripePaymentElementInner({
|
|
|
3317
3452
|
if (!effectiveSessionId || !effectiveEmail) {
|
|
3318
3453
|
throw new Error("Missing sessionId or email for payment.");
|
|
3319
3454
|
}
|
|
3320
|
-
const
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
sessionId: effectiveSessionId,
|
|
3327
|
-
email: effectiveEmail,
|
|
3328
|
-
// Wire type, not pm_xxx — backend scopes `payment_method_types`
|
|
3329
|
-
// to this single method so unrelated account-wide methods
|
|
3330
|
-
// (apple_pay without domain verification, …) don't poison PI
|
|
3331
|
-
// creation.
|
|
3455
|
+
const intent = await new import_js2.PaymentAPI(billingApiUrl).createSessionIntent(
|
|
3456
|
+
effectiveSessionId,
|
|
3457
|
+
effectiveNonce ?? "",
|
|
3458
|
+
{
|
|
3459
|
+
provider: "stripe",
|
|
3460
|
+
paymentMethodCategory: "apm",
|
|
3332
3461
|
paymentMethodType: method,
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
onDecline?.(buildDeclineEvent("card", intentError));
|
|
3340
|
-
return;
|
|
3462
|
+
paymentMethodId: null,
|
|
3463
|
+
intentKind: "payment"
|
|
3464
|
+
}
|
|
3465
|
+
);
|
|
3466
|
+
if (intent.provider !== "stripe") {
|
|
3467
|
+
throw new Error("Invalid provider returned for APM intent");
|
|
3341
3468
|
}
|
|
3342
|
-
const
|
|
3343
|
-
const intentClientSecret = intentJson.data?.id;
|
|
3344
|
-
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
3469
|
+
const intentClientSecret = intent.clientSecret;
|
|
3345
3470
|
if (typeof window !== "undefined") {
|
|
3346
3471
|
try {
|
|
3347
3472
|
localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
|
|
@@ -3382,11 +3507,21 @@ function StripePaymentElementInner({
|
|
|
3382
3507
|
const message = confirmError.message ?? "Payment failed.";
|
|
3383
3508
|
onErrorChange?.(message);
|
|
3384
3509
|
onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
|
|
3510
|
+
await reportNonCardDecline({
|
|
3511
|
+
billingApiUrl,
|
|
3512
|
+
sessionId: effectiveSessionId,
|
|
3513
|
+
nonce: effectiveNonce,
|
|
3514
|
+
request: {
|
|
3515
|
+
provider: "stripe",
|
|
3516
|
+
paymentMethodCategory: "apm",
|
|
3517
|
+
paymentMethodType: method,
|
|
3518
|
+
providerDeclineReason: confirmError.code ?? "provider_declined"
|
|
3519
|
+
}
|
|
3520
|
+
});
|
|
3385
3521
|
return;
|
|
3386
3522
|
}
|
|
3387
|
-
const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
|
|
3388
3523
|
const piStatus = paymentIntent?.status;
|
|
3389
|
-
if (!
|
|
3524
|
+
if (!isSuccessfulPaymentIntentStatus(piStatus)) {
|
|
3390
3525
|
if (typeof window !== "undefined") {
|
|
3391
3526
|
try {
|
|
3392
3527
|
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
@@ -3430,7 +3565,7 @@ function StripePaymentElementInner({
|
|
|
3430
3565
|
sessionId,
|
|
3431
3566
|
nonce,
|
|
3432
3567
|
email,
|
|
3433
|
-
|
|
3568
|
+
billingApiUrl,
|
|
3434
3569
|
billingName,
|
|
3435
3570
|
onTokenizedBody,
|
|
3436
3571
|
onErrorChange,
|
|
@@ -3531,15 +3666,10 @@ function SplitCardFormInner({
|
|
|
3531
3666
|
firstName,
|
|
3532
3667
|
lastName,
|
|
3533
3668
|
chv,
|
|
3534
|
-
submitLabel = "CONFIRM PAYMENT",
|
|
3535
3669
|
className,
|
|
3536
|
-
children,
|
|
3537
3670
|
isProcessing: externalProcessing,
|
|
3538
3671
|
error: externalError,
|
|
3539
3672
|
onErrorChange,
|
|
3540
|
-
onFullNameChange,
|
|
3541
|
-
onFirstNameChange,
|
|
3542
|
-
onLastNameChange,
|
|
3543
3673
|
showPayPal = true,
|
|
3544
3674
|
showStripe = true,
|
|
3545
3675
|
enabledPaymentMethods,
|
|
@@ -3578,17 +3708,14 @@ function SplitCardFormInner({
|
|
|
3578
3708
|
directPaypal,
|
|
3579
3709
|
isSubscription = false,
|
|
3580
3710
|
session,
|
|
3581
|
-
debug = false
|
|
3582
|
-
innerRef
|
|
3711
|
+
debug = false
|
|
3583
3712
|
}) {
|
|
3584
3713
|
const flopay = useFloPay();
|
|
3585
3714
|
const paypalFlopay = usePayPalFloPay();
|
|
3586
|
-
const elements = useElements();
|
|
3587
3715
|
const checkout = (0, import_react9.useContext)(CheckoutContext);
|
|
3588
3716
|
const contextBillingUrl = useBillingApiUrl();
|
|
3589
3717
|
const [processing, setProcessing] = (0, import_react9.useState)(false);
|
|
3590
3718
|
const [error, setError] = (0, import_react9.useState)(null);
|
|
3591
|
-
const [is3DSActive, setIs3DSActive] = (0, import_react9.useState)(false);
|
|
3592
3719
|
const [selectedCountry, setSelectedCountry] = (0, import_react9.useState)(countryProp ?? "US");
|
|
3593
3720
|
const [zipCode, setZipCode] = (0, import_react9.useState)(zipProp ?? "");
|
|
3594
3721
|
const [addressLine1, setAddressLine1] = (0, import_react9.useState)(addressLine1Prop ?? "");
|
|
@@ -3606,8 +3733,9 @@ function SplitCardFormInner({
|
|
|
3606
3733
|
const enableAVS = avsConfig !== null;
|
|
3607
3734
|
const vaultBlockReady = Boolean(session?.vault?.html);
|
|
3608
3735
|
const vaultGatewayAdvertised = Boolean(session?.gateways?.pcivault);
|
|
3736
|
+
const vaultCapabilityAvailable = vaultBlockReady || vaultGatewayAdvertised;
|
|
3609
3737
|
const vaultActive = Boolean(
|
|
3610
|
-
showStripe &&
|
|
3738
|
+
showStripe && vaultCapabilityAvailable && flopay && sessionId
|
|
3611
3739
|
);
|
|
3612
3740
|
const cardCapture = (0, import_react9.useMemo)(() => {
|
|
3613
3741
|
if (!flopay || !vaultActive || !sessionId) return null;
|
|
@@ -3639,7 +3767,9 @@ function SplitCardFormInner({
|
|
|
3639
3767
|
const [vaultMount, setVaultMount] = (0, import_react9.useState)(
|
|
3640
3768
|
() => toVaultMount(session?.vault)
|
|
3641
3769
|
);
|
|
3642
|
-
const [viewState, setViewState] = (0, import_react9.useState)(
|
|
3770
|
+
const [viewState, setViewState] = (0, import_react9.useState)(
|
|
3771
|
+
initialCardOpen && vaultCapabilityAvailable ? "card" : "buttons"
|
|
3772
|
+
);
|
|
3643
3773
|
const [expandedApmMethod, setExpandedApmMethod] = (0, import_react9.useState)(null);
|
|
3644
3774
|
const showCardForm = viewState === "expanding" || viewState === "card";
|
|
3645
3775
|
const TRANSITION_MS = 280;
|
|
@@ -3664,12 +3794,10 @@ function SplitCardFormInner({
|
|
|
3664
3794
|
}, TRANSITION_MS);
|
|
3665
3795
|
}, []);
|
|
3666
3796
|
(0, import_react9.useEffect)(() => {
|
|
3667
|
-
if (layout === "buttons" && initialCardOpen) {
|
|
3797
|
+
if (layout === "buttons" && initialCardOpen && vaultCapabilityAvailable) {
|
|
3668
3798
|
setViewState("card");
|
|
3669
3799
|
}
|
|
3670
|
-
}, [layout, initialCardOpen]);
|
|
3671
|
-
const [fullName, setFullName] = (0, import_react9.useState)("");
|
|
3672
|
-
const [formReady, setFormReady] = (0, import_react9.useState)(false);
|
|
3800
|
+
}, [layout, initialCardOpen, vaultCapabilityAvailable]);
|
|
3673
3801
|
const [overlayStatus, setOverlayStatus] = (0, import_react9.useState)(null);
|
|
3674
3802
|
const processingRef = (0, import_react9.useRef)(false);
|
|
3675
3803
|
const [paypalDirectRetry, setPaypalDirectRetry] = (0, import_react9.useState)(null);
|
|
@@ -3722,7 +3850,6 @@ function SplitCardFormInner({
|
|
|
3722
3850
|
}, [appearance, bStyles, layout]);
|
|
3723
3851
|
const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
|
|
3724
3852
|
const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
|
|
3725
|
-
const isSelfContained = !onTokenizedBody;
|
|
3726
3853
|
const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
|
|
3727
3854
|
const resolvedAccount = (0, import_react9.useMemo)(() => mergeAccountPatch({
|
|
3728
3855
|
userId,
|
|
@@ -3732,6 +3859,7 @@ function SplitCardFormInner({
|
|
|
3732
3859
|
country: countryProp,
|
|
3733
3860
|
zip: zipProp
|
|
3734
3861
|
}, accountPatch), [userId, email, firstName, lastName, countryProp, zipProp, accountPatch]);
|
|
3862
|
+
const resolvedBillingName = [resolvedAccount.firstName, resolvedAccount.lastName].filter((part) => Boolean(part?.trim())).join(" ");
|
|
3735
3863
|
const stripeInstance = (0, import_react9.useMemo)(() => {
|
|
3736
3864
|
if (!flopay) return null;
|
|
3737
3865
|
return flopay.getRawProvider();
|
|
@@ -3871,7 +3999,6 @@ function SplitCardFormInner({
|
|
|
3871
3999
|
updateError,
|
|
3872
4000
|
emitDecline,
|
|
3873
4001
|
resolvedAccount,
|
|
3874
|
-
fullName,
|
|
3875
4002
|
avsConfig,
|
|
3876
4003
|
avsCheckProp,
|
|
3877
4004
|
sessionId,
|
|
@@ -3884,7 +4011,6 @@ function SplitCardFormInner({
|
|
|
3884
4011
|
updateError,
|
|
3885
4012
|
emitDecline,
|
|
3886
4013
|
resolvedAccount,
|
|
3887
|
-
fullName,
|
|
3888
4014
|
avsConfig,
|
|
3889
4015
|
avsCheckProp,
|
|
3890
4016
|
sessionId,
|
|
@@ -3893,7 +4019,7 @@ function SplitCardFormInner({
|
|
|
3893
4019
|
};
|
|
3894
4020
|
const vaultCompletedRef = (0, import_react9.useRef)(false);
|
|
3895
4021
|
const buildVaultAccountSnapshot = (0, import_react9.useCallback)(() => {
|
|
3896
|
-
const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2,
|
|
4022
|
+
const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
|
|
3897
4023
|
const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
|
|
3898
4024
|
const stateVisible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.state, cc) : false;
|
|
3899
4025
|
const line1Visible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.address_line_1, cc) : false;
|
|
@@ -3906,8 +4032,8 @@ function SplitCardFormInner({
|
|
|
3906
4032
|
accountData: {
|
|
3907
4033
|
userId: resolvedAccount2.userId ?? "",
|
|
3908
4034
|
email: resolvedAccount2.email ?? "",
|
|
3909
|
-
firstName: resolvedAccount2.firstName ??
|
|
3910
|
-
lastName: resolvedAccount2.lastName ??
|
|
4035
|
+
firstName: resolvedAccount2.firstName ?? "",
|
|
4036
|
+
lastName: resolvedAccount2.lastName ?? "",
|
|
3911
4037
|
...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
|
|
3912
4038
|
...cityVisible && cityRef.current ? { city: cityRef.current } : {},
|
|
3913
4039
|
...stateValue2 ? { state: stateValue2 } : {},
|
|
@@ -4035,6 +4161,7 @@ function SplitCardFormInner({
|
|
|
4035
4161
|
const [walletLoadState, setWalletLoadState] = (0, import_react9.useState)("loading");
|
|
4036
4162
|
const [paymentElementLoadState, setPaymentElementLoadState] = (0, import_react9.useState)("loading");
|
|
4037
4163
|
const [directPaypalReady, setDirectPaypalReady] = (0, import_react9.useState)(false);
|
|
4164
|
+
const [directPaypalInitializationState, setDirectPaypalInitializationState] = (0, import_react9.useState)("loading");
|
|
4038
4165
|
const [inAppBrowserDetected, setInAppBrowserDetected] = (0, import_react9.useState)();
|
|
4039
4166
|
(0, import_react9.useEffect)(() => {
|
|
4040
4167
|
setInAppBrowserDetected(isInAppBrowser());
|
|
@@ -4042,12 +4169,15 @@ function SplitCardFormInner({
|
|
|
4042
4169
|
const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
|
|
4043
4170
|
const shouldShowWallets = showStripe && showWallets;
|
|
4044
4171
|
const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
|
|
4045
|
-
const
|
|
4172
|
+
const directPaypalUnavailable = directPaypalInitializationState === "retrying" || directPaypalInitializationState === "exhausted";
|
|
4173
|
+
const shouldRenderStripePayPal = shouldShowPayPal && !!paypalStripeInstance && (!directPaypalConfigured || directPaypalUnavailable);
|
|
4046
4174
|
const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
|
|
4047
4175
|
const shouldRenderPaymentElement = showStripe && hasEnabledMethods && paymentElementMethodsForCurrency.length > 0 && !!stripeInstance;
|
|
4048
|
-
const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
|
|
4176
|
+
const shouldDisplayPayPalRow = shouldRenderDirectPayPal && !directPaypalUnavailable ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
|
|
4049
4177
|
const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
|
|
4050
4178
|
const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
|
|
4179
|
+
const expressSurfaceUsable = (state) => state === "loading" || state === "ready";
|
|
4180
|
+
const hasUsableNonCardCapability = shouldRenderDirectPayPal || shouldRenderStripePayPal && expressSurfaceUsable(paypalLoadState) || shouldRenderWallets && expressSurfaceUsable(walletLoadState) || shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
|
|
4051
4181
|
const validationFiredRef = (0, import_react9.useRef)(false);
|
|
4052
4182
|
(0, import_react9.useEffect)(() => {
|
|
4053
4183
|
if (validationFiredRef.current) return;
|
|
@@ -4067,8 +4197,27 @@ function SplitCardFormInner({
|
|
|
4067
4197
|
);
|
|
4068
4198
|
onError?.(err);
|
|
4069
4199
|
updateError(err.message);
|
|
4200
|
+
} else if (inAppBrowserDetected !== void 0 && !vaultCapabilityAvailable && !hasUsableNonCardCapability) {
|
|
4201
|
+
validationFiredRef.current = true;
|
|
4202
|
+
const err = new import_shared6.FloPayError(
|
|
4203
|
+
"FloPay: this checkout session has no vault card capability and no usable supported non-card payment methods. Upgrade the billing backend integration.",
|
|
4204
|
+
"api_error",
|
|
4205
|
+
{ code: "UnsupportedBackendVaultCapability" }
|
|
4206
|
+
);
|
|
4207
|
+
onError?.(err);
|
|
4208
|
+
updateError(err.message);
|
|
4070
4209
|
}
|
|
4071
|
-
}, [
|
|
4210
|
+
}, [
|
|
4211
|
+
showStripe,
|
|
4212
|
+
showPayPal,
|
|
4213
|
+
directPaypalConfigured,
|
|
4214
|
+
paypalStripeInstance,
|
|
4215
|
+
inAppBrowserDetected,
|
|
4216
|
+
vaultCapabilityAvailable,
|
|
4217
|
+
hasUsableNonCardCapability,
|
|
4218
|
+
onError,
|
|
4219
|
+
updateError
|
|
4220
|
+
]);
|
|
4072
4221
|
const deprecationLoggedRef = (0, import_react9.useRef)(false);
|
|
4073
4222
|
(0, import_react9.useEffect)(() => {
|
|
4074
4223
|
if (deprecationLoggedRef.current) return;
|
|
@@ -4082,13 +4231,6 @@ function SplitCardFormInner({
|
|
|
4082
4231
|
`[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.`
|
|
4083
4232
|
);
|
|
4084
4233
|
}, [hasEnabledMethods, showApplePay, showGooglePay]);
|
|
4085
|
-
const handleNameChange = (0, import_react9.useCallback)((value) => {
|
|
4086
|
-
setFullName(value);
|
|
4087
|
-
onFullNameChange?.(value);
|
|
4088
|
-
const parts = value.trim().split(/\s+/);
|
|
4089
|
-
onFirstNameChange?.(parts[0] ?? "");
|
|
4090
|
-
onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
|
|
4091
|
-
}, [onFullNameChange, onFirstNameChange, onLastNameChange]);
|
|
4092
4234
|
const applyInlineSessionPatch = (0, import_react9.useCallback)(
|
|
4093
4235
|
(patch, method) => {
|
|
4094
4236
|
if (!checkout.applyInlineSessionPatch) {
|
|
@@ -4169,8 +4311,8 @@ function SplitCardFormInner({
|
|
|
4169
4311
|
accountData: {
|
|
4170
4312
|
userId: effectiveAccount.userId ?? "",
|
|
4171
4313
|
email: effectiveAccount.email ?? "",
|
|
4172
|
-
firstName: effectiveAccount.firstName ??
|
|
4173
|
-
lastName: effectiveAccount.lastName ??
|
|
4314
|
+
firstName: effectiveAccount.firstName ?? "",
|
|
4315
|
+
lastName: effectiveAccount.lastName ?? "",
|
|
4174
4316
|
...avsConfig ? (() => {
|
|
4175
4317
|
const c = selectedCountryRef.current;
|
|
4176
4318
|
const stateVisible = (0, import_shared5.isAVSFieldVisible)(avsConfig.state, c);
|
|
@@ -4218,70 +4360,6 @@ function SplitCardFormInner({
|
|
|
4218
4360
|
return;
|
|
4219
4361
|
}
|
|
4220
4362
|
const json = await response.json().catch(() => null);
|
|
4221
|
-
if (json?.type === "3ds_required") {
|
|
4222
|
-
const secret = json["threeDSecureToken"];
|
|
4223
|
-
if (!flopay || !secret) {
|
|
4224
|
-
setOverlayStatus("error");
|
|
4225
|
-
updateError("3DS authentication required but no token provided.");
|
|
4226
|
-
return;
|
|
4227
|
-
}
|
|
4228
|
-
setIs3DSActive(true);
|
|
4229
|
-
try {
|
|
4230
|
-
const retryBillingAddress = {};
|
|
4231
|
-
const retryCC = selectedCountryRef.current;
|
|
4232
|
-
const retryCountry = enableAVS ? retryCC : effectiveAccount.country;
|
|
4233
|
-
if (retryCountry) retryBillingAddress["country"] = retryCountry;
|
|
4234
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, retryCC) && zipCodeRef.current.trim()) {
|
|
4235
|
-
retryBillingAddress["postal_code"] = zipCodeRef.current.trim();
|
|
4236
|
-
} else if (!avsConfig && effectiveAccount.zip) {
|
|
4237
|
-
retryBillingAddress["postal_code"] = effectiveAccount.zip;
|
|
4238
|
-
}
|
|
4239
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, retryCC) && addressLine1Ref.current.trim()) retryBillingAddress["line1"] = addressLine1Ref.current.trim();
|
|
4240
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_2, retryCC) && addressLine2Ref.current.trim()) retryBillingAddress["line2"] = addressLine2Ref.current.trim();
|
|
4241
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.city, retryCC) && cityRef.current.trim()) retryBillingAddress["city"] = cityRef.current.trim();
|
|
4242
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.state, retryCC) && stateRef.current.trim()) {
|
|
4243
|
-
retryBillingAddress["state"] = stateRef.current.trim();
|
|
4244
|
-
} else if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, retryCC) && !(0, import_shared5.isAVSFieldVisible)(avsConfig.state, retryCC) && (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, retryCC)) {
|
|
4245
|
-
const derivedRetryState = (0, import_shared5.getStateFromPostalCode)(retryCC, zipCodeRef.current.trim());
|
|
4246
|
-
if (derivedRetryState) retryBillingAddress["state"] = derivedRetryState;
|
|
4247
|
-
}
|
|
4248
|
-
const result = await flopay.confirmPayment({
|
|
4249
|
-
clientSecret: secret,
|
|
4250
|
-
returnUrl: window.location.href,
|
|
4251
|
-
billingDetails: {
|
|
4252
|
-
...effectiveAccount.email ? { email: effectiveAccount.email } : {},
|
|
4253
|
-
...fullName.trim() ? { name: fullName.trim() } : {},
|
|
4254
|
-
...Object.keys(retryBillingAddress).length > 0 ? { address: retryBillingAddress } : {}
|
|
4255
|
-
}
|
|
4256
|
-
});
|
|
4257
|
-
if (result.error) {
|
|
4258
|
-
setOverlayStatus("error");
|
|
4259
|
-
updateError(result.error.message);
|
|
4260
|
-
onError?.(result.error);
|
|
4261
|
-
emitDecline("card", result.error);
|
|
4262
|
-
return;
|
|
4263
|
-
}
|
|
4264
|
-
if (result.status === "succeeded" || result.status === "processing") {
|
|
4265
|
-
processingRef.current = false;
|
|
4266
|
-
await processPaymentInternal({
|
|
4267
|
-
id: result.paymentIntentId,
|
|
4268
|
-
type: "card",
|
|
4269
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
4270
|
-
}, {
|
|
4271
|
-
// Preserve the session/nonce/account overrides resolved
|
|
4272
|
-
// above so the retry talks to the same (possibly
|
|
4273
|
-
// freshly bootstrapped) session as the original attempt.
|
|
4274
|
-
accountPatch: overrides?.accountPatch,
|
|
4275
|
-
sessionId: effectiveSessionId,
|
|
4276
|
-
nonce: effectiveNonce,
|
|
4277
|
-
completionPaymentMethodId: resolvedCompletionPaymentMethodId
|
|
4278
|
-
});
|
|
4279
|
-
}
|
|
4280
|
-
} finally {
|
|
4281
|
-
setIs3DSActive(false);
|
|
4282
|
-
}
|
|
4283
|
-
return;
|
|
4284
|
-
}
|
|
4285
4363
|
if (json?.type === "paypal_redirect_required") {
|
|
4286
4364
|
const secret = json["threeDSecureToken"];
|
|
4287
4365
|
const savedPmId = json["paymentMethodId"] || tokenizedBody.id || "";
|
|
@@ -4335,6 +4413,8 @@ function SplitCardFormInner({
|
|
|
4335
4413
|
emitDecline("paypal", "paypal_direct_required retry limit exceeded");
|
|
4336
4414
|
return;
|
|
4337
4415
|
}
|
|
4416
|
+
setDirectPaypalReady(false);
|
|
4417
|
+
setDirectPaypalInitializationState("loading");
|
|
4338
4418
|
setPaypalDirectRetry({ orderId, attempts: prevAttempts + 1 });
|
|
4339
4419
|
setOverlayStatus(null);
|
|
4340
4420
|
return;
|
|
@@ -4357,7 +4437,7 @@ function SplitCardFormInner({
|
|
|
4357
4437
|
processingRef.current = false;
|
|
4358
4438
|
}
|
|
4359
4439
|
},
|
|
4360
|
-
[baseUrl, sessionId, nonce, resolvedAccount,
|
|
4440
|
+
[baseUrl, sessionId, nonce, resolvedAccount, resolvedBillingName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
|
|
4361
4441
|
);
|
|
4362
4442
|
const dispatchTokenizedBody = (0, import_react9.useCallback)(
|
|
4363
4443
|
(tokenizedBody, overrides) => {
|
|
@@ -4369,33 +4449,6 @@ function SplitCardFormInner({
|
|
|
4369
4449
|
},
|
|
4370
4450
|
[onTokenizedBody, processPaymentInternal]
|
|
4371
4451
|
);
|
|
4372
|
-
(0, import_react9.useImperativeHandle)(innerRef, () => ({
|
|
4373
|
-
async handleNextAction(secret) {
|
|
4374
|
-
if (!flopay) return;
|
|
4375
|
-
setIs3DSActive(true);
|
|
4376
|
-
try {
|
|
4377
|
-
const result = await flopay.confirmPayment({
|
|
4378
|
-
clientSecret: secret,
|
|
4379
|
-
returnUrl: window.location.href
|
|
4380
|
-
});
|
|
4381
|
-
if (result.error) {
|
|
4382
|
-
updateError(result.error.message);
|
|
4383
|
-
onError?.(result.error);
|
|
4384
|
-
emitDecline("card", result.error);
|
|
4385
|
-
} else if (result.status === "succeeded" || result.status === "processing") {
|
|
4386
|
-
dispatchTokenizedBody({
|
|
4387
|
-
id: result.paymentIntentId,
|
|
4388
|
-
type: "card",
|
|
4389
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
4390
|
-
});
|
|
4391
|
-
}
|
|
4392
|
-
} catch (err) {
|
|
4393
|
-
updateError(err instanceof Error ? err.message : "Payment authentication failed.");
|
|
4394
|
-
} finally {
|
|
4395
|
-
setIs3DSActive(false);
|
|
4396
|
-
}
|
|
4397
|
-
}
|
|
4398
|
-
}), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
|
|
4399
4452
|
const stripeResumeAttemptedRef = (0, import_react9.useRef)(false);
|
|
4400
4453
|
(0, import_react9.useEffect)(() => {
|
|
4401
4454
|
if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
|
|
@@ -4458,7 +4511,7 @@ function SplitCardFormInner({
|
|
|
4458
4511
|
return;
|
|
4459
4512
|
}
|
|
4460
4513
|
const piStatus = paymentIntent?.status;
|
|
4461
|
-
const successful = piStatus
|
|
4514
|
+
const successful = isSuccessfulPaymentIntentStatus(piStatus);
|
|
4462
4515
|
if (!paymentIntent || !successful) {
|
|
4463
4516
|
const message = piStatus === "canceled" ? "Payment was canceled." : "Payment was not completed. Please try again.";
|
|
4464
4517
|
updateError(message);
|
|
@@ -4475,165 +4528,10 @@ function SplitCardFormInner({
|
|
|
4475
4528
|
}, persistedOverrides);
|
|
4476
4529
|
})();
|
|
4477
4530
|
}, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
|
|
4478
|
-
const handleSubmit = (0, import_react9.useCallback)(
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
if (vaultActive) return;
|
|
4483
|
-
if (layout !== "buttons") {
|
|
4484
|
-
onButtonClick?.("card");
|
|
4485
|
-
}
|
|
4486
|
-
setProcessing(true);
|
|
4487
|
-
setOverlayStatus("processing");
|
|
4488
|
-
updateError(null);
|
|
4489
|
-
let handedOff = false;
|
|
4490
|
-
try {
|
|
4491
|
-
if (avsConfig) {
|
|
4492
|
-
const country = selectedCountryRef.current;
|
|
4493
|
-
const postal = computePostalCodeState(
|
|
4494
|
-
country,
|
|
4495
|
-
zipCodeRef.current,
|
|
4496
|
-
(0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, country)
|
|
4497
|
-
);
|
|
4498
|
-
if (postal.empty) {
|
|
4499
|
-
setZipTouched(true);
|
|
4500
|
-
updateError((0, import_shared5.getPostalCodeLabel)(country) + " is required");
|
|
4501
|
-
return;
|
|
4502
|
-
}
|
|
4503
|
-
if (postal.malformed) {
|
|
4504
|
-
setZipTouched(true);
|
|
4505
|
-
updateError(malformedPostcodeMessage(country));
|
|
4506
|
-
return;
|
|
4507
|
-
}
|
|
4508
|
-
if ((0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
|
|
4509
|
-
updateError("Street address is required");
|
|
4510
|
-
return;
|
|
4511
|
-
}
|
|
4512
|
-
if ((0, import_shared5.isAVSFieldVisible)(avsConfig.city, country) && !cityRef.current.trim()) {
|
|
4513
|
-
updateError("City is required");
|
|
4514
|
-
return;
|
|
4515
|
-
}
|
|
4516
|
-
if ((0, import_shared5.isAVSFieldVisible)(avsConfig.state, country) && !stateRef.current.trim()) {
|
|
4517
|
-
updateError((0, import_shared5.getStateLabel)(country) + " is required");
|
|
4518
|
-
return;
|
|
4519
|
-
}
|
|
4520
|
-
}
|
|
4521
|
-
if (!vaultActive) {
|
|
4522
|
-
const submitResult = await flopay.submitElements();
|
|
4523
|
-
if (submitResult.error) {
|
|
4524
|
-
updateError(submitResult.error.message);
|
|
4525
|
-
onError?.(submitResult.error);
|
|
4526
|
-
return;
|
|
4527
|
-
}
|
|
4528
|
-
}
|
|
4529
|
-
const billingAddress = {};
|
|
4530
|
-
const cc = selectedCountryRef.current;
|
|
4531
|
-
const avsCountry = enableAVS ? cc : resolvedAccount.country;
|
|
4532
|
-
if (avsCountry) billingAddress["country"] = avsCountry;
|
|
4533
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, cc) && zipCodeRef.current.trim()) {
|
|
4534
|
-
billingAddress["postal_code"] = zipCodeRef.current.trim();
|
|
4535
|
-
} else if (!avsConfig && resolvedAccount.zip) {
|
|
4536
|
-
billingAddress["postal_code"] = resolvedAccount.zip;
|
|
4537
|
-
}
|
|
4538
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, cc) && addressLine1Ref.current.trim()) billingAddress["line1"] = addressLine1Ref.current.trim();
|
|
4539
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_2, cc) && addressLine2Ref.current.trim()) billingAddress["line2"] = addressLine2Ref.current.trim();
|
|
4540
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.city, cc) && cityRef.current.trim()) billingAddress["city"] = cityRef.current.trim();
|
|
4541
|
-
if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.state, cc) && stateRef.current.trim()) {
|
|
4542
|
-
billingAddress["state"] = stateRef.current.trim();
|
|
4543
|
-
} else if (avsConfig && (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, cc) && !(0, import_shared5.isAVSFieldVisible)(avsConfig.state, cc) && (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, cc)) {
|
|
4544
|
-
const derivedSubmitState = (0, import_shared5.getStateFromPostalCode)(cc, zipCodeRef.current.trim());
|
|
4545
|
-
if (derivedSubmitState) billingAddress["state"] = derivedSubmitState;
|
|
4546
|
-
}
|
|
4547
|
-
const billingDetails = {
|
|
4548
|
-
...resolvedAccount.email ? { email: resolvedAccount.email } : {},
|
|
4549
|
-
...fullName.trim() ? { name: fullName.trim() } : {},
|
|
4550
|
-
...Object.keys(billingAddress).length > 0 ? { address: billingAddress } : {}
|
|
4551
|
-
};
|
|
4552
|
-
if (!sessionId || !resolvedAccount.email) {
|
|
4553
|
-
throw new import_shared6.FloPayError("Missing sessionId or email", "validation_error");
|
|
4554
|
-
}
|
|
4555
|
-
{
|
|
4556
|
-
const pmResult = await flopay.createPaymentMethod(billingDetails);
|
|
4557
|
-
if (pmResult.error || !pmResult.paymentMethodId) {
|
|
4558
|
-
updateError(pmResult.error?.message ?? "Failed to create payment method.");
|
|
4559
|
-
return;
|
|
4560
|
-
}
|
|
4561
|
-
const intentHeaders = { "Content-Type": "application/json" };
|
|
4562
|
-
if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
|
|
4563
|
-
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
4564
|
-
method: "POST",
|
|
4565
|
-
headers: intentHeaders,
|
|
4566
|
-
body: JSON.stringify({
|
|
4567
|
-
sessionId,
|
|
4568
|
-
email: resolvedAccount.email,
|
|
4569
|
-
paymentMethodType: pmResult.paymentMethodId,
|
|
4570
|
-
isPaypal: false
|
|
4571
|
-
})
|
|
4572
|
-
});
|
|
4573
|
-
if (!intentResponse.ok) {
|
|
4574
|
-
const intentError = await buildFloPayApiErrorFromResponse(
|
|
4575
|
-
intentResponse,
|
|
4576
|
-
"Failed to create payment intent"
|
|
4577
|
-
);
|
|
4578
|
-
setOverlayStatus("error");
|
|
4579
|
-
updateError(intentError.message);
|
|
4580
|
-
onError?.(intentError);
|
|
4581
|
-
emitDecline("card", intentError);
|
|
4582
|
-
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
4583
|
-
return;
|
|
4584
|
-
}
|
|
4585
|
-
const intentJson = await intentResponse.json();
|
|
4586
|
-
const intentClientSecret = intentJson.data?.id;
|
|
4587
|
-
if (!intentClientSecret) throw new import_shared6.FloPayError("No client_secret in payment intent response", "api_error");
|
|
4588
|
-
const confirmResult = await flopay.confirmCardPayment({
|
|
4589
|
-
clientSecret: intentClientSecret,
|
|
4590
|
-
paymentMethodId: pmResult.paymentMethodId
|
|
4591
|
-
});
|
|
4592
|
-
if (confirmResult.error) {
|
|
4593
|
-
setOverlayStatus("error");
|
|
4594
|
-
updateError(confirmResult.error.message);
|
|
4595
|
-
onError?.(confirmResult.error);
|
|
4596
|
-
emitDecline("card", confirmResult.error);
|
|
4597
|
-
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
4598
|
-
return;
|
|
4599
|
-
}
|
|
4600
|
-
const rawProvider = typeof flopay.getRawProvider === "function" ? flopay.getRawProvider() : null;
|
|
4601
|
-
const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
|
|
4602
|
-
rawProvider,
|
|
4603
|
-
intentClientSecret
|
|
4604
|
-
);
|
|
4605
|
-
const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
|
|
4606
|
-
const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
|
|
4607
|
-
if (!paymentIntentId) {
|
|
4608
|
-
const error2 = new import_shared6.FloPayError("No payment intent returned after confirmation.", "api_error");
|
|
4609
|
-
setOverlayStatus("error");
|
|
4610
|
-
updateError(error2.message);
|
|
4611
|
-
onError?.(error2);
|
|
4612
|
-
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
4613
|
-
return;
|
|
4614
|
-
}
|
|
4615
|
-
handedOff = isSelfContained;
|
|
4616
|
-
dispatchTokenizedBody({
|
|
4617
|
-
id: paymentMethodId,
|
|
4618
|
-
type: "card",
|
|
4619
|
-
threeDSecureActionResultTokenId: paymentIntentId,
|
|
4620
|
-
originalPaymentMethodId: pmResult.paymentMethodId
|
|
4621
|
-
});
|
|
4622
|
-
}
|
|
4623
|
-
} catch (err) {
|
|
4624
|
-
setOverlayStatus("error");
|
|
4625
|
-
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
4626
|
-
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
4627
|
-
} finally {
|
|
4628
|
-
if (!handedOff) {
|
|
4629
|
-
setProcessing(false);
|
|
4630
|
-
setOverlayStatus(null);
|
|
4631
|
-
}
|
|
4632
|
-
}
|
|
4633
|
-
},
|
|
4634
|
-
[flopay, elements, isSubmitting, sessionId, nonce, resolvedAccount.email, baseUrl, isSelfContained, vaultActive, dispatchTokenizedBody, onButtonClick, onError, updateError, emitDecline, layout]
|
|
4635
|
-
);
|
|
4636
|
-
const isReady = flopay !== null && (vaultActive || elements !== null);
|
|
4531
|
+
const handleSubmit = (0, import_react9.useCallback)((event) => {
|
|
4532
|
+
event.preventDefault();
|
|
4533
|
+
}, []);
|
|
4534
|
+
const isReady = flopay !== null;
|
|
4637
4535
|
if (!isReady) {
|
|
4638
4536
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." });
|
|
4639
4537
|
}
|
|
@@ -4676,21 +4574,6 @@ function SplitCardFormInner({
|
|
|
4676
4574
|
"--flopay-input-font-family": resolvedInputFontFamily,
|
|
4677
4575
|
"--flopay-input-font-weight": String(resolvedInputFontWeight)
|
|
4678
4576
|
};
|
|
4679
|
-
const stripeElementStyle = {
|
|
4680
|
-
style: {
|
|
4681
|
-
base: {
|
|
4682
|
-
...sharedInputTypography,
|
|
4683
|
-
fontSmoothing: "antialiased",
|
|
4684
|
-
"::placeholder": {
|
|
4685
|
-
color: resolvedPlaceholderColor,
|
|
4686
|
-
fontSize: resolvedInputFontSize,
|
|
4687
|
-
fontFamily: resolvedInputFontFamily,
|
|
4688
|
-
fontWeight: resolvedInputFontWeight
|
|
4689
|
-
}
|
|
4690
|
-
},
|
|
4691
|
-
invalid: { color: "#ef4444" }
|
|
4692
|
-
}
|
|
4693
|
-
};
|
|
4694
4577
|
const containerOverrides = bStyles.cardFormContainer ?? {};
|
|
4695
4578
|
const containerPadding = containerOverrides.padding ?? (isButtons ? "0" : "1rem");
|
|
4696
4579
|
const containerRadius = containerOverrides.borderRadius ?? resolvedBorderRadius;
|
|
@@ -4703,7 +4586,6 @@ function SplitCardFormInner({
|
|
|
4703
4586
|
expectedOrigin: vaultMount.expectedOrigin,
|
|
4704
4587
|
theme: vaultThemeColors,
|
|
4705
4588
|
onReady: () => {
|
|
4706
|
-
setFormReady(true);
|
|
4707
4589
|
if (!avsConfig || typeof document === "undefined") return;
|
|
4708
4590
|
const focusFirstAvs = () => {
|
|
4709
4591
|
const block = document.querySelector('[data-testid="flopay-avs-fields"]');
|
|
@@ -4834,68 +4716,6 @@ function SplitCardFormInner({
|
|
|
4834
4716
|
order: vaultActive ? -3 : 0,
|
|
4835
4717
|
...bStyles.title
|
|
4836
4718
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TitleContentSlot, { content: cardTitleContent }) }),
|
|
4837
|
-
!vaultActive && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
4838
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4839
|
-
backgroundColor: cardInputBg,
|
|
4840
|
-
border: `1px solid ${resolvedBorder}`,
|
|
4841
|
-
borderTopLeftRadius: resolvedBorderRadius,
|
|
4842
|
-
borderTopRightRadius: resolvedBorderRadius,
|
|
4843
|
-
padding: "10px"
|
|
4844
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardNumberElement, { onReady: () => setFormReady(true), options: stripeElementStyle }) }),
|
|
4845
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex" }, children: [
|
|
4846
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4847
|
-
flex: 1,
|
|
4848
|
-
backgroundColor: cardInputBg,
|
|
4849
|
-
// Longhand only — mixing `border` shorthand with per-side
|
|
4850
|
-
// overrides triggers React's "shorthand vs longhand" warning
|
|
4851
|
-
// because render order isn't deterministic.
|
|
4852
|
-
borderTop: "none",
|
|
4853
|
-
borderRight: "none",
|
|
4854
|
-
borderBottom: `1px solid ${resolvedBorder}`,
|
|
4855
|
-
borderLeft: `1px solid ${resolvedBorder}`,
|
|
4856
|
-
borderBottomLeftRadius: resolvedBorderRadius,
|
|
4857
|
-
padding: "10px"
|
|
4858
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
4859
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4860
|
-
flex: 1,
|
|
4861
|
-
backgroundColor: cardInputBg,
|
|
4862
|
-
borderTop: "none",
|
|
4863
|
-
borderRight: `1px solid ${resolvedBorder}`,
|
|
4864
|
-
borderBottom: `1px solid ${resolvedBorder}`,
|
|
4865
|
-
borderLeft: `1px solid ${resolvedBorder}`,
|
|
4866
|
-
borderBottomRightRadius: resolvedBorderRadius,
|
|
4867
|
-
padding: "10px"
|
|
4868
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardCvcElement, { options: stripeElementStyle }) })
|
|
4869
|
-
] })
|
|
4870
|
-
] }),
|
|
4871
|
-
!vaultActive && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4872
|
-
backgroundColor: cardInputBg,
|
|
4873
|
-
border: `1px solid ${resolvedBorder}`,
|
|
4874
|
-
borderRadius: resolvedBorderRadius,
|
|
4875
|
-
marginTop: "0.5rem",
|
|
4876
|
-
padding: "10px"
|
|
4877
|
-
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4878
|
-
"input",
|
|
4879
|
-
{
|
|
4880
|
-
className: "flopay-shared-input",
|
|
4881
|
-
id: "flopay-cc-name",
|
|
4882
|
-
name: "cc-name",
|
|
4883
|
-
placeholder: "Full Name on Card",
|
|
4884
|
-
autoComplete: "cc-name",
|
|
4885
|
-
value: fullName,
|
|
4886
|
-
onChange: (e) => handleNameChange(e.target.value),
|
|
4887
|
-
disabled: isSubmitting,
|
|
4888
|
-
required: true,
|
|
4889
|
-
style: {
|
|
4890
|
-
width: "100%",
|
|
4891
|
-
border: "none",
|
|
4892
|
-
outline: "none",
|
|
4893
|
-
background: "transparent",
|
|
4894
|
-
...sharedInputTypography,
|
|
4895
|
-
...isButtons && bStyles.nameInput ? bStyles.nameInput : {}
|
|
4896
|
-
}
|
|
4897
|
-
}
|
|
4898
|
-
) }),
|
|
4899
4719
|
avsConfig && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { order: vaultActive ? -1 : 0 }, "data-testid": "flopay-avs-fields", children: (() => {
|
|
4900
4720
|
const cc = selectedCountry;
|
|
4901
4721
|
const inputWrapStyle = (invalid = false) => ({
|
|
@@ -5120,55 +4940,9 @@ function SplitCardFormInner({
|
|
|
5120
4940
|
)
|
|
5121
4941
|
] });
|
|
5122
4942
|
})() }),
|
|
5123
|
-
vaultActive && cardPreFormSlot && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
|
|
5124
|
-
vaultActive && vaultCardFieldsNode,
|
|
5125
|
-
displayError && /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
5126
|
-
margin: "0.75rem 0",
|
|
5127
|
-
padding: "0.625rem 0.875rem",
|
|
5128
|
-
background: "#FEF2F2",
|
|
5129
|
-
border: "1px solid #FECACA",
|
|
5130
|
-
borderRadius: "8px",
|
|
5131
|
-
color: "#991B1B",
|
|
5132
|
-
fontSize: "0.85rem",
|
|
5133
|
-
fontWeight: 600,
|
|
5134
|
-
display: "flex",
|
|
5135
|
-
alignItems: "center",
|
|
5136
|
-
gap: "0.5rem",
|
|
5137
|
-
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
5138
|
-
}, children: [
|
|
5139
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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" }) }),
|
|
5140
|
-
displayError
|
|
5141
|
-
] }),
|
|
5142
|
-
!vaultActive && (children ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
5143
|
-
"button",
|
|
5144
|
-
{
|
|
5145
|
-
type: "submit",
|
|
5146
|
-
disabled: !formReady || isSubmitting,
|
|
5147
|
-
"data-testid": "flopay-submit",
|
|
5148
|
-
onMouseEnter: (e) => {
|
|
5149
|
-
if (formReady && !isSubmitting) e.currentTarget.style.backgroundColor = resolvedPrimaryHoverColor;
|
|
5150
|
-
},
|
|
5151
|
-
onMouseLeave: (e) => {
|
|
5152
|
-
e.currentTarget.style.backgroundColor = bStyles.submitButton?.backgroundColor ?? resolvedPrimaryColor;
|
|
5153
|
-
},
|
|
5154
|
-
style: {
|
|
5155
|
-
width: "100%",
|
|
5156
|
-
padding: "0.875rem",
|
|
5157
|
-
marginTop: "1rem",
|
|
5158
|
-
backgroundColor: resolvedPrimaryColor,
|
|
5159
|
-
color: "white",
|
|
5160
|
-
border: "none",
|
|
5161
|
-
borderRadius: resolvedBorderRadius,
|
|
5162
|
-
fontSize: bStyles.submitButtonFontSize ?? "1rem",
|
|
5163
|
-
fontWeight: 600,
|
|
5164
|
-
cursor: !formReady || isSubmitting ? "not-allowed" : "pointer",
|
|
5165
|
-
opacity: !formReady || isSubmitting ? 0.5 : 1,
|
|
5166
|
-
transition: "background-color 0.15s",
|
|
5167
|
-
...bStyles.submitButton
|
|
5168
|
-
},
|
|
5169
|
-
children: isSubmitting ? "PROCESSING..." : submitLabel
|
|
5170
|
-
}
|
|
5171
|
-
))
|
|
4943
|
+
vaultActive && cardPreFormSlot && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
|
|
4944
|
+
vaultActive && vaultCardFieldsNode,
|
|
4945
|
+
displayError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBanner, { margin: "0.75rem 0", styleOverride: bStyles.errorBanner, children: displayError })
|
|
5172
4946
|
] });
|
|
5173
4947
|
const gateDebugStyle = {
|
|
5174
4948
|
margin: 0,
|
|
@@ -5288,6 +5062,7 @@ function SplitCardFormInner({
|
|
|
5288
5062
|
runBeforeButtonClick,
|
|
5289
5063
|
isProcessing: isSubmitting,
|
|
5290
5064
|
onLoadStateChange: setDirectPaypalReady,
|
|
5065
|
+
onInitializationStateChange: setDirectPaypalInitializationState,
|
|
5291
5066
|
session: session ?? null,
|
|
5292
5067
|
existingOrderId: paypalDirectRetry?.orderId,
|
|
5293
5068
|
debug
|
|
@@ -5320,6 +5095,7 @@ function SplitCardFormInner({
|
|
|
5320
5095
|
nonce,
|
|
5321
5096
|
email: resolvedAccount.email,
|
|
5322
5097
|
billingApiUrl: resolvedBillingApiUrl,
|
|
5098
|
+
intentKind: totalAmount > 0 ? "payment" : "setup",
|
|
5323
5099
|
expressMethods: walletExpressMethods,
|
|
5324
5100
|
onTokenizedBody: dispatchTokenizedBody,
|
|
5325
5101
|
onErrorChange: updateError,
|
|
@@ -5363,7 +5139,7 @@ function SplitCardFormInner({
|
|
|
5363
5139
|
}
|
|
5364
5140
|
}
|
|
5365
5141
|
),
|
|
5366
|
-
|
|
5142
|
+
vaultActive && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
5367
5143
|
"button",
|
|
5368
5144
|
{
|
|
5369
5145
|
type: "button",
|
|
@@ -5422,27 +5198,11 @@ function SplitCardFormInner({
|
|
|
5422
5198
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CardButtonContentSlot, { content: cardButtonContent })
|
|
5423
5199
|
}
|
|
5424
5200
|
),
|
|
5425
|
-
displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime7.
|
|
5426
|
-
margin: "0.25rem 0",
|
|
5427
|
-
padding: "0.625rem 0.875rem",
|
|
5428
|
-
background: "#FEF2F2",
|
|
5429
|
-
border: "1px solid #FECACA",
|
|
5430
|
-
borderRadius: "8px",
|
|
5431
|
-
color: "#991B1B",
|
|
5432
|
-
fontSize: "0.85rem",
|
|
5433
|
-
fontWeight: 600,
|
|
5434
|
-
display: "flex",
|
|
5435
|
-
alignItems: "center",
|
|
5436
|
-
gap: "0.5rem",
|
|
5437
|
-
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
5438
|
-
}, children: [
|
|
5439
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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" }) }),
|
|
5440
|
-
displayError
|
|
5441
|
-
] })
|
|
5201
|
+
displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ErrorBanner, { margin: "0.25rem 0", styleOverride: bStyles.errorBanner, children: displayError })
|
|
5442
5202
|
]
|
|
5443
5203
|
}
|
|
5444
5204
|
),
|
|
5445
|
-
|
|
5205
|
+
vaultActive && isCardView && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
5446
5206
|
gridArea: "1 / 1",
|
|
5447
5207
|
...cardAnim ? { animation: cardAnim } : {},
|
|
5448
5208
|
...viewState === "collapsing" ? { pointerEvents: "none" } : {}
|
|
@@ -5686,6 +5446,7 @@ function SplitCardFormInner({
|
|
|
5686
5446
|
runBeforeButtonClick,
|
|
5687
5447
|
isProcessing: isSubmitting,
|
|
5688
5448
|
onLoadStateChange: setDirectPaypalReady,
|
|
5449
|
+
onInitializationStateChange: setDirectPaypalInitializationState,
|
|
5689
5450
|
session: session ?? null,
|
|
5690
5451
|
existingOrderId: paypalDirectRetry?.orderId,
|
|
5691
5452
|
debug
|
|
@@ -5718,6 +5479,7 @@ function SplitCardFormInner({
|
|
|
5718
5479
|
nonce,
|
|
5719
5480
|
email: resolvedAccount.email,
|
|
5720
5481
|
billingApiUrl: resolvedBillingApiUrl,
|
|
5482
|
+
intentKind: totalAmount > 0 ? "payment" : "setup",
|
|
5721
5483
|
expressMethods: walletExpressMethods,
|
|
5722
5484
|
onTokenizedBody: dispatchTokenizedBody,
|
|
5723
5485
|
onErrorChange: updateError,
|
|
@@ -5762,7 +5524,7 @@ function SplitCardFormInner({
|
|
|
5762
5524
|
}
|
|
5763
5525
|
)
|
|
5764
5526
|
] }),
|
|
5765
|
-
|
|
5527
|
+
vaultActive && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: {
|
|
5766
5528
|
display: "flex",
|
|
5767
5529
|
alignItems: "center",
|
|
5768
5530
|
gap: "0.75rem",
|
|
@@ -5774,7 +5536,7 @@ function SplitCardFormInner({
|
|
|
5774
5536
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "or pay with card" }),
|
|
5775
5537
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
|
|
5776
5538
|
] }),
|
|
5777
|
-
|
|
5539
|
+
vaultActive && cardFormBlock
|
|
5778
5540
|
] });
|
|
5779
5541
|
}
|
|
5780
5542
|
|
|
@@ -5802,6 +5564,9 @@ async function confirmResumeIntent(stripe, clientSecret, data, isSetupIntent) {
|
|
|
5802
5564
|
const { paymentIntent, error } = await stripe.confirmCardPayment(clientSecret, data);
|
|
5803
5565
|
return { error, intent: paymentIntent ?? null };
|
|
5804
5566
|
}
|
|
5567
|
+
function withCheckoutMethod(error, checkoutMethod) {
|
|
5568
|
+
return Object.assign(error, { checkoutMethod });
|
|
5569
|
+
}
|
|
5805
5570
|
function getRedirectResultFromCheckoutProcessError(error) {
|
|
5806
5571
|
if (!error?.type || !error.threeDSecureToken) {
|
|
5807
5572
|
return null;
|
|
@@ -6023,7 +5788,7 @@ function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment fai
|
|
|
6023
5788
|
const checkoutMethod = options?.checkoutMethod ?? error?.checkoutMethod ?? (error?.type === "paypal_redirect_required" ? "paypal" : "card");
|
|
6024
5789
|
const rawMessage = error?.message ?? fallbackMessage;
|
|
6025
5790
|
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
6026
|
-
return
|
|
5791
|
+
return withCheckoutMethod(
|
|
6027
5792
|
new import_shared7.FloPayError(
|
|
6028
5793
|
message,
|
|
6029
5794
|
"api_error",
|
|
@@ -6031,7 +5796,7 @@ function checkoutProcessErrorToFloPayError(error, fallbackMessage = "Payment fai
|
|
|
6031
5796
|
code: error?.gatewayErrorCode
|
|
6032
5797
|
}
|
|
6033
5798
|
),
|
|
6034
|
-
|
|
5799
|
+
checkoutMethod
|
|
6035
5800
|
);
|
|
6036
5801
|
}
|
|
6037
5802
|
function resolveSavedPaymentReturnUrl(session) {
|
|
@@ -6203,13 +5968,13 @@ async function processSavedPaymentForMode({
|
|
|
6203
5968
|
if (recoveredRedirect) {
|
|
6204
5969
|
return recoveredRedirect;
|
|
6205
5970
|
}
|
|
6206
|
-
throw
|
|
5971
|
+
throw withCheckoutMethod(
|
|
6207
5972
|
new import_shared7.FloPayError(
|
|
6208
5973
|
"Your card requires authentication. Please enter your payment details below.",
|
|
6209
5974
|
"api_error",
|
|
6210
5975
|
{ code: "authentication_required" }
|
|
6211
5976
|
),
|
|
6212
|
-
|
|
5977
|
+
"card"
|
|
6213
5978
|
);
|
|
6214
5979
|
}
|
|
6215
5980
|
throw new import_shared7.FloPayError(
|
|
@@ -6237,13 +6002,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6237
6002
|
}
|
|
6238
6003
|
if (redirectResult.type === "3ds_required") {
|
|
6239
6004
|
if (!attempt3DS || !redirectResult.threeDSecureToken) {
|
|
6240
|
-
throw
|
|
6005
|
+
throw withCheckoutMethod(
|
|
6241
6006
|
new import_shared7.FloPayError(
|
|
6242
6007
|
"Your card requires authentication. Please enter your payment details below.",
|
|
6243
6008
|
"api_error",
|
|
6244
6009
|
{ code: "authentication_required" }
|
|
6245
6010
|
),
|
|
6246
|
-
|
|
6011
|
+
"card"
|
|
6247
6012
|
);
|
|
6248
6013
|
}
|
|
6249
6014
|
const isSetupIntent = (0, import_shared7.isSetupIntentClientSecret)(redirectResult.threeDSecureToken);
|
|
@@ -6252,13 +6017,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6252
6017
|
const retrieved = await retrieveResumeIntent(stripe, redirectResult.threeDSecureToken, isSetupIntent);
|
|
6253
6018
|
if (retrieved) {
|
|
6254
6019
|
if (retrieved.error) {
|
|
6255
|
-
throw
|
|
6020
|
+
throw withCheckoutMethod(
|
|
6256
6021
|
new import_shared7.FloPayError(
|
|
6257
6022
|
retrieved.error.message ?? `Failed to retrieve 3DS ${isSetupIntent ? "setup" : "payment"} status.`,
|
|
6258
6023
|
"api_error",
|
|
6259
6024
|
{ code: retrieved.error.code }
|
|
6260
6025
|
),
|
|
6261
|
-
|
|
6026
|
+
"card"
|
|
6262
6027
|
);
|
|
6263
6028
|
}
|
|
6264
6029
|
const existingIntent = retrieved.intent;
|
|
@@ -6281,13 +6046,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6281
6046
|
) : null;
|
|
6282
6047
|
if (confirmed) {
|
|
6283
6048
|
if (confirmed.error) {
|
|
6284
|
-
throw
|
|
6049
|
+
throw withCheckoutMethod(
|
|
6285
6050
|
new import_shared7.FloPayError(
|
|
6286
6051
|
confirmed.error.message ?? "3DS authentication failed.",
|
|
6287
6052
|
"api_error",
|
|
6288
6053
|
{ code: confirmed.error.code }
|
|
6289
6054
|
),
|
|
6290
|
-
|
|
6055
|
+
"card"
|
|
6291
6056
|
);
|
|
6292
6057
|
}
|
|
6293
6058
|
paymentIntent = confirmed.intent;
|
|
@@ -6296,13 +6061,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6296
6061
|
clientSecret: redirectResult.threeDSecureToken
|
|
6297
6062
|
});
|
|
6298
6063
|
if (nextAction.error) {
|
|
6299
|
-
throw
|
|
6064
|
+
throw withCheckoutMethod(
|
|
6300
6065
|
new import_shared7.FloPayError(
|
|
6301
6066
|
nextAction.error.message ?? "3DS authentication failed.",
|
|
6302
6067
|
"api_error",
|
|
6303
6068
|
{ code: nextAction.error.code }
|
|
6304
6069
|
),
|
|
6305
|
-
|
|
6070
|
+
"card"
|
|
6306
6071
|
);
|
|
6307
6072
|
}
|
|
6308
6073
|
paymentIntent = (isSetupIntent ? nextAction.setupIntent : nextAction.paymentIntent) ?? null;
|
|
@@ -6338,17 +6103,17 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6338
6103
|
telemetry
|
|
6339
6104
|
});
|
|
6340
6105
|
}
|
|
6341
|
-
throw
|
|
6106
|
+
throw withCheckoutMethod(
|
|
6342
6107
|
new import_shared7.FloPayError("3DS authentication did not complete successfully.", "api_error"),
|
|
6343
|
-
|
|
6108
|
+
"card"
|
|
6344
6109
|
);
|
|
6345
6110
|
}
|
|
6346
6111
|
if (redirectResult.type === "paypal_redirect_required") {
|
|
6347
6112
|
const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
|
|
6348
6113
|
if (!paypalStripe) {
|
|
6349
|
-
throw
|
|
6114
|
+
throw withCheckoutMethod(
|
|
6350
6115
|
new import_shared7.FloPayError("PayPal is not available.", "api_error"),
|
|
6351
|
-
|
|
6116
|
+
"paypal"
|
|
6352
6117
|
);
|
|
6353
6118
|
}
|
|
6354
6119
|
if (redirectResult.paymentMethodId) {
|
|
@@ -6356,13 +6121,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6356
6121
|
clientSecret: redirectResult.threeDSecureToken
|
|
6357
6122
|
});
|
|
6358
6123
|
if (error2) {
|
|
6359
|
-
throw
|
|
6124
|
+
throw withCheckoutMethod(
|
|
6360
6125
|
new import_shared7.FloPayError(
|
|
6361
6126
|
error2.message ?? "PayPal authorization failed.",
|
|
6362
6127
|
"api_error",
|
|
6363
6128
|
{ code: error2.code }
|
|
6364
6129
|
),
|
|
6365
|
-
|
|
6130
|
+
"paypal"
|
|
6366
6131
|
);
|
|
6367
6132
|
}
|
|
6368
6133
|
return {
|
|
@@ -6379,13 +6144,13 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
6379
6144
|
redirect: "if_required"
|
|
6380
6145
|
});
|
|
6381
6146
|
if (error) {
|
|
6382
|
-
throw
|
|
6147
|
+
throw withCheckoutMethod(
|
|
6383
6148
|
new import_shared7.FloPayError(
|
|
6384
6149
|
error.message ?? "PayPal authorization failed.",
|
|
6385
6150
|
"api_error",
|
|
6386
6151
|
{ code: error.code }
|
|
6387
6152
|
),
|
|
6388
|
-
|
|
6153
|
+
"paypal"
|
|
6389
6154
|
);
|
|
6390
6155
|
}
|
|
6391
6156
|
return {
|
|
@@ -6616,7 +6381,6 @@ function FloPayCheckout({
|
|
|
6616
6381
|
onComplete,
|
|
6617
6382
|
onError,
|
|
6618
6383
|
onDecline,
|
|
6619
|
-
onFullNameChange,
|
|
6620
6384
|
onCountryChange,
|
|
6621
6385
|
onZipChange,
|
|
6622
6386
|
showPayPal = true,
|
|
@@ -6638,7 +6402,6 @@ function FloPayCheckout({
|
|
|
6638
6402
|
cardFieldOrder,
|
|
6639
6403
|
cardPreFormSlot,
|
|
6640
6404
|
avsLayout,
|
|
6641
|
-
submitLabel,
|
|
6642
6405
|
className,
|
|
6643
6406
|
initialErrorMessage = null,
|
|
6644
6407
|
children,
|
|
@@ -6671,8 +6434,11 @@ function FloPayCheckout({
|
|
|
6671
6434
|
const [createSessionPatch, setCreateSessionPatch] = (0, import_react10.useState)(void 0);
|
|
6672
6435
|
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = (0, import_react10.useState)("");
|
|
6673
6436
|
const [cardBootstrapPending, setCardBootstrapPending] = (0, import_react10.useState)(false);
|
|
6437
|
+
const [paypalInitializationState, setPaypalInitializationState] = (0, import_react10.useState)("loading");
|
|
6438
|
+
const [paypalManualRetryGeneration, setPaypalManualRetryGeneration] = (0, import_react10.useState)(0);
|
|
6674
6439
|
const autoCheckoutAttempted = (0, import_react10.useRef)(false);
|
|
6675
6440
|
const paypalResumeAttempted = (0, import_react10.useRef)(false);
|
|
6441
|
+
const paypalInitializationErrorEmittedRef = (0, import_react10.useRef)(false);
|
|
6676
6442
|
const savedPaymentKeysRef = (0, import_react10.useRef)(null);
|
|
6677
6443
|
const telemetryCheckoutContext = (0, import_react10.useMemo)(() => ({
|
|
6678
6444
|
checkoutMode: checkoutModeProp ?? currentMode,
|
|
@@ -6692,6 +6458,28 @@ function FloPayCheckout({
|
|
|
6692
6458
|
}, [flopay, paypalFlopay, telemetryCheckoutContext]);
|
|
6693
6459
|
const onSessionCompletedRef = (0, import_react10.useRef)(onSessionCompleted);
|
|
6694
6460
|
onSessionCompletedRef.current = onSessionCompleted;
|
|
6461
|
+
const handlePayPalInitializationStateChange = (0, import_react10.useCallback)(
|
|
6462
|
+
(state) => {
|
|
6463
|
+
setPaypalInitializationState(state);
|
|
6464
|
+
if (state !== "exhausted" || paypalInitializationErrorEmittedRef.current) return;
|
|
6465
|
+
paypalInitializationErrorEmittedRef.current = true;
|
|
6466
|
+
onErrorRef.current?.(new import_shared8.FloPayError(
|
|
6467
|
+
"PayPal is temporarily unavailable. Please try again.",
|
|
6468
|
+
"api_error",
|
|
6469
|
+
{ code: "paypal_init_timeout" }
|
|
6470
|
+
));
|
|
6471
|
+
},
|
|
6472
|
+
[]
|
|
6473
|
+
);
|
|
6474
|
+
const retryPayPalInitialization = (0, import_react10.useCallback)(() => {
|
|
6475
|
+
setPaypalInitializationState("retrying");
|
|
6476
|
+
setPaypalManualRetryGeneration((generation) => generation + 1);
|
|
6477
|
+
}, []);
|
|
6478
|
+
(0, import_react10.useEffect)(() => {
|
|
6479
|
+
setPaypalInitializationState("loading");
|
|
6480
|
+
setPaypalManualRetryGeneration(0);
|
|
6481
|
+
paypalInitializationErrorEmittedRef.current = false;
|
|
6482
|
+
}, [activeSessionId]);
|
|
6695
6483
|
(0, import_react10.useEffect)(() => {
|
|
6696
6484
|
console.info("[FloPay] Checkout initialized", {
|
|
6697
6485
|
sdk_version: import_shared8.SDK_VERSION,
|
|
@@ -7775,42 +7563,41 @@ function FloPayCheckout({
|
|
|
7775
7563
|
if (isPaypalOnlySession && session && directPaypalConfig) {
|
|
7776
7564
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
7777
7565
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckoutContext.Provider, { value: checkoutValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
7778
|
-
modeError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
7566
|
+
modeError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ErrorBanner, { icon: false, children: modeError }),
|
|
7567
|
+
paypalInitializationState === "retrying" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { role: "status", "aria-live": "polite", children: "PayPal is temporarily unavailable. Retrying\u2026" }),
|
|
7568
|
+
paypalInitializationState === "exhausted" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { role: "alert", children: [
|
|
7569
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { children: "PayPal is temporarily unavailable. Please try again." }),
|
|
7570
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", onClick: retryPayPalInitialization, children: "Retry PayPal" })
|
|
7571
|
+
] }),
|
|
7572
|
+
paypalInitializationState !== "exhausted" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
7779
7573
|
"div",
|
|
7780
7574
|
{
|
|
7781
|
-
|
|
7782
|
-
"
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
onErrorChange: setModeError,
|
|
7808
|
-
onDecline,
|
|
7809
|
-
onButtonClick,
|
|
7810
|
-
session,
|
|
7811
|
-
telemetry,
|
|
7812
|
-
telemetryContext: telemetryCheckoutContext,
|
|
7813
|
-
debug
|
|
7575
|
+
"aria-hidden": paypalInitializationState === "retrying" ? true : void 0,
|
|
7576
|
+
style: paypalInitializationState === "retrying" ? { position: "relative", height: 0, opacity: 0, pointerEvents: "none" } : void 0,
|
|
7577
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
7578
|
+
InstrumentedDirectPayPalButton,
|
|
7579
|
+
{
|
|
7580
|
+
sessionId: activeSessionId,
|
|
7581
|
+
nonce: session.clientSecret || void 0,
|
|
7582
|
+
billingApiUrl: resolvedBillingUrl,
|
|
7583
|
+
email: session.customer?.email,
|
|
7584
|
+
clientId: directPaypalConfig.clientId,
|
|
7585
|
+
environment: directPaypalConfig.environment,
|
|
7586
|
+
currency: (session.currency ?? "usd").toUpperCase(),
|
|
7587
|
+
isSubscription: session.mode === "subscription",
|
|
7588
|
+
onComplete,
|
|
7589
|
+
onErrorChange: setModeError,
|
|
7590
|
+
onDecline,
|
|
7591
|
+
onButtonClick,
|
|
7592
|
+
onInitializationStateChange: handlePayPalInitializationStateChange,
|
|
7593
|
+
allowAutomaticRetry: paypalManualRetryGeneration === 0,
|
|
7594
|
+
session,
|
|
7595
|
+
telemetry,
|
|
7596
|
+
telemetryContext: telemetryCheckoutContext,
|
|
7597
|
+
debug
|
|
7598
|
+
},
|
|
7599
|
+
`paypal-init-${paypalManualRetryGeneration}`
|
|
7600
|
+
)
|
|
7814
7601
|
}
|
|
7815
7602
|
)
|
|
7816
7603
|
] }) }),
|
|
@@ -7906,7 +7693,6 @@ function FloPayCheckout({
|
|
|
7906
7693
|
onDecline,
|
|
7907
7694
|
error: modeError,
|
|
7908
7695
|
onErrorChange: setModeError,
|
|
7909
|
-
onFullNameChange,
|
|
7910
7696
|
showPayPal,
|
|
7911
7697
|
showStripe,
|
|
7912
7698
|
enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
|
|
@@ -7935,7 +7721,6 @@ function FloPayCheckout({
|
|
|
7935
7721
|
avsCheck: !!enableAVS,
|
|
7936
7722
|
checkoutType: createSessionParams ? "embedded_checkout" : "standard_checkout",
|
|
7937
7723
|
checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout",
|
|
7938
|
-
submitLabel,
|
|
7939
7724
|
className,
|
|
7940
7725
|
directPaypal: resolveDirectPaypalConfig(unified),
|
|
7941
7726
|
isSubscription: session?.mode === "subscription",
|
|
@@ -7974,10 +7759,6 @@ function SessionInjector({
|
|
|
7974
7759
|
}
|
|
7975
7760
|
function InterimButtonsView({
|
|
7976
7761
|
onButtonClick,
|
|
7977
|
-
onCardButtonClick,
|
|
7978
|
-
cardLoading = false,
|
|
7979
|
-
cardOpen,
|
|
7980
|
-
errorMessage,
|
|
7981
7762
|
showPayPal,
|
|
7982
7763
|
showStripe = true,
|
|
7983
7764
|
showApplePay,
|
|
@@ -7990,12 +7771,6 @@ function InterimButtonsView({
|
|
|
7990
7771
|
cardTitleContent
|
|
7991
7772
|
}) {
|
|
7992
7773
|
const [showCardForm, setShowCardForm] = (0, import_react10.useState)(false);
|
|
7993
|
-
const isCardOpenControlled = typeof cardOpen === "boolean";
|
|
7994
|
-
(0, import_react10.useEffect)(() => {
|
|
7995
|
-
if (isCardOpenControlled) {
|
|
7996
|
-
setShowCardForm(cardOpen);
|
|
7997
|
-
}
|
|
7998
|
-
}, [cardOpen, isCardOpenControlled]);
|
|
7999
7774
|
const themeBundle = (0, import_react10.useMemo)(() => (0, import_shared8.resolveTheme)(theme), [theme]);
|
|
8000
7775
|
const bStyles = (0, import_react10.useMemo)(() => {
|
|
8001
7776
|
const base = themeBundle?.buttonsLayout ?? (0, import_shared8.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
@@ -8036,13 +7811,8 @@ function InterimButtonsView({
|
|
|
8036
7811
|
"button",
|
|
8037
7812
|
{
|
|
8038
7813
|
type: "button",
|
|
8039
|
-
onClick: () =>
|
|
8040
|
-
if (!isCardOpenControlled) {
|
|
8041
|
-
setShowCardForm(false);
|
|
8042
|
-
}
|
|
8043
|
-
},
|
|
7814
|
+
onClick: () => setShowCardForm(false),
|
|
8044
7815
|
"aria-label": "Back to payment methods",
|
|
8045
|
-
disabled: isCardOpenControlled || cardLoading,
|
|
8046
7816
|
style: {
|
|
8047
7817
|
display: "inline-flex",
|
|
8048
7818
|
alignItems: "center",
|
|
@@ -8054,8 +7824,8 @@ function InterimButtonsView({
|
|
|
8054
7824
|
fontWeight: 500,
|
|
8055
7825
|
padding: 0,
|
|
8056
7826
|
flexShrink: 0,
|
|
8057
|
-
opacity:
|
|
8058
|
-
cursor:
|
|
7827
|
+
opacity: 1,
|
|
7828
|
+
cursor: "pointer",
|
|
8059
7829
|
...bStyles.backButton
|
|
8060
7830
|
},
|
|
8061
7831
|
children: [
|
|
@@ -8135,16 +7905,10 @@ function InterimButtonsView({
|
|
|
8135
7905
|
"button",
|
|
8136
7906
|
{
|
|
8137
7907
|
type: "button",
|
|
8138
|
-
onClick:
|
|
8139
|
-
if (cardLoading) return;
|
|
8140
|
-
if (onCardButtonClick) {
|
|
8141
|
-
await onCardButtonClick();
|
|
8142
|
-
return;
|
|
8143
|
-
}
|
|
7908
|
+
onClick: () => {
|
|
8144
7909
|
onButtonClick?.("card");
|
|
8145
7910
|
setShowCardForm(true);
|
|
8146
7911
|
},
|
|
8147
|
-
disabled: cardLoading,
|
|
8148
7912
|
style: {
|
|
8149
7913
|
width: "100%",
|
|
8150
7914
|
...cardButtonSizing,
|
|
@@ -8154,7 +7918,7 @@ function InterimButtonsView({
|
|
|
8154
7918
|
borderRadius: "8px",
|
|
8155
7919
|
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
8156
7920
|
fontWeight: 600,
|
|
8157
|
-
cursor:
|
|
7921
|
+
cursor: "pointer",
|
|
8158
7922
|
display: "flex",
|
|
8159
7923
|
alignItems: "center",
|
|
8160
7924
|
justifyContent: "center",
|
|
@@ -8162,7 +7926,7 @@ function InterimButtonsView({
|
|
|
8162
7926
|
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
8163
7927
|
transition: "transform 0.1s",
|
|
8164
7928
|
position: "relative",
|
|
8165
|
-
opacity:
|
|
7929
|
+
opacity: 1,
|
|
8166
7930
|
...bStyles.cardButton
|
|
8167
7931
|
},
|
|
8168
7932
|
onMouseDown: (e) => {
|
|
@@ -8174,375 +7938,75 @@ function InterimButtonsView({
|
|
|
8174
7938
|
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CardButtonContentSlot, { content: cardButtonContent })
|
|
8175
7939
|
}
|
|
8176
7940
|
),
|
|
8177
|
-
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: {
|
|
8178
|
-
margin: "0.25rem 0",
|
|
8179
|
-
padding: "0.625rem 0.875rem",
|
|
8180
|
-
background: "#FEF2F2",
|
|
8181
|
-
border: "1px solid #FECACA",
|
|
8182
|
-
borderRadius: "8px",
|
|
8183
|
-
color: "#991B1B",
|
|
8184
|
-
fontSize: "0.85rem",
|
|
8185
|
-
fontWeight: 600,
|
|
8186
|
-
display: "flex",
|
|
8187
|
-
alignItems: "center",
|
|
8188
|
-
gap: "0.5rem",
|
|
8189
|
-
...bStyles.errorBanner
|
|
8190
|
-
}, children: [
|
|
8191
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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" }) }),
|
|
8192
|
-
errorMessage
|
|
8193
|
-
] }),
|
|
8194
7941
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("style", { children: `@keyframes flopay-interim-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
8195
7942
|
] });
|
|
8196
7943
|
}
|
|
8197
7944
|
|
|
8198
|
-
// src/
|
|
8199
|
-
var import_js5 = require("@flopay/js");
|
|
8200
|
-
var import_shared9 = require("@flopay/shared");
|
|
7945
|
+
// src/elements.tsx
|
|
8201
7946
|
var import_react11 = require("react");
|
|
8202
7947
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
lastName,
|
|
8226
|
-
chv,
|
|
8227
|
-
isProcessing: externalProcessing,
|
|
8228
|
-
error: externalError,
|
|
8229
|
-
onErrorChange,
|
|
8230
|
-
innerRef
|
|
8231
|
-
}) {
|
|
8232
|
-
const flopay = useFloPay();
|
|
8233
|
-
const paypalFlopay = usePayPalFloPay();
|
|
8234
|
-
const elements = useElements();
|
|
8235
|
-
const contextBillingUrl = useBillingApiUrl();
|
|
8236
|
-
const [processing, setProcessing] = (0, import_react11.useState)(false);
|
|
8237
|
-
const [error, setError] = (0, import_react11.useState)(null);
|
|
8238
|
-
const [is3DSActive, setIs3DSActive] = (0, import_react11.useState)(false);
|
|
8239
|
-
const displayError = externalError ?? error;
|
|
8240
|
-
const isSubmitting = externalProcessing ?? processing;
|
|
8241
|
-
const isSelfContained = !onTokenizedBody;
|
|
8242
|
-
const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
|
|
8243
|
-
const updateError = (0, import_react11.useCallback)(
|
|
8244
|
-
(err) => {
|
|
8245
|
-
setError(err);
|
|
8246
|
-
onErrorChange?.(err);
|
|
8247
|
-
},
|
|
8248
|
-
[onErrorChange]
|
|
8249
|
-
);
|
|
8250
|
-
const emitDecline = (0, import_react11.useCallback)(
|
|
8251
|
-
(input, overrides) => {
|
|
8252
|
-
onDecline?.(buildDeclineEvent("card", input, overrides));
|
|
8253
|
-
},
|
|
8254
|
-
[onDecline]
|
|
8255
|
-
);
|
|
8256
|
-
const processPaymentInternal = (0, import_react11.useCallback)(
|
|
8257
|
-
async (tokenizedBody, completionPaymentMethodId) => {
|
|
8258
|
-
setProcessing(true);
|
|
8259
|
-
updateError(null);
|
|
8260
|
-
const resolvedCompletionPaymentMethodId = completionPaymentMethodId ?? resolveTokenizedPaymentMethodId(tokenizedBody);
|
|
8261
|
-
const requestTokenizedBody = tokenizedBody.originalPaymentMethodId ? { ...tokenizedBody, originalPaymentMethodId: void 0 } : tokenizedBody;
|
|
8262
|
-
try {
|
|
8263
|
-
const api = new import_js5.PaymentAPI(baseUrl);
|
|
8264
|
-
const response = await api.processPayment(userId ?? "", {
|
|
8265
|
-
sessionId,
|
|
8266
|
-
nonce,
|
|
8267
|
-
tokenizedData: requestTokenizedBody,
|
|
8268
|
-
accountData: {
|
|
8269
|
-
userId: userId ?? "",
|
|
8270
|
-
email: email ?? "",
|
|
8271
|
-
firstName: firstName ?? "",
|
|
8272
|
-
lastName: lastName ?? ""
|
|
8273
|
-
},
|
|
8274
|
-
chv
|
|
8275
|
-
});
|
|
8276
|
-
if (response.ok) {
|
|
8277
|
-
onComplete?.({
|
|
8278
|
-
status: "succeeded",
|
|
8279
|
-
paymentIntentId: tokenizedBody.threeDSecureActionResultTokenId,
|
|
8280
|
-
paymentMethodId: resolvedCompletionPaymentMethodId,
|
|
8281
|
-
checkoutMethod: tokenizedBody.isPaypal ? "paypal" : "card"
|
|
8282
|
-
});
|
|
8283
|
-
return;
|
|
8284
|
-
}
|
|
8285
|
-
const json = await response.json().catch(() => null);
|
|
8286
|
-
if (json?.type === "3ds_required") {
|
|
8287
|
-
const secret = json["threeDSecureToken"];
|
|
8288
|
-
if (!flopay || !secret) {
|
|
8289
|
-
updateError("3DS authentication required but no token provided.");
|
|
8290
|
-
return;
|
|
8291
|
-
}
|
|
8292
|
-
setIs3DSActive(true);
|
|
8293
|
-
try {
|
|
8294
|
-
const retryFullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
|
|
8295
|
-
const result = await flopay.confirmPayment({
|
|
8296
|
-
clientSecret: secret,
|
|
8297
|
-
returnUrl: window.location.href,
|
|
8298
|
-
billingDetails: {
|
|
8299
|
-
...email ? { email } : {},
|
|
8300
|
-
...retryFullName ? { name: retryFullName } : {}
|
|
8301
|
-
}
|
|
8302
|
-
});
|
|
8303
|
-
if (result.error) {
|
|
8304
|
-
updateError(result.error.message);
|
|
8305
|
-
onError?.(result.error);
|
|
8306
|
-
emitDecline(result.error);
|
|
8307
|
-
return;
|
|
8308
|
-
}
|
|
8309
|
-
if (result.status === "succeeded" || result.status === "processing") {
|
|
8310
|
-
await processPaymentInternal({
|
|
8311
|
-
id: result.paymentIntentId,
|
|
8312
|
-
type: "card",
|
|
8313
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
8314
|
-
}, resolvedCompletionPaymentMethodId);
|
|
8315
|
-
}
|
|
8316
|
-
} finally {
|
|
8317
|
-
setIs3DSActive(false);
|
|
8318
|
-
}
|
|
8319
|
-
return;
|
|
8320
|
-
}
|
|
8321
|
-
if (json?.type === "paypal_redirect_required") {
|
|
8322
|
-
const secret = json["threeDSecureToken"] ?? json["clientSecret"];
|
|
8323
|
-
const pmId = json["paymentMethodId"];
|
|
8324
|
-
const paypalStripe = (paypalFlopay ?? flopay)?.getRawProvider();
|
|
8325
|
-
if (!paypalStripe || !secret) {
|
|
8326
|
-
const message = "PayPal is not available.";
|
|
8327
|
-
updateError(message);
|
|
8328
|
-
onDecline?.(buildDeclineEvent("paypal", message));
|
|
8329
|
-
return;
|
|
8330
|
-
}
|
|
8331
|
-
localStorage.setItem(WALLET_RESUME_KEY, JSON.stringify({
|
|
8332
|
-
sessionId,
|
|
8333
|
-
paymentIntentId: "",
|
|
8334
|
-
tokenType: "card",
|
|
8335
|
-
tokenId: pmId ?? "",
|
|
8336
|
-
status: "pending_redirect"
|
|
8337
|
-
}));
|
|
8338
|
-
const confirmParams = {
|
|
8339
|
-
return_url: window.location.href
|
|
8340
|
-
};
|
|
8341
|
-
if (pmId) confirmParams["payment_method"] = pmId;
|
|
8342
|
-
const { error: confirmError } = await paypalStripe.confirmPayment({
|
|
8343
|
-
clientSecret: secret,
|
|
8344
|
-
confirmParams,
|
|
8345
|
-
redirect: "if_required"
|
|
8346
|
-
});
|
|
8347
|
-
if (confirmError) {
|
|
8348
|
-
const message = confirmError.message ?? "PayPal payment failed.";
|
|
8349
|
-
updateError(message);
|
|
8350
|
-
onDecline?.(buildDeclineEvent("paypal", message, { code: confirmError.code }));
|
|
8351
|
-
}
|
|
8352
|
-
return;
|
|
8353
|
-
}
|
|
8354
|
-
const errorMessage = json?.message ?? "Payment failed. Please try again.";
|
|
8355
|
-
updateError(errorMessage);
|
|
8356
|
-
emitDecline(errorMessage, {
|
|
8357
|
-
code: json?.code,
|
|
8358
|
-
declineCode: json?.declineCode ?? json?.gatewayDeclineReason
|
|
8359
|
-
});
|
|
8360
|
-
} catch (err) {
|
|
8361
|
-
updateError(err instanceof Error ? err.message : "An unexpected error occurred");
|
|
8362
|
-
} finally {
|
|
8363
|
-
setProcessing(false);
|
|
8364
|
-
}
|
|
8365
|
-
},
|
|
8366
|
-
[baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
|
|
8367
|
-
);
|
|
8368
|
-
const dispatchTokenizedBody = (0, import_react11.useCallback)(
|
|
8369
|
-
(tokenizedBody) => {
|
|
8370
|
-
if (onTokenizedBody) {
|
|
8371
|
-
onTokenizedBody(tokenizedBody);
|
|
8372
|
-
} else {
|
|
8373
|
-
processPaymentInternal(tokenizedBody);
|
|
8374
|
-
}
|
|
8375
|
-
},
|
|
8376
|
-
[onTokenizedBody, processPaymentInternal]
|
|
8377
|
-
);
|
|
8378
|
-
(0, import_react11.useImperativeHandle)(innerRef, () => ({
|
|
8379
|
-
async handleNextAction(secret) {
|
|
8380
|
-
if (!flopay) return;
|
|
8381
|
-
setIs3DSActive(true);
|
|
8382
|
-
try {
|
|
8383
|
-
const result = await flopay.confirmPayment({
|
|
8384
|
-
clientSecret: secret,
|
|
8385
|
-
returnUrl: window.location.href
|
|
8386
|
-
});
|
|
8387
|
-
if (result.error) {
|
|
8388
|
-
updateError(result.error.message);
|
|
8389
|
-
onError?.(result.error);
|
|
8390
|
-
emitDecline(result.error);
|
|
8391
|
-
} else if (result.status === "succeeded" || result.status === "processing") {
|
|
8392
|
-
dispatchTokenizedBody({
|
|
8393
|
-
id: result.paymentIntentId,
|
|
8394
|
-
type: "card",
|
|
8395
|
-
threeDSecureActionResultTokenId: result.paymentIntentId
|
|
8396
|
-
});
|
|
8397
|
-
}
|
|
8398
|
-
} catch (err) {
|
|
8399
|
-
updateError(err instanceof Error ? err.message : "Payment authentication failed.");
|
|
8400
|
-
} finally {
|
|
8401
|
-
setIs3DSActive(false);
|
|
8402
|
-
}
|
|
8403
|
-
}
|
|
8404
|
-
}), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
|
|
8405
|
-
(0, import_react11.useEffect)(() => {
|
|
8406
|
-
if (typeof window === "undefined") return;
|
|
8407
|
-
const stored = localStorage.getItem(WALLET_RESUME_KEY);
|
|
8408
|
-
if (!stored) return;
|
|
8409
|
-
try {
|
|
8410
|
-
const payload = JSON.parse(stored);
|
|
8411
|
-
if (payload.sessionId === sessionId) {
|
|
8412
|
-
localStorage.removeItem(WALLET_RESUME_KEY);
|
|
8413
|
-
dispatchTokenizedBody({
|
|
8414
|
-
id: payload.tokenId,
|
|
8415
|
-
type: payload.tokenType,
|
|
8416
|
-
threeDSecureActionResultTokenId: payload.paymentIntentId
|
|
8417
|
-
});
|
|
8418
|
-
}
|
|
8419
|
-
} catch {
|
|
8420
|
-
localStorage.removeItem(WALLET_RESUME_KEY);
|
|
8421
|
-
}
|
|
8422
|
-
}, [sessionId, dispatchTokenizedBody]);
|
|
8423
|
-
const handleSubmit = (0, import_react11.useCallback)(
|
|
8424
|
-
async (e) => {
|
|
8425
|
-
e.preventDefault();
|
|
8426
|
-
if (!flopay || !elements || isSubmitting) return;
|
|
8427
|
-
setProcessing(true);
|
|
8428
|
-
updateError(null);
|
|
8429
|
-
let handedOff = false;
|
|
8430
|
-
try {
|
|
8431
|
-
const submitResult = await flopay.submitElements();
|
|
8432
|
-
if (submitResult.error) {
|
|
8433
|
-
updateError(submitResult.error.message);
|
|
8434
|
-
onError?.(submitResult.error);
|
|
8435
|
-
return;
|
|
8436
|
-
}
|
|
8437
|
-
const fullName = `${firstName ?? ""} ${lastName ?? ""}`.trim();
|
|
8438
|
-
const pmResult = await flopay.createPaymentMethod({
|
|
8439
|
-
...email ? { email } : {},
|
|
8440
|
-
...fullName ? { name: fullName } : {}
|
|
8441
|
-
});
|
|
8442
|
-
if (pmResult.error || !pmResult.paymentMethodId) {
|
|
8443
|
-
updateError(pmResult.error?.message ?? "Failed to create payment method.");
|
|
8444
|
-
return;
|
|
8445
|
-
}
|
|
8446
|
-
if (!sessionId || !email) {
|
|
8447
|
-
throw new import_shared9.FloPayError("Missing sessionId or email", "validation_error");
|
|
8448
|
-
}
|
|
8449
|
-
const intentHeaders = { "Content-Type": "application/json" };
|
|
8450
|
-
if (nonce) intentHeaders["x-checkout-session-token"] = nonce;
|
|
8451
|
-
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
8452
|
-
method: "POST",
|
|
8453
|
-
headers: intentHeaders,
|
|
8454
|
-
body: JSON.stringify({
|
|
8455
|
-
sessionId,
|
|
8456
|
-
email,
|
|
8457
|
-
paymentMethodType: pmResult.paymentMethodId,
|
|
8458
|
-
isPaypal: false
|
|
8459
|
-
})
|
|
8460
|
-
});
|
|
8461
|
-
if (!intentResponse.ok) {
|
|
8462
|
-
const intentError = await buildFloPayApiErrorFromResponse(
|
|
8463
|
-
intentResponse,
|
|
8464
|
-
"Failed to create payment intent"
|
|
8465
|
-
);
|
|
8466
|
-
updateError(intentError.message);
|
|
8467
|
-
onError?.(intentError);
|
|
8468
|
-
emitDecline(intentError);
|
|
8469
|
-
return;
|
|
8470
|
-
}
|
|
8471
|
-
const intentJson = await intentResponse.json();
|
|
8472
|
-
const intentClientSecret = intentJson.data?.id;
|
|
8473
|
-
if (!intentClientSecret) throw new import_shared9.FloPayError("No client_secret in payment intent response", "api_error");
|
|
8474
|
-
const confirmResult = await flopay.confirmCardPayment({
|
|
8475
|
-
clientSecret: intentClientSecret,
|
|
8476
|
-
paymentMethodId: pmResult.paymentMethodId
|
|
8477
|
-
});
|
|
8478
|
-
if (confirmResult.error) {
|
|
8479
|
-
updateError(confirmResult.error.message);
|
|
8480
|
-
onError?.(confirmResult.error);
|
|
8481
|
-
emitDecline(confirmResult.error);
|
|
8482
|
-
return;
|
|
7948
|
+
function createElementComponent(elementType, displayName) {
|
|
7949
|
+
function ElementComponent({
|
|
7950
|
+
className,
|
|
7951
|
+
id,
|
|
7952
|
+
style,
|
|
7953
|
+
options,
|
|
7954
|
+
onChange,
|
|
7955
|
+
onReady,
|
|
7956
|
+
onFocus,
|
|
7957
|
+
onBlur,
|
|
7958
|
+
onEscape
|
|
7959
|
+
}) {
|
|
7960
|
+
const containerRef = (0, import_react11.useRef)(null);
|
|
7961
|
+
const elementRef = (0, import_react11.useRef)(null);
|
|
7962
|
+
const { elements, reportInteractive } = (0, import_react11.useContext)(FloPayContext);
|
|
7963
|
+
(0, import_react11.useEffect)(() => {
|
|
7964
|
+
if (!elements || !containerRef.current) return;
|
|
7965
|
+
let mounted = true;
|
|
7966
|
+
(async () => {
|
|
7967
|
+
let element = elements.getElement(elementType);
|
|
7968
|
+
if (!element) {
|
|
7969
|
+
element = await elements.create(elementType, options);
|
|
8483
7970
|
}
|
|
8484
|
-
|
|
8485
|
-
const confirmedPaymentIntent = await retrievePaymentIntentFromProvider(
|
|
8486
|
-
rawProvider,
|
|
8487
|
-
intentClientSecret
|
|
8488
|
-
);
|
|
8489
|
-
const paymentIntentId = confirmResult.paymentIntentId ?? confirmedPaymentIntent?.id;
|
|
8490
|
-
const paymentMethodId = confirmResult.paymentMethodId ?? resolvePaymentIntentPaymentMethodId(confirmedPaymentIntent) ?? pmResult.paymentMethodId;
|
|
8491
|
-
if (!paymentIntentId) {
|
|
8492
|
-
const error2 = new import_shared9.FloPayError("No payment intent returned after confirmation.", "api_error");
|
|
8493
|
-
updateError(error2.message);
|
|
8494
|
-
onError?.(error2);
|
|
7971
|
+
if (!mounted || !containerRef.current) {
|
|
8495
7972
|
return;
|
|
8496
7973
|
}
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
7974
|
+
element.mount(containerRef.current);
|
|
7975
|
+
elementRef.current = element;
|
|
7976
|
+
if (onChange) element.on("change", onChange);
|
|
7977
|
+
element.on("ready", () => {
|
|
7978
|
+
reportInteractive?.();
|
|
7979
|
+
onReady?.();
|
|
8503
7980
|
});
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
(is3DSActive || isSubmitting) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-testid": "flopay-overlay", style: {
|
|
8517
|
-
position: "absolute",
|
|
8518
|
-
inset: 0,
|
|
8519
|
-
background: "rgba(255,255,255,0.7)",
|
|
8520
|
-
display: "flex",
|
|
8521
|
-
alignItems: "center",
|
|
8522
|
-
justifyContent: "center",
|
|
8523
|
-
zIndex: 10
|
|
8524
|
-
}, children: is3DSActive ? "Verifying payment..." : "Processing..." }),
|
|
8525
|
-
!isReady && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-testid": "flopay-loading", "aria-busy": "true", children: "Loading payment form..." }),
|
|
8526
|
-
isReady && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
|
|
8527
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PaymentElement, { options: { layout } }),
|
|
8528
|
-
showAddress && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AddressElement, { options: { mode: showAddress === true ? "billing" : showAddress } }),
|
|
8529
|
-
displayError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { role: "alert", "data-testid": "flopay-error", style: { color: "red", margin: "0.75rem 0" }, children: displayError }),
|
|
8530
|
-
children ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
8531
|
-
"button",
|
|
8532
|
-
{
|
|
8533
|
-
type: "submit",
|
|
8534
|
-
disabled: isSubmitting || !isReady,
|
|
8535
|
-
"data-testid": "flopay-submit",
|
|
8536
|
-
children: isSubmitting ? "Processing..." : submitLabel
|
|
7981
|
+
if (onFocus) element.on("focus", onFocus);
|
|
7982
|
+
if (onBlur) element.on("blur", onBlur);
|
|
7983
|
+
if (onEscape) element.on("escape", onEscape);
|
|
7984
|
+
})();
|
|
7985
|
+
return () => {
|
|
7986
|
+
mounted = false;
|
|
7987
|
+
if (elementRef.current) {
|
|
7988
|
+
try {
|
|
7989
|
+
elementRef.current.unmount();
|
|
7990
|
+
} catch {
|
|
7991
|
+
}
|
|
7992
|
+
elementRef.current = null;
|
|
8537
7993
|
}
|
|
8538
|
-
|
|
8539
|
-
]
|
|
8540
|
-
|
|
7994
|
+
};
|
|
7995
|
+
}, [elements, reportInteractive]);
|
|
7996
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: containerRef, className, id, style });
|
|
7997
|
+
}
|
|
7998
|
+
ElementComponent.displayName = displayName;
|
|
7999
|
+
return ElementComponent;
|
|
8541
8000
|
}
|
|
8001
|
+
var PaymentElement2 = createElementComponent(
|
|
8002
|
+
"payment",
|
|
8003
|
+
"PaymentElement"
|
|
8004
|
+
);
|
|
8005
|
+
var AddressElement = createElementComponent("address", "AddressElement");
|
|
8542
8006
|
|
|
8543
8007
|
// src/paypal-button.tsx
|
|
8544
|
-
var
|
|
8545
|
-
var
|
|
8008
|
+
var import_js5 = require("@flopay/js");
|
|
8009
|
+
var import_shared9 = require("@flopay/shared");
|
|
8546
8010
|
var import_react12 = require("react");
|
|
8547
8011
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
8548
8012
|
function PayPalButton({
|
|
@@ -8569,7 +8033,7 @@ function PayPalButton({
|
|
|
8569
8033
|
const processPaymentInternal = (0, import_react12.useCallback)(
|
|
8570
8034
|
async (tokenizedBody) => {
|
|
8571
8035
|
try {
|
|
8572
|
-
const api = new
|
|
8036
|
+
const api = new import_js5.PaymentAPI(baseUrl);
|
|
8573
8037
|
const response = await api.processPayment(userId ?? "", {
|
|
8574
8038
|
sessionId,
|
|
8575
8039
|
nonce,
|
|
@@ -8658,7 +8122,7 @@ function PayPalButton({
|
|
|
8658
8122
|
setSubmitting(true);
|
|
8659
8123
|
onErrorChange?.(null);
|
|
8660
8124
|
if (!sessionId || !email) {
|
|
8661
|
-
throw new
|
|
8125
|
+
throw new import_shared9.FloPayError("Missing sessionId or email for PayPal payment", "validation_error");
|
|
8662
8126
|
}
|
|
8663
8127
|
const result = await flopay.confirmPayPalPayment({
|
|
8664
8128
|
billingApiUrl: baseUrl,
|
|
@@ -8733,18 +8197,18 @@ function PayPalButton({
|
|
|
8733
8197
|
|
|
8734
8198
|
// src/automatic-payment-button.tsx
|
|
8735
8199
|
var import_react13 = require("react");
|
|
8736
|
-
var
|
|
8737
|
-
var
|
|
8200
|
+
var import_js6 = require("@flopay/js");
|
|
8201
|
+
var import_shared10 = require("@flopay/shared");
|
|
8738
8202
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
8739
8203
|
var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT3 = 44;
|
|
8740
8204
|
function sleep2(ms) {
|
|
8741
8205
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8742
8206
|
}
|
|
8743
8207
|
function coerceError(err, fallbackMessage) {
|
|
8744
|
-
if (err instanceof
|
|
8208
|
+
if (err instanceof import_shared10.FloPayError) {
|
|
8745
8209
|
return err;
|
|
8746
8210
|
}
|
|
8747
|
-
return new
|
|
8211
|
+
return new import_shared10.FloPayError(
|
|
8748
8212
|
err instanceof Error ? err.message : fallbackMessage,
|
|
8749
8213
|
"api_error"
|
|
8750
8214
|
);
|
|
@@ -8816,7 +8280,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8816
8280
|
...buttonProps
|
|
8817
8281
|
}) {
|
|
8818
8282
|
const resolvedBillingUrl = (0, import_react13.useMemo)(
|
|
8819
|
-
() => (0,
|
|
8283
|
+
() => (0, import_shared10.resolveBillingApiUrl)(billingApiUrl),
|
|
8820
8284
|
[billingApiUrl]
|
|
8821
8285
|
);
|
|
8822
8286
|
const createSessionDraft = (0, import_react13.useMemo)(
|
|
@@ -8917,7 +8381,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8917
8381
|
const processResolvedSession = (0, import_react13.useCallback)(async (apiResult, resolvedSessionId, options) => {
|
|
8918
8382
|
const session = apiResult.data.session ?? null;
|
|
8919
8383
|
if (!session) {
|
|
8920
|
-
throw new
|
|
8384
|
+
throw new import_shared10.FloPayError("No session data returned", "api_error");
|
|
8921
8385
|
}
|
|
8922
8386
|
const effectiveNonce = session.clientSecret || nonce;
|
|
8923
8387
|
if (session.status === "complete") {
|
|
@@ -8930,19 +8394,19 @@ function FloPayAutomaticPaymentButton({
|
|
|
8930
8394
|
return;
|
|
8931
8395
|
}
|
|
8932
8396
|
if (session.status === "expired") {
|
|
8933
|
-
throw new
|
|
8397
|
+
throw new import_shared10.FloPayError("Checkout session has expired.", "api_error", {
|
|
8934
8398
|
code: "checkout_session_expired"
|
|
8935
8399
|
});
|
|
8936
8400
|
}
|
|
8937
8401
|
try {
|
|
8938
8402
|
if (apiResult.autoProcessingPending) {
|
|
8939
|
-
const api = new
|
|
8403
|
+
const api = new import_js6.PaymentAPI(resolvedBillingUrl);
|
|
8940
8404
|
const completed = await api.waitForCheckoutSessionCompletion(apiResult.autoProcessingPending.sessionId, {
|
|
8941
8405
|
initialDelayMs: apiResult.autoProcessingPending.retryAfterMs
|
|
8942
8406
|
});
|
|
8943
8407
|
const completedSession = completed.data.session;
|
|
8944
8408
|
if (!completedSession || completedSession.status !== "complete") {
|
|
8945
|
-
throw new
|
|
8409
|
+
throw new import_shared10.FloPayError("Automatic payment failed. Please try again.", "api_error", {
|
|
8946
8410
|
code: completedSession?.status === "expired" ? "checkout_session_expired" : "checkout_processing_timeout"
|
|
8947
8411
|
});
|
|
8948
8412
|
}
|
|
@@ -8985,7 +8449,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8985
8449
|
}
|
|
8986
8450
|
);
|
|
8987
8451
|
}
|
|
8988
|
-
const api = new
|
|
8452
|
+
const api = new import_js6.PaymentAPI(resolvedBillingUrl);
|
|
8989
8453
|
const completed = await api.waitForCheckoutSessionCompletion(resolvedSessionId, {
|
|
8990
8454
|
initialDelayMs: 0,
|
|
8991
8455
|
nonce: effectiveNonce
|
|
@@ -9077,7 +8541,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
9077
8541
|
}
|
|
9078
8542
|
setFallbackSession(null);
|
|
9079
8543
|
if (sessionId && createSessionDraft) {
|
|
9080
|
-
const error = new
|
|
8544
|
+
const error = new import_shared10.FloPayError(
|
|
9081
8545
|
"Provide either sessionId or create-session props, not both.",
|
|
9082
8546
|
"validation_error"
|
|
9083
8547
|
);
|
|
@@ -9089,7 +8553,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
9089
8553
|
return;
|
|
9090
8554
|
}
|
|
9091
8555
|
if (!sessionId && !createSessionDraft) {
|
|
9092
|
-
const error = new
|
|
8556
|
+
const error = new import_shared10.FloPayError(
|
|
9093
8557
|
"Provide a sessionId or the props required to create an automatic payment session.",
|
|
9094
8558
|
"validation_error"
|
|
9095
8559
|
);
|
|
@@ -9104,7 +8568,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
9104
8568
|
setOverlayError(null);
|
|
9105
8569
|
setOverlayStatus("processing");
|
|
9106
8570
|
try {
|
|
9107
|
-
const api = new
|
|
8571
|
+
const api = new import_js6.PaymentAPI(resolvedBillingUrl);
|
|
9108
8572
|
if (sessionId) {
|
|
9109
8573
|
const result = await api.getUnifiedCheckoutSession(sessionId, nonce);
|
|
9110
8574
|
await processResolvedSession(result, sessionId);
|
|
@@ -9118,7 +8582,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
9118
8582
|
fromCreateSession: true
|
|
9119
8583
|
});
|
|
9120
8584
|
} catch (err) {
|
|
9121
|
-
if (err instanceof
|
|
8585
|
+
if (err instanceof import_shared10.FloPayError && err.code === "session_auto_completed") {
|
|
9122
8586
|
await showSuccess({
|
|
9123
8587
|
result: { status: "succeeded" },
|
|
9124
8588
|
session: null,
|
|
@@ -9168,9 +8632,9 @@ function FloPayAutomaticPaymentButton({
|
|
|
9168
8632
|
const handleFallbackDecline = (0, import_react13.useCallback)((decline) => {
|
|
9169
8633
|
onDeclineRef.current?.(decline);
|
|
9170
8634
|
}, []);
|
|
9171
|
-
const themeBundle = (0, import_react13.useMemo)(() => (0,
|
|
8635
|
+
const themeBundle = (0, import_react13.useMemo)(() => (0, import_shared10.resolveTheme)(theme), [theme]);
|
|
9172
8636
|
const bStyles = (0, import_react13.useMemo)(() => {
|
|
9173
|
-
const base = themeBundle?.buttonsLayout ?? (0,
|
|
8637
|
+
const base = themeBundle?.buttonsLayout ?? (0, import_shared10.resolveButtonsLayoutTheme)(buttonsTheme);
|
|
9174
8638
|
if (!stylesOverride) return base;
|
|
9175
8639
|
return {
|
|
9176
8640
|
...base,
|
|
@@ -9336,11 +8800,6 @@ function FloPayAutomaticPaymentButton({
|
|
|
9336
8800
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9337
8801
|
0 && (module.exports = {
|
|
9338
8802
|
AddressElement,
|
|
9339
|
-
CardCvcElement,
|
|
9340
|
-
CardElement,
|
|
9341
|
-
CardExpiryElement,
|
|
9342
|
-
CardNumberElement,
|
|
9343
|
-
CheckoutForm,
|
|
9344
8803
|
DirectPayPalButton,
|
|
9345
8804
|
FloPayAutomaticPaymentButton,
|
|
9346
8805
|
FloPayCheckout,
|