@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.cjs
CHANGED
|
@@ -69,6 +69,59 @@ var CheckoutContext = (0, import_react.createContext)({
|
|
|
69
69
|
error: null
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
// src/telemetry-bridge.ts
|
|
73
|
+
var FLOPAY_TELEMETRY_BRIDGE = /* @__PURE__ */ Symbol.for("@flopay/js.telemetry.bridge.v1");
|
|
74
|
+
var TELEMETRY_REPORTER_FACTORY = /* @__PURE__ */ Symbol.for("@flopay/js.telemetry.reporter-factory.v1");
|
|
75
|
+
function noopTelemetryBridge() {
|
|
76
|
+
return {
|
|
77
|
+
error: () => {
|
|
78
|
+
},
|
|
79
|
+
log: () => {
|
|
80
|
+
},
|
|
81
|
+
performance: () => {
|
|
82
|
+
},
|
|
83
|
+
terminal: () => {
|
|
84
|
+
},
|
|
85
|
+
now: () => globalThis.performance?.now() ?? 0,
|
|
86
|
+
elapsed: (startedAt) => Math.max(0, (globalThis.performance?.now() ?? startedAt) - startedAt),
|
|
87
|
+
setCheckoutContext: () => {
|
|
88
|
+
},
|
|
89
|
+
beginCheckout: () => globalThis.performance?.now() ?? 0,
|
|
90
|
+
disable: () => {
|
|
91
|
+
},
|
|
92
|
+
flush: async () => {
|
|
93
|
+
},
|
|
94
|
+
destroy: () => {
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function normalizeTelemetryBridge(source) {
|
|
99
|
+
const fallback = noopTelemetryBridge();
|
|
100
|
+
const bind = (candidate, defaultValue) => candidate ? candidate.bind(source) : defaultValue;
|
|
101
|
+
const now = bind(source.now, fallback.now);
|
|
102
|
+
return {
|
|
103
|
+
error: bind(source.error, fallback.error),
|
|
104
|
+
log: bind(source.log, fallback.log),
|
|
105
|
+
performance: bind(source.performance, fallback.performance),
|
|
106
|
+
terminal: bind(source.terminal, fallback.terminal),
|
|
107
|
+
now,
|
|
108
|
+
elapsed: source.elapsed ? source.elapsed.bind(source) : (startedAt) => Math.max(0, now() - startedAt),
|
|
109
|
+
setCheckoutContext: bind(source.setCheckoutContext, fallback.setCheckoutContext),
|
|
110
|
+
beginCheckout: bind(source.beginCheckout, fallback.beginCheckout),
|
|
111
|
+
disable: bind(source.disable, fallback.disable),
|
|
112
|
+
flush: bind(source.flush, fallback.flush),
|
|
113
|
+
destroy: bind(source.destroy, fallback.destroy)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function createTelemetryBridge(options) {
|
|
117
|
+
const factory = globalThis[TELEMETRY_REPORTER_FACTORY];
|
|
118
|
+
return normalizeTelemetryBridge(factory?.(options) ?? {});
|
|
119
|
+
}
|
|
120
|
+
function getFloPayTelemetryBridge(floPay) {
|
|
121
|
+
if (!floPay) return void 0;
|
|
122
|
+
return floPay[FLOPAY_TELEMETRY_BRIDGE];
|
|
123
|
+
}
|
|
124
|
+
|
|
72
125
|
// src/provider.tsx
|
|
73
126
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
74
127
|
function FloPayProvider({
|
|
@@ -84,6 +137,18 @@ function FloPayProvider({
|
|
|
84
137
|
paypalFloPayProp instanceof Promise || !paypalFloPayProp ? null : paypalFloPayProp
|
|
85
138
|
);
|
|
86
139
|
const [elements, setElements] = (0, import_react2.useState)(null);
|
|
140
|
+
const mountedAt = (0, import_react2.useRef)(null);
|
|
141
|
+
const renderedElements = (0, import_react2.useRef)(null);
|
|
142
|
+
const interactiveElements = (0, import_react2.useRef)(null);
|
|
143
|
+
(0, import_react2.useEffect)(() => {
|
|
144
|
+
if (!flopay) return;
|
|
145
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
146
|
+
mountedAt.current = telemetry?.beginCheckout() ?? telemetry?.now() ?? 0;
|
|
147
|
+
telemetry?.log({ name: "checkout.mount", stage: "checkout_mount" });
|
|
148
|
+
return () => {
|
|
149
|
+
telemetry?.log({ name: "checkout.unmount", stage: "unmount" });
|
|
150
|
+
};
|
|
151
|
+
}, [flopay]);
|
|
87
152
|
(0, import_react2.useEffect)(() => {
|
|
88
153
|
let cancelled = false;
|
|
89
154
|
if (floPayProp instanceof Promise) {
|
|
@@ -144,10 +209,38 @@ function FloPayProvider({
|
|
|
144
209
|
options?.paymentMethodCreation,
|
|
145
210
|
options?.setupFutureUsage
|
|
146
211
|
]);
|
|
212
|
+
(0, import_react2.useEffect)(() => {
|
|
213
|
+
if (!flopay || !elements || renderedElements.current === elements) return;
|
|
214
|
+
renderedElements.current = elements;
|
|
215
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
216
|
+
telemetry?.log({ name: "checkout.rendered", stage: "checkout_render" });
|
|
217
|
+
telemetry?.performance({
|
|
218
|
+
stage: "checkout_render",
|
|
219
|
+
durationMs: telemetry.elapsed(mountedAt.current ?? 0),
|
|
220
|
+
durationMode: "machine"
|
|
221
|
+
});
|
|
222
|
+
}, [elements, flopay]);
|
|
223
|
+
const reportInteractive = (0, import_react2.useCallback)(() => {
|
|
224
|
+
if (!flopay || !elements || interactiveElements.current === elements) return;
|
|
225
|
+
interactiveElements.current = elements;
|
|
226
|
+
const telemetry = getFloPayTelemetryBridge(flopay);
|
|
227
|
+
telemetry?.log({ name: "checkout.interactive", stage: "checkout_interactive" });
|
|
228
|
+
telemetry?.performance({
|
|
229
|
+
stage: "checkout_interactive",
|
|
230
|
+
durationMs: telemetry.elapsed(mountedAt.current ?? 0),
|
|
231
|
+
durationMode: "machine"
|
|
232
|
+
});
|
|
233
|
+
}, [elements, flopay]);
|
|
147
234
|
const resolvedBillingApiUrl = (0, import_shared.resolveBillingApiUrl)(options?.billingApiUrl);
|
|
148
235
|
const value = (0, import_react2.useMemo)(
|
|
149
|
-
() => ({
|
|
150
|
-
|
|
236
|
+
() => ({
|
|
237
|
+
flopay,
|
|
238
|
+
paypalFlopay,
|
|
239
|
+
elements,
|
|
240
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
241
|
+
reportInteractive
|
|
242
|
+
}),
|
|
243
|
+
[flopay, paypalFlopay, elements, resolvedBillingApiUrl, reportInteractive]
|
|
151
244
|
);
|
|
152
245
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FloPayContext.Provider, { value, children });
|
|
153
246
|
}
|
|
@@ -241,7 +334,7 @@ function createElementComponent(elementType, displayName) {
|
|
|
241
334
|
}) {
|
|
242
335
|
const containerRef = (0, import_react4.useRef)(null);
|
|
243
336
|
const elementRef = (0, import_react4.useRef)(null);
|
|
244
|
-
const { elements } = (0, import_react4.useContext)(FloPayContext);
|
|
337
|
+
const { elements, reportInteractive } = (0, import_react4.useContext)(FloPayContext);
|
|
245
338
|
(0, import_react4.useEffect)(() => {
|
|
246
339
|
if (!elements || !containerRef.current) return;
|
|
247
340
|
let mounted = true;
|
|
@@ -256,7 +349,10 @@ function createElementComponent(elementType, displayName) {
|
|
|
256
349
|
element.mount(containerRef.current);
|
|
257
350
|
elementRef.current = element;
|
|
258
351
|
if (onChange) element.on("change", onChange);
|
|
259
|
-
|
|
352
|
+
element.on("ready", () => {
|
|
353
|
+
reportInteractive?.();
|
|
354
|
+
onReady?.();
|
|
355
|
+
});
|
|
260
356
|
if (onFocus) element.on("focus", onFocus);
|
|
261
357
|
if (onBlur) element.on("blur", onBlur);
|
|
262
358
|
if (onEscape) element.on("escape", onEscape);
|
|
@@ -271,7 +367,7 @@ function createElementComponent(elementType, displayName) {
|
|
|
271
367
|
elementRef.current = null;
|
|
272
368
|
}
|
|
273
369
|
};
|
|
274
|
-
}, [elements]);
|
|
370
|
+
}, [elements, reportInteractive]);
|
|
275
371
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: containerRef, className, id, style });
|
|
276
372
|
}
|
|
277
373
|
ElementComponent.displayName = displayName;
|
|
@@ -803,6 +899,19 @@ var import_paypal_js = require("@paypal/paypal-js");
|
|
|
803
899
|
var import_js = require("@flopay/js");
|
|
804
900
|
var import_shared4 = require("@flopay/shared");
|
|
805
901
|
|
|
902
|
+
// src/merchant-callback.ts
|
|
903
|
+
function invokeMerchantCallback(callback) {
|
|
904
|
+
if (!callback) return;
|
|
905
|
+
const reportFailure = (error) => {
|
|
906
|
+
console.error("[FloPay] Merchant callback failed; checkout continued.", error);
|
|
907
|
+
};
|
|
908
|
+
try {
|
|
909
|
+
void Promise.resolve(callback()).catch(reportFailure);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
reportFailure(error);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
806
915
|
// src/external-method-recovery.ts
|
|
807
916
|
var EXTERNAL_METHOD_CALLBACK_GRACE_MS = 600;
|
|
808
917
|
function getProviderErrorMessage(err) {
|
|
@@ -859,7 +968,7 @@ var DIRECT_PAYPAL_RECOVERY_ACTION_STYLE = {
|
|
|
859
968
|
function buildDirectPayPalRecoveryMessage(popupBlocked) {
|
|
860
969
|
return popupBlocked ? `${DIRECT_PAYPAL_RECOVERY_MESSAGE} Allow pop-ups for this site, then try again.` : DIRECT_PAYPAL_RECOVERY_MESSAGE;
|
|
861
970
|
}
|
|
862
|
-
function
|
|
971
|
+
function DirectPayPalButtonImplementation({
|
|
863
972
|
sessionId,
|
|
864
973
|
nonce,
|
|
865
974
|
billingApiUrl,
|
|
@@ -879,9 +988,57 @@ function DirectPayPalButton({
|
|
|
879
988
|
runBeforeButtonClick,
|
|
880
989
|
session,
|
|
881
990
|
existingOrderId,
|
|
991
|
+
telemetry,
|
|
992
|
+
telemetryContext,
|
|
882
993
|
debug = false
|
|
883
994
|
}) {
|
|
995
|
+
const flopay = useFloPay();
|
|
996
|
+
const standaloneTelemetry = (0, import_react8.useMemo)(() => {
|
|
997
|
+
if (flopay) return null;
|
|
998
|
+
const reporter = createTelemetryBridge({
|
|
999
|
+
billingApiUrl,
|
|
1000
|
+
sdkPackage: "@flopay/react",
|
|
1001
|
+
sdkVersion: import_shared4.SDK_VERSION,
|
|
1002
|
+
enabled: telemetry !== false
|
|
1003
|
+
});
|
|
1004
|
+
reporter.setCheckoutContext(telemetryContext ?? {});
|
|
1005
|
+
reporter.beginCheckout(telemetryContext ?? {});
|
|
1006
|
+
return reporter;
|
|
1007
|
+
}, [
|
|
1008
|
+
billingApiUrl,
|
|
1009
|
+
flopay,
|
|
1010
|
+
telemetry,
|
|
1011
|
+
telemetryContext?.checkoutMode,
|
|
1012
|
+
telemetryContext?.layout
|
|
1013
|
+
]);
|
|
1014
|
+
const floPayTelemetry = (0, import_react8.useMemo)(() => getFloPayTelemetryBridge(flopay), [flopay]);
|
|
1015
|
+
(0, import_react8.useEffect)(() => () => {
|
|
1016
|
+
if (!standaloneTelemetry) return;
|
|
1017
|
+
void standaloneTelemetry.flush().catch(() => {
|
|
1018
|
+
}).finally(() => standaloneTelemetry.destroy());
|
|
1019
|
+
}, [standaloneTelemetry]);
|
|
1020
|
+
const telemetrySource = (0, import_react8.useMemo)(() => ({
|
|
1021
|
+
error: (input) => {
|
|
1022
|
+
if (floPayTelemetry) floPayTelemetry.error(input);
|
|
1023
|
+
else standaloneTelemetry?.error(input);
|
|
1024
|
+
},
|
|
1025
|
+
log: (input) => {
|
|
1026
|
+
if (floPayTelemetry) floPayTelemetry.log(input);
|
|
1027
|
+
else standaloneTelemetry?.log(input);
|
|
1028
|
+
},
|
|
1029
|
+
performance: (input) => {
|
|
1030
|
+
if (floPayTelemetry) floPayTelemetry.performance(input);
|
|
1031
|
+
else standaloneTelemetry?.performance(input);
|
|
1032
|
+
},
|
|
1033
|
+
terminal: (input) => {
|
|
1034
|
+
if (floPayTelemetry) floPayTelemetry.terminal(input);
|
|
1035
|
+
else standaloneTelemetry?.terminal(input);
|
|
1036
|
+
},
|
|
1037
|
+
startTiming: () => floPayTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
|
|
1038
|
+
elapsed: (startedAt) => floPayTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
|
|
1039
|
+
}), [floPayTelemetry, standaloneTelemetry]);
|
|
884
1040
|
const containerRef = (0, import_react8.useRef)(null);
|
|
1041
|
+
const providerStartedAt = (0, import_react8.useRef)(0);
|
|
885
1042
|
const focusTargetRef = (0, import_react8.useRef)(null);
|
|
886
1043
|
const pendingProviderFocusRef = (0, import_react8.useRef)(false);
|
|
887
1044
|
const [ready, setReady] = (0, import_react8.useState)(false);
|
|
@@ -1070,7 +1227,7 @@ function DirectPayPalButton({
|
|
|
1070
1227
|
};
|
|
1071
1228
|
}, []);
|
|
1072
1229
|
(0, import_react8.useEffect)(() => {
|
|
1073
|
-
onLoadStateChangeRef.current?.(ready && !failed);
|
|
1230
|
+
invokeMerchantCallback(() => onLoadStateChangeRef.current?.(ready && !failed));
|
|
1074
1231
|
}, [ready, failed]);
|
|
1075
1232
|
const normalizedEnv = (0, import_shared4.normalizeGatewayEnvironment)(environment);
|
|
1076
1233
|
(0, import_react8.useEffect)(() => {
|
|
@@ -1081,6 +1238,13 @@ function DirectPayPalButton({
|
|
|
1081
1238
|
if (!clientId) {
|
|
1082
1239
|
appendDebug("FAIL: clientId empty \u2014 gateway misconfigured");
|
|
1083
1240
|
setFailed(true);
|
|
1241
|
+
telemetrySource.error({
|
|
1242
|
+
errorCode: "CONFIGURATION_INVALID",
|
|
1243
|
+
stage: "provider_load",
|
|
1244
|
+
provider: "paypal",
|
|
1245
|
+
paymentMethodCategory: "paypal",
|
|
1246
|
+
requestCategory: "provider_sdk"
|
|
1247
|
+
});
|
|
1084
1248
|
console.error("[FloPay] DirectPayPal: clientId empty \u2014 gateway misconfigured");
|
|
1085
1249
|
return;
|
|
1086
1250
|
}
|
|
@@ -1088,8 +1252,33 @@ function DirectPayPalButton({
|
|
|
1088
1252
|
appendDebug("FAIL: containerRef not attached");
|
|
1089
1253
|
return;
|
|
1090
1254
|
}
|
|
1255
|
+
providerStartedAt.current = telemetrySource.startTiming();
|
|
1256
|
+
telemetrySource.log({
|
|
1257
|
+
name: "provider.load.started",
|
|
1258
|
+
stage: "provider_load",
|
|
1259
|
+
provider: "paypal",
|
|
1260
|
+
paymentMethodCategory: "paypal"
|
|
1261
|
+
});
|
|
1091
1262
|
let cancelled = false;
|
|
1092
1263
|
let activeButtons = null;
|
|
1264
|
+
let overlayStartedAt = null;
|
|
1265
|
+
const finishOverlay = () => {
|
|
1266
|
+
if (overlayStartedAt === null) return;
|
|
1267
|
+
telemetrySource.log({
|
|
1268
|
+
name: "provider.overlay.returned",
|
|
1269
|
+
stage: "overlay_return",
|
|
1270
|
+
provider: "paypal",
|
|
1271
|
+
paymentMethodCategory: "paypal"
|
|
1272
|
+
});
|
|
1273
|
+
telemetrySource.performance({
|
|
1274
|
+
stage: "overlay_return",
|
|
1275
|
+
durationMs: telemetrySource.elapsed(overlayStartedAt),
|
|
1276
|
+
durationMode: "buyer",
|
|
1277
|
+
provider: "paypal",
|
|
1278
|
+
paymentMethodCategory: "paypal"
|
|
1279
|
+
});
|
|
1280
|
+
overlayStartedAt = null;
|
|
1281
|
+
};
|
|
1093
1282
|
let rendered = false;
|
|
1094
1283
|
const container = containerRef.current;
|
|
1095
1284
|
let containerObserver = null;
|
|
@@ -1193,7 +1382,7 @@ function DirectPayPalButton({
|
|
|
1193
1382
|
if (cancelled) return;
|
|
1194
1383
|
if (isZoidLifecycleMessage(message)) return;
|
|
1195
1384
|
const friendly = applyFriendlyMessageOverride(message) ?? message;
|
|
1196
|
-
onErrorChangeRef.current?.(friendly);
|
|
1385
|
+
invokeMerchantCallback(() => onErrorChangeRef.current?.(friendly));
|
|
1197
1386
|
};
|
|
1198
1387
|
const markRenderFailed = (message) => {
|
|
1199
1388
|
if (cancelled) return;
|
|
@@ -1202,24 +1391,55 @@ function DirectPayPalButton({
|
|
|
1202
1391
|
return;
|
|
1203
1392
|
}
|
|
1204
1393
|
setFailed(true);
|
|
1394
|
+
if (!message.includes("paypal_ineligible")) {
|
|
1395
|
+
telemetrySource.error({
|
|
1396
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
1397
|
+
stage: "provider_load",
|
|
1398
|
+
provider: "paypal",
|
|
1399
|
+
paymentMethodCategory: "paypal",
|
|
1400
|
+
requestCategory: "provider_sdk"
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1205
1403
|
console.error("[FloPay] DirectPayPal load/render failure:", message);
|
|
1206
1404
|
};
|
|
1207
1405
|
setFailed(false);
|
|
1208
1406
|
const dispatchTokenizedBody = async (body, prepared = beforeClickRef.current) => {
|
|
1209
1407
|
const effectiveSessionId = prepared?.sessionId ?? sessionId;
|
|
1210
1408
|
if (onTokenizedBodyRef.current) {
|
|
1211
|
-
onTokenizedBodyRef.current(body, {
|
|
1409
|
+
await onTokenizedBodyRef.current(body, {
|
|
1212
1410
|
sessionId: effectiveSessionId,
|
|
1213
1411
|
accountPatch: prepared?.accountPatch,
|
|
1214
1412
|
nonce: prepared?.nonce
|
|
1215
1413
|
});
|
|
1216
1414
|
return;
|
|
1217
1415
|
}
|
|
1416
|
+
const processingStartedAt = telemetrySource.startTiming();
|
|
1417
|
+
telemetrySource.log({
|
|
1418
|
+
name: "payment.processing.started",
|
|
1419
|
+
stage: "processing",
|
|
1420
|
+
provider: "paypal",
|
|
1421
|
+
paymentMethodCategory: "paypal"
|
|
1422
|
+
});
|
|
1423
|
+
const finishProcessing = () => {
|
|
1424
|
+
telemetrySource.log({
|
|
1425
|
+
name: "payment.processing.completed",
|
|
1426
|
+
stage: "processing",
|
|
1427
|
+
provider: "paypal",
|
|
1428
|
+
paymentMethodCategory: "paypal"
|
|
1429
|
+
});
|
|
1430
|
+
telemetrySource.performance({
|
|
1431
|
+
stage: "processing",
|
|
1432
|
+
durationMs: telemetrySource.elapsed(processingStartedAt),
|
|
1433
|
+
durationMode: "machine",
|
|
1434
|
+
provider: "paypal",
|
|
1435
|
+
paymentMethodCategory: "paypal"
|
|
1436
|
+
});
|
|
1437
|
+
};
|
|
1218
1438
|
try {
|
|
1219
1439
|
const currentSession = sessionRef.current;
|
|
1220
1440
|
const currentEmail = prepared?.accountPatch?.email ?? emailRef.current;
|
|
1221
1441
|
const effectiveUserId = prepared?.accountPatch?.userId ?? currentSession?.customer?.id ?? currentSession?.accountData?.userId ?? "";
|
|
1222
|
-
const api = new import_js.PaymentAPI(baseUrl);
|
|
1442
|
+
const api = new import_js.PaymentAPI(baseUrl, { telemetry: false });
|
|
1223
1443
|
const response = await api.processPayment(
|
|
1224
1444
|
effectiveUserId,
|
|
1225
1445
|
{
|
|
@@ -1237,23 +1457,57 @@ function DirectPayPalButton({
|
|
|
1237
1457
|
}
|
|
1238
1458
|
);
|
|
1239
1459
|
if (response.ok) {
|
|
1240
|
-
|
|
1460
|
+
finishProcessing();
|
|
1461
|
+
telemetrySource.terminal({
|
|
1462
|
+
outcome: "payment_succeeded",
|
|
1463
|
+
provider: "paypal",
|
|
1464
|
+
paymentMethodCategory: "paypal"
|
|
1465
|
+
});
|
|
1466
|
+
} else {
|
|
1467
|
+
const json = await response.json().catch(() => null);
|
|
1468
|
+
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1469
|
+
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1470
|
+
finishProcessing();
|
|
1471
|
+
if (typeof json?.["declineCode"] === "string") {
|
|
1472
|
+
telemetrySource.terminal({
|
|
1473
|
+
outcome: "payment_declined",
|
|
1474
|
+
provider: "paypal",
|
|
1475
|
+
paymentMethodCategory: "paypal"
|
|
1476
|
+
});
|
|
1477
|
+
} else {
|
|
1478
|
+
telemetrySource.error({
|
|
1479
|
+
errorCode: "PAYMENT_PROCESSING_FAILED",
|
|
1480
|
+
stage: "processing",
|
|
1481
|
+
provider: "paypal",
|
|
1482
|
+
paymentMethodCategory: "paypal",
|
|
1483
|
+
requestCategory: "process_payment",
|
|
1484
|
+
statusClass: response.status >= 500 ? "5xx" : response.status >= 400 ? "4xx" : response.status >= 300 ? "3xx" : "unknown"
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
forwardError(message);
|
|
1488
|
+
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1489
|
+
code: json?.["code"],
|
|
1490
|
+
declineCode: json?.["declineCode"]
|
|
1491
|
+
})));
|
|
1241
1492
|
return;
|
|
1242
1493
|
}
|
|
1243
|
-
const json = await response.json().catch(() => null);
|
|
1244
|
-
const rawMessage = json?.["message"] ?? "PayPal payment failed.";
|
|
1245
|
-
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1246
|
-
forwardError(message);
|
|
1247
|
-
onDeclineRef.current?.(buildDeclineEvent("paypal", message, {
|
|
1248
|
-
code: json?.["code"],
|
|
1249
|
-
declineCode: json?.["declineCode"]
|
|
1250
|
-
}));
|
|
1251
1494
|
} catch (err) {
|
|
1495
|
+
finishProcessing();
|
|
1496
|
+
telemetrySource.error({
|
|
1497
|
+
errorCode: "NETWORK_REQUEST_FAILED",
|
|
1498
|
+
stage: "processing",
|
|
1499
|
+
provider: "paypal",
|
|
1500
|
+
paymentMethodCategory: "paypal",
|
|
1501
|
+
requestCategory: "process_payment",
|
|
1502
|
+
statusClass: "network_error"
|
|
1503
|
+
});
|
|
1252
1504
|
const rawMessage = err instanceof Error ? err.message : "PayPal payment failed.";
|
|
1253
1505
|
const message = applyFriendlyMessageOverride(rawMessage) ?? rawMessage;
|
|
1254
1506
|
forwardError(message);
|
|
1255
|
-
onDeclineRef.current?.(buildDeclineEvent("paypal", message));
|
|
1507
|
+
invokeMerchantCallback(() => onDeclineRef.current?.(buildDeclineEvent("paypal", message)));
|
|
1508
|
+
return;
|
|
1256
1509
|
}
|
|
1510
|
+
invokeMerchantCallback(() => onCompleteRef.current?.({ status: "succeeded", checkoutMethod: "paypal" }));
|
|
1257
1511
|
};
|
|
1258
1512
|
const createPaypalIntent = async (fallbackMessage) => {
|
|
1259
1513
|
const prepared = beforeClickRef.current;
|
|
@@ -1262,6 +1516,13 @@ function DirectPayPalButton({
|
|
|
1262
1516
|
const intentHeaders = { "Content-Type": "application/json" };
|
|
1263
1517
|
const currentNonce = prepared?.nonce ?? nonceRef.current;
|
|
1264
1518
|
if (currentNonce) intentHeaders["x-checkout-session-token"] = currentNonce;
|
|
1519
|
+
telemetrySource.log({
|
|
1520
|
+
name: "payment.intent.started",
|
|
1521
|
+
stage: "processing",
|
|
1522
|
+
provider: "paypal",
|
|
1523
|
+
paymentMethodCategory: "paypal",
|
|
1524
|
+
requestCategory: "intent_create"
|
|
1525
|
+
});
|
|
1265
1526
|
const response = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1266
1527
|
method: "POST",
|
|
1267
1528
|
headers: intentHeaders,
|
|
@@ -1280,6 +1541,14 @@ function DirectPayPalButton({
|
|
|
1280
1541
|
if (!id) {
|
|
1281
1542
|
throw new Error(fallbackMessage);
|
|
1282
1543
|
}
|
|
1544
|
+
telemetrySource.log({
|
|
1545
|
+
name: "payment.intent.completed",
|
|
1546
|
+
stage: "processing",
|
|
1547
|
+
provider: "paypal",
|
|
1548
|
+
paymentMethodCategory: "paypal",
|
|
1549
|
+
requestCategory: "intent_create",
|
|
1550
|
+
statusClass: "2xx"
|
|
1551
|
+
});
|
|
1283
1552
|
return id;
|
|
1284
1553
|
};
|
|
1285
1554
|
const effectRenderGeneration = renderGeneration;
|
|
@@ -1314,6 +1583,12 @@ function DirectPayPalButton({
|
|
|
1314
1583
|
if (!cancelled && !paypal?.Buttons) appendDebug("FAIL: namespace missing Buttons factory");
|
|
1315
1584
|
return;
|
|
1316
1585
|
}
|
|
1586
|
+
telemetrySource.log({
|
|
1587
|
+
name: "provider.availability.checked",
|
|
1588
|
+
stage: "provider_ready",
|
|
1589
|
+
provider: "paypal",
|
|
1590
|
+
paymentMethodCategory: "paypal"
|
|
1591
|
+
});
|
|
1317
1592
|
const handleApprove = async (data) => {
|
|
1318
1593
|
if (cancelled || activeRenderGenerationRef.current !== effectRenderGeneration) return;
|
|
1319
1594
|
const token = data.subscriptionID ?? data.orderID ?? "";
|
|
@@ -1322,9 +1597,10 @@ function DirectPayPalButton({
|
|
|
1322
1597
|
if (!finishAttempt(context.generation)) return;
|
|
1323
1598
|
approvalContextByTokenRef.current.delete(token);
|
|
1324
1599
|
attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1600
|
+
finishOverlay();
|
|
1325
1601
|
try {
|
|
1326
1602
|
setSubmitting(true);
|
|
1327
|
-
onErrorChangeRef.current?.(null);
|
|
1603
|
+
invokeMerchantCallback(() => onErrorChangeRef.current?.(null));
|
|
1328
1604
|
if (!token) {
|
|
1329
1605
|
throw new import_shared4.FloPayError(
|
|
1330
1606
|
"PayPal did not return an approval token.",
|
|
@@ -1374,7 +1650,7 @@ function DirectPayPalButton({
|
|
|
1374
1650
|
return;
|
|
1375
1651
|
}
|
|
1376
1652
|
}
|
|
1377
|
-
onButtonClickRef.current?.("paypal");
|
|
1653
|
+
invokeMerchantCallback(() => onButtonClickRef.current?.("paypal"));
|
|
1378
1654
|
const generation = startAttempt();
|
|
1379
1655
|
const context = {
|
|
1380
1656
|
generation,
|
|
@@ -1384,6 +1660,25 @@ function DirectPayPalButton({
|
|
|
1384
1660
|
beforeClickRef.current = context;
|
|
1385
1661
|
attemptContextBySurfaceRef.current.set(effectRenderGeneration, context);
|
|
1386
1662
|
await actions.resolve();
|
|
1663
|
+
telemetrySource.log({
|
|
1664
|
+
name: "payment.method.selected",
|
|
1665
|
+
stage: "processing",
|
|
1666
|
+
provider: "paypal",
|
|
1667
|
+
paymentMethodCategory: "paypal"
|
|
1668
|
+
});
|
|
1669
|
+
telemetrySource.log({
|
|
1670
|
+
name: "provider.popup.opened",
|
|
1671
|
+
stage: "overlay_open",
|
|
1672
|
+
provider: "paypal",
|
|
1673
|
+
paymentMethodCategory: "paypal"
|
|
1674
|
+
});
|
|
1675
|
+
telemetrySource.log({
|
|
1676
|
+
name: "provider.overlay.opened",
|
|
1677
|
+
stage: "overlay_open",
|
|
1678
|
+
provider: "paypal",
|
|
1679
|
+
paymentMethodCategory: "paypal"
|
|
1680
|
+
});
|
|
1681
|
+
overlayStartedAt = telemetrySource.startTiming();
|
|
1387
1682
|
},
|
|
1388
1683
|
// When the SDK is already holding an order/subscription id from a
|
|
1389
1684
|
// prior backend round-trip (the `paypal_direct_required` retry
|
|
@@ -1396,6 +1691,12 @@ function DirectPayPalButton({
|
|
|
1396
1691
|
const context = attemptContextBySurfaceRef.current.get(effectRenderGeneration) ?? beforeClickRef.current;
|
|
1397
1692
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1398
1693
|
beforeClickRef.current = null;
|
|
1694
|
+
finishOverlay();
|
|
1695
|
+
telemetrySource.terminal({
|
|
1696
|
+
outcome: "payment_cancelled",
|
|
1697
|
+
provider: "paypal",
|
|
1698
|
+
paymentMethodCategory: "paypal"
|
|
1699
|
+
});
|
|
1399
1700
|
pendingProviderFocusRef.current = true;
|
|
1400
1701
|
invalidateAttempt({ generation: context?.generation, remount: true, focus: false });
|
|
1401
1702
|
},
|
|
@@ -1411,6 +1712,15 @@ function DirectPayPalButton({
|
|
|
1411
1712
|
finishAttempt(context?.generation);
|
|
1412
1713
|
return;
|
|
1413
1714
|
}
|
|
1715
|
+
finishOverlay();
|
|
1716
|
+
const lower = message.toLowerCase();
|
|
1717
|
+
telemetrySource.error({
|
|
1718
|
+
errorCode: lower.includes("popup") && lower.includes("block") ? "POPUP_BLOCKED" : "PROVIDER_RUNTIME_FAILED",
|
|
1719
|
+
stage: "provider_ready",
|
|
1720
|
+
provider: "paypal",
|
|
1721
|
+
paymentMethodCategory: "paypal",
|
|
1722
|
+
requestCategory: "provider_sdk"
|
|
1723
|
+
});
|
|
1414
1724
|
if (context) attemptContextBySurfaceRef.current.delete(context.surfaceKey);
|
|
1415
1725
|
beforeClickRef.current = null;
|
|
1416
1726
|
invalidateAttempt({ generation: context?.generation, remount: true, showRetry: true, focus: true });
|
|
@@ -1422,6 +1732,12 @@ function DirectPayPalButton({
|
|
|
1422
1732
|
});
|
|
1423
1733
|
const eligible = buttons.isEligible();
|
|
1424
1734
|
appendDebug(`isEligible=${eligible}`);
|
|
1735
|
+
telemetrySource.log({
|
|
1736
|
+
name: "provider.eligibility.checked",
|
|
1737
|
+
stage: "provider_ready",
|
|
1738
|
+
provider: "paypal",
|
|
1739
|
+
paymentMethodCategory: "paypal"
|
|
1740
|
+
});
|
|
1425
1741
|
if (!eligible) {
|
|
1426
1742
|
setReady(false);
|
|
1427
1743
|
markRenderFailed(
|
|
@@ -1493,6 +1809,19 @@ function DirectPayPalButton({
|
|
|
1493
1809
|
activeButtons = typedButtons;
|
|
1494
1810
|
rendered = true;
|
|
1495
1811
|
setReady(true);
|
|
1812
|
+
telemetrySource.log({
|
|
1813
|
+
name: "provider.ready",
|
|
1814
|
+
stage: "provider_ready",
|
|
1815
|
+
provider: "paypal",
|
|
1816
|
+
paymentMethodCategory: "paypal"
|
|
1817
|
+
});
|
|
1818
|
+
telemetrySource.performance({
|
|
1819
|
+
stage: "provider_ready",
|
|
1820
|
+
durationMs: telemetrySource.elapsed(providerStartedAt.current),
|
|
1821
|
+
durationMode: "machine",
|
|
1822
|
+
provider: "paypal",
|
|
1823
|
+
paymentMethodCategory: "paypal"
|
|
1824
|
+
});
|
|
1496
1825
|
}).catch((err) => {
|
|
1497
1826
|
const message = err instanceof Error ? err.message : "PayPal failed to render.";
|
|
1498
1827
|
appendDebug(`render:rejected msg=${message.slice(0, 120)}`);
|
|
@@ -1514,7 +1843,7 @@ function DirectPayPalButton({
|
|
|
1514
1843
|
});
|
|
1515
1844
|
}
|
|
1516
1845
|
};
|
|
1517
|
-
}, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration]);
|
|
1846
|
+
}, [baseUrl, clientId, currency, environment, isSubscription, sessionId, existingOrderId, renderGeneration, telemetrySource]);
|
|
1518
1847
|
const debugPanel = debug ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1519
1848
|
"pre",
|
|
1520
1849
|
{
|
|
@@ -1595,6 +1924,12 @@ ${debugLines.join("\n")}`
|
|
|
1595
1924
|
] })
|
|
1596
1925
|
);
|
|
1597
1926
|
}
|
|
1927
|
+
function DirectPayPalButton(props) {
|
|
1928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DirectPayPalButtonImplementation, { ...props });
|
|
1929
|
+
}
|
|
1930
|
+
function InstrumentedDirectPayPalButton(props) {
|
|
1931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DirectPayPalButtonImplementation, { ...props });
|
|
1932
|
+
}
|
|
1598
1933
|
|
|
1599
1934
|
// src/split-card-form.tsx
|
|
1600
1935
|
var import_shared6 = require("@flopay/shared");
|
|
@@ -1911,6 +2246,7 @@ function PayPalButtonInner({
|
|
|
1911
2246
|
onLoadStateChange,
|
|
1912
2247
|
placeholderBorderRadius
|
|
1913
2248
|
}) {
|
|
2249
|
+
const flopay = useFloPay();
|
|
1914
2250
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
1915
2251
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
1916
2252
|
const [loadState, setLoadState] = (0, import_react9.useState)("loading");
|
|
@@ -2225,6 +2561,11 @@ function PayPalButtonInner({
|
|
|
2225
2561
|
onCancel: () => {
|
|
2226
2562
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
2227
2563
|
attemptContextBySurfaceRef.current.delete(surfaceKey);
|
|
2564
|
+
getFloPayTelemetryBridge(flopay)?.terminal({
|
|
2565
|
+
outcome: "payment_cancelled",
|
|
2566
|
+
provider: "paypal",
|
|
2567
|
+
paymentMethodCategory: "paypal"
|
|
2568
|
+
});
|
|
2228
2569
|
invalidateAttempt(attemptContext?.generation);
|
|
2229
2570
|
setShowRecoveryAction(false);
|
|
2230
2571
|
pendingProviderFocusRef.current = true;
|
|
@@ -2268,6 +2609,7 @@ function WalletButtonInner({
|
|
|
2268
2609
|
onLoadStateChange,
|
|
2269
2610
|
placeholderBorderRadius
|
|
2270
2611
|
}) {
|
|
2612
|
+
const flopay = useFloPay();
|
|
2271
2613
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
2272
2614
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
2273
2615
|
const [loadState, setLoadState] = (0, import_react9.useState)("loading");
|
|
@@ -2489,6 +2831,13 @@ function WalletButtonInner({
|
|
|
2489
2831
|
},
|
|
2490
2832
|
onLoadError: (_event) => {
|
|
2491
2833
|
setLoadState("load_error");
|
|
2834
|
+
getFloPayTelemetryBridge(flopay)?.error({
|
|
2835
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
2836
|
+
stage: "provider_load",
|
|
2837
|
+
provider: "stripe",
|
|
2838
|
+
paymentMethodCategory: "wallet",
|
|
2839
|
+
requestCategory: "provider_sdk"
|
|
2840
|
+
});
|
|
2492
2841
|
},
|
|
2493
2842
|
onClick: async (event) => {
|
|
2494
2843
|
lastWalletProviderMethodRef.current = event.expressPaymentType;
|
|
@@ -2515,6 +2864,11 @@ function WalletButtonInner({
|
|
|
2515
2864
|
onCancel: () => {
|
|
2516
2865
|
const attemptContext = attemptContextBySurfaceRef.current.get(surfaceKey);
|
|
2517
2866
|
attemptContextBySurfaceRef.current.delete(surfaceKey);
|
|
2867
|
+
getFloPayTelemetryBridge(flopay)?.terminal({
|
|
2868
|
+
outcome: "payment_cancelled",
|
|
2869
|
+
provider: "stripe",
|
|
2870
|
+
paymentMethodCategory: "wallet"
|
|
2871
|
+
});
|
|
2518
2872
|
invalidateAttempt(attemptContext?.generation);
|
|
2519
2873
|
setShowRecoveryAction(false);
|
|
2520
2874
|
pendingProviderFocusRef.current = true;
|
|
@@ -2685,6 +3039,7 @@ function StripeMethodInlineForm({
|
|
|
2685
3039
|
submitButtonStyle,
|
|
2686
3040
|
errorText
|
|
2687
3041
|
}) {
|
|
3042
|
+
const flopay = useFloPay();
|
|
2688
3043
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
2689
3044
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
2690
3045
|
const [submitting, setSubmitting] = (0, import_react9.useState)(false);
|
|
@@ -2837,7 +3192,16 @@ function StripeMethodInlineForm({
|
|
|
2837
3192
|
import_react_stripe_js.PaymentElement,
|
|
2838
3193
|
{
|
|
2839
3194
|
onReady: () => setLoadState("ready"),
|
|
2840
|
-
onLoadError: () =>
|
|
3195
|
+
onLoadError: () => {
|
|
3196
|
+
setLoadState("load_error");
|
|
3197
|
+
getFloPayTelemetryBridge(flopay)?.error({
|
|
3198
|
+
errorCode: "PROVIDER_LOAD_FAILED",
|
|
3199
|
+
stage: "provider_load",
|
|
3200
|
+
provider: "stripe",
|
|
3201
|
+
paymentMethodCategory: "apm",
|
|
3202
|
+
requestCategory: "provider_sdk"
|
|
3203
|
+
});
|
|
3204
|
+
},
|
|
2841
3205
|
onChange: (event) => {
|
|
2842
3206
|
const evRecord = event;
|
|
2843
3207
|
setIsMethodComplete(!!evRecord.complete);
|
|
@@ -5753,7 +6117,7 @@ async function recover3DSRedirectResult({
|
|
|
5753
6117
|
return null;
|
|
5754
6118
|
}
|
|
5755
6119
|
try {
|
|
5756
|
-
const api = new import_js3.PaymentAPI(billingApiUrl);
|
|
6120
|
+
const api = new import_js3.PaymentAPI(billingApiUrl, { telemetry: false });
|
|
5757
6121
|
const unified = await api.getUnifiedCheckoutSession(sessionId, nonce);
|
|
5758
6122
|
const refreshedToken = unified.data.stripe?.clientSecret;
|
|
5759
6123
|
if (isStripePaymentIntentClientSecret(refreshedToken)) {
|
|
@@ -5772,7 +6136,8 @@ async function processSavedPaymentForMode({
|
|
|
5772
6136
|
session,
|
|
5773
6137
|
nonce,
|
|
5774
6138
|
tokenizedData,
|
|
5775
|
-
returnUrl
|
|
6139
|
+
returnUrl,
|
|
6140
|
+
telemetry
|
|
5776
6141
|
}) {
|
|
5777
6142
|
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
5778
6143
|
const resolvedSessionId = sessionId ?? session.id;
|
|
@@ -5783,7 +6148,10 @@ async function processSavedPaymentForMode({
|
|
|
5783
6148
|
const lastName = session.customer?.lastName ?? session.accountData?.lastName ?? "";
|
|
5784
6149
|
const country = session.customer?.country ?? session.accountData?.country ?? void 0;
|
|
5785
6150
|
const zip = session.customer?.zip ?? session.accountData?.zip ?? void 0;
|
|
5786
|
-
const api = new import_js3.PaymentAPI(
|
|
6151
|
+
const api = new import_js3.PaymentAPI(
|
|
6152
|
+
baseUrl,
|
|
6153
|
+
telemetry === false ? { telemetry: false } : void 0
|
|
6154
|
+
);
|
|
5787
6155
|
const response = await retryOnceOnFetchFailure(() => api.processPayment(customerId, {
|
|
5788
6156
|
sessionId: resolvedSessionId,
|
|
5789
6157
|
nonce: resolvedNonce,
|
|
@@ -5860,7 +6228,8 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5860
6228
|
billingApiUrl,
|
|
5861
6229
|
sessionId,
|
|
5862
6230
|
session,
|
|
5863
|
-
returnUrl
|
|
6231
|
+
returnUrl,
|
|
6232
|
+
telemetry
|
|
5864
6233
|
}) {
|
|
5865
6234
|
const stripe = flopay?.getRawProvider();
|
|
5866
6235
|
if (!stripe) {
|
|
@@ -5943,6 +6312,7 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5943
6312
|
billingApiUrl,
|
|
5944
6313
|
sessionId,
|
|
5945
6314
|
session,
|
|
6315
|
+
telemetry,
|
|
5946
6316
|
tokenizedData: {
|
|
5947
6317
|
id: paymentIntent.id,
|
|
5948
6318
|
type: "card",
|
|
@@ -5964,7 +6334,8 @@ async function handleSavedPaymentRedirectResult(redirectResult, {
|
|
|
5964
6334
|
billingApiUrl,
|
|
5965
6335
|
sessionId,
|
|
5966
6336
|
session,
|
|
5967
|
-
returnUrl
|
|
6337
|
+
returnUrl,
|
|
6338
|
+
telemetry
|
|
5968
6339
|
});
|
|
5969
6340
|
}
|
|
5970
6341
|
throw Object.assign(
|
|
@@ -6070,7 +6441,8 @@ async function loadSavedPaymentProviders({
|
|
|
6070
6441
|
publishableKey,
|
|
6071
6442
|
paypalPublishableKey,
|
|
6072
6443
|
billingApiUrl,
|
|
6073
|
-
locale
|
|
6444
|
+
locale,
|
|
6445
|
+
telemetry
|
|
6074
6446
|
}) {
|
|
6075
6447
|
if (!publishableKey) {
|
|
6076
6448
|
return { flopay: null, paypalFlopay: null };
|
|
@@ -6079,11 +6451,13 @@ async function loadSavedPaymentProviders({
|
|
|
6079
6451
|
const [instance, paypalInstanceOrError] = await Promise.all([
|
|
6080
6452
|
(0, import_js3.loadFloPay)(publishableKey, {
|
|
6081
6453
|
billingApiUrl,
|
|
6082
|
-
locale
|
|
6454
|
+
locale,
|
|
6455
|
+
telemetry
|
|
6083
6456
|
}),
|
|
6084
6457
|
needsSeparatePaypal ? (0, import_js3.loadFloPay)(paypalPublishableKey, {
|
|
6085
6458
|
billingApiUrl,
|
|
6086
|
-
locale
|
|
6459
|
+
locale,
|
|
6460
|
+
telemetry
|
|
6087
6461
|
}).catch((err) => {
|
|
6088
6462
|
console.warn("[FloPay] Failed to load PayPal Stripe instance:", err);
|
|
6089
6463
|
return null;
|
|
@@ -6103,6 +6477,33 @@ var sessionInflightMap = /* @__PURE__ */ new Map();
|
|
|
6103
6477
|
function sleep(ms) {
|
|
6104
6478
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6105
6479
|
}
|
|
6480
|
+
function createStandaloneTelemetryReporter(billingApiUrl, enabled, context) {
|
|
6481
|
+
const reporter = createTelemetryBridge({
|
|
6482
|
+
billingApiUrl,
|
|
6483
|
+
sdkPackage: "@flopay/react",
|
|
6484
|
+
sdkVersion: import_shared8.SDK_VERSION,
|
|
6485
|
+
enabled: enabled !== false
|
|
6486
|
+
});
|
|
6487
|
+
reporter.beginCheckout(context);
|
|
6488
|
+
return reporter;
|
|
6489
|
+
}
|
|
6490
|
+
function finishStandaloneTelemetry(reporter) {
|
|
6491
|
+
void reporter.flush().catch(() => {
|
|
6492
|
+
}).finally(() => reporter.destroy());
|
|
6493
|
+
}
|
|
6494
|
+
function reportStandaloneTelemetryError(billingApiUrl, enabled, context, errorCode, stage) {
|
|
6495
|
+
const reporter = createStandaloneTelemetryReporter(billingApiUrl, enabled, context);
|
|
6496
|
+
reporter.error({
|
|
6497
|
+
errorCode,
|
|
6498
|
+
stage,
|
|
6499
|
+
paymentMethodCategory: "unknown",
|
|
6500
|
+
...stage === "session_read" ? { requestCategory: "session_read" } : {}
|
|
6501
|
+
});
|
|
6502
|
+
finishStandaloneTelemetry(reporter);
|
|
6503
|
+
}
|
|
6504
|
+
function isExpectedExistingSessionError(error) {
|
|
6505
|
+
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";
|
|
6506
|
+
}
|
|
6106
6507
|
function resolveDirectPaypalConfig(unified) {
|
|
6107
6508
|
const clientId = unified?.data.paypal?.publishableKey;
|
|
6108
6509
|
if (!clientId) return void 0;
|
|
@@ -6207,6 +6608,7 @@ function FloPayCheckout({
|
|
|
6207
6608
|
nonce: nonceProp,
|
|
6208
6609
|
createSession: createSessionParams,
|
|
6209
6610
|
billingApiUrl,
|
|
6611
|
+
telemetry,
|
|
6210
6612
|
appearance: appearanceOverride,
|
|
6211
6613
|
locale,
|
|
6212
6614
|
loading: loadingNode,
|
|
@@ -6272,12 +6674,22 @@ function FloPayCheckout({
|
|
|
6272
6674
|
const autoCheckoutAttempted = (0, import_react10.useRef)(false);
|
|
6273
6675
|
const paypalResumeAttempted = (0, import_react10.useRef)(false);
|
|
6274
6676
|
const savedPaymentKeysRef = (0, import_react10.useRef)(null);
|
|
6677
|
+
const telemetryCheckoutContext = (0, import_react10.useMemo)(() => ({
|
|
6678
|
+
checkoutMode: checkoutModeProp ?? currentMode,
|
|
6679
|
+
layout: children ? "unknown" : layout === "buttons" ? "buttons" : "embedded"
|
|
6680
|
+
}), [checkoutModeProp, children, currentMode, layout]);
|
|
6275
6681
|
const onCompleteRef = (0, import_react10.useRef)(onComplete);
|
|
6276
6682
|
onCompleteRef.current = onComplete;
|
|
6277
6683
|
const onErrorRef = (0, import_react10.useRef)(onError);
|
|
6278
6684
|
onErrorRef.current = onError;
|
|
6279
6685
|
const onDeclineRef = (0, import_react10.useRef)(onDecline);
|
|
6280
6686
|
onDeclineRef.current = onDecline;
|
|
6687
|
+
(0, import_react10.useEffect)(() => {
|
|
6688
|
+
getFloPayTelemetryBridge(flopay)?.setCheckoutContext(telemetryCheckoutContext);
|
|
6689
|
+
if (paypalFlopay && paypalFlopay !== flopay) {
|
|
6690
|
+
getFloPayTelemetryBridge(paypalFlopay)?.setCheckoutContext(telemetryCheckoutContext);
|
|
6691
|
+
}
|
|
6692
|
+
}, [flopay, paypalFlopay, telemetryCheckoutContext]);
|
|
6281
6693
|
const onSessionCompletedRef = (0, import_react10.useRef)(onSessionCompleted);
|
|
6282
6694
|
onSessionCompletedRef.current = onSessionCompleted;
|
|
6283
6695
|
(0, import_react10.useEffect)(() => {
|
|
@@ -6317,9 +6729,11 @@ function FloPayCheckout({
|
|
|
6317
6729
|
}, [initialErrorMessage]);
|
|
6318
6730
|
const emitDecline = (0, import_react10.useCallback)(
|
|
6319
6731
|
(method, input, overrides) => {
|
|
6320
|
-
|
|
6732
|
+
invokeMerchantCallback(() => {
|
|
6733
|
+
onDeclineRef.current?.(buildDeclineEvent(method, input, overrides));
|
|
6734
|
+
});
|
|
6321
6735
|
},
|
|
6322
|
-
[]
|
|
6736
|
+
[invokeMerchantCallback]
|
|
6323
6737
|
);
|
|
6324
6738
|
const runSavedPaymentFlow = (0, import_react10.useCallback)(
|
|
6325
6739
|
async (sess, options) => {
|
|
@@ -6327,10 +6741,98 @@ function FloPayCheckout({
|
|
|
6327
6741
|
setModeOverlayError(null);
|
|
6328
6742
|
setModeOverlayStatus("processing");
|
|
6329
6743
|
const activeSessionId2 = options?.sessionId ?? sess.id;
|
|
6744
|
+
const activeFloPay = flopayRef.current ?? paypalFlopayRef.current;
|
|
6745
|
+
const activeTelemetry = getFloPayTelemetryBridge(activeFloPay);
|
|
6746
|
+
const standaloneTelemetry = activeFloPay ? null : createStandaloneTelemetryReporter(
|
|
6747
|
+
resolvedBillingUrl,
|
|
6748
|
+
telemetry,
|
|
6749
|
+
telemetryCheckoutContext
|
|
6750
|
+
);
|
|
6751
|
+
const telemetrySource = {
|
|
6752
|
+
log: (input) => {
|
|
6753
|
+
if (activeTelemetry) activeTelemetry.log(input);
|
|
6754
|
+
else standaloneTelemetry?.log(input);
|
|
6755
|
+
},
|
|
6756
|
+
error: (input) => {
|
|
6757
|
+
if (activeTelemetry) activeTelemetry.error(input);
|
|
6758
|
+
else standaloneTelemetry?.error(input);
|
|
6759
|
+
},
|
|
6760
|
+
terminal: (input) => {
|
|
6761
|
+
if (activeTelemetry) activeTelemetry.terminal(input);
|
|
6762
|
+
else standaloneTelemetry?.terminal(input);
|
|
6763
|
+
},
|
|
6764
|
+
performance: (input) => {
|
|
6765
|
+
if (activeTelemetry) activeTelemetry.performance(input);
|
|
6766
|
+
else standaloneTelemetry?.performance(input);
|
|
6767
|
+
},
|
|
6768
|
+
now: () => activeTelemetry?.now() ?? standaloneTelemetry?.now() ?? 0,
|
|
6769
|
+
elapsed: (startedAt) => activeTelemetry?.elapsed(startedAt) ?? Math.max(0, (standaloneTelemetry?.now() ?? startedAt) - startedAt)
|
|
6770
|
+
};
|
|
6771
|
+
const processingStartedAt = telemetrySource.now();
|
|
6772
|
+
let recoveryFlow = Boolean(
|
|
6773
|
+
options?.initialAutoProcessingError || options?.initialAutoProcessingPending
|
|
6774
|
+
);
|
|
6775
|
+
let recoveryStarted = false;
|
|
6776
|
+
let recoveryStartedAt;
|
|
6777
|
+
const startRecovery = () => {
|
|
6778
|
+
if (recoveryStarted) return;
|
|
6779
|
+
recoveryStarted = true;
|
|
6780
|
+
recoveryFlow = true;
|
|
6781
|
+
recoveryStartedAt = telemetrySource.now();
|
|
6782
|
+
telemetrySource.log({
|
|
6783
|
+
name: "checkout.recovery.started",
|
|
6784
|
+
stage: "recovery",
|
|
6785
|
+
paymentMethodCategory: "saved"
|
|
6786
|
+
});
|
|
6787
|
+
telemetrySource.log({
|
|
6788
|
+
name: "operation.recovery.started",
|
|
6789
|
+
stage: "recovery",
|
|
6790
|
+
paymentMethodCategory: "saved"
|
|
6791
|
+
});
|
|
6792
|
+
};
|
|
6793
|
+
let processingFinished = false;
|
|
6794
|
+
const finishProcessing = () => {
|
|
6795
|
+
if (processingFinished) return;
|
|
6796
|
+
processingFinished = true;
|
|
6797
|
+
telemetrySource.log({
|
|
6798
|
+
name: "payment.processing.completed",
|
|
6799
|
+
stage: "processing",
|
|
6800
|
+
paymentMethodCategory: "saved"
|
|
6801
|
+
});
|
|
6802
|
+
telemetrySource.performance({
|
|
6803
|
+
stage: "processing",
|
|
6804
|
+
durationMs: telemetrySource.elapsed(processingStartedAt),
|
|
6805
|
+
durationMode: "machine",
|
|
6806
|
+
paymentMethodCategory: "saved"
|
|
6807
|
+
});
|
|
6808
|
+
if (recoveryStartedAt !== void 0) {
|
|
6809
|
+
telemetrySource.performance({
|
|
6810
|
+
stage: "recovery",
|
|
6811
|
+
durationMs: telemetrySource.elapsed(recoveryStartedAt),
|
|
6812
|
+
durationMode: "machine",
|
|
6813
|
+
paymentMethodCategory: "saved"
|
|
6814
|
+
});
|
|
6815
|
+
}
|
|
6816
|
+
};
|
|
6817
|
+
telemetrySource.log({
|
|
6818
|
+
name: "payment.method.selected",
|
|
6819
|
+
stage: "processing",
|
|
6820
|
+
paymentMethodCategory: "saved"
|
|
6821
|
+
});
|
|
6822
|
+
telemetrySource.log({
|
|
6823
|
+
name: "payment.processing.started",
|
|
6824
|
+
stage: "processing",
|
|
6825
|
+
paymentMethodCategory: "saved"
|
|
6826
|
+
});
|
|
6827
|
+
if (recoveryFlow) {
|
|
6828
|
+
startRecovery();
|
|
6829
|
+
}
|
|
6830
|
+
let completionCallback;
|
|
6330
6831
|
try {
|
|
6331
6832
|
const redirectResult = getRedirectResultFromCheckoutProcessError(options?.initialAutoProcessingError);
|
|
6332
6833
|
let paymentResult;
|
|
6333
6834
|
if (redirectResult) {
|
|
6835
|
+
startRecovery();
|
|
6334
6836
|
if (redirectResult.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
|
|
6335
6837
|
persistPayPalResumeState({
|
|
6336
6838
|
sessionId: activeSessionId2,
|
|
@@ -6344,13 +6846,14 @@ function FloPayCheckout({
|
|
|
6344
6846
|
attempt3DS: options?.attempt3DS,
|
|
6345
6847
|
billingApiUrl: resolvedBillingUrl,
|
|
6346
6848
|
sessionId: activeSessionId2,
|
|
6347
|
-
session: sess
|
|
6849
|
+
session: sess,
|
|
6850
|
+
telemetry: false
|
|
6348
6851
|
});
|
|
6349
6852
|
if (redirectResult.type === "paypal_redirect_required") {
|
|
6350
6853
|
clearPayPalResumeState();
|
|
6351
6854
|
}
|
|
6352
6855
|
} else if (options?.initialAutoProcessingPending) {
|
|
6353
|
-
const api = new import_js4.PaymentAPI(resolvedBillingUrl);
|
|
6856
|
+
const api = new import_js4.PaymentAPI(resolvedBillingUrl, { telemetry: false });
|
|
6354
6857
|
const completed = await api.waitForCheckoutSessionCompletion(options.initialAutoProcessingPending.sessionId, {
|
|
6355
6858
|
initialDelayMs: options.initialAutoProcessingPending.retryAfterMs
|
|
6356
6859
|
});
|
|
@@ -6378,11 +6881,13 @@ function FloPayCheckout({
|
|
|
6378
6881
|
const result = await processSavedPaymentForMode({
|
|
6379
6882
|
billingApiUrl: resolvedBillingUrl,
|
|
6380
6883
|
sessionId: activeSessionId2,
|
|
6381
|
-
session: sess
|
|
6884
|
+
session: sess,
|
|
6885
|
+
telemetry: false
|
|
6382
6886
|
});
|
|
6383
6887
|
if (result.type === "success") {
|
|
6384
6888
|
paymentResult = result.result;
|
|
6385
6889
|
} else {
|
|
6890
|
+
startRecovery();
|
|
6386
6891
|
if (result.type === "paypal_redirect_required" && savedPaymentKeysRef.current?.publishableKey) {
|
|
6387
6892
|
persistPayPalResumeState({
|
|
6388
6893
|
sessionId: activeSessionId2,
|
|
@@ -6396,7 +6901,8 @@ function FloPayCheckout({
|
|
|
6396
6901
|
attempt3DS: options?.attempt3DS,
|
|
6397
6902
|
billingApiUrl: resolvedBillingUrl,
|
|
6398
6903
|
sessionId: activeSessionId2,
|
|
6399
|
-
session: sess
|
|
6904
|
+
session: sess,
|
|
6905
|
+
telemetry: false
|
|
6400
6906
|
});
|
|
6401
6907
|
if (result.type === "paypal_redirect_required") {
|
|
6402
6908
|
clearPayPalResumeState();
|
|
@@ -6404,20 +6910,57 @@ function FloPayCheckout({
|
|
|
6404
6910
|
}
|
|
6405
6911
|
}
|
|
6406
6912
|
if (activeSessionId2) markSessionRecentlyCompleted(activeSessionId2);
|
|
6913
|
+
finishProcessing();
|
|
6914
|
+
if (recoveryFlow) {
|
|
6915
|
+
telemetrySource.log({
|
|
6916
|
+
name: "checkout.recovery.completed",
|
|
6917
|
+
stage: "recovery",
|
|
6918
|
+
paymentMethodCategory: "saved"
|
|
6919
|
+
});
|
|
6920
|
+
telemetrySource.log({
|
|
6921
|
+
name: "operation.recovery.completed",
|
|
6922
|
+
stage: "recovery",
|
|
6923
|
+
paymentMethodCategory: "saved"
|
|
6924
|
+
});
|
|
6925
|
+
}
|
|
6926
|
+
telemetrySource.terminal({
|
|
6927
|
+
outcome: "payment_succeeded",
|
|
6928
|
+
paymentMethodCategory: "saved"
|
|
6929
|
+
});
|
|
6407
6930
|
setModeOverlayStatus("success");
|
|
6408
6931
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6409
|
-
onCompleteRef.current?.(paymentResult);
|
|
6410
|
-
return true;
|
|
6932
|
+
completionCallback = () => onCompleteRef.current?.(paymentResult);
|
|
6411
6933
|
} catch (err) {
|
|
6412
6934
|
const floPayErr = normalizeSavedPaymentError(err);
|
|
6413
6935
|
const method = floPayErr.checkoutMethod ?? DEFAULT_SAVED_PAYMENT_DECLINE_METHOD;
|
|
6414
6936
|
setModeError(floPayErr.message);
|
|
6415
6937
|
setModeOverlayError(floPayErr.message);
|
|
6416
6938
|
if (options?.fallbackToFull) {
|
|
6939
|
+
telemetrySource.log({
|
|
6940
|
+
name: "operation.fallback",
|
|
6941
|
+
stage: "recovery",
|
|
6942
|
+
paymentMethodCategory: "saved"
|
|
6943
|
+
});
|
|
6417
6944
|
setCurrentMode("full");
|
|
6418
6945
|
replaceCheckoutModeQueryParam("full");
|
|
6419
6946
|
}
|
|
6420
|
-
|
|
6947
|
+
finishProcessing();
|
|
6948
|
+
const expectedDecline = Boolean(
|
|
6949
|
+
floPayErr.declineCode || floPayErr.code?.toLowerCase().includes("declin")
|
|
6950
|
+
);
|
|
6951
|
+
if (expectedDecline) {
|
|
6952
|
+
telemetrySource.terminal({
|
|
6953
|
+
outcome: "payment_declined",
|
|
6954
|
+
paymentMethodCategory: "saved"
|
|
6955
|
+
});
|
|
6956
|
+
} else {
|
|
6957
|
+
telemetrySource.error({
|
|
6958
|
+
errorCode: recoveryFlow ? "RECOVERY_FAILED" : "PAYMENT_PROCESSING_FAILED",
|
|
6959
|
+
stage: recoveryFlow ? "recovery" : "processing",
|
|
6960
|
+
paymentMethodCategory: "saved"
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
invokeMerchantCallback(() => onErrorRef.current?.(floPayErr));
|
|
6421
6964
|
emitDecline(method, floPayErr, {
|
|
6422
6965
|
code: floPayErr.code,
|
|
6423
6966
|
declineCode: floPayErr.declineCode
|
|
@@ -6427,16 +6970,35 @@ function FloPayCheckout({
|
|
|
6427
6970
|
sleep(PROCESSING_OVERLAY_ERROR_DELAY_MS),
|
|
6428
6971
|
options?.ensureProvidersReady ? options.ensureProvidersReady() : Promise.resolve()
|
|
6429
6972
|
]);
|
|
6973
|
+
if (recoveryFlow) {
|
|
6974
|
+
telemetrySource.log({
|
|
6975
|
+
name: "checkout.recovery.completed",
|
|
6976
|
+
stage: "recovery",
|
|
6977
|
+
paymentMethodCategory: "saved"
|
|
6978
|
+
});
|
|
6979
|
+
telemetrySource.log({
|
|
6980
|
+
name: "operation.recovery.completed",
|
|
6981
|
+
stage: "recovery",
|
|
6982
|
+
paymentMethodCategory: "saved"
|
|
6983
|
+
});
|
|
6984
|
+
}
|
|
6430
6985
|
return false;
|
|
6431
6986
|
} finally {
|
|
6987
|
+
finishProcessing();
|
|
6988
|
+
if (standaloneTelemetry) finishStandaloneTelemetry(standaloneTelemetry);
|
|
6432
6989
|
setModeOverlayStatus(null);
|
|
6433
6990
|
setModeOverlayError(null);
|
|
6434
6991
|
}
|
|
6992
|
+
invokeMerchantCallback(completionCallback);
|
|
6993
|
+
return true;
|
|
6435
6994
|
},
|
|
6436
6995
|
[
|
|
6437
6996
|
emitDecline,
|
|
6997
|
+
invokeMerchantCallback,
|
|
6438
6998
|
normalizeSavedPaymentError,
|
|
6439
|
-
resolvedBillingUrl
|
|
6999
|
+
resolvedBillingUrl,
|
|
7000
|
+
telemetry,
|
|
7001
|
+
telemetryCheckoutContext
|
|
6440
7002
|
]
|
|
6441
7003
|
);
|
|
6442
7004
|
(0, import_react10.useEffect)(() => {
|
|
@@ -6454,6 +7016,8 @@ function FloPayCheckout({
|
|
|
6454
7016
|
}
|
|
6455
7017
|
paypalResumeAttempted.current = true;
|
|
6456
7018
|
void (async () => {
|
|
7019
|
+
let resumeTelemetry;
|
|
7020
|
+
let redirectResumeStartedAt = 0;
|
|
6457
7021
|
setModeError(null);
|
|
6458
7022
|
setModeOverlayError(null);
|
|
6459
7023
|
setModeOverlayStatus("processing");
|
|
@@ -6461,7 +7025,11 @@ function FloPayCheckout({
|
|
|
6461
7025
|
try {
|
|
6462
7026
|
if (params.get("redirect_status") === "failed") {
|
|
6463
7027
|
throw Object.assign(
|
|
6464
|
-
new import_shared8.FloPayError(
|
|
7028
|
+
new import_shared8.FloPayError(
|
|
7029
|
+
"PayPal payment was declined. Please try again.",
|
|
7030
|
+
"api_error",
|
|
7031
|
+
{ declineCode: "paypal_redirect_failed" }
|
|
7032
|
+
),
|
|
6465
7033
|
{ checkoutMethod: "paypal" }
|
|
6466
7034
|
);
|
|
6467
7035
|
}
|
|
@@ -6472,7 +7040,22 @@ function FloPayCheckout({
|
|
|
6472
7040
|
publishableKey: resumeState.publishableKey,
|
|
6473
7041
|
paypalPublishableKey: resumeState.paypalPublishableKey,
|
|
6474
7042
|
billingApiUrl: resolvedBillingUrl,
|
|
6475
|
-
locale
|
|
7043
|
+
locale,
|
|
7044
|
+
telemetry
|
|
7045
|
+
});
|
|
7046
|
+
resumeTelemetry = getFloPayTelemetryBridge(resumePaypalFlopay ?? resumeFlopay);
|
|
7047
|
+
redirectResumeStartedAt = resumeTelemetry?.now() ?? 0;
|
|
7048
|
+
resumeTelemetry?.log({
|
|
7049
|
+
name: "provider.redirect.resumed",
|
|
7050
|
+
stage: "redirect_resume",
|
|
7051
|
+
provider: "paypal",
|
|
7052
|
+
paymentMethodCategory: "paypal"
|
|
7053
|
+
});
|
|
7054
|
+
resumeTelemetry?.log({
|
|
7055
|
+
name: "operation.recovery.started",
|
|
7056
|
+
stage: "recovery",
|
|
7057
|
+
provider: "paypal",
|
|
7058
|
+
paymentMethodCategory: "paypal"
|
|
6476
7059
|
});
|
|
6477
7060
|
const paypalStripe = (resumePaypalFlopay ?? resumeFlopay)?.getRawProvider();
|
|
6478
7061
|
if (!paypalStripe) {
|
|
@@ -6502,7 +7085,7 @@ function FloPayCheckout({
|
|
|
6502
7085
|
const paymentMethodId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
|
|
6503
7086
|
let finalResultStatus = resultStatus;
|
|
6504
7087
|
if (resumeState.sessionId) {
|
|
6505
|
-
const resumeApi = new import_js4.PaymentAPI(resolvedBillingUrl);
|
|
7088
|
+
const resumeApi = new import_js4.PaymentAPI(resolvedBillingUrl, { telemetry: false });
|
|
6506
7089
|
const resumeSessionResult = await resumeApi.getUnifiedCheckoutSession(resumeState.sessionId);
|
|
6507
7090
|
const resumeSession = resumeSessionResult.data.session;
|
|
6508
7091
|
if (resumeSession && resumeSession.status !== "complete") {
|
|
@@ -6510,6 +7093,7 @@ function FloPayCheckout({
|
|
|
6510
7093
|
billingApiUrl: resolvedBillingUrl,
|
|
6511
7094
|
sessionId: resumeState.sessionId,
|
|
6512
7095
|
session: resumeSession,
|
|
7096
|
+
telemetry: false,
|
|
6513
7097
|
tokenizedData: {
|
|
6514
7098
|
id: paymentMethodId ?? paymentIntent.id,
|
|
6515
7099
|
type: "card",
|
|
@@ -6527,20 +7111,84 @@ function FloPayCheckout({
|
|
|
6527
7111
|
}
|
|
6528
7112
|
}
|
|
6529
7113
|
if (resumeState.sessionId) markSessionRecentlyCompleted(resumeState.sessionId);
|
|
7114
|
+
resumeTelemetry?.log({
|
|
7115
|
+
name: "operation.recovery.completed",
|
|
7116
|
+
stage: "recovery",
|
|
7117
|
+
provider: "paypal",
|
|
7118
|
+
paymentMethodCategory: "paypal"
|
|
7119
|
+
});
|
|
7120
|
+
resumeTelemetry?.performance({
|
|
7121
|
+
stage: "redirect_resume",
|
|
7122
|
+
durationMs: resumeTelemetry.elapsed(redirectResumeStartedAt),
|
|
7123
|
+
durationMode: "machine",
|
|
7124
|
+
provider: "paypal",
|
|
7125
|
+
paymentMethodCategory: "paypal"
|
|
7126
|
+
});
|
|
7127
|
+
resumeTelemetry?.terminal({
|
|
7128
|
+
outcome: "payment_succeeded",
|
|
7129
|
+
provider: "paypal",
|
|
7130
|
+
paymentMethodCategory: "paypal"
|
|
7131
|
+
});
|
|
6530
7132
|
setModeOverlayStatus("success");
|
|
6531
7133
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6532
|
-
onCompleteRef.current?.({
|
|
7134
|
+
invokeMerchantCallback(() => onCompleteRef.current?.({
|
|
6533
7135
|
status: finalResultStatus,
|
|
6534
7136
|
paymentIntentId: paymentIntent.id,
|
|
6535
7137
|
paymentMethodId,
|
|
6536
7138
|
checkoutMethod: "paypal"
|
|
6537
|
-
});
|
|
7139
|
+
}));
|
|
6538
7140
|
} catch (err) {
|
|
6539
7141
|
const floPayErr = normalizeSavedPaymentError(err);
|
|
6540
7142
|
const method = floPayErr.checkoutMethod ?? "paypal";
|
|
7143
|
+
const expectedDecline = Boolean(
|
|
7144
|
+
floPayErr.declineCode || floPayErr.code?.toLowerCase().includes("declin")
|
|
7145
|
+
);
|
|
7146
|
+
if (resumeTelemetry) {
|
|
7147
|
+
resumeTelemetry.performance({
|
|
7148
|
+
stage: "redirect_resume",
|
|
7149
|
+
durationMs: resumeTelemetry.elapsed(redirectResumeStartedAt),
|
|
7150
|
+
durationMode: "machine",
|
|
7151
|
+
provider: "paypal",
|
|
7152
|
+
paymentMethodCategory: "paypal"
|
|
7153
|
+
});
|
|
7154
|
+
if (expectedDecline) {
|
|
7155
|
+
resumeTelemetry.terminal({
|
|
7156
|
+
outcome: "payment_declined",
|
|
7157
|
+
provider: "paypal",
|
|
7158
|
+
paymentMethodCategory: "paypal"
|
|
7159
|
+
});
|
|
7160
|
+
} else {
|
|
7161
|
+
resumeTelemetry.error({
|
|
7162
|
+
errorCode: "REDIRECT_RESUME_FAILED",
|
|
7163
|
+
stage: "redirect_resume",
|
|
7164
|
+
provider: "paypal",
|
|
7165
|
+
paymentMethodCategory: "paypal"
|
|
7166
|
+
});
|
|
7167
|
+
}
|
|
7168
|
+
} else if (expectedDecline) {
|
|
7169
|
+
const reporter = createStandaloneTelemetryReporter(
|
|
7170
|
+
resolvedBillingUrl,
|
|
7171
|
+
telemetry,
|
|
7172
|
+
telemetryCheckoutContext
|
|
7173
|
+
);
|
|
7174
|
+
reporter.terminal({
|
|
7175
|
+
outcome: "payment_declined",
|
|
7176
|
+
provider: "paypal",
|
|
7177
|
+
paymentMethodCategory: "paypal"
|
|
7178
|
+
});
|
|
7179
|
+
finishStandaloneTelemetry(reporter);
|
|
7180
|
+
} else {
|
|
7181
|
+
reportStandaloneTelemetryError(
|
|
7182
|
+
resolvedBillingUrl,
|
|
7183
|
+
telemetry,
|
|
7184
|
+
telemetryCheckoutContext,
|
|
7185
|
+
"REDIRECT_RESUME_FAILED",
|
|
7186
|
+
"redirect_resume"
|
|
7187
|
+
);
|
|
7188
|
+
}
|
|
6541
7189
|
setModeError(floPayErr.message);
|
|
6542
7190
|
setModeOverlayError(floPayErr.message);
|
|
6543
|
-
onErrorRef.current?.(floPayErr);
|
|
7191
|
+
invokeMerchantCallback(() => onErrorRef.current?.(floPayErr));
|
|
6544
7192
|
emitDecline(method, floPayErr, {
|
|
6545
7193
|
code: floPayErr.code,
|
|
6546
7194
|
declineCode: floPayErr.declineCode
|
|
@@ -6555,7 +7203,7 @@ function FloPayCheckout({
|
|
|
6555
7203
|
setConfirmProcessing(false);
|
|
6556
7204
|
}
|
|
6557
7205
|
})();
|
|
6558
|
-
}, [emitDecline, locale, normalizeSavedPaymentError, resolvedBillingUrl]);
|
|
7206
|
+
}, [emitDecline, invokeMerchantCallback, locale, normalizeSavedPaymentError, resolvedBillingUrl, telemetry]);
|
|
6559
7207
|
const initializedHashRef = (0, import_react10.useRef)(null);
|
|
6560
7208
|
function hashCreateParams(params) {
|
|
6561
7209
|
const key = JSON.stringify({
|
|
@@ -6605,42 +7253,105 @@ function FloPayCheckout({
|
|
|
6605
7253
|
setModeOverlayStatus(null);
|
|
6606
7254
|
}, [createSessionHash, initialErrorMessage, sessionIdProp]);
|
|
6607
7255
|
async function resolveInlineSession(params, cacheKey) {
|
|
6608
|
-
const
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
7256
|
+
const reporter = createStandaloneTelemetryReporter(
|
|
7257
|
+
resolvedBillingUrl,
|
|
7258
|
+
telemetry,
|
|
7259
|
+
telemetryCheckoutContext
|
|
7260
|
+
);
|
|
7261
|
+
const api = new import_js4.PaymentAPI(resolvedBillingUrl, { telemetry: false });
|
|
7262
|
+
try {
|
|
7263
|
+
const cached = readCachedInlineSession(cacheKey);
|
|
7264
|
+
reporter.log({
|
|
7265
|
+
name: cached ? "operation.cache.hit" : "operation.cache.miss",
|
|
7266
|
+
stage: "session_create",
|
|
7267
|
+
requestCategory: "session_create"
|
|
7268
|
+
});
|
|
7269
|
+
let sid = cached?.sid ?? null;
|
|
7270
|
+
let realResult = null;
|
|
7271
|
+
if (sid) {
|
|
7272
|
+
try {
|
|
7273
|
+
realResult = await api.getUnifiedCheckoutSession(sid, cached?.nonce);
|
|
7274
|
+
const status = realResult.data.session?.status;
|
|
7275
|
+
if (status === "complete") {
|
|
7276
|
+
if (wasSessionRecentlyCompleted(sid)) {
|
|
7277
|
+
return { sid, result: realResult };
|
|
7278
|
+
}
|
|
7279
|
+
clearCachedInlineSession(cacheKey);
|
|
7280
|
+
sid = null;
|
|
7281
|
+
realResult = null;
|
|
6619
7282
|
}
|
|
7283
|
+
} catch {
|
|
7284
|
+
reporter.log({
|
|
7285
|
+
name: "operation.fallback",
|
|
7286
|
+
stage: "session_read",
|
|
7287
|
+
requestCategory: "session_read"
|
|
7288
|
+
});
|
|
6620
7289
|
clearCachedInlineSession(cacheKey);
|
|
6621
7290
|
sid = null;
|
|
6622
|
-
realResult = null;
|
|
6623
7291
|
}
|
|
6624
|
-
} catch {
|
|
6625
|
-
clearCachedInlineSession(cacheKey);
|
|
6626
|
-
sid = null;
|
|
6627
7292
|
}
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
7293
|
+
if (!sid) {
|
|
7294
|
+
const sessionCreateStartedAt = reporter.now();
|
|
7295
|
+
reporter.log({
|
|
7296
|
+
name: "session.create.started",
|
|
7297
|
+
stage: "session_create",
|
|
7298
|
+
requestCategory: "session_create"
|
|
7299
|
+
});
|
|
7300
|
+
const paramsWithAnalytics = {
|
|
7301
|
+
...params,
|
|
7302
|
+
avsCheck: !!enableAVS,
|
|
7303
|
+
avsConfig: typeof enableAVS === "object" ? enableAVS : void 0,
|
|
7304
|
+
checkoutType: "embedded_checkout",
|
|
7305
|
+
checkoutLayout: children ? "custom_layout" : layout === "buttons" ? "buttons_layout" : "default_layout"
|
|
7306
|
+
};
|
|
7307
|
+
try {
|
|
7308
|
+
realResult = await api.createAndFetchSession(paramsWithAnalytics);
|
|
7309
|
+
reporter.log({
|
|
7310
|
+
name: "session.request.completed",
|
|
7311
|
+
stage: "session_complete",
|
|
7312
|
+
requestCategory: "session_create",
|
|
7313
|
+
statusClass: "2xx"
|
|
7314
|
+
});
|
|
7315
|
+
reporter.performance({
|
|
7316
|
+
stage: "session_create",
|
|
7317
|
+
durationMs: reporter.now() - sessionCreateStartedAt,
|
|
7318
|
+
durationMode: "machine",
|
|
7319
|
+
requestCategory: "session_create",
|
|
7320
|
+
statusClass: "2xx"
|
|
7321
|
+
});
|
|
7322
|
+
} catch (error) {
|
|
7323
|
+
reporter.performance({
|
|
7324
|
+
stage: "session_create",
|
|
7325
|
+
durationMs: reporter.now() - sessionCreateStartedAt,
|
|
7326
|
+
durationMode: "machine",
|
|
7327
|
+
requestCategory: "session_create",
|
|
7328
|
+
statusClass: "network_error"
|
|
7329
|
+
});
|
|
7330
|
+
if (error instanceof import_shared8.FloPayError && error.type === "validation_error") {
|
|
7331
|
+
reporter.terminal({
|
|
7332
|
+
outcome: "validation_rejected",
|
|
7333
|
+
stage: "session_create",
|
|
7334
|
+
paymentMethodCategory: "unknown"
|
|
7335
|
+
});
|
|
7336
|
+
} else {
|
|
7337
|
+
reporter.error({
|
|
7338
|
+
errorCode: "CHECKOUT_SESSION_CREATE_FAILED",
|
|
7339
|
+
stage: "session_create",
|
|
7340
|
+
paymentMethodCategory: "unknown",
|
|
7341
|
+
requestCategory: "session_create"
|
|
7342
|
+
});
|
|
7343
|
+
}
|
|
7344
|
+
throw error;
|
|
7345
|
+
}
|
|
7346
|
+
sid = realResult.data.session?.id ?? "";
|
|
7347
|
+
if (sid) {
|
|
7348
|
+
persistCachedInlineSession(cacheKey, sid, realResult.data.session?.clientSecret);
|
|
7349
|
+
}
|
|
6641
7350
|
}
|
|
7351
|
+
return { sid: sid ?? "", result: realResult };
|
|
7352
|
+
} finally {
|
|
7353
|
+
finishStandaloneTelemetry(reporter);
|
|
6642
7354
|
}
|
|
6643
|
-
return { sid: sid ?? "", result: realResult };
|
|
6644
7355
|
}
|
|
6645
7356
|
const bootstrapInlineSession = (0, import_react10.useCallback)(
|
|
6646
7357
|
async (patch) => {
|
|
@@ -6686,7 +7397,8 @@ function FloPayCheckout({
|
|
|
6686
7397
|
publishableKey,
|
|
6687
7398
|
paypalPublishableKey,
|
|
6688
7399
|
billingApiUrl: resolvedBillingUrl,
|
|
6689
|
-
locale
|
|
7400
|
+
locale,
|
|
7401
|
+
telemetry
|
|
6690
7402
|
});
|
|
6691
7403
|
flopayRef.current = instance;
|
|
6692
7404
|
setFloPay(instance);
|
|
@@ -6701,7 +7413,7 @@ function FloPayCheckout({
|
|
|
6701
7413
|
}
|
|
6702
7414
|
return resolved;
|
|
6703
7415
|
},
|
|
6704
|
-
[locale, resolvedBillingUrl]
|
|
7416
|
+
[locale, resolvedBillingUrl, telemetry, telemetryCheckoutContext]
|
|
6705
7417
|
);
|
|
6706
7418
|
const handleInlineSessionPatch = (0, import_react10.useCallback)(async (patch) => {
|
|
6707
7419
|
if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) {
|
|
@@ -6780,7 +7492,9 @@ function FloPayCheckout({
|
|
|
6780
7492
|
setModeOverlayStatus("success");
|
|
6781
7493
|
await sleep(PROCESSING_OVERLAY_SUCCESS_DELAY_MS);
|
|
6782
7494
|
if (!cancelled) {
|
|
6783
|
-
|
|
7495
|
+
invokeMerchantCallback(() => {
|
|
7496
|
+
onCompleteRef.current?.({ status: "succeeded" });
|
|
7497
|
+
});
|
|
6784
7498
|
setModeOverlayStatus(null);
|
|
6785
7499
|
setModeOverlayError(null);
|
|
6786
7500
|
}
|
|
@@ -6800,8 +7514,9 @@ function FloPayCheckout({
|
|
|
6800
7514
|
}
|
|
6801
7515
|
setIsLoading(true);
|
|
6802
7516
|
async function init() {
|
|
7517
|
+
let sessionReadCompleted = false;
|
|
6803
7518
|
try {
|
|
6804
|
-
const api = new import_js4.PaymentAPI(resolvedBillingUrl);
|
|
7519
|
+
const api = new import_js4.PaymentAPI(resolvedBillingUrl, { telemetry: false });
|
|
6805
7520
|
const result = await api.getUnifiedCheckoutSession(activeSessionId, nonceProp);
|
|
6806
7521
|
if (cancelled) return;
|
|
6807
7522
|
setUnified(result);
|
|
@@ -6812,7 +7527,9 @@ function FloPayCheckout({
|
|
|
6812
7527
|
}
|
|
6813
7528
|
if (sess.status === "complete") {
|
|
6814
7529
|
setIsLoading(false);
|
|
6815
|
-
|
|
7530
|
+
invokeMerchantCallback(() => {
|
|
7531
|
+
onSessionCompletedRef.current?.(sess.successUrl ?? "");
|
|
7532
|
+
});
|
|
6816
7533
|
return;
|
|
6817
7534
|
}
|
|
6818
7535
|
if (sess.status === "expired") {
|
|
@@ -6820,6 +7537,7 @@ function FloPayCheckout({
|
|
|
6820
7537
|
code: "checkout_session_expired"
|
|
6821
7538
|
});
|
|
6822
7539
|
}
|
|
7540
|
+
sessionReadCompleted = true;
|
|
6823
7541
|
const effectiveMode = checkoutModeProp ?? sess.checkoutMode ?? "full";
|
|
6824
7542
|
setCurrentMode(effectiveMode);
|
|
6825
7543
|
const hasPayPalRedirectParams = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("payment_intent");
|
|
@@ -6851,6 +7569,23 @@ function FloPayCheckout({
|
|
|
6851
7569
|
if (!cancelled) setIsLoading(false);
|
|
6852
7570
|
} catch (err) {
|
|
6853
7571
|
if (cancelled) return;
|
|
7572
|
+
if (!(err instanceof import_shared8.FloPayError)) {
|
|
7573
|
+
reportStandaloneTelemetryError(
|
|
7574
|
+
resolvedBillingUrl,
|
|
7575
|
+
telemetry,
|
|
7576
|
+
telemetryCheckoutContext,
|
|
7577
|
+
"INTERNAL_SDK_ERROR",
|
|
7578
|
+
"checkout_mount"
|
|
7579
|
+
);
|
|
7580
|
+
} else if (!sessionReadCompleted && !isExpectedExistingSessionError(err)) {
|
|
7581
|
+
reportStandaloneTelemetryError(
|
|
7582
|
+
resolvedBillingUrl,
|
|
7583
|
+
telemetry,
|
|
7584
|
+
telemetryCheckoutContext,
|
|
7585
|
+
"NETWORK_REQUEST_FAILED",
|
|
7586
|
+
"session_read"
|
|
7587
|
+
);
|
|
7588
|
+
}
|
|
6854
7589
|
const floPayErr = err instanceof import_shared8.FloPayError ? err : new import_shared8.FloPayError(err instanceof Error ? err.message : "Failed to initialize checkout", "api_error");
|
|
6855
7590
|
setLoadError(floPayErr);
|
|
6856
7591
|
setIsLoading(false);
|
|
@@ -6872,7 +7607,8 @@ function FloPayCheckout({
|
|
|
6872
7607
|
publishableKey,
|
|
6873
7608
|
paypalPublishableKey,
|
|
6874
7609
|
billingApiUrl: resolvedBillingUrl,
|
|
6875
|
-
locale
|
|
7610
|
+
locale,
|
|
7611
|
+
telemetry
|
|
6876
7612
|
});
|
|
6877
7613
|
flopayRef.current = instance;
|
|
6878
7614
|
setFloPay(instance);
|
|
@@ -6889,6 +7625,7 @@ function FloPayCheckout({
|
|
|
6889
7625
|
createSessionHash,
|
|
6890
7626
|
effectiveCreateSessionMode,
|
|
6891
7627
|
initSessionDependency,
|
|
7628
|
+
invokeMerchantCallback,
|
|
6892
7629
|
nonceProp,
|
|
6893
7630
|
runSavedPaymentFlow
|
|
6894
7631
|
]);
|
|
@@ -7056,7 +7793,7 @@ function FloPayCheckout({
|
|
|
7056
7793
|
}
|
|
7057
7794
|
),
|
|
7058
7795
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
7059
|
-
|
|
7796
|
+
InstrumentedDirectPayPalButton,
|
|
7060
7797
|
{
|
|
7061
7798
|
sessionId: activeSessionId,
|
|
7062
7799
|
nonce: session.clientSecret || void 0,
|
|
@@ -7071,6 +7808,8 @@ function FloPayCheckout({
|
|
|
7071
7808
|
onDecline,
|
|
7072
7809
|
onButtonClick,
|
|
7073
7810
|
session,
|
|
7811
|
+
telemetry,
|
|
7812
|
+
telemetryContext: telemetryCheckoutContext,
|
|
7074
7813
|
debug
|
|
7075
7814
|
}
|
|
7076
7815
|
)
|
|
@@ -8138,7 +8877,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8138
8877
|
};
|
|
8139
8878
|
}, []);
|
|
8140
8879
|
(0, import_react13.useEffect)(() => {
|
|
8141
|
-
if (
|
|
8880
|
+
if (typeof window === "undefined") {
|
|
8142
8881
|
return;
|
|
8143
8882
|
}
|
|
8144
8883
|
const handleKeyDown = (event) => {
|
|
@@ -8150,7 +8889,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
8150
8889
|
return () => {
|
|
8151
8890
|
window.removeEventListener("keydown", handleKeyDown);
|
|
8152
8891
|
};
|
|
8153
|
-
}, [
|
|
8892
|
+
}, []);
|
|
8154
8893
|
const emitDecline = (0, import_react13.useCallback)((error, method = DEFAULT_SAVED_PAYMENT_DECLINE_METHOD) => {
|
|
8155
8894
|
onDeclineRef.current?.(buildDeclineEvent(method, error, {
|
|
8156
8895
|
code: error.code,
|