@flopay/react 1.3.4 → 1.4.0
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 +18 -0
- package/dist/index.cjs +826 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -10
- package/dist/index.d.ts +7 -10
- package/dist/index.mjs +976 -237
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/provider.tsx
|
|
2
|
-
import { useEffect, useState, useMemo } from "react";
|
|
2
|
+
import { useCallback, useEffect, useState, useMemo, useRef } from "react";
|
|
3
3
|
import { resolveBillingApiUrl } from "@flopay/shared";
|
|
4
4
|
|
|
5
5
|
// src/context.ts
|
|
@@ -16,6 +16,59 @@ var CheckoutContext = createContext({
|
|
|
16
16
|
error: null
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
// src/telemetry-bridge.ts
|
|
20
|
+
var FLOPAY_TELEMETRY_BRIDGE = /* @__PURE__ */ Symbol.for("@flopay/js.telemetry.bridge.v1");
|
|
21
|
+
var TELEMETRY_REPORTER_FACTORY = /* @__PURE__ */ Symbol.for("@flopay/js.telemetry.reporter-factory.v1");
|
|
22
|
+
function noopTelemetryBridge() {
|
|
23
|
+
return {
|
|
24
|
+
error: () => {
|
|
25
|
+
},
|
|
26
|
+
log: () => {
|
|
27
|
+
},
|
|
28
|
+
performance: () => {
|
|
29
|
+
},
|
|
30
|
+
terminal: () => {
|
|
31
|
+
},
|
|
32
|
+
now: () => globalThis.performance?.now() ?? 0,
|
|
33
|
+
elapsed: (startedAt) => Math.max(0, (globalThis.performance?.now() ?? startedAt) - startedAt),
|
|
34
|
+
setCheckoutContext: () => {
|
|
35
|
+
},
|
|
36
|
+
beginCheckout: () => globalThis.performance?.now() ?? 0,
|
|
37
|
+
disable: () => {
|
|
38
|
+
},
|
|
39
|
+
flush: async () => {
|
|
40
|
+
},
|
|
41
|
+
destroy: () => {
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function normalizeTelemetryBridge(source) {
|
|
46
|
+
const fallback = noopTelemetryBridge();
|
|
47
|
+
const bind = (candidate, defaultValue) => candidate ? candidate.bind(source) : defaultValue;
|
|
48
|
+
const now = bind(source.now, fallback.now);
|
|
49
|
+
return {
|
|
50
|
+
error: bind(source.error, fallback.error),
|
|
51
|
+
log: bind(source.log, fallback.log),
|
|
52
|
+
performance: bind(source.performance, fallback.performance),
|
|
53
|
+
terminal: bind(source.terminal, fallback.terminal),
|
|
54
|
+
now,
|
|
55
|
+
elapsed: source.elapsed ? source.elapsed.bind(source) : (startedAt) => Math.max(0, now() - startedAt),
|
|
56
|
+
setCheckoutContext: bind(source.setCheckoutContext, fallback.setCheckoutContext),
|
|
57
|
+
beginCheckout: bind(source.beginCheckout, fallback.beginCheckout),
|
|
58
|
+
disable: bind(source.disable, fallback.disable),
|
|
59
|
+
flush: bind(source.flush, fallback.flush),
|
|
60
|
+
destroy: bind(source.destroy, fallback.destroy)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function createTelemetryBridge(options) {
|
|
64
|
+
const factory = globalThis[TELEMETRY_REPORTER_FACTORY];
|
|
65
|
+
return normalizeTelemetryBridge(factory?.(options) ?? {});
|
|
66
|
+
}
|
|
67
|
+
function getFloPayTelemetryBridge(floPay) {
|
|
68
|
+
if (!floPay) return void 0;
|
|
69
|
+
return floPay[FLOPAY_TELEMETRY_BRIDGE];
|
|
70
|
+
}
|
|
71
|
+
|
|
19
72
|
// src/provider.tsx
|
|
20
73
|
import { jsx } from "react/jsx-runtime";
|
|
21
74
|
function FloPayProvider({
|
|
@@ -31,6 +84,18 @@ function FloPayProvider({
|
|
|
31
84
|
paypalFloPayProp instanceof Promise || !paypalFloPayProp ? null : paypalFloPayProp
|
|
32
85
|
);
|
|
33
86
|
const [elements, setElements] = useState(null);
|
|
87
|
+
const mountedAt = useRef(null);
|
|
88
|
+
const renderedElements = useRef(null);
|
|
89
|
+
const interactiveElements = useRef(null);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!flopay) return;
|
|
92
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
93
|
+
mountedAt.current = telemetry?.beginCheckout() ?? telemetry?.now() ?? 0;
|
|
94
|
+
telemetry?.log({ name: "checkout.mount", stage: "checkout_mount" });
|
|
95
|
+
return () => {
|
|
96
|
+
telemetry?.log({ name: "checkout.unmount", stage: "unmount" });
|
|
97
|
+
};
|
|
98
|
+
}, [flopay]);
|
|
34
99
|
useEffect(() => {
|
|
35
100
|
let cancelled = false;
|
|
36
101
|
if (floPayProp instanceof Promise) {
|
|
@@ -91,18 +156,46 @@ function FloPayProvider({
|
|
|
91
156
|
options?.paymentMethodCreation,
|
|
92
157
|
options?.setupFutureUsage
|
|
93
158
|
]);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (!flopay || !elements || renderedElements.current === elements) return;
|
|
161
|
+
renderedElements.current = elements;
|
|
162
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
163
|
+
telemetry?.log({ name: "checkout.rendered", stage: "checkout_render" });
|
|
164
|
+
telemetry?.performance({
|
|
165
|
+
stage: "checkout_render",
|
|
166
|
+
durationMs: telemetry.elapsed(mountedAt.current ?? 0),
|
|
167
|
+
durationMode: "machine"
|
|
168
|
+
});
|
|
169
|
+
}, [elements, flopay]);
|
|
170
|
+
const reportInteractive = useCallback(() => {
|
|
171
|
+
if (!flopay || !elements || interactiveElements.current === elements) return;
|
|
172
|
+
interactiveElements.current = elements;
|
|
173
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
174
|
+
telemetry?.log({ name: "checkout.interactive", stage: "checkout_interactive" });
|
|
175
|
+
telemetry?.performance({
|
|
176
|
+
stage: "checkout_interactive",
|
|
177
|
+
durationMs: telemetry.elapsed(mountedAt.current ?? 0),
|
|
178
|
+
durationMode: "machine"
|
|
179
|
+
});
|
|
180
|
+
}, [elements, flopay]);
|
|
94
181
|
const resolvedBillingApiUrl = resolveBillingApiUrl(options?.billingApiUrl);
|
|
95
182
|
const value = useMemo(
|
|
96
|
-
() => ({
|
|
97
|
-
|
|
183
|
+
() => ({
|
|
184
|
+
flopay,
|
|
185
|
+
paypalFlopay,
|
|
186
|
+
elements,
|
|
187
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
188
|
+
reportInteractive
|
|
189
|
+
}),
|
|
190
|
+
[flopay, paypalFlopay, elements, resolvedBillingApiUrl, reportInteractive]
|
|
98
191
|
);
|
|
99
192
|
return /* @__PURE__ */ jsx(FloPayContext.Provider, { value, children });
|
|
100
193
|
}
|
|
101
194
|
|
|
102
195
|
// src/flopay-checkout.tsx
|
|
103
|
-
import React8, { useCallback as
|
|
196
|
+
import React8, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
|
|
104
197
|
import { PaymentAPI as PaymentAPI4 } from "@flopay/js";
|
|
105
|
-
import { SDK_VERSION, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2, resolveTheme as resolveTheme2 } from "@flopay/shared";
|
|
198
|
+
import { SDK_VERSION as SDK_VERSION2, FloPayError as FloPayError5, resolveBillingApiUrl as resolveBillingApiUrl3, buildCheckoutDisplayData, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme2, resolveTheme as resolveTheme2 } from "@flopay/shared";
|
|
106
199
|
|
|
107
200
|
// src/card-button-content.tsx
|
|
108
201
|
import "react";
|
|
@@ -172,7 +265,7 @@ function TitleContentSlot({
|
|
|
172
265
|
}
|
|
173
266
|
|
|
174
267
|
// src/elements.tsx
|
|
175
|
-
import { useEffect as useEffect2, useRef, useContext } from "react";
|
|
268
|
+
import { useEffect as useEffect2, useRef as useRef2, useContext } from "react";
|
|
176
269
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
177
270
|
function createElementComponent(elementType, displayName) {
|
|
178
271
|
function ElementComponent({
|
|
@@ -186,9 +279,9 @@ function createElementComponent(elementType, displayName) {
|
|
|
186
279
|
onBlur,
|
|
187
280
|
onEscape
|
|
188
281
|
}) {
|
|
189
|
-
const containerRef =
|
|
190
|
-
const elementRef =
|
|
191
|
-
const { elements } = useContext(FloPayContext);
|
|
282
|
+
const containerRef = useRef2(null);
|
|
283
|
+
const elementRef = useRef2(null);
|
|
284
|
+
const { elements, reportInteractive } = useContext(FloPayContext);
|
|
192
285
|
useEffect2(() => {
|
|
193
286
|
if (!elements || !containerRef.current) return;
|
|
194
287
|
let mounted = true;
|
|
@@ -203,7 +296,10 @@ function createElementComponent(elementType, displayName) {
|
|
|
203
296
|
element.mount(containerRef.current);
|
|
204
297
|
elementRef.current = element;
|
|
205
298
|
if (onChange) element.on("change", onChange);
|
|
206
|
-
|
|
299
|
+
element.on("ready", () => {
|
|
300
|
+
reportInteractive?.();
|
|
301
|
+
onReady?.();
|
|
302
|
+
});
|
|
207
303
|
if (onFocus) element.on("focus", onFocus);
|
|
208
304
|
if (onBlur) element.on("blur", onBlur);
|
|
209
305
|
if (onEscape) element.on("escape", onEscape);
|
|
@@ -218,7 +314,7 @@ function createElementComponent(elementType, displayName) {
|
|
|
218
314
|
elementRef.current = null;
|
|
219
315
|
}
|
|
220
316
|
};
|
|
221
|
-
}, [elements]);
|
|
317
|
+
}, [elements, reportInteractive]);
|
|
222
318
|
return /* @__PURE__ */ jsx3("div", { ref: containerRef, className, id, style });
|
|
223
319
|
}
|
|
224
320
|
ElementComponent.displayName = displayName;
|
|
@@ -262,7 +358,7 @@ import {
|
|
|
262
358
|
import { PaymentAPI as PaymentAPI2 } from "@flopay/js";
|
|
263
359
|
|
|
264
360
|
// src/vault-card-fields.tsx
|
|
265
|
-
import { useEffect as useEffect3, useRef as
|
|
361
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
266
362
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
267
363
|
function VaultCardFields({
|
|
268
364
|
capture,
|
|
@@ -275,14 +371,14 @@ function VaultCardFields({
|
|
|
275
371
|
onError,
|
|
276
372
|
onValidation
|
|
277
373
|
}) {
|
|
278
|
-
const containerRef =
|
|
279
|
-
const onReadyRef =
|
|
280
|
-
const onErrorRef =
|
|
281
|
-
const onValidationRef =
|
|
374
|
+
const containerRef = useRef3(null);
|
|
375
|
+
const onReadyRef = useRef3(onReady);
|
|
376
|
+
const onErrorRef = useRef3(onError);
|
|
377
|
+
const onValidationRef = useRef3(onValidation);
|
|
282
378
|
onReadyRef.current = onReady;
|
|
283
379
|
onErrorRef.current = onError;
|
|
284
380
|
onValidationRef.current = onValidation;
|
|
285
|
-
const themeRef =
|
|
381
|
+
const themeRef = useRef3(theme);
|
|
286
382
|
themeRef.current = theme;
|
|
287
383
|
useEffect3(() => {
|
|
288
384
|
const el = containerRef.current;
|
|
@@ -333,7 +429,7 @@ function VaultCardFields({
|
|
|
333
429
|
}
|
|
334
430
|
|
|
335
431
|
// src/split-card-form.tsx
|
|
336
|
-
import React7, { forwardRef, useCallback as
|
|
432
|
+
import React7, { forwardRef, useCallback as useCallback3, useContext as useContext3, useEffect as useEffect5, useImperativeHandle, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
|
|
337
433
|
|
|
338
434
|
// src/hooks.ts
|
|
339
435
|
import { useContext as useContext2 } from "react";
|
|
@@ -770,10 +866,23 @@ function isInAppBrowser(userAgent) {
|
|
|
770
866
|
}
|
|
771
867
|
|
|
772
868
|
// src/direct-paypal-button.tsx
|
|
773
|
-
import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useRef as
|
|
869
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState2 } from "react";
|
|
774
870
|
import { loadScript } from "@paypal/paypal-js";
|
|
775
871
|
import { PaymentAPI } from "@flopay/js";
|
|
776
|
-
import { FloPayError as FloPayError2, normalizeGatewayEnvironment } from "@flopay/shared";
|
|
872
|
+
import { FloPayError as FloPayError2, SDK_VERSION, normalizeGatewayEnvironment } from "@flopay/shared";
|
|
873
|
+
|
|
874
|
+
// src/merchant-callback.ts
|
|
875
|
+
function invokeMerchantCallback(callback) {
|
|
876
|
+
if (!callback) return;
|
|
877
|
+
const reportFailure = (error) => {
|
|
878
|
+
console.error("[FloPay] Merchant callback failed; checkout continued.", error);
|
|
879
|
+
};
|
|
880
|
+
try {
|
|
881
|
+
void Promise.resolve(callback()).catch(reportFailure);
|
|
882
|
+
} catch (error) {
|
|
883
|
+
reportFailure(error);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
777
886
|
|
|
778
887
|
// src/external-method-recovery.ts
|
|
779
888
|
var EXTERNAL_METHOD_CALLBACK_GRACE_MS = 600;
|
|
@@ -831,7 +940,7 @@ var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
|
|
|
831
940
|
function buildDirectPayPalRecoveryMessage(popupBlocked) {
|
|
832
941
|
return popupBlocked ? `${DIRECT_PAYPAL_RECOVERY_MESSAGE} Allow pop-ups for this site, then try again.` : DIRECT_PAYPAL_RECOVERY_MESSAGE;
|
|
833
942
|
}
|
|
834
|
-
function
|
|
943
|
+
function DirectPayPalButtonImplementation({
|
|
835
944
|
sessionId,
|
|
836
945
|
nonce,
|
|
837
946
|
billingApiUrl,
|
|
@@ -851,14 +960,62 @@ function DirectPayPalButton({
|
|
|
851
960
|
runBeforeButtonClick,
|
|
852
961
|
session,
|
|
853
962
|
existingOrderId,
|
|
963
|
+
telemetry,
|
|
964
|
+
telemetryContext,
|
|
854
965
|
debug = false
|
|
855
966
|
}) {
|
|
856
|
-
const
|
|
857
|
-
const
|
|
858
|
-
|
|
967
|
+
const flopay = useFloPay();
|
|
968
|
+
const standaloneTelemetry = useMemo2(() => {
|
|
969
|
+
if (flopay) return null;
|
|
970
|
+
const reporter = createTelemetryBridge({
|
|
971
|
+
billingApiUrl,
|
|
972
|
+
sdkPackage: "@flopay/react",
|
|
973
|
+
sdkVersion: SDK_VERSION,
|
|
974
|
+
enabled: telemetry !== false
|
|
975
|
+
});
|
|
976
|
+
reporter.setCheckoutContext(telemetryContext ?? {});
|
|
977
|
+
reporter.beginCheckout(telemetryContext ?? {});
|
|
978
|
+
return reporter;
|
|
979
|
+
}, [
|
|
980
|
+
billingApiUrl,
|
|
981
|
+
flopay,
|
|
982
|
+
telemetry,
|
|
983
|
+
telemetryContext?.checkoutMode,
|
|
984
|
+
telemetryContext?.layout
|
|
985
|
+
]);
|
|
986
|
+
const floPayTelemetry = useMemo2(() => getFloPayTelemetryBridge(flopay), [flopay]);
|
|
987
|
+
useEffect4(() => () => {
|
|
988
|
+
if (!standaloneTelemetry) return;
|
|
989
|
+
void standaloneTelemetry.flush().catch(() => {
|
|
990
|
+
}).finally(() => standaloneTelemetry.destroy());
|
|
991
|
+
}, [standaloneTelemetry]);
|
|
992
|
+
const telemetrySource = useMemo2(() => ({
|
|
993
|
+
error: (input) => {
|
|
994
|
+
if (floPayTelemetry) floPayTelemetry.error(input);
|
|
995
|
+
else standaloneTelemetry?.error(input);
|
|
996
|
+
},
|
|
997
|
+
log: (input) => {
|
|
998
|
+
if (floPayTelemetry) floPayTelemetry.log(input);
|
|
999
|
+
else standaloneTelemetry?.log(input);
|
|
1000
|
+
},
|
|
1001
|
+
performance: (input) => {
|
|
1002
|
+
if (floPayTelemetry) floPayTelemetry.performance(input);
|
|
1003
|
+
else standaloneTelemetry?.performance(input);
|
|
1004
|
+
},
|
|
1005
|
+
terminal: (input) => {
|
|
1006
|
+
if (floPayTelemetry) floPayTelemetry.terminal(input);
|
|
1007
|
+
else standaloneTelemetry?.terminal(input);
|
|
1008
|
+
},
|
|
1009
|
+
startTiming: () => floPayTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
|
|
1010
|
+
elapsed: (startedAt) => floPayTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
|
|
1011
|
+
}), [floPayTelemetry, standaloneTelemetry]);
|
|
1012
|
+
const containerRef = useRef4(null);
|
|
1013
|
+
const providerStartedAt = useRef4(0);
|
|
1014
|
+
const focusTargetRef = useRef4(null);
|
|
1015
|
+
const pendingProviderFocusRef = useRef4(false);
|
|
859
1016
|
const [ready, setReady] = useState2(false);
|
|
860
1017
|
const [renderGeneration, setRenderGeneration] = useState2(0);
|
|
861
|
-
const activeRenderGenerationRef =
|
|
1018
|
+
const activeRenderGenerationRef = useRef4(0);
|
|
862
1019
|
const [showRetryFocusTarget, setShowRetryFocusTarget] = useState2(false);
|
|
863
1020
|
const [failed, setFailed] = useState2(false);
|
|
864
1021
|
const [submitting, setSubmitting] = useState2(false);
|
|
@@ -868,23 +1025,23 @@ function DirectPayPalButton({
|
|
|
868
1025
|
if (!debug) return;
|
|
869
1026
|
setDebugLines((prev) => [...prev, `${(/* @__PURE__ */ new Date()).toISOString().slice(11, 23)} ${line}`]);
|
|
870
1027
|
};
|
|
871
|
-
const onTokenizedBodyRef =
|
|
872
|
-
const onCompleteRef =
|
|
873
|
-
const onErrorChangeRef =
|
|
874
|
-
const onDeclineRef =
|
|
875
|
-
const onTechnicalFailureRef =
|
|
876
|
-
const onButtonClickRef =
|
|
877
|
-
const onLoadStateChangeRef =
|
|
878
|
-
const runBeforeButtonClickRef =
|
|
879
|
-
const sessionRef =
|
|
880
|
-
const emailRef =
|
|
881
|
-
const nonceRef =
|
|
882
|
-
const beforeClickRef =
|
|
883
|
-
const attemptGenerationRef =
|
|
884
|
-
const attemptRef =
|
|
885
|
-
const invalidatedAttemptGenerationRef =
|
|
886
|
-
const attemptContextBySurfaceRef =
|
|
887
|
-
const approvalContextByTokenRef =
|
|
1028
|
+
const onTokenizedBodyRef = useRef4(onTokenizedBody);
|
|
1029
|
+
const onCompleteRef = useRef4(onComplete);
|
|
1030
|
+
const onErrorChangeRef = useRef4(onErrorChange);
|
|
1031
|
+
const onDeclineRef = useRef4(onDecline);
|
|
1032
|
+
const onTechnicalFailureRef = useRef4(onTechnicalFailure);
|
|
1033
|
+
const onButtonClickRef = useRef4(onButtonClick);
|
|
1034
|
+
const onLoadStateChangeRef = useRef4(onLoadStateChange);
|
|
1035
|
+
const runBeforeButtonClickRef = useRef4(runBeforeButtonClick);
|
|
1036
|
+
const sessionRef = useRef4(session);
|
|
1037
|
+
const emailRef = useRef4(email);
|
|
1038
|
+
const nonceRef = useRef4(nonce);
|
|
1039
|
+
const beforeClickRef = useRef4(null);
|
|
1040
|
+
const attemptGenerationRef = useRef4(0);
|
|
1041
|
+
const attemptRef = useRef4(null);
|
|
1042
|
+
const invalidatedAttemptGenerationRef = useRef4(null);
|
|
1043
|
+
const attemptContextBySurfaceRef = useRef4(/* @__PURE__ */ new Map());
|
|
1044
|
+
const approvalContextByTokenRef = useRef4(/* @__PURE__ */ new Map());
|
|
888
1045
|
useEffect4(() => {
|
|
889
1046
|
onTokenizedBodyRef.current = onTokenizedBody;
|
|
890
1047
|
}, [onTokenizedBody]);
|
|
@@ -924,16 +1081,16 @@ function DirectPayPalButton({
|
|
|
924
1081
|
useEffect4(() => {
|
|
925
1082
|
if (showRetryFocusTarget) focusTargetRef.current?.focus();
|
|
926
1083
|
}, [showRetryFocusTarget, renderGeneration]);
|
|
927
|
-
const focusRetryTarget =
|
|
1084
|
+
const focusRetryTarget = useCallback2(() => {
|
|
928
1085
|
window.setTimeout(() => {
|
|
929
1086
|
focusTargetRef.current?.focus();
|
|
930
1087
|
}, 0);
|
|
931
1088
|
}, []);
|
|
932
|
-
const remountPayPalButtons =
|
|
1089
|
+
const remountPayPalButtons = useCallback2(() => {
|
|
933
1090
|
setReady(false);
|
|
934
1091
|
setRenderGeneration((current) => current + 1);
|
|
935
1092
|
}, []);
|
|
936
|
-
const focusPayPalSurface =
|
|
1093
|
+
const focusPayPalSurface = useCallback2(() => {
|
|
937
1094
|
setShowRetryFocusTarget(false);
|
|
938
1095
|
const target = containerRef.current?.querySelector(
|
|
939
1096
|
'iframe, button, [tabindex]:not([tabindex="-1"])'
|
|
@@ -945,7 +1102,7 @@ function DirectPayPalButton({
|
|
|
945
1102
|
pendingProviderFocusRef.current = false;
|
|
946
1103
|
focusPayPalSurface();
|
|
947
1104
|
}, [focusPayPalSurface, ready, renderGeneration]);
|
|
948
|
-
const notifyTechnicalFailure =
|
|
1105
|
+
const notifyTechnicalFailure = useCallback2((err, options) => {
|
|
949
1106
|
const handler = onTechnicalFailureRef.current;
|
|
950
1107
|
if (handler) {
|
|
951
1108
|
handler("paypal", err, options);
|
|
@@ -1042,7 +1199,7 @@ function DirectPayPalButton({
|
|
|
1042
1199
|
};
|
|
1043
1200
|
}, []);
|
|
1044
1201
|
useEffect4(() => {
|
|
1045
|
-
onLoadStateChangeRef.current?.(ready && !failed);
|
|
1202
|
+
invokeMerchantCallback(() => onLoadStateChangeRef.current?.(ready && !failed));
|
|
1046
1203
|
}, [ready, failed]);
|
|
1047
1204
|
const normalizedEnv = normalizeGatewayEnvironment(environment);
|
|
1048
1205
|
useEffect4(() => {
|
|
@@ -1053,6 +1210,13 @@ function DirectPayPalButton({
|
|
|
1053
1210
|
if (!clientId) {
|
|
1054
1211
|
appendDebug("FAIL: clientId empty \u2014 gateway misconfigured");
|
|
1055
1212
|
setFailed(true);
|
|
1213
|
+
telemetrySource.error({
|
|
1214
|
+
errorCode: "CONFIGURATION_INVALID",
|
|
1215
|
+
stage: "provider_load",
|
|
1216
|
+
provider: "paypal",
|
|
1217
|
+
paymentMethodCategory: "paypal",
|
|
1218
|
+
requestCategory: "provider_sdk"
|
|
1219
|
+
});
|
|
1056
1220
|
console.error("[FloPay] DirectPayPal: clientId empty \u2014 gateway misconfigured");
|
|
1057
1221
|
return;
|
|
1058
1222
|
}
|
|
@@ -1060,8 +1224,33 @@ function DirectPayPalButton({
|
|
|
1060
1224
|
appendDebug("FAIL: containerRef not attached");
|
|
1061
1225
|
return;
|
|
1062
1226
|
}
|
|
1227
|
+
providerStartedAt.current = telemetrySource.startTiming();
|
|
1228
|
+
telemetrySource.log({
|
|
1229
|
+
name: "provider.load.started",
|
|
1230
|
+
stage: "provider_load",
|
|
1231
|
+
provider: "paypal",
|
|
1232
|
+
paymentMethodCategory: "paypal"
|
|
1233
|
+
});
|
|
1063
1234
|
let cancelled = false;
|
|
1064
1235
|
let activeButtons = null;
|
|
1236
|
+
let overlayStartedAt = null;
|
|
1237
|
+
const finishOverlay = () => {
|
|
1238
|
+
if (overlayStartedAt === null) return;
|
|
1239
|
+
telemetrySource.log({
|
|
1240
|
+
name: "provider.overlay.returned",
|
|
1241
|
+
stage: "overlay_return",
|
|
1242
|
+
provider: "paypal",
|
|
1243
|
+
paymentMethodCategory: "paypal"
|
|
1244
|
+
});
|
|
1245
|
+
telemetrySource.performance({
|
|
1246
|
+
stage: "overlay_return",
|
|
1247
|
+
durationMs: telemetrySource.elapsed(overlayStartedAt),
|
|
1248
|
+
durationMode: "buyer",
|
|
1249
|
+
provider: "paypal",
|
|
1250
|
+
paymentMethodCategory: "paypal"
|
|
1251
|
+
});
|
|
1252
|
+
overlayStartedAt = null;
|
|
1253
|
+
};
|
|
1065
1254
|
let rendered = false;
|
|
1066
1255
|
const container = containerRef.current;
|
|
1067
1256
|
let containerObserver = null;
|
|
@@ -1165,7 +1354,7 @@ function DirectPayPalButton({
|
|
|
1165
1354
|
if (cancelled) return;
|
|
1166
1355
|
if (isZoidLifecycleMessage(message)) return;
|
|
1167
1356
|
const friendly = applyFriendlyMessageOverride(message) ?? message;
|
|
1168
|
-
onErrorChangeRef.current?.(friendly);
|
|
1357
|
+
invokeMerchantCallback(() => onErrorChangeRef.current?.(friendly));
|
|
1169
1358
|
};
|
|
1170
1359
|
const markRenderFailed = (message) => {
|
|
1171
1360
|
if (cancelled) return;
|
|
@@ -1174,24 +1363,55 @@ function DirectPayPalButton({
|
|
|
1174
1363
|
return;
|
|
1175
1364
|
}
|
|
1176
1365
|
setFailed(true);
|
|
1366
|
+
if (!message.includes("paypal_ineligible")) {
|
|
1367
|
+
telemetrySource.error({
|
|
1368
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
1369
|
+
stage: "provider_load",
|
|
1370
|
+
provider: "paypal",
|
|
1371
|
+
paymentMethodCategory: "paypal",
|
|
1372
|
+
requestCategory: "provider_sdk"
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1177
1375
|
console.error("[FloPay] DirectPayPal load/render failure:", message);
|
|
1178
1376
|
};
|
|
1179
1377
|
setFailed(false);
|
|
1180
1378
|
const dispatchTokenizedBody = async (body, prepared = beforeClickRef.current) => {
|
|
1181
1379
|
const effectiveSessionId = prepared?.sessionId ?? sessionId;
|
|
1182
1380
|
if (onTokenizedBodyRef.current) {
|
|
1183
|
-
onTokenizedBodyRef.current(body, {
|
|
1381
|
+
await onTokenizedBodyRef.current(body, {
|
|
1184
1382
|
sessionId: effectiveSessionId,
|
|
1185
1383
|
accountPatch: prepared?.accountPatch,
|
|
1186
1384
|
nonce: prepared?.nonce
|
|
1187
1385
|
});
|
|
1188
1386
|
return;
|
|
1189
1387
|
}
|
|
1388
|
+
const processingStartedAt = telemetrySource.startTiming();
|
|
1389
|
+
telemetrySource.log({
|
|
1390
|
+
name: "payment.processing.started",
|
|
1391
|
+
stage: "processing",
|
|
1392
|
+
provider: "paypal",
|
|
1393
|
+
paymentMethodCategory: "paypal"
|
|
1394
|
+
});
|
|
1395
|
+
const finishProcessing = () => {
|
|
1396
|
+
telemetrySource.log({
|
|
1397
|
+
name: "payment.processing.completed",
|
|
1398
|
+
stage: "processing",
|
|
1399
|
+
provider: "paypal",
|
|
1400
|
+
paymentMethodCategory: "paypal"
|
|
1401
|
+
});
|
|
1402
|
+
telemetrySource.performance({
|
|
1403
|
+
stage: "processing",
|
|
1404
|
+
durationMs: telemetrySource.elapsed(processingStartedAt),
|
|
1405
|
+
durationMode: "machine",
|
|
1406
|
+
provider: "paypal",
|
|
1407
|
+
paymentMethodCategory: "paypal"
|
|
1408
|
+
});
|
|
1409
|
+
};
|
|
1190
1410
|
try {
|
|
1191
1411
|
const currentSession = sessionRef.current;
|
|
1192
1412
|
const currentEmail = prepared?.accountPatch?.email ?? emailRef.current;
|
|
1193
1413
|
const effectiveUserId = prepared?.accountPatch?.userId ?? currentSession?.customer?.id ?? currentSession?.accountData?.userId ?? "";
|
|
1194
|
-
const api = new PaymentAPI(baseUrl);
|
|
1414
|
+
const api = new PaymentAPI(baseUrl, { telemetry: false });
|
|
1195
1415
|
const response = await api.processPayment(
|
|
1196
1416
|
effectiveUserId,
|
|
1197
1417
|
{
|
|
@@ -1209,23 +1429,57 @@ function DirectPayPalButton({
|
|
|
1209
1429
|
}
|
|
1210
1430
|
);
|
|
1211
1431
|
if (response.ok) {
|
|
1212
|
-
|
|
1432
|
+
finishProcessing();
|
|
1433
|
+
telemetrySource.terminal({
|
|
1434
|
+
outcome: "payment_succeeded",
|
|
1435
|
+
provider: "paypal",
|
|
1436
|
+
paymentMethodCategory: "paypal"
|
|
1437
|
+
});
|
|
1438
|
+
} else {
|
|
1439
|
+
const json = await response.json().catch(() => null);
|
|
1440
|
+
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1441
|
+
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1442
|
+
finishProcessing();
|
|
1443
|
+
if (typeof json?.["declineCode"] === "string") {
|
|
1444
|
+
telemetrySource.terminal({
|
|
1445
|
+
outcome: "payment_declined",
|
|
1446
|
+
provider: "paypal",
|
|
1447
|
+
paymentMethodCategory: "paypal"
|
|
1448
|
+
});
|
|
1449
|
+
} else {
|
|
1450
|
+
telemetrySource.error({
|
|
1451
|
+
errorCode: "PAYMENT_PROCESSING_FAILED",
|
|
1452
|
+
stage: "processing",
|
|
1453
|
+
provider: "paypal",
|
|
1454
|
+
paymentMethodCategory: "paypal",
|
|
1455
|
+
requestCategory: "process_payment",
|
|
1456
|
+
statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
forwardError(message);
|
|
1460
|
+
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1461
|
+
code: json?.["code"],
|
|
1462
|
+
declineCode: json?.["declineCode"]
|
|
1463
|
+
})));
|
|
1213
1464
|
return;
|
|
1214
1465
|
}
|
|
1215
|
-
const json = await response.json().catch(() => null);
|
|
1216
|
-
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1217
|
-
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1218
|
-
forwardError(message);
|
|
1219
|
-
onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1220
|
-
code: json?.["code"],
|
|
1221
|
-
declineCode: json?.["declineCode"]
|
|
1222
|
-
}));
|
|
1223
1466
|
} catch (err) {
|
|
1467
|
+
finishProcessing();
|
|
1468
|
+
telemetrySource.error({
|
|
1469
|
+
errorCode: "NETWORK_REQUEST_FAILED",
|
|
1470
|
+
stage: "processing",
|
|
1471
|
+
provider: "paypal",
|
|
1472
|
+
paymentMethodCategory: "paypal",
|
|
1473
|
+
requestCategory: "process_payment",
|
|
1474
|
+
statusClass: "network_error"
|
|
1475
|
+
});
|
|
1224
1476
|
const rawMessage = err instanceof Error ? err.message : "PayPal payment failed.";
|
|
1225
1477
|
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1226
1478
|
forwardError(message);
|
|
1227
|
-
onDeclineRef.current?.(buildDeclineEvent("paypal", message));
|
|
1479
|
+
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message)));
|
|
1480
|
+
return;
|
|
1228
1481
|
}
|
|
1482
|
+
invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
|
|
1229
1483
|
};
|
|
1230
1484
|
const createPaypalIntent = async (fallbackMessage) => {
|
|
1231
1485
|
const prepared = beforeClickRef.current;
|
|
@@ -1234,6 +1488,13 @@ function DirectPayPalButton({
|
|
|
1234
1488
|
const intentHeaders = { "Content-Type": "application/json" };
|
|
1235
1489
|
const currentNonce = prepared?.nonce ?? nonceRef.current;
|
|
1236
1490
|
if (currentNonce) intentHeaders["x-checkout-session-token"] = currentNonce;
|
|
1491
|
+
telemetrySource.log({
|
|
1492
|
+
name: "payment.intent.started",
|
|
1493
|
+
stage: "processing",
|
|
1494
|
+
provider: "paypal",
|
|
1495
|
+
paymentMethodCategory: "paypal",
|
|
1496
|
+
requestCategory: "intent_create"
|
|
1497
|
+
});
|
|
1237
1498
|
const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1238
1499
|
method: "POST",
|
|
1239
1500
|
headers: intentHeaders,
|
|
@@ -1252,6 +1513,14 @@ function DirectPayPalButton({
|
|
|
1252
1513
|
if (!id) {
|
|
1253
1514
|
throw new Error(fallbackMessage);
|
|
1254
1515
|
}
|
|
1516
|
+
telemetrySource.log({
|
|
1517
|
+
name: "payment.intent.completed",
|
|
1518
|
+
stage: "processing",
|
|
1519
|
+
provider: "paypal",
|
|
1520
|
+
paymentMethodCategory: "paypal",
|
|
1521
|
+
requestCategory: "intent_create",
|
|
1522
|
+
statusClass: "2xx"
|
|
1523
|
+
});
|
|
1255
1524
|
return id;
|
|
1256
1525
|
};
|
|
1257
1526
|
const effectRenderGeneration = renderGeneration;
|
|
@@ -1286,6 +1555,12 @@ function DirectPayPalButton({
|
|
|
1286
1555
|
if (!cancelled && !paypal?.Buttons) appendDebug("FAIL: namespace missing Buttons factory");
|
|
1287
1556
|
return;
|
|
1288
1557
|
}
|
|
1558
|
+
telemetrySource.log({
|
|
1559
|
+
name: "provider.availability.checked",
|
|
1560
|
+
stage: "provider_ready",
|
|
1561
|
+
provider: "paypal",
|
|
1562
|
+
paymentMethodCategory: "paypal"
|
|
1563
|
+
});
|
|
1289
1564
|
const handleApprove = async (data) => {
|
|
1290
1565
|
if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
|
|
1291
1566
|
const token = data.subscriptionID ?? data.orderID ?? "";
|
|
@@ -1294,9 +1569,10 @@ function DirectPayPalButton({
|
|
|
1294
1569
|
if (!finishAttempt(context.generation)) return;
|
|
1295
1570
|
approvalContextByTokenRef.current.delete(token);
|
|
1296
1571
|
attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1572
|
+
finishOverlay();
|
|
1297
1573
|
try {
|
|
1298
1574
|
setSubmitting(true);
|
|
1299
|
-
onErrorChangeRef.current?.(null);
|
|
1575
|
+
invokeMerchantCallback(() => onErrorChangeRef.current?.(null));
|
|
1300
1576
|
if (!token) {
|
|
1301
1577
|
throw new FloPayError2(
|
|
1302
1578
|
"PayPal did not return an approval token.",
|
|
@@ -1346,7 +1622,7 @@ function DirectPayPalButton({
|
|
|
1346
1622
|
return;
|
|
1347
1623
|
}
|
|
1348
1624
|
}
|
|
1349
|
-
onButtonClickRef.current?.("paypal");
|
|
1625
|
+
invokeMerchantCallback(() => onButtonClickRef.current?.("paypal"));
|
|
1350
1626
|
const generation = startAttempt();
|
|
1351
1627
|
const context = {
|
|
1352
1628
|
generation,
|
|
@@ -1356,6 +1632,25 @@ function DirectPayPalButton({
|
|
|
1356
1632
|
beforeClickRef.current = context;
|
|
1357
1633
|
attemptContextBySurfaceRef.current.set(effectRenderGeneration, context);
|
|
1358
1634
|
await actions.resolve();
|
|
1635
|
+
telemetrySource.log({
|
|
1636
|
+
name: "payment.method.selected",
|
|
1637
|
+
stage: "processing",
|
|
1638
|
+
provider: "paypal",
|
|
1639
|
+
paymentMethodCategory: "paypal"
|
|
1640
|
+
});
|
|
1641
|
+
telemetrySource.log({
|
|
1642
|
+
name: "provider.popup.opened",
|
|
1643
|
+
stage: "overlay_open",
|
|
1644
|
+
provider: "paypal",
|
|
1645
|
+
paymentMethodCategory: "paypal"
|
|
1646
|
+
});
|
|
1647
|
+
telemetrySource.log({
|
|
1648
|
+
name: "provider.overlay.opened",
|
|
1649
|
+
stage: "overlay_open",
|
|
1650
|
+
provider: "paypal",
|
|
1651
|
+
paymentMethodCategory: "paypal"
|
|
1652
|
+
});
|
|
1653
|
+
overlayStartedAt = telemetrySource.startTiming();
|
|
1359
1654
|
},
|
|
1360
1655
|
// When the SDK is already holding an order/subscription id from a
|
|
1361
1656
|
// prior backend round-trip (the `paypal_direct_required` retry
|
|
@@ -1368,6 +1663,12 @@ function DirectPayPalButton({
|
|
|
1368
1663
|
const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
|
|
1369
1664
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1370
1665
|
beforeClickRef.current = null;
|
|
1666
|
+
finishOverlay();
|
|
1667
|
+
telemetrySource.terminal({
|
|
1668
|
+
outcome: "payment_cancelled",
|
|
1669
|
+
provider: "paypal",
|
|
1670
|
+
paymentMethodCategory: "paypal"
|
|
1671
|
+
});
|
|
1371
1672
|
pendingProviderFocusRef.current = true;
|
|
1372
1673
|
invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
|
|
1373
1674
|
},
|
|
@@ -1383,6 +1684,15 @@ function DirectPayPalButton({
|
|
|
1383
1684
|
finishAttempt(context?.generation);
|
|
1384
1685
|
return;
|
|
1385
1686
|
}
|
|
1687
|
+
finishOverlay();
|
|
1688
|
+
const lower = message.toLowerCase();
|
|
1689
|
+
telemetrySource.error({
|
|
1690
|
+
errorCode: lower.includes("popup") && lower.includes("block") ? "POPUP_BLOCKED" : "PROVIDER_RUNTIME_FAILED",
|
|
1691
|
+
stage: "provider_ready",
|
|
1692
|
+
provider: "paypal",
|
|
1693
|
+
paymentMethodCategory: "paypal",
|
|
1694
|
+
requestCategory: "provider_sdk"
|
|
1695
|
+
});
|
|
1386
1696
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1387
1697
|
beforeClickRef.current = null;
|
|
1388
1698
|
invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
|
|
@@ -1394,6 +1704,12 @@ function DirectPayPalButton({
|
|
|
1394
1704
|
});
|
|
1395
1705
|
const eligible = buttons.isEligible();
|
|
1396
1706
|
appendDebug(`isEligible=${eligible}`);
|
|
1707
|
+
telemetrySource.log({
|
|
1708
|
+
name: "provider.eligibility.checked",
|
|
1709
|
+
stage: "provider_ready",
|
|
1710
|
+
provider: "paypal",
|
|
1711
|
+
paymentMethodCategory: "paypal"
|
|
1712
|
+
});
|
|
1397
1713
|
if (!eligible) {
|
|
1398
1714
|
setReady(false);
|
|
1399
1715
|
markRenderFailed(
|
|
@@ -1465,6 +1781,19 @@ function DirectPayPalButton({
|
|
|
1465
1781
|
activeButtons = typedButtons;
|
|
1466
1782
|
rendered = true;
|
|
1467
1783
|
setReady(true);
|
|
1784
|
+
telemetrySource.log({
|
|
1785
|
+
name: "provider.ready",
|
|
1786
|
+
stage: "provider_ready",
|
|
1787
|
+
provider: "paypal",
|
|
1788
|
+
paymentMethodCategory: "paypal"
|
|
1789
|
+
});
|
|
1790
|
+
telemetrySource.performance({
|
|
1791
|
+
stage: "provider_ready",
|
|
1792
|
+
durationMs: telemetrySource.elapsed(providerStartedAt.current),
|
|
1793
|
+
durationMode: "machine",
|
|
1794
|
+
provider: "paypal",
|
|
1795
|
+
paymentMethodCategory: "paypal"
|
|
1796
|
+
});
|
|
1468
1797
|
}).catch((err) => {
|
|
1469
1798
|
const message = err instanceof Error ? err.message : "PayPal failed to render.";
|
|
1470
1799
|
appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
|
|
@@ -1486,7 +1815,7 @@ function DirectPayPalButton({
|
|
|
1486
1815
|
});
|
|
1487
1816
|
}
|
|
1488
1817
|
};
|
|
1489
|
-
}, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration]);
|
|
1818
|
+
}, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
|
|
1490
1819
|
const debugPanel = debug ? /* @__PURE__ */ jsxs3(
|
|
1491
1820
|
"pre",
|
|
1492
1821
|
{
|
|
@@ -1567,6 +1896,12 @@ ${debugLines.join("\n")}`
|
|
|
1567
1896
|
] })
|
|
1568
1897
|
);
|
|
1569
1898
|
}
|
|
1899
|
+
function DirectPayPalButton(props) {
|
|
1900
|
+
return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
|
|
1901
|
+
}
|
|
1902
|
+
function InstrumentedDirectPayPalButton(props) {
|
|
1903
|
+
return /* @__PURE__ */ jsx6(DirectPayPalButtonImplementation, { ...props });
|
|
1904
|
+
}
|
|
1570
1905
|
|
|
1571
1906
|
// src/split-card-form.tsx
|
|
1572
1907
|
import { FloPayError as FloPayError3, isSetupIntentClientSecret as isSetupIntentClientSecret2, resolveTheme } from "@flopay/shared";
|
|
@@ -1685,17 +2020,17 @@ function buildExternalMethodRecoveryMessage(method, popupBlocked) {
|
|
|
1685
2020
|
return popupBlocked ? `${base} Allow pop-ups for this site, then try again.` : base;
|
|
1686
2021
|
}
|
|
1687
2022
|
function useExternalAttemptReconciliation(onMissingTerminal) {
|
|
1688
|
-
const generationRef =
|
|
1689
|
-
const invalidatedGenerationRef =
|
|
1690
|
-
const attemptRef =
|
|
1691
|
-
const clearAttemptTimer =
|
|
2023
|
+
const generationRef = useRef5(0);
|
|
2024
|
+
const invalidatedGenerationRef = useRef5(null);
|
|
2025
|
+
const attemptRef = useRef5(null);
|
|
2026
|
+
const clearAttemptTimer = useCallback3((targetAttempt = attemptRef.current) => {
|
|
1692
2027
|
const attempt = targetAttempt;
|
|
1693
2028
|
if (attempt?.timer) {
|
|
1694
2029
|
clearTimeout(attempt.timer);
|
|
1695
2030
|
attempt.timer = null;
|
|
1696
2031
|
}
|
|
1697
2032
|
}, []);
|
|
1698
|
-
const armAttemptTimer =
|
|
2033
|
+
const armAttemptTimer = useCallback3((attempt, delayMs) => {
|
|
1699
2034
|
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
|
1700
2035
|
clearAttemptTimer(attempt);
|
|
1701
2036
|
attempt.timer = setTimeout(() => {
|
|
@@ -1710,11 +2045,11 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
|
|
|
1710
2045
|
);
|
|
1711
2046
|
}, delayMs);
|
|
1712
2047
|
}, [clearAttemptTimer, onMissingTerminal]);
|
|
1713
|
-
const armRecoveryTimer =
|
|
2048
|
+
const armRecoveryTimer = useCallback3((attempt) => {
|
|
1714
2049
|
if (!attempt.yieldedControl) return;
|
|
1715
2050
|
armAttemptTimer(attempt, EXTERNAL_METHOD_CALLBACK_GRACE_MS);
|
|
1716
2051
|
}, [armAttemptTimer]);
|
|
1717
|
-
const startAttempt =
|
|
2052
|
+
const startAttempt = useCallback3((method) => {
|
|
1718
2053
|
clearAttemptTimer();
|
|
1719
2054
|
const generation = generationRef.current + 1;
|
|
1720
2055
|
generationRef.current = generation;
|
|
@@ -1723,7 +2058,7 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
|
|
|
1723
2058
|
attemptRef.current = attempt;
|
|
1724
2059
|
return generation;
|
|
1725
2060
|
}, [clearAttemptTimer]);
|
|
1726
|
-
const finishAttempt =
|
|
2061
|
+
const finishAttempt = useCallback3((generation) => {
|
|
1727
2062
|
const attempt = attemptRef.current;
|
|
1728
2063
|
if (typeof generation === "number" && attempt?.generation !== generation) return false;
|
|
1729
2064
|
clearAttemptTimer(attempt);
|
|
@@ -1733,7 +2068,7 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
|
|
|
1733
2068
|
}
|
|
1734
2069
|
return true;
|
|
1735
2070
|
}, [clearAttemptTimer]);
|
|
1736
|
-
const invalidateAttempt =
|
|
2071
|
+
const invalidateAttempt = useCallback3((generation) => {
|
|
1737
2072
|
const attempt = attemptRef.current;
|
|
1738
2073
|
const targetGeneration = generation ?? attempt?.generation ?? generationRef.current;
|
|
1739
2074
|
if (!generation || attempt?.generation === generation) {
|
|
@@ -1742,20 +2077,20 @@ function useExternalAttemptReconciliation(onMissingTerminal) {
|
|
|
1742
2077
|
}
|
|
1743
2078
|
invalidatedGenerationRef.current = targetGeneration;
|
|
1744
2079
|
}, [clearAttemptTimer]);
|
|
1745
|
-
const isAttemptInvalidated =
|
|
2080
|
+
const isAttemptInvalidated = useCallback3(
|
|
1746
2081
|
(generation) => invalidatedGenerationRef.current === (generation ?? generationRef.current),
|
|
1747
2082
|
[]
|
|
1748
2083
|
);
|
|
1749
|
-
const isAttemptCurrent =
|
|
2084
|
+
const isAttemptCurrent = useCallback3(
|
|
1750
2085
|
(generation) => attemptRef.current?.generation === generation && invalidatedGenerationRef.current !== generation,
|
|
1751
2086
|
[]
|
|
1752
2087
|
);
|
|
1753
|
-
const scheduleRecoveryIfReturned =
|
|
2088
|
+
const scheduleRecoveryIfReturned = useCallback3(() => {
|
|
1754
2089
|
const attempt = attemptRef.current;
|
|
1755
2090
|
if (!attempt) return;
|
|
1756
2091
|
armRecoveryTimer(attempt);
|
|
1757
2092
|
}, [armRecoveryTimer]);
|
|
1758
|
-
const markAttemptYieldedControl =
|
|
2093
|
+
const markAttemptYieldedControl = useCallback3(() => {
|
|
1759
2094
|
const attempt = attemptRef.current;
|
|
1760
2095
|
if (!attempt) return;
|
|
1761
2096
|
attempt.yieldedControl = true;
|
|
@@ -1883,6 +2218,7 @@ function PayPalButtonInner({
|
|
|
1883
2218
|
onLoadStateChange,
|
|
1884
2219
|
placeholderBorderRadius
|
|
1885
2220
|
}) {
|
|
2221
|
+
const flopay = useFloPay();
|
|
1886
2222
|
const stripe = useStripeRaw();
|
|
1887
2223
|
const elements = useStripeElements();
|
|
1888
2224
|
const [loadState, setLoadState] = useState3("loading");
|
|
@@ -1890,13 +2226,13 @@ function PayPalButtonInner({
|
|
|
1890
2226
|
onLoadStateChange?.(loadState);
|
|
1891
2227
|
}, [loadState, onLoadStateChange]);
|
|
1892
2228
|
const [submitting, setSubmitting] = useState3(false);
|
|
1893
|
-
const paypalResumeAttempted =
|
|
1894
|
-
const focusTargetRef =
|
|
1895
|
-
const recoveryActionRef =
|
|
2229
|
+
const paypalResumeAttempted = useRef5(false);
|
|
2230
|
+
const focusTargetRef = useRef5(null);
|
|
2231
|
+
const recoveryActionRef = useRef5(null);
|
|
1896
2232
|
const [surfaceKey, setSurfaceKey] = useState3(0);
|
|
1897
2233
|
const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
|
|
1898
|
-
const pendingProviderFocusRef =
|
|
1899
|
-
const attemptContextBySurfaceRef =
|
|
2234
|
+
const pendingProviderFocusRef = useRef5(false);
|
|
2235
|
+
const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
|
|
1900
2236
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
1901
2237
|
const {
|
|
1902
2238
|
startAttempt,
|
|
@@ -1915,16 +2251,16 @@ function PayPalButtonInner({
|
|
|
1915
2251
|
useEffect5(() => {
|
|
1916
2252
|
if (showRecoveryAction) recoveryActionRef.current?.focus();
|
|
1917
2253
|
}, [showRecoveryAction, surfaceKey]);
|
|
1918
|
-
const resetSurface =
|
|
2254
|
+
const resetSurface = useCallback3(() => {
|
|
1919
2255
|
setSurfaceKey((key) => key + 1);
|
|
1920
2256
|
}, []);
|
|
1921
|
-
const recoverTechnicalFailure =
|
|
2257
|
+
const recoverTechnicalFailure = useCallback3((err, code, generation) => {
|
|
1922
2258
|
invalidateAttempt(generation);
|
|
1923
2259
|
onTechnicalFailure?.("paypal", err, { code, popupBlocked: isPopupBlockedError(err) });
|
|
1924
2260
|
setShowRecoveryAction(true);
|
|
1925
2261
|
resetSurface();
|
|
1926
2262
|
}, [invalidateAttempt, onTechnicalFailure, resetSurface]);
|
|
1927
|
-
const focusProviderSurface =
|
|
2263
|
+
const focusProviderSurface = useCallback3(() => {
|
|
1928
2264
|
window.setTimeout(() => {
|
|
1929
2265
|
const target = focusTargetRef.current?.querySelector("iframe");
|
|
1930
2266
|
(target ?? focusTargetRef.current)?.focus();
|
|
@@ -1935,7 +2271,7 @@ function PayPalButtonInner({
|
|
|
1935
2271
|
pendingProviderFocusRef.current = false;
|
|
1936
2272
|
focusProviderSurface();
|
|
1937
2273
|
}, [focusProviderSurface, surfaceKey]);
|
|
1938
|
-
const handleRecoveryActionClick =
|
|
2274
|
+
const handleRecoveryActionClick = useCallback3(() => {
|
|
1939
2275
|
setShowRecoveryAction(false);
|
|
1940
2276
|
onErrorChange?.(null);
|
|
1941
2277
|
focusProviderSurface();
|
|
@@ -2006,7 +2342,7 @@ function PayPalButtonInner({
|
|
|
2006
2342
|
}
|
|
2007
2343
|
})();
|
|
2008
2344
|
}, [stripe, onTokenizedBody, onErrorChange, onDecline]);
|
|
2009
|
-
const handlePayPalClick =
|
|
2345
|
+
const handlePayPalClick = useCallback3(async (event) => {
|
|
2010
2346
|
if (isProcessing || submitting) {
|
|
2011
2347
|
event.reject();
|
|
2012
2348
|
return;
|
|
@@ -2028,7 +2364,7 @@ function PayPalButtonInner({
|
|
|
2028
2364
|
onButtonClick?.("paypal");
|
|
2029
2365
|
event.resolve();
|
|
2030
2366
|
}, [attemptContextBySurfaceRef, isProcessing, onButtonClick, runBeforeButtonClick, startAttempt, submitting, surfaceKey]);
|
|
2031
|
-
const handlePayPalConfirm =
|
|
2367
|
+
const handlePayPalConfirm = useCallback3(async (event) => {
|
|
2032
2368
|
if (!stripe || !elements) return;
|
|
2033
2369
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
2034
2370
|
if (attemptContext && !isAttemptCurrent(attemptContext.generation)) {
|
|
@@ -2197,6 +2533,11 @@ function PayPalButtonInner({
|
|
|
2197
2533
|
onCancel: () => {
|
|
2198
2534
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
2199
2535
|
attemptContextBySurfaceRef.current.delete(surfaceKey);
|
|
2536
|
+
getFloPayTelemetryBridge(flopay)?.terminal({
|
|
2537
|
+
outcome: "payment_cancelled",
|
|
2538
|
+
provider: "paypal",
|
|
2539
|
+
paymentMethodCategory: "paypal"
|
|
2540
|
+
});
|
|
2200
2541
|
invalidateAttempt(attemptContext?.generation);
|
|
2201
2542
|
setShowRecoveryAction(false);
|
|
2202
2543
|
pendingProviderFocusRef.current = true;
|
|
@@ -2240,6 +2581,7 @@ function WalletButtonInner({
|
|
|
2240
2581
|
onLoadStateChange,
|
|
2241
2582
|
placeholderBorderRadius
|
|
2242
2583
|
}) {
|
|
2584
|
+
const flopay = useFloPay();
|
|
2243
2585
|
const stripe = useStripeRaw();
|
|
2244
2586
|
const elements = useStripeElements();
|
|
2245
2587
|
const [loadState, setLoadState] = useState3("loading");
|
|
@@ -2248,15 +2590,15 @@ function WalletButtonInner({
|
|
|
2248
2590
|
}, [loadState, onLoadStateChange]);
|
|
2249
2591
|
const [submitting, setSubmitting] = useState3(false);
|
|
2250
2592
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2251
|
-
const focusTargetRef =
|
|
2252
|
-
const recoveryActionRef =
|
|
2593
|
+
const focusTargetRef = useRef5(null);
|
|
2594
|
+
const recoveryActionRef = useRef5(null);
|
|
2253
2595
|
const [surfaceKey, setSurfaceKey] = useState3(0);
|
|
2254
2596
|
const [showRecoveryAction, setShowRecoveryAction] = useState3(false);
|
|
2255
2597
|
const [recoveryActionMethod, setRecoveryActionMethod] = useState3("google_pay");
|
|
2256
|
-
const pendingProviderFocusRef =
|
|
2257
|
-
const lastWalletProviderMethodRef =
|
|
2258
|
-
const lastWalletMethodRef =
|
|
2259
|
-
const attemptContextBySurfaceRef =
|
|
2598
|
+
const pendingProviderFocusRef = useRef5(false);
|
|
2599
|
+
const lastWalletProviderMethodRef = useRef5("google_pay");
|
|
2600
|
+
const lastWalletMethodRef = useRef5("google_pay");
|
|
2601
|
+
const attemptContextBySurfaceRef = useRef5(/* @__PURE__ */ new Map());
|
|
2260
2602
|
const {
|
|
2261
2603
|
startAttempt,
|
|
2262
2604
|
finishAttempt,
|
|
@@ -2275,17 +2617,17 @@ function WalletButtonInner({
|
|
|
2275
2617
|
useEffect5(() => {
|
|
2276
2618
|
if (showRecoveryAction) recoveryActionRef.current?.focus();
|
|
2277
2619
|
}, [showRecoveryAction, surfaceKey]);
|
|
2278
|
-
const resetSurface =
|
|
2620
|
+
const resetSurface = useCallback3(() => {
|
|
2279
2621
|
setSurfaceKey((key) => key + 1);
|
|
2280
2622
|
}, []);
|
|
2281
|
-
const recoverTechnicalFailure =
|
|
2623
|
+
const recoverTechnicalFailure = useCallback3((method, err, code, generation) => {
|
|
2282
2624
|
invalidateAttempt(generation);
|
|
2283
2625
|
onTechnicalFailure?.(method, err, { code, popupBlocked: isPopupBlockedError(err) });
|
|
2284
2626
|
setRecoveryActionMethod(method);
|
|
2285
2627
|
setShowRecoveryAction(true);
|
|
2286
2628
|
resetSurface();
|
|
2287
2629
|
}, [invalidateAttempt, onTechnicalFailure, resetSurface]);
|
|
2288
|
-
const focusProviderSurface =
|
|
2630
|
+
const focusProviderSurface = useCallback3(() => {
|
|
2289
2631
|
window.setTimeout(() => {
|
|
2290
2632
|
const target = focusTargetRef.current?.querySelector("iframe");
|
|
2291
2633
|
(target ?? focusTargetRef.current)?.focus();
|
|
@@ -2296,12 +2638,12 @@ function WalletButtonInner({
|
|
|
2296
2638
|
pendingProviderFocusRef.current = false;
|
|
2297
2639
|
focusProviderSurface();
|
|
2298
2640
|
}, [focusProviderSurface, surfaceKey]);
|
|
2299
|
-
const handleRecoveryActionClick =
|
|
2641
|
+
const handleRecoveryActionClick = useCallback3(() => {
|
|
2300
2642
|
setShowRecoveryAction(false);
|
|
2301
2643
|
onErrorChange?.(null);
|
|
2302
2644
|
focusProviderSurface();
|
|
2303
2645
|
}, [focusProviderSurface, onErrorChange]);
|
|
2304
|
-
const handleWalletConfirm =
|
|
2646
|
+
const handleWalletConfirm = useCallback3(
|
|
2305
2647
|
async (event) => {
|
|
2306
2648
|
if (!stripe || !elements) return;
|
|
2307
2649
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
@@ -2461,6 +2803,13 @@ function WalletButtonInner({
|
|
|
2461
2803
|
},
|
|
2462
2804
|
onLoadError: (_event) => {
|
|
2463
2805
|
setLoadState("load_error");
|
|
2806
|
+
getFloPayTelemetryBridge(flopay)?.error({
|
|
2807
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
2808
|
+
stage: "provider_load",
|
|
2809
|
+
provider: "stripe",
|
|
2810
|
+
paymentMethodCategory: "wallet",
|
|
2811
|
+
requestCategory: "provider_sdk"
|
|
2812
|
+
});
|
|
2464
2813
|
},
|
|
2465
2814
|
onClick: async (event) => {
|
|
2466
2815
|
lastWalletProviderMethodRef.current = event.expressPaymentType;
|
|
@@ -2487,6 +2836,11 @@ function WalletButtonInner({
|
|
|
2487
2836
|
onCancel: () => {
|
|
2488
2837
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
2489
2838
|
attemptContextBySurfaceRef.current.delete(surfaceKey);
|
|
2839
|
+
getFloPayTelemetryBridge(flopay)?.terminal({
|
|
2840
|
+
outcome: "payment_cancelled",
|
|
2841
|
+
provider: "stripe",
|
|
2842
|
+
paymentMethodCategory: "wallet"
|
|
2843
|
+
});
|
|
2490
2844
|
invalidateAttempt(attemptContext?.generation);
|
|
2491
2845
|
setShowRecoveryAction(false);
|
|
2492
2846
|
pendingProviderFocusRef.current = true;
|
|
@@ -2657,14 +3011,15 @@ function StripeMethodInlineForm({
|
|
|
2657
3011
|
submitButtonStyle,
|
|
2658
3012
|
errorText
|
|
2659
3013
|
}) {
|
|
3014
|
+
const flopay = useFloPay();
|
|
2660
3015
|
const stripe = useStripeRaw();
|
|
2661
3016
|
const elements = useStripeElements();
|
|
2662
3017
|
const [submitting, setSubmitting] = useState3(false);
|
|
2663
|
-
const submittingRef =
|
|
3018
|
+
const submittingRef = useRef5(false);
|
|
2664
3019
|
const [isMethodComplete, setIsMethodComplete] = useState3(false);
|
|
2665
3020
|
const [loadState, setLoadState] = useState3("loading");
|
|
2666
3021
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2667
|
-
const handlePay =
|
|
3022
|
+
const handlePay = useCallback3(async () => {
|
|
2668
3023
|
if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
|
|
2669
3024
|
submittingRef.current = true;
|
|
2670
3025
|
setSubmitting(true);
|
|
@@ -2809,7 +3164,16 @@ function StripeMethodInlineForm({
|
|
|
2809
3164
|
PaymentElement2,
|
|
2810
3165
|
{
|
|
2811
3166
|
onReady: () => setLoadState("ready"),
|
|
2812
|
-
onLoadError: () =>
|
|
3167
|
+
onLoadError: () => {
|
|
3168
|
+
setLoadState("load_error");
|
|
3169
|
+
getFloPayTelemetryBridge(flopay)?.error({
|
|
3170
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
3171
|
+
stage: "provider_load",
|
|
3172
|
+
provider: "stripe",
|
|
3173
|
+
paymentMethodCategory: "apm",
|
|
3174
|
+
requestCategory: "provider_sdk"
|
|
3175
|
+
});
|
|
3176
|
+
},
|
|
2813
3177
|
onChange: (event) => {
|
|
2814
3178
|
const evRecord = event;
|
|
2815
3179
|
setIsMethodComplete(!!evRecord.complete);
|
|
@@ -2897,7 +3261,7 @@ function StripePaymentElementInner({
|
|
|
2897
3261
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2898
3262
|
const [expandedMethod, setExpandedMethod] = useState3(null);
|
|
2899
3263
|
const [submittingMethod, setSubmittingMethod] = useState3(null);
|
|
2900
|
-
const submittingRef =
|
|
3264
|
+
const submittingRef = useRef5(false);
|
|
2901
3265
|
useEffect5(() => {
|
|
2902
3266
|
if (paymentElementMethods.length > 0 && stripeInstance) {
|
|
2903
3267
|
onLoadStateChange?.("ready");
|
|
@@ -2905,7 +3269,7 @@ function StripePaymentElementInner({
|
|
|
2905
3269
|
onLoadStateChange?.("loading");
|
|
2906
3270
|
}
|
|
2907
3271
|
}, [paymentElementMethods.length, stripeInstance, onLoadStateChange]);
|
|
2908
|
-
const handleAutoConfirm =
|
|
3272
|
+
const handleAutoConfirm = useCallback3(async (method) => {
|
|
2909
3273
|
if (!stripeInstance) return;
|
|
2910
3274
|
if (submittingRef.current || isProcessing) return;
|
|
2911
3275
|
submittingRef.current = true;
|
|
@@ -3046,7 +3410,7 @@ function StripePaymentElementInner({
|
|
|
3046
3410
|
]);
|
|
3047
3411
|
const [localExpandedMethod, setLocalExpandedMethod] = useState3(null);
|
|
3048
3412
|
const activeExpandedMethod = onExpandApm ? expandedApmMethod ?? null : localExpandedMethod;
|
|
3049
|
-
const handleMethodClick =
|
|
3413
|
+
const handleMethodClick = useCallback3((method) => {
|
|
3050
3414
|
if (submittingRef.current || isProcessing) return;
|
|
3051
3415
|
if (activeExpandedMethod && activeExpandedMethod !== method) return;
|
|
3052
3416
|
if (needsStripeMethodExplicitConfirm(method)) {
|
|
@@ -3204,12 +3568,12 @@ function SplitCardFormInner({
|
|
|
3204
3568
|
const [city, setCity] = useState3(cityProp ?? "");
|
|
3205
3569
|
const [stateValue, setStateValue] = useState3(stateProp ?? "");
|
|
3206
3570
|
const [accountPatch, setAccountPatch] = useState3({});
|
|
3207
|
-
const zipCodeRef =
|
|
3208
|
-
const selectedCountryRef =
|
|
3209
|
-
const addressLine1Ref =
|
|
3210
|
-
const addressLine2Ref =
|
|
3211
|
-
const cityRef =
|
|
3212
|
-
const stateRef =
|
|
3571
|
+
const zipCodeRef = useRef5(zipProp ?? "");
|
|
3572
|
+
const selectedCountryRef = useRef5(countryProp ?? "US");
|
|
3573
|
+
const addressLine1Ref = useRef5(addressLine1Prop ?? "");
|
|
3574
|
+
const addressLine2Ref = useRef5(addressLine2Prop ?? "");
|
|
3575
|
+
const cityRef = useRef5(cityProp ?? "");
|
|
3576
|
+
const stateRef = useRef5(stateProp ?? "");
|
|
3213
3577
|
const avsConfig = useMemo3(() => resolveAVSConfig(enableAVSProp), [enableAVSProp]);
|
|
3214
3578
|
const enableAVS = avsConfig !== null;
|
|
3215
3579
|
const vaultBlockReady = Boolean(session?.vault?.html);
|
|
@@ -3251,20 +3615,20 @@ function SplitCardFormInner({
|
|
|
3251
3615
|
const [expandedApmMethod, setExpandedApmMethod] = useState3(null);
|
|
3252
3616
|
const showCardForm = viewState === "expanding" || viewState === "card";
|
|
3253
3617
|
const TRANSITION_MS = 280;
|
|
3254
|
-
const expandToCard =
|
|
3618
|
+
const expandToCard = useCallback3(() => {
|
|
3255
3619
|
setViewState("expanding");
|
|
3256
3620
|
setTimeout(() => setViewState("card"), TRANSITION_MS);
|
|
3257
3621
|
}, []);
|
|
3258
|
-
const collapseToButtons =
|
|
3622
|
+
const collapseToButtons = useCallback3(() => {
|
|
3259
3623
|
setViewState("collapsing");
|
|
3260
3624
|
setTimeout(() => setViewState("buttons"), TRANSITION_MS);
|
|
3261
3625
|
}, []);
|
|
3262
|
-
const expandToApm =
|
|
3626
|
+
const expandToApm = useCallback3((method) => {
|
|
3263
3627
|
setExpandedApmMethod(method);
|
|
3264
3628
|
setViewState("apm-expanding");
|
|
3265
3629
|
setTimeout(() => setViewState("apm-form"), TRANSITION_MS);
|
|
3266
3630
|
}, []);
|
|
3267
|
-
const collapseFromApm =
|
|
3631
|
+
const collapseFromApm = useCallback3(() => {
|
|
3268
3632
|
setViewState("apm-collapsing");
|
|
3269
3633
|
setTimeout(() => {
|
|
3270
3634
|
setViewState("buttons");
|
|
@@ -3279,9 +3643,9 @@ function SplitCardFormInner({
|
|
|
3279
3643
|
const [fullName, setFullName] = useState3("");
|
|
3280
3644
|
const [formReady, setFormReady] = useState3(false);
|
|
3281
3645
|
const [overlayStatus, setOverlayStatus] = useState3(null);
|
|
3282
|
-
const processingRef =
|
|
3646
|
+
const processingRef = useRef5(false);
|
|
3283
3647
|
const [paypalDirectRetry, setPaypalDirectRetry] = useState3(null);
|
|
3284
|
-
const paypalDirectRetryRef =
|
|
3648
|
+
const paypalDirectRetryRef = useRef5(paypalDirectRetry);
|
|
3285
3649
|
useEffect5(() => {
|
|
3286
3650
|
paypalDirectRetryRef.current = paypalDirectRetry;
|
|
3287
3651
|
}, [paypalDirectRetry]);
|
|
@@ -3389,7 +3753,7 @@ function SplitCardFormInner({
|
|
|
3389
3753
|
setupFutureUsage: "off_session",
|
|
3390
3754
|
...stripeAppearanceProp
|
|
3391
3755
|
}), [amountInCents, currency, stripeAppearanceProp]);
|
|
3392
|
-
const updateError =
|
|
3756
|
+
const updateError = useCallback3(
|
|
3393
3757
|
(err) => {
|
|
3394
3758
|
setError(err);
|
|
3395
3759
|
onErrorChange?.(err);
|
|
@@ -3405,13 +3769,13 @@ function SplitCardFormInner({
|
|
|
3405
3769
|
updateError(null);
|
|
3406
3770
|
}
|
|
3407
3771
|
}, [viewState, updateError]);
|
|
3408
|
-
const emitDecline =
|
|
3772
|
+
const emitDecline = useCallback3(
|
|
3409
3773
|
(method, input, overrides) => {
|
|
3410
3774
|
onDecline?.(buildDeclineEvent(method, input, overrides));
|
|
3411
3775
|
},
|
|
3412
3776
|
[onDecline]
|
|
3413
3777
|
);
|
|
3414
|
-
const recoverExternalMethodTechnicalFailure =
|
|
3778
|
+
const recoverExternalMethodTechnicalFailure = useCallback3(
|
|
3415
3779
|
(method, err, options) => {
|
|
3416
3780
|
const popupBlocked = options?.popupBlocked ?? isPopupBlockedError(err);
|
|
3417
3781
|
const message = buildExternalMethodRecoveryMessage(method, popupBlocked);
|
|
@@ -3473,7 +3837,7 @@ function SplitCardFormInner({
|
|
|
3473
3837
|
nonce,
|
|
3474
3838
|
updateError
|
|
3475
3839
|
]);
|
|
3476
|
-
const vaultOutcomeRef =
|
|
3840
|
+
const vaultOutcomeRef = useRef5({
|
|
3477
3841
|
onComplete,
|
|
3478
3842
|
onError,
|
|
3479
3843
|
updateError,
|
|
@@ -3499,8 +3863,8 @@ function SplitCardFormInner({
|
|
|
3499
3863
|
nonce,
|
|
3500
3864
|
baseUrl
|
|
3501
3865
|
};
|
|
3502
|
-
const vaultCompletedRef =
|
|
3503
|
-
const buildVaultAccountSnapshot =
|
|
3866
|
+
const vaultCompletedRef = useRef5(false);
|
|
3867
|
+
const buildVaultAccountSnapshot = useCallback3(() => {
|
|
3504
3868
|
const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
|
|
3505
3869
|
const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
|
|
3506
3870
|
const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
|
|
@@ -3656,7 +4020,7 @@ function SplitCardFormInner({
|
|
|
3656
4020
|
const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
|
|
3657
4021
|
const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
|
|
3658
4022
|
const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
|
|
3659
|
-
const validationFiredRef =
|
|
4023
|
+
const validationFiredRef = useRef5(false);
|
|
3660
4024
|
useEffect5(() => {
|
|
3661
4025
|
if (validationFiredRef.current) return;
|
|
3662
4026
|
if (!showStripe && !showPayPal) {
|
|
@@ -3677,7 +4041,7 @@ function SplitCardFormInner({
|
|
|
3677
4041
|
updateError(err.message);
|
|
3678
4042
|
}
|
|
3679
4043
|
}, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
|
|
3680
|
-
const deprecationLoggedRef =
|
|
4044
|
+
const deprecationLoggedRef = useRef5(false);
|
|
3681
4045
|
useEffect5(() => {
|
|
3682
4046
|
if (deprecationLoggedRef.current) return;
|
|
3683
4047
|
if (!hasEnabledMethods) return;
|
|
@@ -3690,14 +4054,14 @@ function SplitCardFormInner({
|
|
|
3690
4054
|
`[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.`
|
|
3691
4055
|
);
|
|
3692
4056
|
}, [hasEnabledMethods, showApplePay, showGooglePay]);
|
|
3693
|
-
const handleNameChange =
|
|
4057
|
+
const handleNameChange = useCallback3((value) => {
|
|
3694
4058
|
setFullName(value);
|
|
3695
4059
|
onFullNameChange?.(value);
|
|
3696
4060
|
const parts = value.trim().split(/\s+/);
|
|
3697
4061
|
onFirstNameChange?.(parts[0] ?? "");
|
|
3698
4062
|
onLastNameChange?.(parts.length > 1 ? parts.slice(1).join(" ") : "");
|
|
3699
4063
|
}, [onFullNameChange, onFirstNameChange, onLastNameChange]);
|
|
3700
|
-
const applyInlineSessionPatch =
|
|
4064
|
+
const applyInlineSessionPatch = useCallback3(
|
|
3701
4065
|
(patch, method) => {
|
|
3702
4066
|
if (!checkout.applyInlineSessionPatch) {
|
|
3703
4067
|
return Promise.resolve({ error: null, sessionId, nonce });
|
|
@@ -3719,7 +4083,7 @@ function SplitCardFormInner({
|
|
|
3719
4083
|
},
|
|
3720
4084
|
[checkout.applyInlineSessionPatch, nonce, onError, sessionId, updateError]
|
|
3721
4085
|
);
|
|
3722
|
-
const runBeforeButtonClick =
|
|
4086
|
+
const runBeforeButtonClick = useCallback3(async (method) => {
|
|
3723
4087
|
if (!onBeforeButtonClick) return { proceed: true };
|
|
3724
4088
|
try {
|
|
3725
4089
|
const result = await onBeforeButtonClick({
|
|
@@ -3756,7 +4120,7 @@ function SplitCardFormInner({
|
|
|
3756
4120
|
return { proceed: false };
|
|
3757
4121
|
}
|
|
3758
4122
|
}, [applyInlineSessionPatch, checkout.inlineSessionDraft, onBeforeButtonClick, onError, sessionId, updateError]);
|
|
3759
|
-
const processPaymentInternal =
|
|
4123
|
+
const processPaymentInternal = useCallback3(
|
|
3760
4124
|
async (tokenizedBody, overrides) => {
|
|
3761
4125
|
if (processingRef.current) return;
|
|
3762
4126
|
processingRef.current = true;
|
|
@@ -3967,7 +4331,7 @@ function SplitCardFormInner({
|
|
|
3967
4331
|
},
|
|
3968
4332
|
[baseUrl, sessionId, nonce, resolvedAccount, fullName, chv, flopay, paypalFlopay, onComplete, onError, updateError, emitDecline]
|
|
3969
4333
|
);
|
|
3970
|
-
const dispatchTokenizedBody =
|
|
4334
|
+
const dispatchTokenizedBody = useCallback3(
|
|
3971
4335
|
(tokenizedBody, overrides) => {
|
|
3972
4336
|
if (onTokenizedBody) {
|
|
3973
4337
|
onTokenizedBody(tokenizedBody);
|
|
@@ -4004,7 +4368,7 @@ function SplitCardFormInner({
|
|
|
4004
4368
|
}
|
|
4005
4369
|
}
|
|
4006
4370
|
}), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
|
|
4007
|
-
const stripeResumeAttemptedRef =
|
|
4371
|
+
const stripeResumeAttemptedRef = useRef5(false);
|
|
4008
4372
|
useEffect5(() => {
|
|
4009
4373
|
if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
|
|
4010
4374
|
const params = new URLSearchParams(window.location.search);
|
|
@@ -4083,7 +4447,7 @@ function SplitCardFormInner({
|
|
|
4083
4447
|
}, persistedOverrides);
|
|
4084
4448
|
})();
|
|
4085
4449
|
}, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
|
|
4086
|
-
const handleSubmit =
|
|
4450
|
+
const handleSubmit = useCallback3(
|
|
4087
4451
|
async (e) => {
|
|
4088
4452
|
e.preventDefault();
|
|
4089
4453
|
if (!flopay || !elements || isSubmitting || processingRef.current) return;
|
|
@@ -5725,7 +6089,7 @@ async function recover3DSRedirectResult({
|
|
|
5725
6089
|
return null;
|
|
5726
6090
|
}
|
|
5727
6091
|
try {
|
|
5728
|
-
const api = new PaymentAPI3(billingApiUrl);
|
|
6092
|
+
const api = new PaymentAPI3(billingApiUrl, { telemetry: false });
|
|
5729
6093
|
const unified = await api.getUnifiedCheckoutSession(sessionId, nonce);
|
|
5730
6094
|
const refreshedToken = unified.data.stripe?.clientSecret;
|
|
5731
6095
|
if (isStripePaymentIntentClientSecret(refreshedToken)) {
|
|
@@ -5744,7 +6108,8 @@ async function processSavedPaymentForMode({
|
|
|
5744
6108
|
session,
|
|
5745
6109
|
nonce,
|
|
5746
6110
|
tokenizedData,
|
|
5747
|
-
returnUrl
|
|
6111
|
+
returnUrl,
|
|
6112
|
+
telemetry
|
|
5748
6113
|
}) {
|
|
5749
6114
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
5750
6115
|
const resolvedSessionId = sessionId ?? session.id;
|
|
@@ -5755,7 +6120,10 @@ async function processSavedPaymentForMode({
|
|
|
5755
6120
|
const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
|
|
5756
6121
|
const country = session.customer?.country ?? session.accountData?.country ?? void 0;
|
|
5757
6122
|
const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
|
|
5758
|
-
const api = new PaymentAPI3(
|
|
6123
|
+
const api = new PaymentAPI3(
|
|
6124
|
+
baseUrl,
|
|
6125
|
+
telemetry === false ? { telemetry: false } : void 0
|
|
6126
|
+
);
|
|
5759
6127
|
const response = await retryOnceOnFetchFailure(() => api.processPayment(customerId, {
|
|
5760
6128
|
sessionId: resolvedSessionId,
|
|
5761
6129
|
nonce: resolvedNonce,
|
|
@@ -5832,7 +6200,8 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5832
6200
|
billingApiUrl,
|
|
5833
6201
|
sessionId,
|
|
5834
6202
|
session,
|
|
5835
|
-
returnUrl
|
|
6203
|
+
returnUrl,
|
|
6204
|
+
telemetry
|
|
5836
6205
|
}) {
|
|
5837
6206
|
const stripe = flopay?.getRawProvider();
|
|
5838
6207
|
if (!stripe) {
|
|
@@ -5915,6 +6284,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5915
6284
|
billingApiUrl,
|
|
5916
6285
|
sessionId,
|
|
5917
6286
|
session,
|
|
6287
|
+
telemetry,
|
|
5918
6288
|
tokenizedData: {
|
|
5919
6289
|
id: paymentIntent.id,
|
|
5920
6290
|
type: "card",
|
|
@@ -5936,7 +6306,8 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5936
6306
|
billingApiUrl,
|
|
5937
6307
|
sessionId,
|
|
5938
6308
|
session,
|
|
5939
|
-
returnUrl
|
|
6309
|
+
returnUrl,
|
|
6310
|
+
telemetry
|
|
5940
6311
|
});
|
|
5941
6312
|
}
|
|
5942
6313
|
throw Object.assign(
|
|
@@ -6042,7 +6413,8 @@ async function loadSavedPaymentProviders({
|
|
|
6042
6413
|
publishableKey,
|
|
6043
6414
|
paypalPublishableKey,
|
|
6044
6415
|
billingApiUrl,
|
|
6045
|
-
locale
|
|
6416
|
+
locale,
|
|
6417
|
+
telemetry
|
|
6046
6418
|
}) {
|
|
6047
6419
|
if (!publishableKey) {
|
|
6048
6420
|
return { flopay: null, paypalFlopay: null };
|
|
@@ -6051,11 +6423,13 @@ async function loadSavedPaymentProviders({
|
|
|
6051
6423
|
const [instance, paypalInstanceOrError] = await Promise.all([
|
|
6052
6424
|
loadFloPay(publishableKey, {
|
|
6053
6425
|
billingApiUrl,
|
|
6054
|
-
locale
|
|
6426
|
+
locale,
|
|
6427
|
+
telemetry
|
|
6055
6428
|
}),
|
|
6056
6429
|
needsSeparatePaypal ? loadFloPay(paypalPublishableKey, {
|
|
6057
6430
|
billingApiUrl,
|
|
6058
|
-
locale
|
|
6431
|
+
locale,
|
|
6432
|
+
telemetry
|
|
6059
6433
|
}).catch((err) => {
|
|
6060
6434
|
console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
|
|
6061
6435
|
return null;
|
|
@@ -6075,6 +6449,33 @@ var sessionInflightMap = /* @__PURE__ */ new Map();
|
|
|
6075
6449
|
function sleep(ms) {
|
|
6076
6450
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6077
6451
|
}
|
|
6452
|
+
function createStandaloneTelemetryReporter(billingApiUrl, enabled, context) {
|
|
6453
|
+
const reporter = createTelemetryBridge({
|
|
6454
|
+
billingApiUrl,
|
|
6455
|
+
sdkPackage: "@flopay/react",
|
|
6456
|
+
sdkVersion: SDK_VERSION2,
|
|
6457
|
+
enabled: enabled !== false
|
|
6458
|
+
});
|
|
6459
|
+
reporter.beginCheckout(context);
|
|
6460
|
+
return reporter;
|
|
6461
|
+
}
|
|
6462
|
+
function finishStandaloneTelemetry(reporter) {
|
|
6463
|
+
void reporter.flush().catch(() => {
|
|
6464
|
+
}).finally(() => reporter.destroy());
|
|
6465
|
+
}
|
|
6466
|
+
function reportStandaloneTelemetryError(billingApiUrl, enabled, context, errorCode, stage) {
|
|
6467
|
+
const reporter = createStandaloneTelemetryReporter(billingApiUrl, enabled, context);
|
|
6468
|
+
reporter.error({
|
|
6469
|
+
errorCode,
|
|
6470
|
+
stage,
|
|
6471
|
+
paymentMethodCategory: "unknown",
|
|
6472
|
+
...stage === "session_read" ? { requestCategory: "session_read" } : {}
|
|
6473
|
+
});
|
|
6474
|
+
finishStandaloneTelemetry(reporter);
|
|
6475
|
+
}
|
|
6476
|
+
function isExpectedExistingSessionError(error) {
|
|
6477
|
+
return error.type === "validation_error" || error.code === "checkout_session_not_found" || error.code === "checkout_session_expired" || error.code === "checkout_session_completed" || error.code === "session_auto_completed";
|
|
6478
|
+
}
|
|
6078
6479
|
function resolveDirectPaypalConfig(unified) {
|
|
6079
6480
|
const clientId = unified?.data.paypal?.publishableKey;
|
|
6080
6481
|
if (!clientId) return void 0;
|
|
@@ -6179,6 +6580,7 @@ function FloPayCheckout({
|
|
|
6179
6580
|
nonce: nonceProp,
|
|
6180
6581
|
createSession: createSessionParams,
|
|
6181
6582
|
billingApiUrl,
|
|
6583
|
+
telemetry,
|
|
6182
6584
|
appearance: appearanceOverride,
|
|
6183
6585
|
locale,
|
|
6184
6586
|
loading: loadingNode,
|
|
@@ -6224,9 +6626,9 @@ function FloPayCheckout({
|
|
|
6224
6626
|
const checkoutLayout = children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout";
|
|
6225
6627
|
const [unified, setUnified] = useState4(null);
|
|
6226
6628
|
const [flopay, setFloPay] = useState4(null);
|
|
6227
|
-
const flopayRef =
|
|
6629
|
+
const flopayRef = useRef6(null);
|
|
6228
6630
|
const [paypalFlopay, setPaypalFloPay] = useState4(null);
|
|
6229
|
-
const paypalFlopayRef =
|
|
6631
|
+
const paypalFlopayRef = useRef6(null);
|
|
6230
6632
|
const [session, setSession] = useState4(null);
|
|
6231
6633
|
const [resolvedSessionId, setResolvedSessionId] = useState4(sessionIdProp ?? "");
|
|
6232
6634
|
const activeSessionId = sessionIdProp ?? resolvedSessionId;
|
|
@@ -6241,20 +6643,30 @@ function FloPayCheckout({
|
|
|
6241
6643
|
const [createSessionPatch, setCreateSessionPatch] = useState4(void 0);
|
|
6242
6644
|
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = useState4("");
|
|
6243
6645
|
const [cardBootstrapPending, setCardBootstrapPending] = useState4(false);
|
|
6244
|
-
const autoCheckoutAttempted =
|
|
6245
|
-
const paypalResumeAttempted =
|
|
6246
|
-
const savedPaymentKeysRef =
|
|
6247
|
-
const
|
|
6646
|
+
const autoCheckoutAttempted = useRef6(false);
|
|
6647
|
+
const paypalResumeAttempted = useRef6(false);
|
|
6648
|
+
const savedPaymentKeysRef = useRef6(null);
|
|
6649
|
+
const telemetryCheckoutContext = useMemo4(() => ({
|
|
6650
|
+
checkoutMode: checkoutModeProp ?? currentMode,
|
|
6651
|
+
layout: children ? "unknown" : layout === "buttons" ? "buttons" : "embedded"
|
|
6652
|
+
}), [checkoutModeProp, children, currentMode, layout]);
|
|
6653
|
+
const onCompleteRef = useRef6(onComplete);
|
|
6248
6654
|
onCompleteRef.current = onComplete;
|
|
6249
|
-
const onErrorRef =
|
|
6655
|
+
const onErrorRef = useRef6(onError);
|
|
6250
6656
|
onErrorRef.current = onError;
|
|
6251
|
-
const onDeclineRef =
|
|
6657
|
+
const onDeclineRef = useRef6(onDecline);
|
|
6252
6658
|
onDeclineRef.current = onDecline;
|
|
6253
|
-
|
|
6659
|
+
useEffect6(() => {
|
|
6660
|
+
getFloPayTelemetryBridge(flopay)?.setCheckoutContext(telemetryCheckoutContext);
|
|
6661
|
+
if (paypalFlopay && paypalFlopay !== flopay) {
|
|
6662
|
+
getFloPayTelemetryBridge(paypalFlopay)?.setCheckoutContext(telemetryCheckoutContext);
|
|
6663
|
+
}
|
|
6664
|
+
}, [flopay, paypalFlopay, telemetryCheckoutContext]);
|
|
6665
|
+
const onSessionCompletedRef = useRef6(onSessionCompleted);
|
|
6254
6666
|
onSessionCompletedRef.current = onSessionCompleted;
|
|
6255
6667
|
useEffect6(() => {
|
|
6256
6668
|
console.info("[FloPay] Checkout initialized", {
|
|
6257
|
-
sdk_version:
|
|
6669
|
+
sdk_version: SDK_VERSION2,
|
|
6258
6670
|
checkout_type: checkoutType,
|
|
6259
6671
|
checkout_layout: checkoutLayout,
|
|
6260
6672
|
billing_api_url: resolvedBillingUrl
|
|
@@ -6287,22 +6699,112 @@ function FloPayCheckout({
|
|
|
6287
6699
|
useEffect6(() => {
|
|
6288
6700
|
setModeError(initialErrorMessage);
|
|
6289
6701
|
}, [initialErrorMessage]);
|
|
6290
|
-
const emitDecline =
|
|
6702
|
+
const emitDecline = useCallback4(
|
|
6291
6703
|
(method, input, overrides) => {
|
|
6292
|
-
|
|
6704
|
+
invokeMerchantCallback(() => {
|
|
6705
|
+
onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
|
|
6706
|
+
});
|
|
6293
6707
|
},
|
|
6294
|
-
[]
|
|
6708
|
+
[invokeMerchantCallback]
|
|
6295
6709
|
);
|
|
6296
|
-
const runSavedPaymentFlow =
|
|
6710
|
+
const runSavedPaymentFlow = useCallback4(
|
|
6297
6711
|
async (sess, options) => {
|
|
6298
6712
|
setModeError(null);
|
|
6299
6713
|
setModeOverlayError(null);
|
|
6300
6714
|
setModeOverlayStatus("processing");
|
|
6301
6715
|
const activeSessionId2 = options?.sessionId ?? sess.id;
|
|
6716
|
+
const activeFloPay = flopayRef.current ?? paypalFlopayRef.current;
|
|
6717
|
+
const activeTelemetry = getFloPayTelemetryBridge(activeFloPay);
|
|
6718
|
+
const standaloneTelemetry = activeFloPay ? null : createStandaloneTelemetryReporter(
|
|
6719
|
+
resolvedBillingUrl,
|
|
6720
|
+
telemetry,
|
|
6721
|
+
telemetryCheckoutContext
|
|
6722
|
+
);
|
|
6723
|
+
const telemetrySource = {
|
|
6724
|
+
log: (input) => {
|
|
6725
|
+
if (activeTelemetry) activeTelemetry.log(input);
|
|
6726
|
+
else standaloneTelemetry?.log(input);
|
|
6727
|
+
},
|
|
6728
|
+
error: (input) => {
|
|
6729
|
+
if (activeTelemetry) activeTelemetry.error(input);
|
|
6730
|
+
else standaloneTelemetry?.error(input);
|
|
6731
|
+
},
|
|
6732
|
+
terminal: (input) => {
|
|
6733
|
+
if (activeTelemetry) activeTelemetry.terminal(input);
|
|
6734
|
+
else standaloneTelemetry?.terminal(input);
|
|
6735
|
+
},
|
|
6736
|
+
performance: (input) => {
|
|
6737
|
+
if (activeTelemetry) activeTelemetry.performance(input);
|
|
6738
|
+
else standaloneTelemetry?.performance(input);
|
|
6739
|
+
},
|
|
6740
|
+
now: () => activeTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
|
|
6741
|
+
elapsed: (startedAt) => activeTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
|
|
6742
|
+
};
|
|
6743
|
+
const processingStartedAt = telemetrySource.now();
|
|
6744
|
+
let recoveryFlow = Boolean(
|
|
6745
|
+
options?.initialAutoProcessingError || options?.initialAutoProcessingPending
|
|
6746
|
+
);
|
|
6747
|
+
let recoveryStarted = false;
|
|
6748
|
+
let recoveryStartedAt;
|
|
6749
|
+
const startRecovery = () => {
|
|
6750
|
+
if (recoveryStarted) return;
|
|
6751
|
+
recoveryStarted = true;
|
|
6752
|
+
recoveryFlow = true;
|
|
6753
|
+
recoveryStartedAt = telemetrySource.now();
|
|
6754
|
+
telemetrySource.log({
|
|
6755
|
+
name: "checkout.recovery.started",
|
|
6756
|
+
stage: "recovery",
|
|
6757
|
+
paymentMethodCategory: "saved"
|
|
6758
|
+
});
|
|
6759
|
+
telemetrySource.log({
|
|
6760
|
+
name: "operation.recovery.started",
|
|
6761
|
+
stage: "recovery",
|
|
6762
|
+
paymentMethodCategory: "saved"
|
|
6763
|
+
});
|
|
6764
|
+
};
|
|
6765
|
+
let processingFinished = false;
|
|
6766
|
+
const finishProcessing = () => {
|
|
6767
|
+
if (processingFinished) return;
|
|
6768
|
+
processingFinished = true;
|
|
6769
|
+
telemetrySource.log({
|
|
6770
|
+
name: "payment.processing.completed",
|
|
6771
|
+
stage: "processing",
|
|
6772
|
+
paymentMethodCategory: "saved"
|
|
6773
|
+
});
|
|
6774
|
+
telemetrySource.performance({
|
|
6775
|
+
stage: "processing",
|
|
6776
|
+
durationMs: telemetrySource.elapsed(processingStartedAt),
|
|
6777
|
+
durationMode: "machine",
|
|
6778
|
+
paymentMethodCategory: "saved"
|
|
6779
|
+
});
|
|
6780
|
+
if (recoveryStartedAt !== void 0) {
|
|
6781
|
+
telemetrySource.performance({
|
|
6782
|
+
stage: "recovery",
|
|
6783
|
+
durationMs: telemetrySource.elapsed(recoveryStartedAt),
|
|
6784
|
+
durationMode: "machine",
|
|
6785
|
+
paymentMethodCategory: "saved"
|
|
6786
|
+
});
|
|
6787
|
+
}
|
|
6788
|
+
};
|
|
6789
|
+
telemetrySource.log({
|
|
6790
|
+
name: "payment.method.selected",
|
|
6791
|
+
stage: "processing",
|
|
6792
|
+
paymentMethodCategory: "saved"
|
|
6793
|
+
});
|
|
6794
|
+
telemetrySource.log({
|
|
6795
|
+
name: "payment.processing.started",
|
|
6796
|
+
stage: "processing",
|
|
6797
|
+
paymentMethodCategory: "saved"
|
|
6798
|
+
});
|
|
6799
|
+
if (recoveryFlow) {
|
|
6800
|
+
startRecovery();
|
|
6801
|
+
}
|
|
6802
|
+
let completionCallback;
|
|
6302
6803
|
try {
|
|
6303
6804
|
const redirectResult = getRedirectResultFromCheckoutProcessError(options?.initialAutoProcessingError);
|
|
6304
6805
|
let paymentResult;
|
|
6305
6806
|
if (redirectResult) {
|
|
6807
|
+
startRecovery();
|
|
6306
6808
|
if (redirectResult.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
|
|
6307
6809
|
persistPayPalResumeState({
|
|
6308
6810
|
sessionId: activeSessionId2,
|
|
@@ -6316,13 +6818,14 @@ function FloPayCheckout({
|
|
|
6316
6818
|
attempt3DS: options?.attempt3DS,
|
|
6317
6819
|
billingApiUrl: resolvedBillingUrl,
|
|
6318
6820
|
sessionId: activeSessionId2,
|
|
6319
|
-
session: sess
|
|
6821
|
+
session: sess,
|
|
6822
|
+
telemetry: false
|
|
6320
6823
|
});
|
|
6321
6824
|
if (redirectResult.type === "paypal_redirect_required") {
|
|
6322
6825
|
clearPayPalResumeState();
|
|
6323
6826
|
}
|
|
6324
6827
|
} else if (options?.initialAutoProcessingPending) {
|
|
6325
|
-
const api = new PaymentAPI4(resolvedBillingUrl);
|
|
6828
|
+
const api = new PaymentAPI4(resolvedBillingUrl, { telemetry: false });
|
|
6326
6829
|
const completed = await api.waitForCheckoutSessionCompletion(options.initialAutoProcessingPending.sessionId, {
|
|
6327
6830
|
initialDelayMs: options.initialAutoProcessingPending.retryAfterMs
|
|
6328
6831
|
});
|
|
@@ -6350,11 +6853,13 @@ function FloPayCheckout({
|
|
|
6350
6853
|
const result = await processSavedPaymentForMode({
|
|
6351
6854
|
billingApiUrl: resolvedBillingUrl,
|
|
6352
6855
|
sessionId: activeSessionId2,
|
|
6353
|
-
session: sess
|
|
6856
|
+
session: sess,
|
|
6857
|
+
telemetry: false
|
|
6354
6858
|
});
|
|
6355
6859
|
if (result.type === "success") {
|
|
6356
6860
|
paymentResult = result.result;
|
|
6357
6861
|
} else {
|
|
6862
|
+
startRecovery();
|
|
6358
6863
|
if (result.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
|
|
6359
6864
|
persistPayPalResumeState({
|
|
6360
6865
|
sessionId: activeSessionId2,
|
|
@@ -6368,7 +6873,8 @@ function FloPayCheckout({
|
|
|
6368
6873
|
attempt3DS: options?.attempt3DS,
|
|
6369
6874
|
billingApiUrl: resolvedBillingUrl,
|
|
6370
6875
|
sessionId: activeSessionId2,
|
|
6371
|
-
session: sess
|
|
6876
|
+
session: sess,
|
|
6877
|
+
telemetry: false
|
|
6372
6878
|
});
|
|
6373
6879
|
if (result.type === "paypal_redirect_required") {
|
|
6374
6880
|
clearPayPalResumeState();
|
|
@@ -6376,20 +6882,57 @@ function FloPayCheckout({
|
|
|
6376
6882
|
}
|
|
6377
6883
|
}
|
|
6378
6884
|
if (activeSessionId2) markSessionRecentlyCompleted(activeSessionId2);
|
|
6885
|
+
finishProcessing();
|
|
6886
|
+
if (recoveryFlow) {
|
|
6887
|
+
telemetrySource.log({
|
|
6888
|
+
name: "checkout.recovery.completed",
|
|
6889
|
+
stage: "recovery",
|
|
6890
|
+
paymentMethodCategory: "saved"
|
|
6891
|
+
});
|
|
6892
|
+
telemetrySource.log({
|
|
6893
|
+
name: "operation.recovery.completed",
|
|
6894
|
+
stage: "recovery",
|
|
6895
|
+
paymentMethodCategory: "saved"
|
|
6896
|
+
});
|
|
6897
|
+
}
|
|
6898
|
+
telemetrySource.terminal({
|
|
6899
|
+
outcome: "payment_succeeded",
|
|
6900
|
+
paymentMethodCategory: "saved"
|
|
6901
|
+
});
|
|
6379
6902
|
setModeOverlayStatus("success");
|
|
6380
6903
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6381
|
-
onCompleteRef.current?.(paymentResult);
|
|
6382
|
-
return true;
|
|
6904
|
+
completionCallback = () => onCompleteRef.current?.(paymentResult);
|
|
6383
6905
|
} catch (err) {
|
|
6384
6906
|
const floPayErr = normalizeSavedPaymentError(err);
|
|
6385
6907
|
const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
|
|
6386
6908
|
setModeError(floPayErr.message);
|
|
6387
6909
|
setModeOverlayError(floPayErr.message);
|
|
6388
6910
|
if (options?.fallbackToFull) {
|
|
6911
|
+
telemetrySource.log({
|
|
6912
|
+
name: "operation.fallback",
|
|
6913
|
+
stage: "recovery",
|
|
6914
|
+
paymentMethodCategory: "saved"
|
|
6915
|
+
});
|
|
6389
6916
|
setCurrentMode("full");
|
|
6390
6917
|
replaceCheckoutModeQueryParam("full");
|
|
6391
6918
|
}
|
|
6392
|
-
|
|
6919
|
+
finishProcessing();
|
|
6920
|
+
const expectedDecline = Boolean(
|
|
6921
|
+
floPayErr.declineCode || floPayErr.code?.toLowerCase().includes("declin")
|
|
6922
|
+
);
|
|
6923
|
+
if (expectedDecline) {
|
|
6924
|
+
telemetrySource.terminal({
|
|
6925
|
+
outcome: "payment_declined",
|
|
6926
|
+
paymentMethodCategory: "saved"
|
|
6927
|
+
});
|
|
6928
|
+
} else {
|
|
6929
|
+
telemetrySource.error({
|
|
6930
|
+
errorCode: recoveryFlow ? "RECOVERY_FAILED" : "PAYMENT_PROCESSING_FAILED",
|
|
6931
|
+
stage: recoveryFlow ? "recovery" : "processing",
|
|
6932
|
+
paymentMethodCategory: "saved"
|
|
6933
|
+
});
|
|
6934
|
+
}
|
|
6935
|
+
invokeMerchantCallback(() => onErrorRef.current?.(floPayErr));
|
|
6393
6936
|
emitDecline(method, floPayErr, {
|
|
6394
6937
|
code: floPayErr.code,
|
|
6395
6938
|
declineCode: floPayErr.declineCode
|
|
@@ -6399,16 +6942,35 @@ function FloPayCheckout({
|
|
|
6399
6942
|
sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
|
|
6400
6943
|
options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
|
|
6401
6944
|
]);
|
|
6945
|
+
if (recoveryFlow) {
|
|
6946
|
+
telemetrySource.log({
|
|
6947
|
+
name: "checkout.recovery.completed",
|
|
6948
|
+
stage: "recovery",
|
|
6949
|
+
paymentMethodCategory: "saved"
|
|
6950
|
+
});
|
|
6951
|
+
telemetrySource.log({
|
|
6952
|
+
name: "operation.recovery.completed",
|
|
6953
|
+
stage: "recovery",
|
|
6954
|
+
paymentMethodCategory: "saved"
|
|
6955
|
+
});
|
|
6956
|
+
}
|
|
6402
6957
|
return false;
|
|
6403
6958
|
} finally {
|
|
6959
|
+
finishProcessing();
|
|
6960
|
+
if (standaloneTelemetry) finishStandaloneTelemetry(standaloneTelemetry);
|
|
6404
6961
|
setModeOverlayStatus(null);
|
|
6405
6962
|
setModeOverlayError(null);
|
|
6406
6963
|
}
|
|
6964
|
+
invokeMerchantCallback(completionCallback);
|
|
6965
|
+
return true;
|
|
6407
6966
|
},
|
|
6408
6967
|
[
|
|
6409
6968
|
emitDecline,
|
|
6969
|
+
invokeMerchantCallback,
|
|
6410
6970
|
normalizeSavedPaymentError,
|
|
6411
|
-
resolvedBillingUrl
|
|
6971
|
+
resolvedBillingUrl,
|
|
6972
|
+
telemetry,
|
|
6973
|
+
telemetryCheckoutContext
|
|
6412
6974
|
]
|
|
6413
6975
|
);
|
|
6414
6976
|
useEffect6(() => {
|
|
@@ -6426,6 +6988,8 @@ function FloPayCheckout({
|
|
|
6426
6988
|
}
|
|
6427
6989
|
paypalResumeAttempted.current = true;
|
|
6428
6990
|
void (async () => {
|
|
6991
|
+
let resumeTelemetry;
|
|
6992
|
+
let redirectResumeStartedAt = 0;
|
|
6429
6993
|
setModeError(null);
|
|
6430
6994
|
setModeOverlayError(null);
|
|
6431
6995
|
setModeOverlayStatus("processing");
|
|
@@ -6433,7 +6997,11 @@ function FloPayCheckout({
|
|
|
6433
6997
|
try {
|
|
6434
6998
|
if (params.get("redirect_status") === "failed") {
|
|
6435
6999
|
throw Object.assign(
|
|
6436
|
-
new FloPayError5(
|
|
7000
|
+
new FloPayError5(
|
|
7001
|
+
"PayPal payment was declined. Please try again.",
|
|
7002
|
+
"api_error",
|
|
7003
|
+
{ declineCode: "paypal_redirect_failed" }
|
|
7004
|
+
),
|
|
6437
7005
|
{ checkoutMethod: "paypal" }
|
|
6438
7006
|
);
|
|
6439
7007
|
}
|
|
@@ -6444,7 +7012,22 @@ function FloPayCheckout({
|
|
|
6444
7012
|
publishableKey: resumeState.publishableKey,
|
|
6445
7013
|
paypalPublishableKey: resumeState.paypalPublishableKey,
|
|
6446
7014
|
billingApiUrl: resolvedBillingUrl,
|
|
6447
|
-
locale
|
|
7015
|
+
locale,
|
|
7016
|
+
telemetry
|
|
7017
|
+
});
|
|
7018
|
+
resumeTelemetry = getFloPayTelemetryBridge(resumePaypalFlopay ?? resumeFlopay);
|
|
7019
|
+
redirectResumeStartedAt = resumeTelemetry?.now() ?? 0;
|
|
7020
|
+
resumeTelemetry?.log({
|
|
7021
|
+
name: "provider.redirect.resumed",
|
|
7022
|
+
stage: "redirect_resume",
|
|
7023
|
+
provider: "paypal",
|
|
7024
|
+
paymentMethodCategory: "paypal"
|
|
7025
|
+
});
|
|
7026
|
+
resumeTelemetry?.log({
|
|
7027
|
+
name: "operation.recovery.started",
|
|
7028
|
+
stage: "recovery",
|
|
7029
|
+
provider: "paypal",
|
|
7030
|
+
paymentMethodCategory: "paypal"
|
|
6448
7031
|
});
|
|
6449
7032
|
const paypalStripe = (resumePaypalFlopay ?? resumeFlopay)?.getRawProvider();
|
|
6450
7033
|
if (!paypalStripe) {
|
|
@@ -6474,7 +7057,7 @@ function FloPayCheckout({
|
|
|
6474
7057
|
const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
|
|
6475
7058
|
let finalResultStatus = resultStatus;
|
|
6476
7059
|
if (resumeState.sessionId) {
|
|
6477
|
-
const resumeApi = new PaymentAPI4(resolvedBillingUrl);
|
|
7060
|
+
const resumeApi = new PaymentAPI4(resolvedBillingUrl, { telemetry: false });
|
|
6478
7061
|
const resumeSessionResult = await resumeApi.getUnifiedCheckoutSession(resumeState.sessionId);
|
|
6479
7062
|
const resumeSession = resumeSessionResult.data.session;
|
|
6480
7063
|
if (resumeSession && resumeSession.status !== "complete") {
|
|
@@ -6482,6 +7065,7 @@ function FloPayCheckout({
|
|
|
6482
7065
|
billingApiUrl: resolvedBillingUrl,
|
|
6483
7066
|
sessionId: resumeState.sessionId,
|
|
6484
7067
|
session: resumeSession,
|
|
7068
|
+
telemetry: false,
|
|
6485
7069
|
tokenizedData: {
|
|
6486
7070
|
id: paymentMethodId ?? paymentIntent.id,
|
|
6487
7071
|
type: "card",
|
|
@@ -6499,20 +7083,84 @@ function FloPayCheckout({
|
|
|
6499
7083
|
}
|
|
6500
7084
|
}
|
|
6501
7085
|
if (resumeState.sessionId) markSessionRecentlyCompleted(resumeState.sessionId);
|
|
7086
|
+
resumeTelemetry?.log({
|
|
7087
|
+
name: "operation.recovery.completed",
|
|
7088
|
+
stage: "recovery",
|
|
7089
|
+
provider: "paypal",
|
|
7090
|
+
paymentMethodCategory: "paypal"
|
|
7091
|
+
});
|
|
7092
|
+
resumeTelemetry?.performance({
|
|
7093
|
+
stage: "redirect_resume",
|
|
7094
|
+
durationMs: resumeTelemetry.elapsed(redirectResumeStartedAt),
|
|
7095
|
+
durationMode: "machine",
|
|
7096
|
+
provider: "paypal",
|
|
7097
|
+
paymentMethodCategory: "paypal"
|
|
7098
|
+
});
|
|
7099
|
+
resumeTelemetry?.terminal({
|
|
7100
|
+
outcome: "payment_succeeded",
|
|
7101
|
+
provider: "paypal",
|
|
7102
|
+
paymentMethodCategory: "paypal"
|
|
7103
|
+
});
|
|
6502
7104
|
setModeOverlayStatus("success");
|
|
6503
7105
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6504
|
-
onCompleteRef.current?.({
|
|
7106
|
+
invokeMerchantCallback(() => onCompleteRef.current?.({
|
|
6505
7107
|
status: finalResultStatus,
|
|
6506
7108
|
paymentIntentId: paymentIntent.id,
|
|
6507
7109
|
paymentMethodId,
|
|
6508
7110
|
checkoutMethod: "paypal"
|
|
6509
|
-
});
|
|
7111
|
+
}));
|
|
6510
7112
|
} catch (err) {
|
|
6511
7113
|
const floPayErr = normalizeSavedPaymentError(err);
|
|
6512
7114
|
const method = floPayErr.checkoutMethod ?? "paypal";
|
|
7115
|
+
const expectedDecline = Boolean(
|
|
7116
|
+
floPayErr.declineCode || floPayErr.code?.toLowerCase().includes("declin")
|
|
7117
|
+
);
|
|
7118
|
+
if (resumeTelemetry) {
|
|
7119
|
+
resumeTelemetry.performance({
|
|
7120
|
+
stage: "redirect_resume",
|
|
7121
|
+
durationMs: resumeTelemetry.elapsed(redirectResumeStartedAt),
|
|
7122
|
+
durationMode: "machine",
|
|
7123
|
+
provider: "paypal",
|
|
7124
|
+
paymentMethodCategory: "paypal"
|
|
7125
|
+
});
|
|
7126
|
+
if (expectedDecline) {
|
|
7127
|
+
resumeTelemetry.terminal({
|
|
7128
|
+
outcome: "payment_declined",
|
|
7129
|
+
provider: "paypal",
|
|
7130
|
+
paymentMethodCategory: "paypal"
|
|
7131
|
+
});
|
|
7132
|
+
} else {
|
|
7133
|
+
resumeTelemetry.error({
|
|
7134
|
+
errorCode: "REDIRECT_RESUME_FAILED",
|
|
7135
|
+
stage: "redirect_resume",
|
|
7136
|
+
provider: "paypal",
|
|
7137
|
+
paymentMethodCategory: "paypal"
|
|
7138
|
+
});
|
|
7139
|
+
}
|
|
7140
|
+
} else if (expectedDecline) {
|
|
7141
|
+
const reporter = createStandaloneTelemetryReporter(
|
|
7142
|
+
resolvedBillingUrl,
|
|
7143
|
+
telemetry,
|
|
7144
|
+
telemetryCheckoutContext
|
|
7145
|
+
);
|
|
7146
|
+
reporter.terminal({
|
|
7147
|
+
outcome: "payment_declined",
|
|
7148
|
+
provider: "paypal",
|
|
7149
|
+
paymentMethodCategory: "paypal"
|
|
7150
|
+
});
|
|
7151
|
+
finishStandaloneTelemetry(reporter);
|
|
7152
|
+
} else {
|
|
7153
|
+
reportStandaloneTelemetryError(
|
|
7154
|
+
resolvedBillingUrl,
|
|
7155
|
+
telemetry,
|
|
7156
|
+
telemetryCheckoutContext,
|
|
7157
|
+
"REDIRECT_RESUME_FAILED",
|
|
7158
|
+
"redirect_resume"
|
|
7159
|
+
);
|
|
7160
|
+
}
|
|
6513
7161
|
setModeError(floPayErr.message);
|
|
6514
7162
|
setModeOverlayError(floPayErr.message);
|
|
6515
|
-
onErrorRef.current?.(floPayErr);
|
|
7163
|
+
invokeMerchantCallback(() => onErrorRef.current?.(floPayErr));
|
|
6516
7164
|
emitDecline(method, floPayErr, {
|
|
6517
7165
|
code: floPayErr.code,
|
|
6518
7166
|
declineCode: floPayErr.declineCode
|
|
@@ -6527,8 +7175,8 @@ function FloPayCheckout({
|
|
|
6527
7175
|
setConfirmProcessing(false);
|
|
6528
7176
|
}
|
|
6529
7177
|
})();
|
|
6530
|
-
}, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
|
|
6531
|
-
const initializedHashRef =
|
|
7178
|
+
}, [emitDecline, invokeMerchantCallback, locale, normalizeSavedPaymentError, resolvedBillingUrl, telemetry]);
|
|
7179
|
+
const initializedHashRef = useRef6(null);
|
|
6532
7180
|
function hashCreateParams(params) {
|
|
6533
7181
|
const key = JSON.stringify({
|
|
6534
7182
|
c: params?.clientId,
|
|
@@ -6565,7 +7213,7 @@ function FloPayCheckout({
|
|
|
6565
7213
|
() => effectiveCreateSession ? hashCreateParams(effectiveCreateSession) : "",
|
|
6566
7214
|
[effectiveCreateSession]
|
|
6567
7215
|
);
|
|
6568
|
-
const createSessionParamsRef =
|
|
7216
|
+
const createSessionParamsRef = useRef6(effectiveCreateSession);
|
|
6569
7217
|
createSessionParamsRef.current = effectiveCreateSession;
|
|
6570
7218
|
useEffect6(() => {
|
|
6571
7219
|
setResolvedSessionId(sessionIdProp ?? "");
|
|
@@ -6577,44 +7225,107 @@ function FloPayCheckout({
|
|
|
6577
7225
|
setModeOverlayStatus(null);
|
|
6578
7226
|
}, [createSessionHash, initialErrorMessage, sessionIdProp]);
|
|
6579
7227
|
async function resolveInlineSession(params, cacheKey) {
|
|
6580
|
-
const
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
7228
|
+
const reporter = createStandaloneTelemetryReporter(
|
|
7229
|
+
resolvedBillingUrl,
|
|
7230
|
+
telemetry,
|
|
7231
|
+
telemetryCheckoutContext
|
|
7232
|
+
);
|
|
7233
|
+
const api = new PaymentAPI4(resolvedBillingUrl, { telemetry: false });
|
|
7234
|
+
try {
|
|
7235
|
+
const cached = readCachedInlineSession(cacheKey);
|
|
7236
|
+
reporter.log({
|
|
7237
|
+
name: cached ? "operation.cache.hit" : "operation.cache.miss",
|
|
7238
|
+
stage: "session_create",
|
|
7239
|
+
requestCategory: "session_create"
|
|
7240
|
+
});
|
|
7241
|
+
let sid = cached?.sid ?? null;
|
|
7242
|
+
let realResult = null;
|
|
7243
|
+
if (sid) {
|
|
7244
|
+
try {
|
|
7245
|
+
realResult = await api.getUnifiedCheckoutSession(sid, cached?.nonce);
|
|
7246
|
+
const status = realResult.data.session?.status;
|
|
7247
|
+
if (status === "complete") {
|
|
7248
|
+
if (wasSessionRecentlyCompleted(sid)) {
|
|
7249
|
+
return { sid, result: realResult };
|
|
7250
|
+
}
|
|
7251
|
+
clearCachedInlineSession(cacheKey);
|
|
7252
|
+
sid = null;
|
|
7253
|
+
realResult = null;
|
|
6591
7254
|
}
|
|
7255
|
+
} catch {
|
|
7256
|
+
reporter.log({
|
|
7257
|
+
name: "operation.fallback",
|
|
7258
|
+
stage: "session_read",
|
|
7259
|
+
requestCategory: "session_read"
|
|
7260
|
+
});
|
|
6592
7261
|
clearCachedInlineSession(cacheKey);
|
|
6593
7262
|
sid = null;
|
|
6594
|
-
realResult = null;
|
|
6595
7263
|
}
|
|
6596
|
-
} catch {
|
|
6597
|
-
clearCachedInlineSession(cacheKey);
|
|
6598
|
-
sid = null;
|
|
6599
7264
|
}
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
7265
|
+
if (!sid) {
|
|
7266
|
+
const sessionCreateStartedAt = reporter.now();
|
|
7267
|
+
reporter.log({
|
|
7268
|
+
name: "session.create.started",
|
|
7269
|
+
stage: "session_create",
|
|
7270
|
+
requestCategory: "session_create"
|
|
7271
|
+
});
|
|
7272
|
+
const paramsWithAnalytics = {
|
|
7273
|
+
...params,
|
|
7274
|
+
avsCheck: !!enableAVS,
|
|
7275
|
+
avsConfig: typeof enableAVS === "object" ? enableAVS : void 0,
|
|
7276
|
+
checkoutType: "embedded_checkout",
|
|
7277
|
+
checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout"
|
|
7278
|
+
};
|
|
7279
|
+
try {
|
|
7280
|
+
realResult = await api.createAndFetchSession(paramsWithAnalytics);
|
|
7281
|
+
reporter.log({
|
|
7282
|
+
name: "session.request.completed",
|
|
7283
|
+
stage: "session_complete",
|
|
7284
|
+
requestCategory: "session_create",
|
|
7285
|
+
statusClass: "2xx"
|
|
7286
|
+
});
|
|
7287
|
+
reporter.performance({
|
|
7288
|
+
stage: "session_create",
|
|
7289
|
+
durationMs: reporter.now() - sessionCreateStartedAt,
|
|
7290
|
+
durationMode: "machine",
|
|
7291
|
+
requestCategory: "session_create",
|
|
7292
|
+
statusClass: "2xx"
|
|
7293
|
+
});
|
|
7294
|
+
} catch (error) {
|
|
7295
|
+
reporter.performance({
|
|
7296
|
+
stage: "session_create",
|
|
7297
|
+
durationMs: reporter.now() - sessionCreateStartedAt,
|
|
7298
|
+
durationMode: "machine",
|
|
7299
|
+
requestCategory: "session_create",
|
|
7300
|
+
statusClass: "network_error"
|
|
7301
|
+
});
|
|
7302
|
+
if (error instanceof FloPayError5 && error.type === "validation_error") {
|
|
7303
|
+
reporter.terminal({
|
|
7304
|
+
outcome: "validation_rejected",
|
|
7305
|
+
stage: "session_create",
|
|
7306
|
+
paymentMethodCategory: "unknown"
|
|
7307
|
+
});
|
|
7308
|
+
} else {
|
|
7309
|
+
reporter.error({
|
|
7310
|
+
errorCode: "CHECKOUT_SESSION_CREATE_FAILED",
|
|
7311
|
+
stage: "session_create",
|
|
7312
|
+
paymentMethodCategory: "unknown",
|
|
7313
|
+
requestCategory: "session_create"
|
|
7314
|
+
});
|
|
7315
|
+
}
|
|
7316
|
+
throw error;
|
|
7317
|
+
}
|
|
7318
|
+
sid = realResult.data.session?.id ?? "";
|
|
7319
|
+
if (sid) {
|
|
7320
|
+
persistCachedInlineSession(cacheKey, sid, realResult.data.session?.clientSecret);
|
|
7321
|
+
}
|
|
6613
7322
|
}
|
|
7323
|
+
return { sid: sid ?? "", result: realResult };
|
|
7324
|
+
} finally {
|
|
7325
|
+
finishStandaloneTelemetry(reporter);
|
|
6614
7326
|
}
|
|
6615
|
-
return { sid: sid ?? "", result: realResult };
|
|
6616
7327
|
}
|
|
6617
|
-
const bootstrapInlineSession =
|
|
7328
|
+
const bootstrapInlineSession = useCallback4(
|
|
6618
7329
|
async (patch) => {
|
|
6619
7330
|
const baseParams = createSessionParamsRef.current;
|
|
6620
7331
|
if (!baseParams) {
|
|
@@ -6658,7 +7369,8 @@ function FloPayCheckout({
|
|
|
6658
7369
|
publishableKey,
|
|
6659
7370
|
paypalPublishableKey,
|
|
6660
7371
|
billingApiUrl: resolvedBillingUrl,
|
|
6661
|
-
locale
|
|
7372
|
+
locale,
|
|
7373
|
+
telemetry
|
|
6662
7374
|
});
|
|
6663
7375
|
flopayRef.current = instance;
|
|
6664
7376
|
setFloPay(instance);
|
|
@@ -6673,9 +7385,9 @@ function FloPayCheckout({
|
|
|
6673
7385
|
}
|
|
6674
7386
|
return resolved;
|
|
6675
7387
|
},
|
|
6676
|
-
[locale, resolvedBillingUrl]
|
|
7388
|
+
[locale, resolvedBillingUrl, telemetry, telemetryCheckoutContext]
|
|
6677
7389
|
);
|
|
6678
|
-
const handleInlineSessionPatch =
|
|
7390
|
+
const handleInlineSessionPatch = useCallback4(async (patch) => {
|
|
6679
7391
|
if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) {
|
|
6680
7392
|
return {
|
|
6681
7393
|
sessionId: resolvedSessionId,
|
|
@@ -6752,7 +7464,9 @@ function FloPayCheckout({
|
|
|
6752
7464
|
setModeOverlayStatus("success");
|
|
6753
7465
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6754
7466
|
if (!cancelled) {
|
|
6755
|
-
|
|
7467
|
+
invokeMerchantCallback(() => {
|
|
7468
|
+
onCompleteRef.current?.({ status: "succeeded" });
|
|
7469
|
+
});
|
|
6756
7470
|
setModeOverlayStatus(null);
|
|
6757
7471
|
setModeOverlayError(null);
|
|
6758
7472
|
}
|
|
@@ -6772,8 +7486,9 @@ function FloPayCheckout({
|
|
|
6772
7486
|
}
|
|
6773
7487
|
setIsLoading(true);
|
|
6774
7488
|
async function init() {
|
|
7489
|
+
let sessionReadCompleted = false;
|
|
6775
7490
|
try {
|
|
6776
|
-
const api = new PaymentAPI4(resolvedBillingUrl);
|
|
7491
|
+
const api = new PaymentAPI4(resolvedBillingUrl, { telemetry: false });
|
|
6777
7492
|
const result = await api.getUnifiedCheckoutSession(activeSessionId, nonceProp);
|
|
6778
7493
|
if (cancelled) return;
|
|
6779
7494
|
setUnified(result);
|
|
@@ -6784,7 +7499,9 @@ function FloPayCheckout({
|
|
|
6784
7499
|
}
|
|
6785
7500
|
if (sess.status === "complete") {
|
|
6786
7501
|
setIsLoading(false);
|
|
6787
|
-
|
|
7502
|
+
invokeMerchantCallback(() => {
|
|
7503
|
+
onSessionCompletedRef.current?.(sess.successUrl ?? "");
|
|
7504
|
+
});
|
|
6788
7505
|
return;
|
|
6789
7506
|
}
|
|
6790
7507
|
if (sess.status === "expired") {
|
|
@@ -6792,6 +7509,7 @@ function FloPayCheckout({
|
|
|
6792
7509
|
code: "checkout_session_expired"
|
|
6793
7510
|
});
|
|
6794
7511
|
}
|
|
7512
|
+
sessionReadCompleted = true;
|
|
6795
7513
|
const effectiveMode = checkoutModeProp ?? sess.checkoutMode ?? "full";
|
|
6796
7514
|
setCurrentMode(effectiveMode);
|
|
6797
7515
|
const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
|
|
@@ -6823,6 +7541,23 @@ function FloPayCheckout({
|
|
|
6823
7541
|
if (!cancelled) setIsLoading(false);
|
|
6824
7542
|
} catch (err) {
|
|
6825
7543
|
if (cancelled) return;
|
|
7544
|
+
if (!(err instanceof FloPayError5)) {
|
|
7545
|
+
reportStandaloneTelemetryError(
|
|
7546
|
+
resolvedBillingUrl,
|
|
7547
|
+
telemetry,
|
|
7548
|
+
telemetryCheckoutContext,
|
|
7549
|
+
"INTERNAL_SDK_ERROR",
|
|
7550
|
+
"checkout_mount"
|
|
7551
|
+
);
|
|
7552
|
+
} else if (!sessionReadCompleted && !isExpectedExistingSessionError(err)) {
|
|
7553
|
+
reportStandaloneTelemetryError(
|
|
7554
|
+
resolvedBillingUrl,
|
|
7555
|
+
telemetry,
|
|
7556
|
+
telemetryCheckoutContext,
|
|
7557
|
+
"NETWORK_REQUEST_FAILED",
|
|
7558
|
+
"session_read"
|
|
7559
|
+
);
|
|
7560
|
+
}
|
|
6826
7561
|
const floPayErr = err instanceof FloPayError5 ? err : new FloPayError5(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
6827
7562
|
setLoadError(floPayErr);
|
|
6828
7563
|
setIsLoading(false);
|
|
@@ -6844,7 +7579,8 @@ function FloPayCheckout({
|
|
|
6844
7579
|
publishableKey,
|
|
6845
7580
|
paypalPublishableKey,
|
|
6846
7581
|
billingApiUrl: resolvedBillingUrl,
|
|
6847
|
-
locale
|
|
7582
|
+
locale,
|
|
7583
|
+
telemetry
|
|
6848
7584
|
});
|
|
6849
7585
|
flopayRef.current = instance;
|
|
6850
7586
|
setFloPay(instance);
|
|
@@ -6861,10 +7597,11 @@ function FloPayCheckout({
|
|
|
6861
7597
|
createSessionHash,
|
|
6862
7598
|
effectiveCreateSessionMode,
|
|
6863
7599
|
initSessionDependency,
|
|
7600
|
+
invokeMerchantCallback,
|
|
6864
7601
|
nonceProp,
|
|
6865
7602
|
runSavedPaymentFlow
|
|
6866
7603
|
]);
|
|
6867
|
-
const handleConfirmCheckout =
|
|
7604
|
+
const handleConfirmCheckout = useCallback4(async () => {
|
|
6868
7605
|
if (confirmProcessing || !session) return;
|
|
6869
7606
|
setConfirmProcessing(true);
|
|
6870
7607
|
setModeError(null);
|
|
@@ -7028,7 +7765,7 @@ function FloPayCheckout({
|
|
|
7028
7765
|
}
|
|
7029
7766
|
),
|
|
7030
7767
|
/* @__PURE__ */ jsx8(
|
|
7031
|
-
|
|
7768
|
+
InstrumentedDirectPayPalButton,
|
|
7032
7769
|
{
|
|
7033
7770
|
sessionId: activeSessionId,
|
|
7034
7771
|
nonce: session.clientSecret || void 0,
|
|
@@ -7043,6 +7780,8 @@ function FloPayCheckout({
|
|
|
7043
7780
|
onDecline,
|
|
7044
7781
|
onButtonClick,
|
|
7045
7782
|
session,
|
|
7783
|
+
telemetry,
|
|
7784
|
+
telemetryContext: telemetryCheckoutContext,
|
|
7046
7785
|
debug
|
|
7047
7786
|
}
|
|
7048
7787
|
)
|
|
@@ -7431,7 +8170,7 @@ function InterimButtonsView({
|
|
|
7431
8170
|
// src/checkout-form.tsx
|
|
7432
8171
|
import { PaymentAPI as PaymentAPI5 } from "@flopay/js";
|
|
7433
8172
|
import { FloPayError as FloPayError6 } from "@flopay/shared";
|
|
7434
|
-
import { forwardRef as forwardRef2, useCallback as
|
|
8173
|
+
import { forwardRef as forwardRef2, useCallback as useCallback5, useEffect as useEffect7, useImperativeHandle as useImperativeHandle2, useState as useState5 } from "react";
|
|
7435
8174
|
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
7436
8175
|
var WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
7437
8176
|
var CheckoutForm = forwardRef2(
|
|
@@ -7473,20 +8212,20 @@ function CheckoutFormInner({
|
|
|
7473
8212
|
const isSubmitting = externalProcessing ?? processing;
|
|
7474
8213
|
const isSelfContained = !onTokenizedBody;
|
|
7475
8214
|
const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
|
|
7476
|
-
const updateError =
|
|
8215
|
+
const updateError = useCallback5(
|
|
7477
8216
|
(err) => {
|
|
7478
8217
|
setError(err);
|
|
7479
8218
|
onErrorChange?.(err);
|
|
7480
8219
|
},
|
|
7481
8220
|
[onErrorChange]
|
|
7482
8221
|
);
|
|
7483
|
-
const emitDecline =
|
|
8222
|
+
const emitDecline = useCallback5(
|
|
7484
8223
|
(input, overrides) => {
|
|
7485
8224
|
onDecline?.(buildDeclineEvent("card", input, overrides));
|
|
7486
8225
|
},
|
|
7487
8226
|
[onDecline]
|
|
7488
8227
|
);
|
|
7489
|
-
const processPaymentInternal =
|
|
8228
|
+
const processPaymentInternal = useCallback5(
|
|
7490
8229
|
async (tokenizedBody, completionPaymentMethodId) => {
|
|
7491
8230
|
setProcessing(true);
|
|
7492
8231
|
updateError(null);
|
|
@@ -7598,7 +8337,7 @@ function CheckoutFormInner({
|
|
|
7598
8337
|
},
|
|
7599
8338
|
[baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, flopay, paypalFlopay, onComplete, onError, onDecline, updateError, emitDecline]
|
|
7600
8339
|
);
|
|
7601
|
-
const dispatchTokenizedBody =
|
|
8340
|
+
const dispatchTokenizedBody = useCallback5(
|
|
7602
8341
|
(tokenizedBody) => {
|
|
7603
8342
|
if (onTokenizedBody) {
|
|
7604
8343
|
onTokenizedBody(tokenizedBody);
|
|
@@ -7653,7 +8392,7 @@ function CheckoutFormInner({
|
|
|
7653
8392
|
localStorage.removeItem(WALLET_RESUME_KEY);
|
|
7654
8393
|
}
|
|
7655
8394
|
}, [sessionId, dispatchTokenizedBody]);
|
|
7656
|
-
const handleSubmit =
|
|
8395
|
+
const handleSubmit = useCallback5(
|
|
7657
8396
|
async (e) => {
|
|
7658
8397
|
e.preventDefault();
|
|
7659
8398
|
if (!flopay || !elements || isSubmitting) return;
|
|
@@ -7776,7 +8515,7 @@ function CheckoutFormInner({
|
|
|
7776
8515
|
// src/paypal-button.tsx
|
|
7777
8516
|
import { PaymentAPI as PaymentAPI6 } from "@flopay/js";
|
|
7778
8517
|
import { FloPayError as FloPayError7 } from "@flopay/shared";
|
|
7779
|
-
import { useCallback as
|
|
8518
|
+
import { useCallback as useCallback6, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
7780
8519
|
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
7781
8520
|
function PayPalButton({
|
|
7782
8521
|
sessionId,
|
|
@@ -7797,9 +8536,9 @@ function PayPalButton({
|
|
|
7797
8536
|
const contextBillingUrl = useBillingApiUrl();
|
|
7798
8537
|
const [ready, setReady] = useState6(false);
|
|
7799
8538
|
const [submitting, setSubmitting] = useState6(false);
|
|
7800
|
-
const paypalResumeAttempted =
|
|
8539
|
+
const paypalResumeAttempted = useRef7(false);
|
|
7801
8540
|
const baseUrl = (billingApiUrl || contextBillingUrl).replace(/\/+$/, "");
|
|
7802
|
-
const processPaymentInternal =
|
|
8541
|
+
const processPaymentInternal = useCallback6(
|
|
7803
8542
|
async (tokenizedBody) => {
|
|
7804
8543
|
try {
|
|
7805
8544
|
const api = new PaymentAPI6(baseUrl);
|
|
@@ -7827,7 +8566,7 @@ function PayPalButton({
|
|
|
7827
8566
|
},
|
|
7828
8567
|
[baseUrl, sessionId, nonce, userId, email, firstName, lastName, chv, onComplete, onErrorChange]
|
|
7829
8568
|
);
|
|
7830
|
-
const dispatchTokenizedBody =
|
|
8569
|
+
const dispatchTokenizedBody = useCallback6(
|
|
7831
8570
|
(body) => {
|
|
7832
8571
|
if (onTokenizedBody) {
|
|
7833
8572
|
onTokenizedBody(body);
|
|
@@ -7885,7 +8624,7 @@ function PayPalButton({
|
|
|
7885
8624
|
}
|
|
7886
8625
|
})();
|
|
7887
8626
|
}, [flopay, dispatchTokenizedBody, onErrorChange]);
|
|
7888
|
-
const handlePayPalConfirm =
|
|
8627
|
+
const handlePayPalConfirm = useCallback6(async () => {
|
|
7889
8628
|
if (!flopay || !elements) return;
|
|
7890
8629
|
try {
|
|
7891
8630
|
setSubmitting(true);
|
|
@@ -7965,7 +8704,7 @@ function PayPalButton({
|
|
|
7965
8704
|
}
|
|
7966
8705
|
|
|
7967
8706
|
// src/automatic-payment-button.tsx
|
|
7968
|
-
import { useCallback as
|
|
8707
|
+
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef8, useState as useState7 } from "react";
|
|
7969
8708
|
import { PaymentAPI as PaymentAPI7 } from "@flopay/js";
|
|
7970
8709
|
import { FloPayError as FloPayError8, resolveBillingApiUrl as resolveBillingApiUrl4, resolveButtonsLayoutTheme as resolveButtonsLayoutTheme3, resolveTheme as resolveTheme3 } from "@flopay/shared";
|
|
7971
8710
|
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -8084,12 +8823,12 @@ function FloPayAutomaticPaymentButton({
|
|
|
8084
8823
|
const [overlayStatus, setOverlayStatus] = useState7(null);
|
|
8085
8824
|
const [overlayError, setOverlayError] = useState7(null);
|
|
8086
8825
|
const [fallbackSession, setFallbackSession] = useState7(null);
|
|
8087
|
-
const isMountedRef =
|
|
8088
|
-
const threeDsAbortRef =
|
|
8089
|
-
const fallbackSessionRef =
|
|
8090
|
-
const onSuccessRef =
|
|
8091
|
-
const onErrorRef =
|
|
8092
|
-
const onDeclineRef =
|
|
8826
|
+
const isMountedRef = useRef8(true);
|
|
8827
|
+
const threeDsAbortRef = useRef8(null);
|
|
8828
|
+
const fallbackSessionRef = useRef8(fallbackSession);
|
|
8829
|
+
const onSuccessRef = useRef8(onSuccess);
|
|
8830
|
+
const onErrorRef = useRef8(onError);
|
|
8831
|
+
const onDeclineRef = useRef8(onDecline);
|
|
8093
8832
|
useEffect9(() => {
|
|
8094
8833
|
fallbackSessionRef.current = fallbackSession;
|
|
8095
8834
|
}, [fallbackSession]);
|
|
@@ -8110,7 +8849,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8110
8849
|
};
|
|
8111
8850
|
}, []);
|
|
8112
8851
|
useEffect9(() => {
|
|
8113
|
-
if (
|
|
8852
|
+
if (typeof window === "undefined") {
|
|
8114
8853
|
return;
|
|
8115
8854
|
}
|
|
8116
8855
|
const handleKeyDown = (event) => {
|
|
@@ -8122,14 +8861,14 @@ function FloPayAutomaticPaymentButton({
|
|
|
8122
8861
|
return () => {
|
|
8123
8862
|
window.removeEventListener("keydown", handleKeyDown);
|
|
8124
8863
|
};
|
|
8125
|
-
}, [
|
|
8126
|
-
const emitDecline =
|
|
8864
|
+
}, []);
|
|
8865
|
+
const emitDecline = useCallback7((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
|
|
8127
8866
|
onDeclineRef.current?.(buildDeclineEvent(method, error, {
|
|
8128
8867
|
code: error.code,
|
|
8129
8868
|
declineCode: error.declineCode
|
|
8130
8869
|
}));
|
|
8131
8870
|
}, []);
|
|
8132
|
-
const showSuccess =
|
|
8871
|
+
const showSuccess = useCallback7(async (event) => {
|
|
8133
8872
|
if (!isMountedRef.current) return;
|
|
8134
8873
|
setOverlayError(null);
|
|
8135
8874
|
setOverlayStatus("success");
|
|
@@ -8137,7 +8876,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8137
8876
|
if (!isMountedRef.current) return;
|
|
8138
8877
|
onSuccessRef.current?.(event);
|
|
8139
8878
|
}, []);
|
|
8140
|
-
const showError =
|
|
8879
|
+
const showError = useCallback7(async (error, options) => {
|
|
8141
8880
|
if (!isMountedRef.current) return;
|
|
8142
8881
|
onErrorRef.current?.(error);
|
|
8143
8882
|
if (options?.emitDecline) {
|
|
@@ -8147,7 +8886,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8147
8886
|
setOverlayStatus("error");
|
|
8148
8887
|
await sleep2(PROCESSING_OVERLAY_ERROR_DELAY_MS);
|
|
8149
8888
|
}, [emitDecline]);
|
|
8150
|
-
const processResolvedSession =
|
|
8889
|
+
const processResolvedSession = useCallback7(async (apiResult, resolvedSessionId, options) => {
|
|
8151
8890
|
const session = apiResult.data.session ?? null;
|
|
8152
8891
|
if (!session) {
|
|
8153
8892
|
throw new FloPayError8("No session data returned", "api_error");
|
|
@@ -8303,7 +9042,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8303
9042
|
showError,
|
|
8304
9043
|
showSuccess
|
|
8305
9044
|
]);
|
|
8306
|
-
const handleButtonClick =
|
|
9045
|
+
const handleButtonClick = useCallback7(async (event) => {
|
|
8307
9046
|
buttonProps.onClick?.(event);
|
|
8308
9047
|
if (event.defaultPrevented || disabled || isProcessing) {
|
|
8309
9048
|
return;
|
|
@@ -8385,7 +9124,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8385
9124
|
showError,
|
|
8386
9125
|
showSuccess
|
|
8387
9126
|
]);
|
|
8388
|
-
const handleFallbackComplete =
|
|
9127
|
+
const handleFallbackComplete = useCallback7((result) => {
|
|
8389
9128
|
const activeFallback = fallbackSessionRef.current;
|
|
8390
9129
|
setFallbackSession(null);
|
|
8391
9130
|
onSuccessRef.current?.({
|
|
@@ -8395,10 +9134,10 @@ function FloPayAutomaticPaymentButton({
|
|
|
8395
9134
|
autoCompleted: false
|
|
8396
9135
|
});
|
|
8397
9136
|
}, []);
|
|
8398
|
-
const handleFallbackError =
|
|
9137
|
+
const handleFallbackError = useCallback7((error) => {
|
|
8399
9138
|
onErrorRef.current?.(error);
|
|
8400
9139
|
}, []);
|
|
8401
|
-
const handleFallbackDecline =
|
|
9140
|
+
const handleFallbackDecline = useCallback7((decline) => {
|
|
8402
9141
|
onDeclineRef.current?.(decline);
|
|
8403
9142
|
}, []);
|
|
8404
9143
|
const themeBundle = useMemo5(() => resolveTheme3(theme), [theme]);
|