@flopay/react 1.1.5 → 1.2.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 +42 -11
- package/dist/index.cjs +1461 -318
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -11
- package/dist/index.d.ts +75 -11
- package/dist/index.mjs +1472 -319
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -287,7 +287,7 @@ var AddressElement = createElementComponent("address", "AddressElement");
|
|
|
287
287
|
var import_react_stripe_js = require("@stripe/react-stripe-js");
|
|
288
288
|
var import_shared5 = require("@flopay/shared");
|
|
289
289
|
var import_js2 = require("@flopay/js");
|
|
290
|
-
var import_react8 = require("react");
|
|
290
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
291
291
|
|
|
292
292
|
// src/hooks.ts
|
|
293
293
|
var import_react5 = require("react");
|
|
@@ -1261,9 +1261,11 @@ ${debugLines.join("\n")}`
|
|
|
1261
1261
|
// src/split-card-form.tsx
|
|
1262
1262
|
var import_shared6 = require("@flopay/shared");
|
|
1263
1263
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1264
|
-
var
|
|
1264
|
+
var STRIPE_RESUME_KEY = "flopay_stripe_resume";
|
|
1265
|
+
var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
1265
1266
|
var FLOPAY_KEYFRAMES = `
|
|
1266
1267
|
.paypal-buttons { margin: 0 !important; vertical-align: top !important; }
|
|
1268
|
+
@keyframes flopay-spin { to { transform: rotate(360deg); } }
|
|
1267
1269
|
@keyframes flopay-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
|
|
1268
1270
|
@keyframes flopay-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } }
|
|
1269
1271
|
@keyframes flopay-buttons-enter {
|
|
@@ -1284,6 +1286,33 @@ var FLOPAY_KEYFRAMES = `
|
|
|
1284
1286
|
}
|
|
1285
1287
|
`;
|
|
1286
1288
|
var DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT = 44;
|
|
1289
|
+
function derivePrimaryTileStyle(opts) {
|
|
1290
|
+
if (!opts.themeBundle) {
|
|
1291
|
+
return {
|
|
1292
|
+
backgroundColor: "white",
|
|
1293
|
+
color: "#262833",
|
|
1294
|
+
border: "1px solid #d1d5db",
|
|
1295
|
+
borderRadius: "8px",
|
|
1296
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.04)"
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
const submit = opts.submitButtonStyle ?? {};
|
|
1300
|
+
return {
|
|
1301
|
+
backgroundColor: submit.backgroundColor ?? opts.resolvedPrimaryColor,
|
|
1302
|
+
color: submit.color ?? "white",
|
|
1303
|
+
border: submit.border ?? "none",
|
|
1304
|
+
borderRadius: submit.borderRadius ?? opts.resolvedBorderRadius,
|
|
1305
|
+
boxShadow: submit.boxShadow
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
var BUTTONS_PANEL_HIDDEN_STYLE = {
|
|
1309
|
+
visibility: "hidden",
|
|
1310
|
+
position: "absolute",
|
|
1311
|
+
pointerEvents: "none",
|
|
1312
|
+
width: "100%",
|
|
1313
|
+
height: 0,
|
|
1314
|
+
overflow: "hidden"
|
|
1315
|
+
};
|
|
1287
1316
|
function getButtonMethodLabel(method) {
|
|
1288
1317
|
switch (method) {
|
|
1289
1318
|
case "paypal":
|
|
@@ -1321,6 +1350,7 @@ function resolveExpressCheckoutLoadState(event, methods) {
|
|
|
1321
1350
|
function ExpressCheckoutReadySwap({
|
|
1322
1351
|
state,
|
|
1323
1352
|
placeholderTestId,
|
|
1353
|
+
borderRadius = 8,
|
|
1324
1354
|
children
|
|
1325
1355
|
}) {
|
|
1326
1356
|
if (state === "unavailable" || state === "load_error") return null;
|
|
@@ -1334,7 +1364,7 @@ function ExpressCheckoutReadySwap({
|
|
|
1334
1364
|
position: "absolute",
|
|
1335
1365
|
inset: 0,
|
|
1336
1366
|
height: 44,
|
|
1337
|
-
borderRadius
|
|
1367
|
+
borderRadius,
|
|
1338
1368
|
background: "#e5e7eb",
|
|
1339
1369
|
animation: state === "ready" ? void 0 : "flopay-pulse 1.5s ease-in-out infinite",
|
|
1340
1370
|
opacity: state === "ready" ? 0 : 1,
|
|
@@ -1377,7 +1407,8 @@ function PayPalButtonInner({
|
|
|
1377
1407
|
onButtonClick,
|
|
1378
1408
|
onDecline,
|
|
1379
1409
|
runBeforeButtonClick,
|
|
1380
|
-
onLoadStateChange
|
|
1410
|
+
onLoadStateChange,
|
|
1411
|
+
placeholderBorderRadius
|
|
1381
1412
|
}) {
|
|
1382
1413
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
1383
1414
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -1547,31 +1578,39 @@ function PayPalButtonInner({
|
|
|
1547
1578
|
}
|
|
1548
1579
|
}, [stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]);
|
|
1549
1580
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1550
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1551
|
-
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1582
|
+
ExpressCheckoutReadySwap,
|
|
1552
1583
|
{
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1584
|
+
state: loadState,
|
|
1585
|
+
placeholderTestId: "flopay-paypal-placeholder",
|
|
1586
|
+
borderRadius: placeholderBorderRadius,
|
|
1587
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1588
|
+
import_react_stripe_js.ExpressCheckoutElement,
|
|
1589
|
+
{
|
|
1590
|
+
onReady: (event) => setLoadState(resolveExpressCheckoutLoadState(event, ["paypal"])),
|
|
1591
|
+
onLoadError: () => setLoadState("load_error"),
|
|
1592
|
+
onClick: handlePayPalClick,
|
|
1593
|
+
onConfirm: handlePayPalConfirm,
|
|
1594
|
+
onCancel: () => {
|
|
1595
|
+
beforeClickRef.current = null;
|
|
1596
|
+
onDecline?.(buildDeclineEvent("paypal", "PayPal checkout was cancelled."));
|
|
1597
|
+
},
|
|
1598
|
+
options: {
|
|
1599
|
+
buttonType: { paypal: "paypal" },
|
|
1600
|
+
billingAddressRequired: false,
|
|
1601
|
+
phoneNumberRequired: false,
|
|
1602
|
+
shippingAddressRequired: false,
|
|
1603
|
+
paymentMethods: {
|
|
1604
|
+
applePay: "never",
|
|
1605
|
+
googlePay: "never",
|
|
1606
|
+
paypal: "auto",
|
|
1607
|
+
link: "never"
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1571
1610
|
}
|
|
1572
|
-
|
|
1611
|
+
)
|
|
1573
1612
|
}
|
|
1574
|
-
)
|
|
1613
|
+
),
|
|
1575
1614
|
submitting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProcessingOverlay, { status: "processing" })
|
|
1576
1615
|
] });
|
|
1577
1616
|
}
|
|
@@ -1579,14 +1618,14 @@ function WalletButtonInner({
|
|
|
1579
1618
|
sessionId,
|
|
1580
1619
|
email,
|
|
1581
1620
|
billingApiUrl,
|
|
1582
|
-
|
|
1583
|
-
showGooglePay = true,
|
|
1621
|
+
expressMethods,
|
|
1584
1622
|
onTokenizedBody,
|
|
1585
1623
|
onErrorChange,
|
|
1586
1624
|
onButtonClick,
|
|
1587
1625
|
onDecline,
|
|
1588
1626
|
runBeforeButtonClick,
|
|
1589
|
-
onLoadStateChange
|
|
1627
|
+
onLoadStateChange,
|
|
1628
|
+
placeholderBorderRadius
|
|
1590
1629
|
}) {
|
|
1591
1630
|
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
1592
1631
|
const elements = (0, import_react_stripe_js.useElements)();
|
|
@@ -1604,10 +1643,9 @@ function WalletButtonInner({
|
|
|
1604
1643
|
const walletType = event.expressPaymentType;
|
|
1605
1644
|
let prepared = beforeClickRef.current;
|
|
1606
1645
|
beforeClickRef.current = null;
|
|
1646
|
+
const buttonMethod = walletType === "apple_pay" ? "apple_pay" : "google_pay";
|
|
1607
1647
|
if (!prepared && runBeforeButtonClick) {
|
|
1608
|
-
const beforeClick = await runBeforeButtonClick(
|
|
1609
|
-
walletType === "apple_pay" ? "apple_pay" : "google_pay"
|
|
1610
|
-
);
|
|
1648
|
+
const beforeClick = await runBeforeButtonClick(buttonMethod);
|
|
1611
1649
|
if (!beforeClick.proceed) {
|
|
1612
1650
|
event.paymentFailed({ reason: "fail", message: "Wallet checkout was cancelled." });
|
|
1613
1651
|
return;
|
|
@@ -1617,7 +1655,7 @@ function WalletButtonInner({
|
|
|
1617
1655
|
sessionId: beforeClick.sessionId
|
|
1618
1656
|
};
|
|
1619
1657
|
}
|
|
1620
|
-
const method =
|
|
1658
|
+
const method = buttonMethod;
|
|
1621
1659
|
const effectiveSessionId = prepared?.sessionId ?? sessionId;
|
|
1622
1660
|
const effectiveEmail = prepared?.accountPatch?.email ?? email;
|
|
1623
1661
|
try {
|
|
@@ -1687,52 +1725,654 @@ function WalletButtonInner({
|
|
|
1687
1725
|
},
|
|
1688
1726
|
[stripe, elements, sessionId, email, baseUrl, onTokenizedBody, onErrorChange, onDecline, runBeforeButtonClick]
|
|
1689
1727
|
);
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1728
|
+
const expressMethodMap = (0, import_react8.useMemo)(() => {
|
|
1729
|
+
const allKeys = ["applePay", "googlePay", "paypal", "link", "amazonPay", "klarna"];
|
|
1730
|
+
const alwaysCapable = /* @__PURE__ */ new Set(["applePay", "googlePay"]);
|
|
1731
|
+
const enabledKeys = new Set(expressMethods.map(import_shared5.stripeExpressMethodToOptionKey));
|
|
1732
|
+
const out = {};
|
|
1733
|
+
for (const key of allKeys) {
|
|
1734
|
+
if (!enabledKeys.has(key)) {
|
|
1735
|
+
out[key] = "never";
|
|
1736
|
+
} else {
|
|
1737
|
+
out[key] = alwaysCapable.has(key) ? "always" : "auto";
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
return out;
|
|
1741
|
+
}, [expressMethods]);
|
|
1742
|
+
const availableMethodKeys = (0, import_react8.useMemo)(
|
|
1743
|
+
() => expressMethods.map(import_shared5.stripeExpressMethodToOptionKey),
|
|
1744
|
+
[expressMethods]
|
|
1745
|
+
);
|
|
1746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1748
|
+
ExpressCheckoutReadySwap,
|
|
1749
|
+
{
|
|
1750
|
+
state: loadState,
|
|
1751
|
+
placeholderTestId: "flopay-wallet-placeholder",
|
|
1752
|
+
borderRadius: placeholderBorderRadius,
|
|
1753
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1754
|
+
import_react_stripe_js.ExpressCheckoutElement,
|
|
1755
|
+
{
|
|
1756
|
+
onReady: (event) => {
|
|
1757
|
+
setLoadState(resolveExpressCheckoutLoadState(event, availableMethodKeys));
|
|
1758
|
+
},
|
|
1759
|
+
onLoadError: (_event) => {
|
|
1760
|
+
setLoadState("load_error");
|
|
1761
|
+
},
|
|
1762
|
+
onClick: async (event) => {
|
|
1763
|
+
lastWalletMethodRef.current = event.expressPaymentType === "apple_pay" ? "apple_pay" : "google_pay";
|
|
1764
|
+
const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick(lastWalletMethodRef.current) : { proceed: true };
|
|
1765
|
+
if (!beforeClick.proceed) {
|
|
1766
|
+
beforeClickRef.current = null;
|
|
1767
|
+
event.reject();
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
beforeClickRef.current = {
|
|
1771
|
+
accountPatch: beforeClick.accountPatch,
|
|
1772
|
+
sessionId: beforeClick.sessionId
|
|
1773
|
+
};
|
|
1774
|
+
onButtonClick?.(lastWalletMethodRef.current);
|
|
1775
|
+
event.resolve();
|
|
1776
|
+
},
|
|
1777
|
+
onConfirm: handleWalletConfirm,
|
|
1778
|
+
onCancel: () => {
|
|
1779
|
+
beforeClickRef.current = null;
|
|
1780
|
+
onDecline?.(buildDeclineEvent(lastWalletMethodRef.current, "Wallet checkout was cancelled."));
|
|
1781
|
+
},
|
|
1782
|
+
options: {
|
|
1783
|
+
buttonType: { applePay: "plain", googlePay: "plain" },
|
|
1784
|
+
paymentMethods: expressMethodMap,
|
|
1785
|
+
layout: { maxColumns: 1, overflow: "never" }
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
)
|
|
1789
|
+
}
|
|
1790
|
+
),
|
|
1791
|
+
submitting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProcessingOverlay, { status: "processing" })
|
|
1792
|
+
] });
|
|
1793
|
+
}
|
|
1794
|
+
function StripeMethodButton({
|
|
1795
|
+
method,
|
|
1796
|
+
themeId,
|
|
1797
|
+
submitting = false,
|
|
1798
|
+
disabled = false,
|
|
1799
|
+
highlighted = false,
|
|
1800
|
+
hasNextStep = false,
|
|
1801
|
+
onClick,
|
|
1802
|
+
backgroundColor,
|
|
1803
|
+
borderColor,
|
|
1804
|
+
textColor,
|
|
1805
|
+
borderRadius,
|
|
1806
|
+
fontFamily
|
|
1807
|
+
}) {
|
|
1808
|
+
const brand = (0, import_shared5.resolveStripeMethodBrandVariant)(method, themeId);
|
|
1809
|
+
const resolvedBackground = brand?.backgroundColor ?? backgroundColor ?? "#ffffff";
|
|
1810
|
+
const resolvedBorder = brand?.borderColor ?? borderColor ?? "#d1d5db";
|
|
1811
|
+
const resolvedTextColor = brand?.textColor ?? textColor ?? "#262833";
|
|
1812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1813
|
+
"button",
|
|
1814
|
+
{
|
|
1815
|
+
type: "button",
|
|
1816
|
+
"data-testid": `flopay-stripe-method-button-${method}`,
|
|
1817
|
+
"data-method": method,
|
|
1818
|
+
onClick: () => {
|
|
1819
|
+
if (!submitting && !disabled) onClick();
|
|
1820
|
+
},
|
|
1821
|
+
disabled: submitting || disabled,
|
|
1822
|
+
"aria-busy": submitting || void 0,
|
|
1823
|
+
style: {
|
|
1824
|
+
position: "relative",
|
|
1825
|
+
width: "100%",
|
|
1826
|
+
boxSizing: "border-box",
|
|
1827
|
+
height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT,
|
|
1828
|
+
padding: "0 1rem",
|
|
1829
|
+
backgroundColor: resolvedBackground,
|
|
1830
|
+
color: resolvedTextColor,
|
|
1831
|
+
border: `1px solid ${resolvedBorder}`,
|
|
1832
|
+
borderRadius: borderRadius ?? 8,
|
|
1833
|
+
fontSize: "0.95rem",
|
|
1834
|
+
fontWeight: 600,
|
|
1835
|
+
fontFamily,
|
|
1836
|
+
cursor: submitting || disabled ? "not-allowed" : "pointer",
|
|
1837
|
+
opacity: disabled && !submitting ? 0.55 : 1,
|
|
1838
|
+
// Submitting state pulses the whole button background, mirroring
|
|
1839
|
+
// the wallet ECE's `flopay-pulse` skeleton. This keeps the loading
|
|
1840
|
+
// affordance consistent between embedded wallets (Apple Pay,
|
|
1841
|
+
// Google Pay) and these custom APM tile buttons — same animation
|
|
1842
|
+
// curve, same duration, only the surface colour changes (grey for
|
|
1843
|
+
// the wallet skeleton vs. brand colour for an in-flight tile).
|
|
1844
|
+
animation: submitting ? "flopay-pulse 1.5s ease-in-out infinite" : void 0,
|
|
1845
|
+
display: "flex",
|
|
1846
|
+
alignItems: "center",
|
|
1847
|
+
// Always center the logo+name group. The drill-in chevron (input
|
|
1848
|
+
// methods) is absolutely positioned at the right edge below so it
|
|
1849
|
+
// doesn't pull the label off-center.
|
|
1850
|
+
justifyContent: "center",
|
|
1851
|
+
gap: 8,
|
|
1852
|
+
transition: "transform 0.1s, opacity 140ms ease-out, border-color 140ms ease-out",
|
|
1853
|
+
boxShadow: highlighted ? "0 0 0 2px rgba(74, 73, 255, 0.15)" : void 0
|
|
1854
|
+
},
|
|
1855
|
+
children: submitting ? (
|
|
1856
|
+
// Pulse-skeleton parity with the wallet ECE: no spinner, just the
|
|
1857
|
+
// status text on top of the pulsing background. Keeps the loading
|
|
1858
|
+
// affordance shape-equivalent across both kinds of tile.
|
|
1859
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { margin: "0 auto" }, children: `Connecting to ${(0, import_shared5.getStripeMethodDisplayName)(method)}\u2026` })
|
|
1860
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1862
|
+
"span",
|
|
1863
|
+
{
|
|
1864
|
+
style: {
|
|
1865
|
+
display: "inline-flex",
|
|
1866
|
+
alignItems: "center",
|
|
1867
|
+
// Real vendored logos are self-contained brand cards, so they
|
|
1868
|
+
// get an 8px gap from the label. The generated placeholder
|
|
1869
|
+
// monogram reads as part of the label (the tile's first letter),
|
|
1870
|
+
// so it sits flush (0px) against the name.
|
|
1871
|
+
gap: (0, import_shared5.hasVendoredStripeMethodLogo)(method) ? 8 : 0
|
|
1872
|
+
},
|
|
1873
|
+
children: [
|
|
1874
|
+
brand?.logoSvg && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1875
|
+
"span",
|
|
1876
|
+
{
|
|
1877
|
+
"aria-hidden": "true",
|
|
1878
|
+
style: {
|
|
1879
|
+
// 3:2 box matching the vendored datatrans logos' 120×80
|
|
1880
|
+
// viewBox. Each logo ships with its own white rounded-rect
|
|
1881
|
+
// background baked in, so the mark stays legible on any
|
|
1882
|
+
// brand-coloured tile without an extra chip wrapper here.
|
|
1883
|
+
width: 30,
|
|
1884
|
+
height: 20,
|
|
1885
|
+
flexShrink: 0,
|
|
1886
|
+
display: "inline-flex",
|
|
1887
|
+
borderRadius: 3,
|
|
1888
|
+
overflow: "hidden"
|
|
1889
|
+
},
|
|
1890
|
+
dangerouslySetInnerHTML: { __html: brand.logoSvg }
|
|
1891
|
+
}
|
|
1892
|
+
),
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: (0, import_shared5.getStripeMethodDisplayName)(method) })
|
|
1894
|
+
]
|
|
1895
|
+
}
|
|
1896
|
+
),
|
|
1897
|
+
hasNextStep && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1898
|
+
"svg",
|
|
1899
|
+
{
|
|
1900
|
+
width: "14",
|
|
1901
|
+
height: "14",
|
|
1902
|
+
viewBox: "0 0 24 24",
|
|
1903
|
+
fill: "none",
|
|
1904
|
+
stroke: resolvedTextColor,
|
|
1905
|
+
strokeWidth: "2.5",
|
|
1906
|
+
strokeLinecap: "round",
|
|
1907
|
+
strokeLinejoin: "round",
|
|
1908
|
+
style: {
|
|
1909
|
+
position: "absolute",
|
|
1910
|
+
right: "1rem",
|
|
1911
|
+
top: "50%",
|
|
1912
|
+
transform: "translateY(-50%)",
|
|
1913
|
+
flexShrink: 0,
|
|
1914
|
+
opacity: 0.7
|
|
1915
|
+
},
|
|
1916
|
+
"aria-hidden": "true",
|
|
1917
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M9 18l6-6-6-6" })
|
|
1918
|
+
}
|
|
1919
|
+
)
|
|
1920
|
+
] })
|
|
1921
|
+
}
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
function StripeMethodInlineForm({
|
|
1925
|
+
method,
|
|
1926
|
+
sessionId,
|
|
1927
|
+
email,
|
|
1928
|
+
billingName,
|
|
1929
|
+
billingApiUrl,
|
|
1930
|
+
onTokenizedBody,
|
|
1931
|
+
onErrorChange,
|
|
1932
|
+
onDecline,
|
|
1933
|
+
onCancel,
|
|
1934
|
+
runBeforeButtonClick,
|
|
1935
|
+
onButtonClick,
|
|
1936
|
+
isProcessing,
|
|
1937
|
+
submitButtonColor,
|
|
1938
|
+
submitButtonBorderRadius,
|
|
1939
|
+
submitButtonStyle
|
|
1940
|
+
}) {
|
|
1941
|
+
const stripe = (0, import_react_stripe_js.useStripe)();
|
|
1942
|
+
const elements = (0, import_react_stripe_js.useElements)();
|
|
1943
|
+
const [submitting, setSubmitting] = (0, import_react8.useState)(false);
|
|
1944
|
+
const submittingRef = (0, import_react8.useRef)(false);
|
|
1945
|
+
const [isMethodComplete, setIsMethodComplete] = (0, import_react8.useState)(false);
|
|
1946
|
+
const [loadState, setLoadState] = (0, import_react8.useState)("loading");
|
|
1947
|
+
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
1948
|
+
const handlePay = (0, import_react8.useCallback)(async () => {
|
|
1949
|
+
if (!stripe || !elements || isProcessing || submittingRef.current || !isMethodComplete) return;
|
|
1950
|
+
submittingRef.current = true;
|
|
1951
|
+
setSubmitting(true);
|
|
1952
|
+
const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick("card") : { proceed: true };
|
|
1953
|
+
if (!beforeClick.proceed) {
|
|
1954
|
+
submittingRef.current = false;
|
|
1955
|
+
setSubmitting(false);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
onButtonClick?.("card");
|
|
1959
|
+
const effectiveSessionId = beforeClick.sessionId ?? sessionId;
|
|
1960
|
+
const effectiveEmail = beforeClick.accountPatch?.email ?? email;
|
|
1961
|
+
try {
|
|
1962
|
+
onErrorChange?.(null);
|
|
1963
|
+
const submitRes = await elements.submit();
|
|
1964
|
+
if (submitRes.error) {
|
|
1965
|
+
onErrorChange?.(submitRes.error.message ?? "Payment failed.");
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
const pmRes = await stripe.createPaymentMethod({ elements });
|
|
1969
|
+
if (pmRes.error || !pmRes.paymentMethod) {
|
|
1970
|
+
onErrorChange?.(pmRes.error?.message ?? "Failed to create payment method.");
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const paymentMethod = pmRes.paymentMethod;
|
|
1974
|
+
if (!effectiveSessionId || !effectiveEmail) {
|
|
1975
|
+
throw new Error("Missing sessionId or email for payment.");
|
|
1976
|
+
}
|
|
1977
|
+
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
1978
|
+
method: "POST",
|
|
1979
|
+
headers: { "Content-Type": "application/json" },
|
|
1980
|
+
body: JSON.stringify({
|
|
1981
|
+
sessionId: effectiveSessionId,
|
|
1982
|
+
email: effectiveEmail,
|
|
1983
|
+
paymentMethodType: paymentMethod.type || method,
|
|
1984
|
+
isPaypal: false
|
|
1985
|
+
})
|
|
1986
|
+
});
|
|
1987
|
+
if (!intentResponse.ok) {
|
|
1988
|
+
const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
|
|
1989
|
+
onErrorChange?.(intentError.message);
|
|
1990
|
+
onDecline?.(buildDeclineEvent("card", intentError));
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1993
|
+
const intentJson = await intentResponse.json();
|
|
1994
|
+
const intentClientSecret = intentJson.data?.id;
|
|
1995
|
+
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
1996
|
+
if (typeof window !== "undefined") {
|
|
1997
|
+
try {
|
|
1998
|
+
localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
|
|
1999
|
+
sessionId: effectiveSessionId,
|
|
2000
|
+
paymentMethodId: paymentMethod.id,
|
|
2001
|
+
paymentMethodType: paymentMethod.type ?? method,
|
|
2002
|
+
gateway: "stripe"
|
|
2003
|
+
}));
|
|
2004
|
+
} catch {
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
const { error: confirmError, paymentIntent } = await stripe.confirmPayment({
|
|
2008
|
+
clientSecret: intentClientSecret,
|
|
2009
|
+
confirmParams: {
|
|
2010
|
+
return_url: window.location.href,
|
|
2011
|
+
payment_method: paymentMethod.id
|
|
2012
|
+
},
|
|
2013
|
+
redirect: "if_required"
|
|
2014
|
+
});
|
|
2015
|
+
if (confirmError) {
|
|
2016
|
+
if (typeof window !== "undefined") {
|
|
2017
|
+
try {
|
|
2018
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2019
|
+
} catch {
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
const message = confirmError.message ?? "Payment failed.";
|
|
2023
|
+
onErrorChange?.(message);
|
|
2024
|
+
onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
|
|
2028
|
+
const piStatus = paymentIntent?.status;
|
|
2029
|
+
if (!piStatus || !SUCCESSFUL_PI_STATUSES.has(piStatus)) {
|
|
2030
|
+
if (typeof window !== "undefined") {
|
|
2031
|
+
try {
|
|
2032
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2033
|
+
} catch {
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
const message = piStatus === "canceled" ? "Payment was canceled." : "Payment was not completed. Please try again.";
|
|
2037
|
+
onErrorChange?.(message);
|
|
2038
|
+
onDecline?.(buildDeclineEvent("card", message, { code: piStatus ?? "missing_payment_intent" }));
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
if (typeof window !== "undefined") {
|
|
2042
|
+
try {
|
|
2043
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2044
|
+
} catch {
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
const confirmedPmId = typeof paymentIntent?.payment_method === "string" ? paymentIntent.payment_method : paymentIntent?.payment_method?.id;
|
|
2048
|
+
onTokenizedBody({
|
|
2049
|
+
id: confirmedPmId ?? paymentMethod.id,
|
|
2050
|
+
type: "card",
|
|
2051
|
+
threeDSecureActionResultTokenId: paymentIntent?.id,
|
|
2052
|
+
gateway: "stripe",
|
|
2053
|
+
paymentMethodType: paymentMethod.type ?? method
|
|
2054
|
+
}, {
|
|
2055
|
+
accountPatch: beforeClick.accountPatch,
|
|
2056
|
+
sessionId: effectiveSessionId
|
|
2057
|
+
});
|
|
2058
|
+
} catch (err) {
|
|
2059
|
+
onErrorChange?.(err instanceof Error ? err.message : "Payment failed. Please try again.");
|
|
2060
|
+
} finally {
|
|
2061
|
+
submittingRef.current = false;
|
|
2062
|
+
setSubmitting(false);
|
|
2063
|
+
}
|
|
2064
|
+
}, [
|
|
2065
|
+
stripe,
|
|
2066
|
+
elements,
|
|
2067
|
+
isProcessing,
|
|
2068
|
+
isMethodComplete,
|
|
2069
|
+
runBeforeButtonClick,
|
|
2070
|
+
onButtonClick,
|
|
2071
|
+
sessionId,
|
|
2072
|
+
email,
|
|
2073
|
+
baseUrl,
|
|
2074
|
+
method,
|
|
2075
|
+
onTokenizedBody,
|
|
2076
|
+
onErrorChange,
|
|
2077
|
+
onDecline
|
|
2078
|
+
]);
|
|
2079
|
+
void onCancel;
|
|
2080
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { "data-testid": `flopay-stripe-method-form-${method}`, style: { display: "flex", flexDirection: "column" }, children: [
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2082
|
+
import_react_stripe_js.PaymentElement,
|
|
2083
|
+
{
|
|
2084
|
+
onReady: () => setLoadState("ready"),
|
|
2085
|
+
onLoadError: () => setLoadState("load_error"),
|
|
2086
|
+
onChange: (event) => {
|
|
2087
|
+
const evRecord = event;
|
|
2088
|
+
setIsMethodComplete(!!evRecord.complete);
|
|
2089
|
+
},
|
|
2090
|
+
options: {
|
|
2091
|
+
layout: { type: "accordion", defaultCollapsed: false, radios: "never" },
|
|
2092
|
+
defaultValues: {
|
|
2093
|
+
billingDetails: {
|
|
2094
|
+
name: billingName && billingName.trim().length >= 3 ? billingName.trim() : "FloPay Customer",
|
|
2095
|
+
...email ? { email } : {}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
),
|
|
2101
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2102
|
+
"button",
|
|
2103
|
+
{
|
|
2104
|
+
type: "button",
|
|
2105
|
+
"data-testid": `flopay-stripe-method-pay-${method}`,
|
|
2106
|
+
onClick: () => {
|
|
2107
|
+
void handlePay();
|
|
2108
|
+
},
|
|
2109
|
+
disabled: !isMethodComplete || submitting || isProcessing || loadState !== "ready",
|
|
2110
|
+
style: {
|
|
2111
|
+
width: "100%",
|
|
2112
|
+
marginTop: "0.75rem",
|
|
2113
|
+
padding: "0.875rem",
|
|
2114
|
+
backgroundColor: submitButtonColor,
|
|
2115
|
+
color: "white",
|
|
2116
|
+
border: "none",
|
|
2117
|
+
borderRadius: submitButtonBorderRadius,
|
|
2118
|
+
fontSize: "1rem",
|
|
2119
|
+
fontWeight: 600,
|
|
2120
|
+
cursor: !isMethodComplete || submitting || isProcessing || loadState !== "ready" ? "not-allowed" : "pointer",
|
|
2121
|
+
opacity: !isMethodComplete || submitting || isProcessing || loadState !== "ready" ? 0.5 : 1,
|
|
2122
|
+
transition: "opacity 120ms ease-out",
|
|
2123
|
+
...submitButtonStyle ?? {}
|
|
2124
|
+
},
|
|
2125
|
+
children: submitting ? "Processing\u2026" : `Pay with ${(0, import_shared5.getStripeMethodDisplayName)(method)}`
|
|
2126
|
+
}
|
|
2127
|
+
)
|
|
2128
|
+
] });
|
|
2129
|
+
}
|
|
2130
|
+
function StripePaymentElementInner({
|
|
2131
|
+
sessionId,
|
|
2132
|
+
email,
|
|
2133
|
+
billingName,
|
|
2134
|
+
billingApiUrl,
|
|
2135
|
+
paymentElementMethods,
|
|
2136
|
+
stripeInstance,
|
|
2137
|
+
paymentElementBaseOptions,
|
|
2138
|
+
onTokenizedBody,
|
|
2139
|
+
onErrorChange,
|
|
2140
|
+
onButtonClick,
|
|
2141
|
+
onDecline,
|
|
2142
|
+
runBeforeButtonClick,
|
|
2143
|
+
onLoadStateChange,
|
|
2144
|
+
isProcessing = false,
|
|
2145
|
+
submitButtonColor = "#4A49FF",
|
|
2146
|
+
submitButtonBorderRadius = 8,
|
|
2147
|
+
submitButtonStyle,
|
|
2148
|
+
buttonAppearance,
|
|
2149
|
+
themeId,
|
|
2150
|
+
onExpandApm,
|
|
2151
|
+
expandedApmMethod
|
|
2152
|
+
}) {
|
|
2153
|
+
const baseUrl = billingApiUrl.replace(/\/+$/, "");
|
|
2154
|
+
const [expandedMethod, setExpandedMethod] = (0, import_react8.useState)(null);
|
|
2155
|
+
const [submittingMethod, setSubmittingMethod] = (0, import_react8.useState)(null);
|
|
2156
|
+
const submittingRef = (0, import_react8.useRef)(false);
|
|
2157
|
+
(0, import_react8.useEffect)(() => {
|
|
2158
|
+
if (paymentElementMethods.length > 0 && stripeInstance) {
|
|
2159
|
+
onLoadStateChange?.("ready");
|
|
2160
|
+
} else if (!stripeInstance) {
|
|
2161
|
+
onLoadStateChange?.("loading");
|
|
2162
|
+
}
|
|
2163
|
+
}, [paymentElementMethods.length, stripeInstance, onLoadStateChange]);
|
|
2164
|
+
const handleAutoConfirm = (0, import_react8.useCallback)(async (method) => {
|
|
2165
|
+
if (!stripeInstance) return;
|
|
2166
|
+
if (submittingRef.current || isProcessing) return;
|
|
2167
|
+
submittingRef.current = true;
|
|
2168
|
+
setSubmittingMethod(method);
|
|
2169
|
+
const beforeClick = runBeforeButtonClick ? await runBeforeButtonClick("card") : { proceed: true };
|
|
2170
|
+
if (!beforeClick.proceed) {
|
|
2171
|
+
submittingRef.current = false;
|
|
2172
|
+
setSubmittingMethod(null);
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
onButtonClick?.("card");
|
|
2176
|
+
const effectiveSessionId = beforeClick.sessionId ?? sessionId;
|
|
2177
|
+
const effectiveEmail = beforeClick.accountPatch?.email ?? email;
|
|
2178
|
+
try {
|
|
2179
|
+
onErrorChange?.(null);
|
|
2180
|
+
if (!effectiveSessionId || !effectiveEmail) {
|
|
2181
|
+
throw new Error("Missing sessionId or email for payment.");
|
|
2182
|
+
}
|
|
2183
|
+
const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {
|
|
2184
|
+
method: "POST",
|
|
2185
|
+
headers: { "Content-Type": "application/json" },
|
|
2186
|
+
body: JSON.stringify({
|
|
2187
|
+
sessionId: effectiveSessionId,
|
|
2188
|
+
email: effectiveEmail,
|
|
2189
|
+
// Wire type, not pm_xxx — backend scopes `payment_method_types`
|
|
2190
|
+
// to this single method so unrelated account-wide methods
|
|
2191
|
+
// (apple_pay without domain verification, …) don't poison PI
|
|
2192
|
+
// creation.
|
|
2193
|
+
paymentMethodType: method,
|
|
2194
|
+
isPaypal: false
|
|
2195
|
+
})
|
|
2196
|
+
});
|
|
2197
|
+
if (!intentResponse.ok) {
|
|
2198
|
+
const intentError = await buildFloPayApiErrorFromResponse(intentResponse, "Failed to create payment intent");
|
|
2199
|
+
onErrorChange?.(intentError.message);
|
|
2200
|
+
onDecline?.(buildDeclineEvent("card", intentError));
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
const intentJson = await intentResponse.json();
|
|
2204
|
+
const intentClientSecret = intentJson.data?.id;
|
|
2205
|
+
if (!intentClientSecret) throw new Error("No client_secret in payment intent response");
|
|
2206
|
+
if (typeof window !== "undefined") {
|
|
2207
|
+
try {
|
|
2208
|
+
localStorage.setItem(STRIPE_RESUME_KEY, JSON.stringify({
|
|
2209
|
+
sessionId: effectiveSessionId,
|
|
2210
|
+
paymentMethodType: method,
|
|
2211
|
+
gateway: "stripe"
|
|
2212
|
+
}));
|
|
2213
|
+
} catch {
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
const { error: confirmError, paymentIntent } = await stripeInstance.confirmPayment({
|
|
2217
|
+
clientSecret: intentClientSecret,
|
|
2218
|
+
confirmParams: {
|
|
2219
|
+
return_url: window.location.href,
|
|
2220
|
+
// Inline the method data — no Elements / PaymentElement
|
|
2221
|
+
// required. Stripe creates the PaymentMethod during confirm
|
|
2222
|
+
// and proceeds to its popup/redirect UI.
|
|
2223
|
+
payment_method_data: {
|
|
2224
|
+
type: method,
|
|
2225
|
+
billing_details: {
|
|
2226
|
+
name: billingName && billingName.trim().length >= 3 ? billingName.trim() : "FloPay Customer",
|
|
2227
|
+
...effectiveEmail ? { email: effectiveEmail } : {}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
},
|
|
2231
|
+
redirect: "if_required"
|
|
2232
|
+
});
|
|
2233
|
+
if (confirmError) {
|
|
2234
|
+
if (typeof window !== "undefined") {
|
|
2235
|
+
try {
|
|
2236
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2237
|
+
} catch {
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
const message = confirmError.message ?? "Payment failed.";
|
|
2241
|
+
onErrorChange?.(message);
|
|
2242
|
+
onDecline?.(buildDeclineEvent("card", message, { code: confirmError.code }));
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
const SUCCESSFUL_PI_STATUSES = /* @__PURE__ */ new Set(["succeeded", "requires_capture", "processing"]);
|
|
2246
|
+
const piStatus = paymentIntent?.status;
|
|
2247
|
+
if (!piStatus || !SUCCESSFUL_PI_STATUSES.has(piStatus)) {
|
|
2248
|
+
if (typeof window !== "undefined") {
|
|
2249
|
+
try {
|
|
2250
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2251
|
+
} catch {
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
const message = piStatus === "canceled" ? "Payment was canceled." : "Payment was not completed. Please try again.";
|
|
2255
|
+
onErrorChange?.(message);
|
|
2256
|
+
onDecline?.(buildDeclineEvent("card", message, { code: piStatus ?? "missing_payment_intent" }));
|
|
2257
|
+
return;
|
|
2258
|
+
}
|
|
2259
|
+
if (typeof window !== "undefined") {
|
|
2260
|
+
try {
|
|
2261
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
2262
|
+
} catch {
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
const confirmedPmId = typeof paymentIntent?.payment_method === "string" ? paymentIntent.payment_method : paymentIntent?.payment_method?.id;
|
|
2266
|
+
onTokenizedBody({
|
|
2267
|
+
id: confirmedPmId ?? paymentIntent?.id,
|
|
2268
|
+
type: "card",
|
|
2269
|
+
threeDSecureActionResultTokenId: paymentIntent?.id,
|
|
2270
|
+
gateway: "stripe",
|
|
2271
|
+
paymentMethodType: method
|
|
2272
|
+
}, {
|
|
2273
|
+
accountPatch: beforeClick.accountPatch,
|
|
2274
|
+
sessionId: effectiveSessionId
|
|
2275
|
+
});
|
|
2276
|
+
} catch (err) {
|
|
2277
|
+
onErrorChange?.(err instanceof Error ? err.message : "Payment failed. Please try again.");
|
|
2278
|
+
} finally {
|
|
2279
|
+
submittingRef.current = false;
|
|
2280
|
+
setSubmittingMethod(null);
|
|
2281
|
+
}
|
|
2282
|
+
}, [
|
|
2283
|
+
stripeInstance,
|
|
2284
|
+
isProcessing,
|
|
2285
|
+
runBeforeButtonClick,
|
|
2286
|
+
onButtonClick,
|
|
2287
|
+
sessionId,
|
|
2288
|
+
email,
|
|
2289
|
+
baseUrl,
|
|
2290
|
+
billingName,
|
|
2291
|
+
onTokenizedBody,
|
|
2292
|
+
onErrorChange,
|
|
2293
|
+
onDecline
|
|
2294
|
+
]);
|
|
2295
|
+
const [localExpandedMethod, setLocalExpandedMethod] = (0, import_react8.useState)(null);
|
|
2296
|
+
const activeExpandedMethod = onExpandApm ? expandedApmMethod ?? null : localExpandedMethod;
|
|
2297
|
+
const handleMethodClick = (0, import_react8.useCallback)((method) => {
|
|
2298
|
+
if (submittingRef.current || isProcessing) return;
|
|
2299
|
+
if (activeExpandedMethod && activeExpandedMethod !== method) return;
|
|
2300
|
+
if ((0, import_shared5.needsStripeMethodExplicitConfirm)(method)) {
|
|
2301
|
+
if (onExpandApm) {
|
|
2302
|
+
onExpandApm(method);
|
|
2303
|
+
} else {
|
|
2304
|
+
setLocalExpandedMethod(method);
|
|
2305
|
+
}
|
|
2306
|
+
} else {
|
|
2307
|
+
void handleAutoConfirm(method);
|
|
2308
|
+
}
|
|
2309
|
+
}, [activeExpandedMethod, isProcessing, handleAutoConfirm, onExpandApm]);
|
|
2310
|
+
const localInlineElementsOptions = (0, import_react8.useMemo)(() => {
|
|
2311
|
+
if (!localExpandedMethod) return null;
|
|
2312
|
+
return {
|
|
2313
|
+
...paymentElementBaseOptions,
|
|
2314
|
+
paymentMethodTypes: [localExpandedMethod]
|
|
2315
|
+
};
|
|
2316
|
+
}, [localExpandedMethod, paymentElementBaseOptions]);
|
|
2317
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2318
|
+
"div",
|
|
2319
|
+
{
|
|
2320
|
+
"data-testid": "flopay-stripe-payment-element-region",
|
|
2321
|
+
style: { display: "flex", flexDirection: "column", gap: "0.5rem" },
|
|
2322
|
+
children: paymentElementMethods.map((method) => {
|
|
2323
|
+
const isExpanded = expandedApmMethod === method;
|
|
2324
|
+
const isSubmittingThis = submittingMethod === method;
|
|
2325
|
+
const otherInProgress = submittingMethod !== null && submittingMethod !== method || expandedApmMethod !== null && expandedApmMethod !== void 0 && expandedApmMethod !== method;
|
|
2326
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react8.default.Fragment, { children: [
|
|
2327
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2328
|
+
StripeMethodButton,
|
|
2329
|
+
{
|
|
2330
|
+
method,
|
|
2331
|
+
themeId,
|
|
2332
|
+
submitting: isSubmittingThis,
|
|
2333
|
+
disabled: otherInProgress,
|
|
2334
|
+
highlighted: isExpanded,
|
|
2335
|
+
hasNextStep: (0, import_shared5.needsStripeMethodExplicitConfirm)(method),
|
|
2336
|
+
onClick: () => handleMethodClick(method),
|
|
2337
|
+
backgroundColor: buttonAppearance?.backgroundColor,
|
|
2338
|
+
borderColor: buttonAppearance?.borderColor,
|
|
2339
|
+
textColor: buttonAppearance?.textColor,
|
|
2340
|
+
borderRadius: buttonAppearance?.borderRadius,
|
|
2341
|
+
fontFamily: buttonAppearance?.fontFamily
|
|
2342
|
+
}
|
|
2343
|
+
),
|
|
2344
|
+
!onExpandApm && localExpandedMethod === method && localInlineElementsOptions && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2345
|
+
import_react_stripe_js.Elements,
|
|
2346
|
+
{
|
|
2347
|
+
stripe: stripeInstance,
|
|
2348
|
+
options: localInlineElementsOptions,
|
|
2349
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2350
|
+
StripeMethodInlineForm,
|
|
2351
|
+
{
|
|
2352
|
+
method,
|
|
2353
|
+
sessionId,
|
|
2354
|
+
email,
|
|
2355
|
+
billingName,
|
|
2356
|
+
billingApiUrl,
|
|
2357
|
+
onTokenizedBody,
|
|
2358
|
+
onErrorChange,
|
|
2359
|
+
onDecline,
|
|
2360
|
+
onCancel: () => setLocalExpandedMethod(null),
|
|
2361
|
+
runBeforeButtonClick,
|
|
2362
|
+
onButtonClick,
|
|
2363
|
+
isProcessing,
|
|
2364
|
+
submitButtonColor,
|
|
2365
|
+
submitButtonBorderRadius,
|
|
2366
|
+
submitButtonStyle
|
|
2367
|
+
}
|
|
2368
|
+
)
|
|
2369
|
+
},
|
|
2370
|
+
method
|
|
2371
|
+
)
|
|
2372
|
+
] }, method);
|
|
2373
|
+
})
|
|
2374
|
+
}
|
|
2375
|
+
);
|
|
1736
2376
|
}
|
|
1737
2377
|
function SplitCardFormInner({
|
|
1738
2378
|
sessionId,
|
|
@@ -1756,6 +2396,8 @@ function SplitCardFormInner({
|
|
|
1756
2396
|
onFirstNameChange,
|
|
1757
2397
|
onLastNameChange,
|
|
1758
2398
|
showPayPal = true,
|
|
2399
|
+
showStripe = true,
|
|
2400
|
+
enabledPaymentMethods,
|
|
1759
2401
|
showApplePay = true,
|
|
1760
2402
|
showGooglePay = true,
|
|
1761
2403
|
layout = "default",
|
|
@@ -1815,6 +2457,7 @@ function SplitCardFormInner({
|
|
|
1815
2457
|
const avsConfig = (0, import_react8.useMemo)(() => (0, import_shared5.resolveAVSConfig)(enableAVSProp), [enableAVSProp]);
|
|
1816
2458
|
const enableAVS = avsConfig !== null;
|
|
1817
2459
|
const [viewState, setViewState] = (0, import_react8.useState)(initialCardOpen ? "card" : "buttons");
|
|
2460
|
+
const [expandedApmMethod, setExpandedApmMethod] = (0, import_react8.useState)(null);
|
|
1818
2461
|
const showCardForm = viewState === "expanding" || viewState === "card";
|
|
1819
2462
|
const TRANSITION_MS = 280;
|
|
1820
2463
|
const expandToCard = (0, import_react8.useCallback)(() => {
|
|
@@ -1825,6 +2468,18 @@ function SplitCardFormInner({
|
|
|
1825
2468
|
setViewState("collapsing");
|
|
1826
2469
|
setTimeout(() => setViewState("buttons"), TRANSITION_MS);
|
|
1827
2470
|
}, []);
|
|
2471
|
+
const expandToApm = (0, import_react8.useCallback)((method) => {
|
|
2472
|
+
setExpandedApmMethod(method);
|
|
2473
|
+
setViewState("apm-expanding");
|
|
2474
|
+
setTimeout(() => setViewState("apm-form"), TRANSITION_MS);
|
|
2475
|
+
}, []);
|
|
2476
|
+
const collapseFromApm = (0, import_react8.useCallback)(() => {
|
|
2477
|
+
setViewState("apm-collapsing");
|
|
2478
|
+
setTimeout(() => {
|
|
2479
|
+
setViewState("buttons");
|
|
2480
|
+
setExpandedApmMethod(null);
|
|
2481
|
+
}, TRANSITION_MS);
|
|
2482
|
+
}, []);
|
|
1828
2483
|
(0, import_react8.useEffect)(() => {
|
|
1829
2484
|
if (layout === "buttons" && initialCardOpen) {
|
|
1830
2485
|
setViewState("card");
|
|
@@ -1878,20 +2533,43 @@ function SplitCardFormInner({
|
|
|
1878
2533
|
return paypalFlopay.getRawProvider();
|
|
1879
2534
|
}, [paypalFlopay]);
|
|
1880
2535
|
const amountInCents = totalAmount || 100;
|
|
2536
|
+
const stripeAppearanceProp = appearance ? { appearance } : null;
|
|
2537
|
+
const paymentElementAppearance = (0, import_react8.useMemo)(() => {
|
|
2538
|
+
const base = appearance ?? {};
|
|
2539
|
+
const baseRules = base.rules ?? {};
|
|
2540
|
+
return {
|
|
2541
|
+
...base,
|
|
2542
|
+
rules: {
|
|
2543
|
+
...baseRules,
|
|
2544
|
+
".AccordionItem": {
|
|
2545
|
+
padding: "10px 14px",
|
|
2546
|
+
boxShadow: "none",
|
|
2547
|
+
...baseRules[".AccordionItem"] ?? {}
|
|
2548
|
+
},
|
|
2549
|
+
".AccordionItemContents": {
|
|
2550
|
+
padding: "6px 14px 12px",
|
|
2551
|
+
boxShadow: "none",
|
|
2552
|
+
...baseRules[".AccordionItemContents"] ?? {}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
}, [appearance]);
|
|
1881
2557
|
const walletOptions = (0, import_react8.useMemo)(() => ({
|
|
1882
2558
|
mode: "payment",
|
|
1883
2559
|
amount: amountInCents,
|
|
1884
2560
|
currency: currency.toLowerCase(),
|
|
1885
2561
|
paymentMethodCreation: "manual",
|
|
1886
|
-
captureMethod: "manual"
|
|
1887
|
-
|
|
2562
|
+
captureMethod: "manual",
|
|
2563
|
+
...stripeAppearanceProp
|
|
2564
|
+
}), [amountInCents, currency, stripeAppearanceProp]);
|
|
1888
2565
|
const paypalOptions = (0, import_react8.useMemo)(() => ({
|
|
1889
2566
|
mode: "payment",
|
|
1890
2567
|
amount: amountInCents,
|
|
1891
2568
|
currency: currency.toLowerCase(),
|
|
1892
2569
|
captureMethod: "manual",
|
|
1893
|
-
setupFutureUsage: "off_session"
|
|
1894
|
-
|
|
2570
|
+
setupFutureUsage: "off_session",
|
|
2571
|
+
...stripeAppearanceProp
|
|
2572
|
+
}), [amountInCents, currency, stripeAppearanceProp]);
|
|
1895
2573
|
const updateError = (0, import_react8.useCallback)(
|
|
1896
2574
|
(err) => {
|
|
1897
2575
|
setError(err);
|
|
@@ -1905,22 +2583,98 @@ function SplitCardFormInner({
|
|
|
1905
2583
|
},
|
|
1906
2584
|
[onDecline]
|
|
1907
2585
|
);
|
|
1908
|
-
const
|
|
2586
|
+
const directPaypalConfigured = !!directPaypal?.clientId;
|
|
2587
|
+
const hasEnabledMethodsPayload = Array.isArray(enabledPaymentMethods);
|
|
2588
|
+
const hasEnabledMethods = hasEnabledMethodsPayload && enabledPaymentMethods.length > 0;
|
|
2589
|
+
const paypalEnabled = hasEnabledMethodsPayload && enabledPaymentMethods.includes("paypal");
|
|
2590
|
+
const { expressMethods, paymentElementMethods } = (0, import_react8.useMemo)(
|
|
2591
|
+
() => (0, import_shared5.partitionStripeMethods)(enabledPaymentMethods, {
|
|
2592
|
+
excludePaypal: directPaypalConfigured
|
|
2593
|
+
}),
|
|
2594
|
+
[enabledPaymentMethods, directPaypalConfigured]
|
|
2595
|
+
);
|
|
2596
|
+
const paypalRenderedSeparately = directPaypalConfigured || !!paypalFlopay;
|
|
2597
|
+
const expressMethodsForWalletRow = (0, import_react8.useMemo)(
|
|
2598
|
+
() => paypalRenderedSeparately ? expressMethods.filter((m) => m !== "paypal") : expressMethods,
|
|
2599
|
+
[expressMethods, paypalRenderedSeparately]
|
|
2600
|
+
);
|
|
2601
|
+
const legacyExpressMethods = (0, import_react8.useMemo)(() => {
|
|
2602
|
+
const out = [];
|
|
2603
|
+
if (showApplePay) out.push("apple_pay");
|
|
2604
|
+
if (showGooglePay) out.push("google_pay");
|
|
2605
|
+
return out;
|
|
2606
|
+
}, [showApplePay, showGooglePay]);
|
|
2607
|
+
const walletExpressMethods = hasEnabledMethodsPayload ? expressMethodsForWalletRow : legacyExpressMethods;
|
|
2608
|
+
const showWallets = showStripe && walletExpressMethods.length > 0;
|
|
2609
|
+
const paymentElementMethodsForCurrency = (0, import_react8.useMemo)(
|
|
2610
|
+
() => (0, import_shared5.filterStripeMethodsByAmount)(
|
|
2611
|
+
(0, import_shared5.filterStripeMethodsByCountry)(
|
|
2612
|
+
(0, import_shared5.filterStripeMethodsByCurrency)(paymentElementMethods, currency),
|
|
2613
|
+
countryProp
|
|
2614
|
+
),
|
|
2615
|
+
currency,
|
|
2616
|
+
amountInCents
|
|
2617
|
+
),
|
|
2618
|
+
[paymentElementMethods, currency, countryProp, amountInCents]
|
|
2619
|
+
);
|
|
2620
|
+
const paymentElementBaseOptions = (0, import_react8.useMemo)(() => ({
|
|
2621
|
+
mode: "payment",
|
|
2622
|
+
amount: amountInCents,
|
|
2623
|
+
currency: currency.toLowerCase(),
|
|
2624
|
+
paymentMethodCreation: "manual",
|
|
2625
|
+
appearance: paymentElementAppearance
|
|
2626
|
+
}), [amountInCents, currency, paymentElementAppearance]);
|
|
1909
2627
|
const [paypalLoadState, setPaypalLoadState] = (0, import_react8.useState)("loading");
|
|
1910
2628
|
const [walletLoadState, setWalletLoadState] = (0, import_react8.useState)("loading");
|
|
2629
|
+
const [paymentElementLoadState, setPaymentElementLoadState] = (0, import_react8.useState)("loading");
|
|
1911
2630
|
const [directPaypalReady, setDirectPaypalReady] = (0, import_react8.useState)(false);
|
|
1912
2631
|
const [inAppBrowserDetected, setInAppBrowserDetected] = (0, import_react8.useState)();
|
|
1913
2632
|
(0, import_react8.useEffect)(() => {
|
|
1914
2633
|
setInAppBrowserDetected(isInAppBrowser());
|
|
1915
2634
|
}, []);
|
|
1916
|
-
const
|
|
1917
|
-
const
|
|
1918
|
-
const shouldShowWallets = showWallets;
|
|
2635
|
+
const shouldShowPayPal = showPayPal && (directPaypalConfigured || (hasEnabledMethodsPayload ? paypalEnabled : inAppBrowserDetected === false));
|
|
2636
|
+
const shouldShowWallets = showStripe && showWallets;
|
|
1919
2637
|
const shouldRenderDirectPayPal = shouldShowPayPal && directPaypalConfigured;
|
|
1920
2638
|
const shouldRenderStripePayPal = shouldShowPayPal && !directPaypalConfigured && !!paypalStripeInstance;
|
|
1921
2639
|
const shouldRenderWallets = shouldShowWallets && !!stripeInstance;
|
|
2640
|
+
const shouldRenderPaymentElement = showStripe && hasEnabledMethods && paymentElementMethodsForCurrency.length > 0 && !!stripeInstance;
|
|
1922
2641
|
const shouldDisplayPayPalRow = shouldRenderDirectPayPal ? directPaypalReady : shouldRenderStripePayPal && isExpressCheckoutRowVisible(paypalLoadState);
|
|
1923
2642
|
const shouldDisplayWalletRow = shouldRenderWallets && isExpressCheckoutRowVisible(walletLoadState);
|
|
2643
|
+
const shouldDisplayPaymentElementRow = shouldRenderPaymentElement && paymentElementLoadState !== "load_error";
|
|
2644
|
+
const validationFiredRef = (0, import_react8.useRef)(false);
|
|
2645
|
+
(0, import_react8.useEffect)(() => {
|
|
2646
|
+
if (validationFiredRef.current) return;
|
|
2647
|
+
if (!showStripe && !showPayPal) {
|
|
2648
|
+
validationFiredRef.current = true;
|
|
2649
|
+
const err = new import_shared6.FloPayError(
|
|
2650
|
+
"FloPay: both `showStripe` and `showPayPal` are false \u2014 nothing to render.",
|
|
2651
|
+
"validation_error"
|
|
2652
|
+
);
|
|
2653
|
+
onError?.(err);
|
|
2654
|
+
updateError(err.message);
|
|
2655
|
+
} else if (!showStripe && showPayPal && !directPaypalConfigured && !paypalStripeInstance) {
|
|
2656
|
+
validationFiredRef.current = true;
|
|
2657
|
+
const err = new import_shared6.FloPayError(
|
|
2658
|
+
"FloPay: `showStripe` is false and no PayPal gateway is configured for this session \u2014 nothing to render.",
|
|
2659
|
+
"validation_error"
|
|
2660
|
+
);
|
|
2661
|
+
onError?.(err);
|
|
2662
|
+
updateError(err.message);
|
|
2663
|
+
}
|
|
2664
|
+
}, [showStripe, showPayPal, directPaypalConfigured, paypalStripeInstance, onError, updateError]);
|
|
2665
|
+
const deprecationLoggedRef = (0, import_react8.useRef)(false);
|
|
2666
|
+
(0, import_react8.useEffect)(() => {
|
|
2667
|
+
if (deprecationLoggedRef.current) return;
|
|
2668
|
+
if (!hasEnabledMethods) return;
|
|
2669
|
+
const stale = [];
|
|
2670
|
+
if (showApplePay !== true) stale.push("showApplePay");
|
|
2671
|
+
if (showGooglePay !== true) stale.push("showGooglePay");
|
|
2672
|
+
if (stale.length === 0) return;
|
|
2673
|
+
deprecationLoggedRef.current = true;
|
|
2674
|
+
console.warn(
|
|
2675
|
+
`[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.`
|
|
2676
|
+
);
|
|
2677
|
+
}, [hasEnabledMethods, showApplePay, showGooglePay]);
|
|
1924
2678
|
const handleNameChange = (0, import_react8.useCallback)((value) => {
|
|
1925
2679
|
setFullName(value);
|
|
1926
2680
|
onFullNameChange?.(value);
|
|
@@ -2221,24 +2975,75 @@ function SplitCardFormInner({
|
|
|
2221
2975
|
}
|
|
2222
2976
|
}
|
|
2223
2977
|
}), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
|
|
2978
|
+
const stripeResumeAttemptedRef = (0, import_react8.useRef)(false);
|
|
2224
2979
|
(0, import_react8.useEffect)(() => {
|
|
2225
|
-
if (typeof window === "undefined") return;
|
|
2226
|
-
const
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2980
|
+
if (typeof window === "undefined" || stripeResumeAttemptedRef.current) return;
|
|
2981
|
+
const params = new URLSearchParams(window.location.search);
|
|
2982
|
+
const paymentIntentId = params.get("payment_intent");
|
|
2983
|
+
const clientSecret = params.get("payment_intent_client_secret");
|
|
2984
|
+
const redirectStatus = params.get("redirect_status");
|
|
2985
|
+
if (!paymentIntentId || !clientSecret) return;
|
|
2986
|
+
const readPayload = (key) => {
|
|
2987
|
+
const raw = localStorage.getItem(key);
|
|
2988
|
+
if (!raw) return null;
|
|
2989
|
+
try {
|
|
2990
|
+
return JSON.parse(raw);
|
|
2991
|
+
} catch {
|
|
2992
|
+
localStorage.removeItem(key);
|
|
2993
|
+
return null;
|
|
2237
2994
|
}
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2995
|
+
};
|
|
2996
|
+
const payload = readPayload(STRIPE_RESUME_KEY) ?? readPayload(LEGACY_WALLET_RESUME_KEY);
|
|
2997
|
+
if (payload?.sessionId && payload.sessionId !== sessionId) return;
|
|
2998
|
+
stripeResumeAttemptedRef.current = true;
|
|
2999
|
+
localStorage.removeItem(STRIPE_RESUME_KEY);
|
|
3000
|
+
localStorage.removeItem(LEGACY_WALLET_RESUME_KEY);
|
|
3001
|
+
const cleanedUrl = new URL(window.location.href);
|
|
3002
|
+
cleanedUrl.searchParams.delete("payment_intent");
|
|
3003
|
+
cleanedUrl.searchParams.delete("payment_intent_client_secret");
|
|
3004
|
+
cleanedUrl.searchParams.delete("redirect_status");
|
|
3005
|
+
window.history.replaceState({}, "", cleanedUrl.toString());
|
|
3006
|
+
const paymentMethodType = payload?.paymentMethodType ?? payload?.tokenType ?? "card";
|
|
3007
|
+
const declineMethod = "card";
|
|
3008
|
+
(async () => {
|
|
3009
|
+
const rawProvider = flopay?.getRawProvider();
|
|
3010
|
+
if (!rawProvider) {
|
|
3011
|
+
const message = "Payment is not available \u2014 please refresh and try again.";
|
|
3012
|
+
updateError(message);
|
|
3013
|
+
emitDecline(declineMethod, message);
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
if (redirectStatus === "failed") {
|
|
3017
|
+
const message = "Payment was declined. Please try again.";
|
|
3018
|
+
updateError(message);
|
|
3019
|
+
emitDecline(declineMethod, message);
|
|
3020
|
+
return;
|
|
3021
|
+
}
|
|
3022
|
+
const { paymentIntent, error: error2 } = await rawProvider.retrievePaymentIntent(clientSecret);
|
|
3023
|
+
if (error2) {
|
|
3024
|
+
const message = error2.message ?? "Failed to retrieve payment status.";
|
|
3025
|
+
updateError(message);
|
|
3026
|
+
emitDecline(declineMethod, message, { code: error2.code });
|
|
3027
|
+
return;
|
|
3028
|
+
}
|
|
3029
|
+
const piStatus = paymentIntent?.status;
|
|
3030
|
+
const successful = piStatus === "succeeded" || piStatus === "requires_capture" || piStatus === "processing";
|
|
3031
|
+
if (!paymentIntent || !successful) {
|
|
3032
|
+
const message = piStatus === "canceled" ? "Payment was canceled." : "Payment was not completed. Please try again.";
|
|
3033
|
+
updateError(message);
|
|
3034
|
+
emitDecline(declineMethod, message, { code: piStatus ?? "missing_payment_intent" });
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
const resolvedPmId = typeof paymentIntent.payment_method === "string" ? paymentIntent.payment_method : paymentIntent.payment_method?.id;
|
|
3038
|
+
dispatchTokenizedBody({
|
|
3039
|
+
id: resolvedPmId ?? payload?.paymentMethodId ?? payload?.tokenId ?? paymentIntent.id,
|
|
3040
|
+
type: "card",
|
|
3041
|
+
threeDSecureActionResultTokenId: paymentIntent.id,
|
|
3042
|
+
gateway: payload?.gateway ?? "stripe",
|
|
3043
|
+
paymentMethodType
|
|
3044
|
+
});
|
|
3045
|
+
})();
|
|
3046
|
+
}, [sessionId, dispatchTokenizedBody, flopay, updateError, emitDecline]);
|
|
2242
3047
|
const handleSubmit = (0, import_react8.useCallback)(
|
|
2243
3048
|
async (e) => {
|
|
2244
3049
|
e.preventDefault();
|
|
@@ -2521,17 +3326,23 @@ function SplitCardFormInner({
|
|
|
2521
3326
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
2522
3327
|
flex: 1,
|
|
2523
3328
|
backgroundColor: cardInputBg,
|
|
2524
|
-
|
|
3329
|
+
// Longhand only — mixing `border` shorthand with per-side
|
|
3330
|
+
// overrides triggers React's "shorthand vs longhand" warning
|
|
3331
|
+
// because render order isn't deterministic.
|
|
2525
3332
|
borderTop: "none",
|
|
2526
3333
|
borderRight: "none",
|
|
3334
|
+
borderBottom: `1px solid ${resolvedBorder}`,
|
|
3335
|
+
borderLeft: `1px solid ${resolvedBorder}`,
|
|
2527
3336
|
borderBottomLeftRadius: "8px",
|
|
2528
3337
|
padding: "10px"
|
|
2529
3338
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardExpiryElement, { options: stripeElementStyle }) }),
|
|
2530
3339
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
2531
3340
|
flex: 1,
|
|
2532
3341
|
backgroundColor: cardInputBg,
|
|
2533
|
-
border: `1px solid ${resolvedBorder}`,
|
|
2534
3342
|
borderTop: "none",
|
|
3343
|
+
borderRight: `1px solid ${resolvedBorder}`,
|
|
3344
|
+
borderBottom: `1px solid ${resolvedBorder}`,
|
|
3345
|
+
borderLeft: `1px solid ${resolvedBorder}`,
|
|
2535
3346
|
borderBottomRightRadius: "8px",
|
|
2536
3347
|
padding: "10px"
|
|
2537
3348
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardCvcElement, { options: stripeElementStyle }) })
|
|
@@ -2623,7 +3434,10 @@ function SplitCardFormInner({
|
|
|
2623
3434
|
(0, import_shared5.isAVSFieldVisible)(avsConfig.city, cc) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
2624
3435
|
flex: 1,
|
|
2625
3436
|
backgroundColor: cardInputBg,
|
|
2626
|
-
|
|
3437
|
+
borderTop: `1px solid ${resolvedBorder}`,
|
|
3438
|
+
borderRight: `1px solid ${resolvedBorder}`,
|
|
3439
|
+
borderBottom: `1px solid ${resolvedBorder}`,
|
|
3440
|
+
borderLeft: `1px solid ${resolvedBorder}`,
|
|
2627
3441
|
padding: "10px",
|
|
2628
3442
|
borderTopLeftRadius: "8px",
|
|
2629
3443
|
borderBottomLeftRadius: "8px",
|
|
@@ -2700,7 +3514,10 @@ function SplitCardFormInner({
|
|
|
2700
3514
|
(0, import_shared5.isAVSFieldVisible)(avsConfig.country, cc) && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
2701
3515
|
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
2702
3516
|
backgroundColor: cardInputBg,
|
|
2703
|
-
|
|
3517
|
+
borderTop: `1px solid ${resolvedBorder}`,
|
|
3518
|
+
borderRight: `1px solid ${resolvedBorder}`,
|
|
3519
|
+
borderBottom: `1px solid ${resolvedBorder}`,
|
|
3520
|
+
borderLeft: `1px solid ${resolvedBorder}`,
|
|
2704
3521
|
padding: "10px",
|
|
2705
3522
|
...avsLayoutProp === "row" && (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, cc) ? { borderRadius: "0", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", borderRight: "none" } : { borderRadius: "8px" },
|
|
2706
3523
|
...isButtons && bStyles.countrySelect ? bStyles.countrySelect : {}
|
|
@@ -2795,221 +3612,465 @@ function SplitCardFormInner({
|
|
|
2795
3612
|
}
|
|
2796
3613
|
)
|
|
2797
3614
|
] });
|
|
3615
|
+
const gateDebugStyle = {
|
|
3616
|
+
margin: 0,
|
|
3617
|
+
padding: "6px 8px",
|
|
3618
|
+
background: "#eef2ff",
|
|
3619
|
+
border: "1px solid #c7d2fe",
|
|
3620
|
+
borderRadius: 6,
|
|
3621
|
+
color: "#111827",
|
|
3622
|
+
font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
3623
|
+
whiteSpace: "pre-wrap",
|
|
3624
|
+
wordBreak: "break-word"
|
|
3625
|
+
};
|
|
3626
|
+
const renderDirectPaypalGateDebug = (testId) => {
|
|
3627
|
+
if (!debug) return null;
|
|
3628
|
+
if (!showPayPal || !directPaypalConfigured) {
|
|
3629
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/DirectPayPal-debug - not enabled" });
|
|
3630
|
+
}
|
|
3631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { "data-testid": testId, style: gateDebugStyle, children: [
|
|
3632
|
+
"FloPay/DirectPayPal-debug (parent gate)",
|
|
3633
|
+
` showPayPal=${showPayPal}`,
|
|
3634
|
+
` directPaypalConfigured=${directPaypalConfigured}`,
|
|
3635
|
+
` inAppBrowserDetected=${String(inAppBrowserDetected)}`,
|
|
3636
|
+
` shouldRenderDirectPayPal=${shouldRenderDirectPayPal}`,
|
|
3637
|
+
` shouldRenderStripePayPal=${shouldRenderStripePayPal}`,
|
|
3638
|
+
` hasPaypalStripeInstance=${!!paypalStripeInstance}`
|
|
3639
|
+
].join("\n") });
|
|
3640
|
+
};
|
|
3641
|
+
const renderStripeGateDebug = (testId) => {
|
|
3642
|
+
if (!debug) return null;
|
|
3643
|
+
if (!showStripe || !stripeInstance) {
|
|
3644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { "data-testid": testId, style: gateDebugStyle, children: "FloPay/Stripe-debug - not enabled" });
|
|
3645
|
+
}
|
|
3646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { "data-testid": testId, style: gateDebugStyle, children: [
|
|
3647
|
+
"FloPay/Stripe-debug (parent gate)",
|
|
3648
|
+
` showStripe=${showStripe}`,
|
|
3649
|
+
` currency=${currency}`,
|
|
3650
|
+
` amountInCents=${amountInCents}`,
|
|
3651
|
+
` enabledPaymentMethods: ${JSON.stringify(enabledPaymentMethods ?? [])}`,
|
|
3652
|
+
` enabledPaymentMethodsProvided=${hasEnabledMethodsPayload}`,
|
|
3653
|
+
` hasEnabledMethods=${hasEnabledMethods}`,
|
|
3654
|
+
` expressMethods: ${JSON.stringify(expressMethods)}`,
|
|
3655
|
+
` walletExpressMethods: ${JSON.stringify(walletExpressMethods)}`,
|
|
3656
|
+
` paymentElementMethods: ${JSON.stringify(paymentElementMethods)}`,
|
|
3657
|
+
` paymentElementMethodsForCurrency: ${JSON.stringify(paymentElementMethodsForCurrency)}`,
|
|
3658
|
+
` hasStripeInstance=${!!stripeInstance}`,
|
|
3659
|
+
` shouldRenderWallets=${shouldRenderWallets}`,
|
|
3660
|
+
` shouldRenderPaymentElement=${shouldRenderPaymentElement}`
|
|
3661
|
+
].join("\n") });
|
|
3662
|
+
};
|
|
3663
|
+
const isApmView = viewState === "apm-expanding" || viewState === "apm-form" || viewState === "apm-collapsing";
|
|
3664
|
+
const apmAnim = viewState === "apm-expanding" ? `flopay-card-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : viewState === "apm-collapsing" ? `flopay-card-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : void 0;
|
|
3665
|
+
const apmInlineOptions = expandedApmMethod ? {
|
|
3666
|
+
...paymentElementBaseOptions,
|
|
3667
|
+
paymentMethodTypes: [expandedApmMethod]
|
|
3668
|
+
} : null;
|
|
2798
3669
|
if (layout === "buttons") {
|
|
2799
|
-
const isButtonsView = viewState === "buttons" || viewState === "expanding";
|
|
3670
|
+
const isButtonsView = viewState === "buttons" || viewState === "expanding" || viewState === "apm-expanding";
|
|
2800
3671
|
const isCardView = viewState === "expanding" || viewState === "card" || viewState === "collapsing";
|
|
2801
3672
|
const cardButtonSizing = cardButtonContent === void 0 ? { boxSizing: "border-box", height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, padding: "0 1rem" } : { padding: "0.9rem 1rem" };
|
|
2802
|
-
const buttonsAnim = viewState === "expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
|
|
3673
|
+
const buttonsAnim = viewState === "expanding" || viewState === "apm-expanding" ? `flopay-buttons-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : viewState === "collapsing" || viewState === "apm-collapsing" ? `flopay-buttons-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : void 0;
|
|
2803
3674
|
const cardAnim = viewState === "expanding" ? `flopay-card-enter ${TRANSITION_MS}ms cubic-bezier(0, 0, 0.2, 1) both` : viewState === "collapsing" ? `flopay-card-exit ${TRANSITION_MS}ms cubic-bezier(0.4, 0, 0.2, 1) both` : void 0;
|
|
2804
3675
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
2805
3676
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FloPayKeyframes, {}),
|
|
2806
3677
|
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
2807
3678
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "grid" }, children: [
|
|
2808
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3679
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3680
|
+
"div",
|
|
3681
|
+
{
|
|
3682
|
+
"data-testid": "flopay-buttons-panel",
|
|
3683
|
+
"aria-hidden": !isButtonsView && !buttonsAnim ? true : void 0,
|
|
3684
|
+
style: {
|
|
3685
|
+
gridArea: "1 / 1",
|
|
3686
|
+
display: "flex",
|
|
3687
|
+
flexDirection: "column",
|
|
3688
|
+
gap: "0.5rem",
|
|
3689
|
+
// When card form is showing (and not transitioning), hide but keep mounted.
|
|
3690
|
+
...!isButtonsView && !buttonsAnim ? BUTTONS_PANEL_HIDDEN_STYLE : {},
|
|
3691
|
+
...buttonsAnim ? { animation: buttonsAnim, pointerEvents: "none" } : {}
|
|
3692
|
+
},
|
|
3693
|
+
children: [
|
|
3694
|
+
renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug"),
|
|
3695
|
+
renderStripeGateDebug("flopay-stripe-gate-debug"),
|
|
3696
|
+
shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3697
|
+
paypalDirectRetry && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3698
|
+
"div",
|
|
3699
|
+
{
|
|
3700
|
+
"data-testid": "flopay-paypal-direct-retry-notice",
|
|
3701
|
+
style: {
|
|
3702
|
+
padding: "8px 10px",
|
|
3703
|
+
background: "#fef3c7",
|
|
3704
|
+
border: "1px solid #fcd34d",
|
|
3705
|
+
borderRadius: 6,
|
|
3706
|
+
color: "#78350f",
|
|
3707
|
+
fontSize: 13,
|
|
3708
|
+
lineHeight: 1.4
|
|
3709
|
+
},
|
|
3710
|
+
children: "Please confirm your PayPal payment to complete checkout."
|
|
3711
|
+
}
|
|
3712
|
+
),
|
|
3713
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3714
|
+
DirectPayPalButton,
|
|
3715
|
+
{
|
|
3716
|
+
sessionId,
|
|
3717
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
3718
|
+
email: resolvedAccount.email,
|
|
3719
|
+
clientId: directPaypal.clientId,
|
|
3720
|
+
environment: directPaypal.environment,
|
|
3721
|
+
currency: currency.toUpperCase(),
|
|
3722
|
+
isSubscription,
|
|
3723
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
3724
|
+
onComplete,
|
|
3725
|
+
onErrorChange: updateError,
|
|
3726
|
+
onDecline,
|
|
3727
|
+
onButtonClick,
|
|
3728
|
+
runBeforeButtonClick,
|
|
3729
|
+
isProcessing: isSubmitting,
|
|
3730
|
+
onLoadStateChange: setDirectPaypalReady,
|
|
3731
|
+
session: session ?? null,
|
|
3732
|
+
existingOrderId: paypalDirectRetry?.orderId,
|
|
3733
|
+
debug
|
|
3734
|
+
},
|
|
3735
|
+
paypalDirectRetry?.orderId ?? "fresh"
|
|
3736
|
+
)
|
|
3737
|
+
] }),
|
|
3738
|
+
shouldRenderStripePayPal && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_stripe_js.Elements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3739
|
+
PayPalButtonInner,
|
|
3740
|
+
{
|
|
3741
|
+
sessionId,
|
|
3742
|
+
email: resolvedAccount.email,
|
|
3743
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
3744
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
3745
|
+
onErrorChange: updateError,
|
|
3746
|
+
isProcessing: isSubmitting,
|
|
3747
|
+
onButtonClick,
|
|
3748
|
+
onDecline,
|
|
3749
|
+
runBeforeButtonClick,
|
|
3750
|
+
onLoadStateChange: setPaypalLoadState,
|
|
3751
|
+
placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
|
|
3752
|
+
}
|
|
3753
|
+
) }),
|
|
3754
|
+
shouldRenderWallets ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3755
|
+
WalletButtonInner,
|
|
3756
|
+
{
|
|
3757
|
+
sessionId,
|
|
3758
|
+
email: resolvedAccount.email,
|
|
3759
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
3760
|
+
expressMethods: walletExpressMethods,
|
|
3761
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
3762
|
+
onErrorChange: updateError,
|
|
3763
|
+
onButtonClick,
|
|
3764
|
+
onDecline,
|
|
3765
|
+
runBeforeButtonClick,
|
|
3766
|
+
onLoadStateChange: setWalletLoadState,
|
|
3767
|
+
placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
|
|
3768
|
+
}
|
|
3769
|
+
) }) : shouldShowWallets ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
|
|
3770
|
+
shouldRenderPaymentElement && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3771
|
+
StripePaymentElementInner,
|
|
3772
|
+
{
|
|
3773
|
+
sessionId,
|
|
3774
|
+
email: resolvedAccount.email,
|
|
3775
|
+
billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
|
|
3776
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
3777
|
+
paymentElementMethods: paymentElementMethodsForCurrency,
|
|
3778
|
+
stripeInstance,
|
|
3779
|
+
paymentElementBaseOptions,
|
|
3780
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
3781
|
+
onErrorChange: updateError,
|
|
3782
|
+
onButtonClick,
|
|
3783
|
+
onDecline,
|
|
3784
|
+
runBeforeButtonClick,
|
|
3785
|
+
onLoadStateChange: setPaymentElementLoadState,
|
|
3786
|
+
isProcessing: isSubmitting,
|
|
3787
|
+
submitButtonColor: resolvedPrimaryColor,
|
|
3788
|
+
submitButtonBorderRadius: resolvedBorderRadius,
|
|
3789
|
+
submitButtonStyle: bStyles.submitButton,
|
|
3790
|
+
onExpandApm: expandToApm,
|
|
3791
|
+
expandedApmMethod,
|
|
3792
|
+
themeId: theme,
|
|
3793
|
+
buttonAppearance: {
|
|
3794
|
+
backgroundColor: bStyles.cardButton?.backgroundColor,
|
|
3795
|
+
borderColor: bStyles.cardInputBorder,
|
|
3796
|
+
textColor: bStyles.cardButton?.color,
|
|
3797
|
+
borderRadius: bStyles.cardButton?.borderRadius
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
),
|
|
3801
|
+
showStripe && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3802
|
+
"button",
|
|
3803
|
+
{
|
|
3804
|
+
type: "button",
|
|
3805
|
+
onClick: async () => {
|
|
3806
|
+
if (isSubmitting) return;
|
|
3807
|
+
const beforeClick = await runBeforeButtonClick("card");
|
|
3808
|
+
if (!beforeClick.proceed) return;
|
|
3809
|
+
onButtonClick?.("card");
|
|
3810
|
+
expandToCard();
|
|
3811
|
+
},
|
|
3812
|
+
disabled: isSubmitting,
|
|
3813
|
+
style: {
|
|
3814
|
+
width: "100%",
|
|
3815
|
+
...cardButtonSizing,
|
|
3816
|
+
// `bStyles.cardButton` first — supplies the bundle's
|
|
3817
|
+
// padding / typography / border-radius — then the
|
|
3818
|
+
// primary surface overrides on top, so the card button
|
|
3819
|
+
// ends up wearing the *submit* button's colours rather
|
|
3820
|
+
// than the bundle's neutral white-tile `cardButton`
|
|
3821
|
+
// colours. Without this order flip, every non-classic
|
|
3822
|
+
// bundle's `cardButton.backgroundColor` was winning
|
|
3823
|
+
// over the primary fill we just derived above it.
|
|
3824
|
+
...bStyles.cardButton,
|
|
3825
|
+
...derivePrimaryTileStyle({
|
|
3826
|
+
themeBundle,
|
|
3827
|
+
resolvedPrimaryColor,
|
|
3828
|
+
resolvedBorderRadius,
|
|
3829
|
+
submitButtonStyle: bStyles.submitButton
|
|
3830
|
+
}),
|
|
3831
|
+
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
3832
|
+
fontWeight: 600,
|
|
3833
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
3834
|
+
display: "flex",
|
|
3835
|
+
alignItems: "center",
|
|
3836
|
+
justifyContent: "center",
|
|
3837
|
+
gap: "0.625rem",
|
|
3838
|
+
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
3839
|
+
position: "relative",
|
|
3840
|
+
opacity: isSubmitting ? 0.6 : 1
|
|
3841
|
+
},
|
|
3842
|
+
onMouseDown: (e) => {
|
|
3843
|
+
e.currentTarget.style.transform = "scale(0.985)";
|
|
3844
|
+
},
|
|
3845
|
+
onMouseUp: (e) => {
|
|
3846
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
3847
|
+
},
|
|
3848
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardButtonContentSlot, { content: cardButtonContent })
|
|
3849
|
+
}
|
|
3850
|
+
),
|
|
3851
|
+
displayError && viewState === "buttons" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "alert", "data-testid": "flopay-error", style: {
|
|
3852
|
+
margin: "0.25rem 0",
|
|
3853
|
+
padding: "0.625rem 0.875rem",
|
|
3854
|
+
background: "#FEF2F2",
|
|
3855
|
+
border: "1px solid #FECACA",
|
|
3856
|
+
borderRadius: "8px",
|
|
3857
|
+
color: "#991B1B",
|
|
3858
|
+
fontSize: "0.85rem",
|
|
3859
|
+
fontWeight: 600,
|
|
3860
|
+
display: "flex",
|
|
3861
|
+
alignItems: "center",
|
|
3862
|
+
gap: "0.5rem",
|
|
3863
|
+
...bStyles.errorBanner ? bStyles.errorBanner : {}
|
|
3864
|
+
}, children: [
|
|
3865
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
3866
|
+
displayError
|
|
3867
|
+
] })
|
|
3868
|
+
]
|
|
3869
|
+
}
|
|
3870
|
+
),
|
|
3871
|
+
showStripe && isCardView && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
2809
3872
|
gridArea: "1 / 1",
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
...
|
|
3873
|
+
...cardAnim ? { animation: cardAnim } : {},
|
|
3874
|
+
...viewState === "collapsing" ? { pointerEvents: "none" } : {}
|
|
3875
|
+
}, children: cardFormBlock }),
|
|
3876
|
+
showStripe && isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
3877
|
+
gridArea: "1 / 1",
|
|
3878
|
+
...apmAnim ? { animation: apmAnim } : {},
|
|
3879
|
+
...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
|
|
3880
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
3881
|
+
backgroundColor: cardBg,
|
|
3882
|
+
borderRadius: containerRadius,
|
|
3883
|
+
...containerOverrides,
|
|
3884
|
+
padding: containerPadding
|
|
2816
3885
|
}, children: [
|
|
2817
|
-
|
|
2818
|
-
"
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
background: "#eef2ff",
|
|
2825
|
-
border: "1px solid #c7d2fe",
|
|
2826
|
-
borderRadius: 6,
|
|
2827
|
-
color: "#111827",
|
|
2828
|
-
font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
2829
|
-
whiteSpace: "pre-wrap",
|
|
2830
|
-
wordBreak: "break-word"
|
|
2831
|
-
},
|
|
2832
|
-
children: [
|
|
2833
|
-
"FloPay/DirectPayPal-debug (parent gate)",
|
|
2834
|
-
` showPayPal=${showPayPal}`,
|
|
2835
|
-
` directPaypalConfigured=${directPaypalConfigured}`,
|
|
2836
|
-
` inAppBrowserDetected=${String(inAppBrowserDetected)}`,
|
|
2837
|
-
` shouldRenderDirectPayPal=${shouldRenderDirectPayPal}`,
|
|
2838
|
-
` shouldRenderStripePayPal=${shouldRenderStripePayPal}`,
|
|
2839
|
-
` hasPaypalStripeInstance=${!!paypalStripeInstance}`
|
|
2840
|
-
].join("\n")
|
|
2841
|
-
}
|
|
2842
|
-
),
|
|
2843
|
-
shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
2844
|
-
paypalDirectRetry && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2845
|
-
"div",
|
|
3886
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
3887
|
+
display: "flex",
|
|
3888
|
+
alignItems: "center",
|
|
3889
|
+
padding: "0.75rem 0 0.625rem"
|
|
3890
|
+
}, children: [
|
|
3891
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
3892
|
+
"button",
|
|
2846
3893
|
{
|
|
2847
|
-
|
|
3894
|
+
type: "button",
|
|
3895
|
+
"data-testid": "flopay-apm-back-button",
|
|
3896
|
+
onClick: collapseFromApm,
|
|
2848
3897
|
style: {
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
3898
|
+
display: "inline-flex",
|
|
3899
|
+
alignItems: "center",
|
|
3900
|
+
gap: hideBackButtonLabel ? 0 : "0.5rem",
|
|
3901
|
+
background: "none",
|
|
3902
|
+
border: "none",
|
|
3903
|
+
cursor: "pointer",
|
|
3904
|
+
color: "#4b5563",
|
|
3905
|
+
fontSize: bStyles.backButtonFontSize ?? "0.85rem",
|
|
3906
|
+
fontWeight: 500,
|
|
3907
|
+
padding: 0,
|
|
3908
|
+
transition: "color 0.15s",
|
|
3909
|
+
flexShrink: 0,
|
|
3910
|
+
...bStyles.backButton
|
|
2856
3911
|
},
|
|
2857
|
-
|
|
3912
|
+
"aria-label": "Back to payment methods",
|
|
3913
|
+
children: [
|
|
3914
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: {
|
|
3915
|
+
display: "inline-flex",
|
|
3916
|
+
alignItems: "center",
|
|
3917
|
+
justifyContent: "center",
|
|
3918
|
+
width: 28,
|
|
3919
|
+
height: 28,
|
|
3920
|
+
borderRadius: "50%",
|
|
3921
|
+
backgroundColor: "#f3f4f6",
|
|
3922
|
+
transition: "background-color 0.15s",
|
|
3923
|
+
...bStyles.backButtonIcon
|
|
3924
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
3925
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BackButtonContentSlot, { content: cardBackButtonContent })
|
|
3926
|
+
]
|
|
2858
3927
|
}
|
|
2859
3928
|
),
|
|
2860
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
isSubscription,
|
|
2870
|
-
onTokenizedBody: dispatchTokenizedBody,
|
|
2871
|
-
onComplete,
|
|
2872
|
-
onErrorChange: updateError,
|
|
2873
|
-
onDecline,
|
|
2874
|
-
onButtonClick,
|
|
2875
|
-
runBeforeButtonClick,
|
|
2876
|
-
isProcessing: isSubmitting,
|
|
2877
|
-
onLoadStateChange: setDirectPaypalReady,
|
|
2878
|
-
session: session ?? null,
|
|
2879
|
-
existingOrderId: paypalDirectRetry?.orderId,
|
|
2880
|
-
debug
|
|
2881
|
-
},
|
|
2882
|
-
paypalDirectRetry?.orderId ?? "fresh"
|
|
2883
|
-
)
|
|
3929
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
3930
|
+
flex: 1,
|
|
3931
|
+
textAlign: "center",
|
|
3932
|
+
fontWeight: 600,
|
|
3933
|
+
fontSize: bStyles.titleFontSize ?? "1.05rem",
|
|
3934
|
+
color: resolvedTitleColor,
|
|
3935
|
+
paddingRight: 80,
|
|
3936
|
+
...bStyles.title
|
|
3937
|
+
}, children: `Pay with ${(0, import_shared5.getStripeMethodDisplayName)(expandedApmMethod)}` })
|
|
2884
3938
|
] }),
|
|
2885
|
-
shouldRenderStripePayPal && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_stripe_js.Elements, { stripe: paypalStripeInstance, options: paypalOptions, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2886
|
-
PayPalButtonInner,
|
|
2887
|
-
{
|
|
2888
|
-
sessionId,
|
|
2889
|
-
email: resolvedAccount.email,
|
|
2890
|
-
billingApiUrl: resolvedBillingApiUrl,
|
|
2891
|
-
onTokenizedBody: dispatchTokenizedBody,
|
|
2892
|
-
onErrorChange: updateError,
|
|
2893
|
-
isProcessing: isSubmitting,
|
|
2894
|
-
onButtonClick,
|
|
2895
|
-
onDecline,
|
|
2896
|
-
runBeforeButtonClick,
|
|
2897
|
-
onLoadStateChange: setPaypalLoadState
|
|
2898
|
-
}
|
|
2899
|
-
) }),
|
|
2900
|
-
shouldRenderWallets ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
2901
|
-
WalletButtonInner,
|
|
2902
|
-
{
|
|
2903
|
-
sessionId,
|
|
2904
|
-
email: resolvedAccount.email,
|
|
2905
|
-
billingApiUrl: resolvedBillingApiUrl,
|
|
2906
|
-
showApplePay,
|
|
2907
|
-
showGooglePay,
|
|
2908
|
-
onTokenizedBody: dispatchTokenizedBody,
|
|
2909
|
-
onErrorChange: updateError,
|
|
2910
|
-
onButtonClick,
|
|
2911
|
-
onDecline,
|
|
2912
|
-
runBeforeButtonClick,
|
|
2913
|
-
onLoadStateChange: setWalletLoadState
|
|
2914
|
-
}
|
|
2915
|
-
) }) : shouldShowWallets ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { height: DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT, borderRadius: 8, background: "#e5e7eb", animation: "flopay-pulse 1.5s ease-in-out infinite" } }) : null,
|
|
2916
3939
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3940
|
+
import_react_stripe_js.Elements,
|
|
3941
|
+
{
|
|
3942
|
+
stripe: stripeInstance,
|
|
3943
|
+
options: apmInlineOptions,
|
|
3944
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3945
|
+
StripeMethodInlineForm,
|
|
3946
|
+
{
|
|
3947
|
+
method: expandedApmMethod,
|
|
3948
|
+
sessionId,
|
|
3949
|
+
email: resolvedAccount.email,
|
|
3950
|
+
billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
|
|
3951
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
3952
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
3953
|
+
onErrorChange: updateError,
|
|
3954
|
+
onDecline,
|
|
3955
|
+
onCancel: collapseFromApm,
|
|
3956
|
+
runBeforeButtonClick,
|
|
3957
|
+
onButtonClick,
|
|
3958
|
+
isProcessing: isSubmitting,
|
|
3959
|
+
submitButtonColor: resolvedPrimaryColor,
|
|
3960
|
+
submitButtonBorderRadius: resolvedBorderRadius,
|
|
3961
|
+
submitButtonStyle: bStyles.submitButton
|
|
3962
|
+
}
|
|
3963
|
+
)
|
|
3964
|
+
},
|
|
3965
|
+
expandedApmMethod
|
|
3966
|
+
)
|
|
3967
|
+
] }) })
|
|
3968
|
+
] })
|
|
3969
|
+
] });
|
|
3970
|
+
}
|
|
3971
|
+
if (isApmView && expandedApmMethod && apmInlineOptions && stripeInstance && showStripe) {
|
|
3972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
3973
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FloPayKeyframes, {}),
|
|
3974
|
+
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
3976
|
+
...apmAnim ? { animation: apmAnim } : {},
|
|
3977
|
+
...viewState === "apm-collapsing" ? { pointerEvents: "none" } : {}
|
|
3978
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
3979
|
+
backgroundColor: cardBg,
|
|
3980
|
+
borderRadius: containerRadius,
|
|
3981
|
+
...containerOverrides,
|
|
3982
|
+
padding: containerPadding
|
|
3983
|
+
}, children: [
|
|
3984
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
3985
|
+
display: "flex",
|
|
3986
|
+
alignItems: "center",
|
|
3987
|
+
padding: "0.75rem 0 0.625rem"
|
|
3988
|
+
}, children: [
|
|
3989
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
2917
3990
|
"button",
|
|
2918
3991
|
{
|
|
2919
3992
|
type: "button",
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
const beforeClick = await runBeforeButtonClick("card");
|
|
2923
|
-
if (!beforeClick.proceed) return;
|
|
2924
|
-
onButtonClick?.("card");
|
|
2925
|
-
expandToCard();
|
|
2926
|
-
},
|
|
2927
|
-
disabled: isSubmitting,
|
|
3993
|
+
"data-testid": "flopay-apm-back-button-default",
|
|
3994
|
+
onClick: collapseFromApm,
|
|
2928
3995
|
style: {
|
|
2929
|
-
|
|
2930
|
-
...cardButtonSizing,
|
|
2931
|
-
backgroundColor: "white",
|
|
2932
|
-
color: "#262833",
|
|
2933
|
-
border: "1px solid #d1d5db",
|
|
2934
|
-
borderRadius: "8px",
|
|
2935
|
-
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
2936
|
-
fontWeight: 600,
|
|
2937
|
-
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
2938
|
-
display: "flex",
|
|
3996
|
+
display: "inline-flex",
|
|
2939
3997
|
alignItems: "center",
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3998
|
+
gap: hideBackButtonLabel ? 0 : "0.5rem",
|
|
3999
|
+
background: "none",
|
|
4000
|
+
border: "none",
|
|
4001
|
+
cursor: "pointer",
|
|
4002
|
+
color: "#4b5563",
|
|
4003
|
+
fontSize: bStyles.backButtonFontSize ?? "0.85rem",
|
|
4004
|
+
fontWeight: 500,
|
|
4005
|
+
padding: 0,
|
|
4006
|
+
transition: "color 0.15s",
|
|
4007
|
+
flexShrink: 0,
|
|
4008
|
+
...bStyles.backButton
|
|
2947
4009
|
},
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
4010
|
+
"aria-label": "Back to payment methods",
|
|
4011
|
+
children: [
|
|
4012
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: {
|
|
4013
|
+
display: "inline-flex",
|
|
4014
|
+
alignItems: "center",
|
|
4015
|
+
justifyContent: "center",
|
|
4016
|
+
width: 28,
|
|
4017
|
+
height: 28,
|
|
4018
|
+
borderRadius: "50%",
|
|
4019
|
+
backgroundColor: "#f3f4f6",
|
|
4020
|
+
transition: "background-color 0.15s",
|
|
4021
|
+
...bStyles.backButtonIcon
|
|
4022
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M15 18l-6-6 6-6" }) }) }),
|
|
4023
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BackButtonContentSlot, { content: cardBackButtonContent })
|
|
4024
|
+
]
|
|
2955
4025
|
}
|
|
2956
4026
|
),
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
background: "#FEF2F2",
|
|
2961
|
-
border: "1px solid #FECACA",
|
|
2962
|
-
borderRadius: "8px",
|
|
2963
|
-
color: "#991B1B",
|
|
2964
|
-
fontSize: "0.85rem",
|
|
4027
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: {
|
|
4028
|
+
flex: 1,
|
|
4029
|
+
textAlign: "center",
|
|
2965
4030
|
fontWeight: 600,
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
...bStyles.
|
|
2970
|
-
}, children:
|
|
2971
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M12 9v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", stroke: "#DC2626", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
2972
|
-
displayError
|
|
2973
|
-
] })
|
|
4031
|
+
fontSize: bStyles.titleFontSize ?? "1.05rem",
|
|
4032
|
+
color: resolvedTitleColor,
|
|
4033
|
+
paddingRight: 80,
|
|
4034
|
+
...bStyles.title
|
|
4035
|
+
}, children: `Pay with ${(0, import_shared5.getStripeMethodDisplayName)(expandedApmMethod)}` })
|
|
2974
4036
|
] }),
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
4037
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4038
|
+
import_react_stripe_js.Elements,
|
|
4039
|
+
{
|
|
4040
|
+
stripe: stripeInstance,
|
|
4041
|
+
options: apmInlineOptions,
|
|
4042
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4043
|
+
StripeMethodInlineForm,
|
|
4044
|
+
{
|
|
4045
|
+
method: expandedApmMethod,
|
|
4046
|
+
sessionId,
|
|
4047
|
+
email: resolvedAccount.email,
|
|
4048
|
+
billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
|
|
4049
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
4050
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
4051
|
+
onErrorChange: updateError,
|
|
4052
|
+
onDecline,
|
|
4053
|
+
onCancel: collapseFromApm,
|
|
4054
|
+
runBeforeButtonClick,
|
|
4055
|
+
onButtonClick,
|
|
4056
|
+
isProcessing: isSubmitting,
|
|
4057
|
+
submitButtonColor: resolvedPrimaryColor,
|
|
4058
|
+
submitButtonBorderRadius: resolvedBorderRadius,
|
|
4059
|
+
submitButtonStyle: bStyles.submitButton
|
|
4060
|
+
}
|
|
4061
|
+
)
|
|
4062
|
+
},
|
|
4063
|
+
expandedApmMethod
|
|
4064
|
+
)
|
|
4065
|
+
] }) })
|
|
2981
4066
|
] });
|
|
2982
4067
|
}
|
|
2983
4068
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className, style: { position: "relative" }, children: [
|
|
2984
4069
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(FloPayKeyframes, {}),
|
|
2985
4070
|
overlayStatus && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ProcessingOverlay, { status: overlayStatus, errorMessage: displayError }),
|
|
2986
4071
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
2987
|
-
debug
|
|
2988
|
-
|
|
2989
|
-
{
|
|
2990
|
-
"data-testid": "flopay-direct-paypal-gate-debug-default",
|
|
2991
|
-
style: {
|
|
2992
|
-
margin: 0,
|
|
2993
|
-
padding: "6px 8px",
|
|
2994
|
-
background: "#eef2ff",
|
|
2995
|
-
border: "1px solid #c7d2fe",
|
|
2996
|
-
borderRadius: 6,
|
|
2997
|
-
color: "#111827",
|
|
2998
|
-
font: "11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
2999
|
-
whiteSpace: "pre-wrap",
|
|
3000
|
-
wordBreak: "break-word"
|
|
3001
|
-
},
|
|
3002
|
-
children: [
|
|
3003
|
-
"FloPay/DirectPayPal-debug (parent gate)",
|
|
3004
|
-
` showPayPal=${showPayPal}`,
|
|
3005
|
-
` directPaypalConfigured=${directPaypalConfigured}`,
|
|
3006
|
-
` inAppBrowserDetected=${String(inAppBrowserDetected)}`,
|
|
3007
|
-
` shouldRenderDirectPayPal=${shouldRenderDirectPayPal}`,
|
|
3008
|
-
` shouldRenderStripePayPal=${shouldRenderStripePayPal}`,
|
|
3009
|
-
` hasPaypalStripeInstance=${!!paypalStripeInstance}`
|
|
3010
|
-
].join("\n")
|
|
3011
|
-
}
|
|
3012
|
-
),
|
|
4072
|
+
renderDirectPaypalGateDebug("flopay-direct-paypal-gate-debug-default"),
|
|
4073
|
+
renderStripeGateDebug("flopay-stripe-gate-debug-default"),
|
|
3013
4074
|
shouldRenderDirectPayPal && directPaypal && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
3014
4075
|
paypalDirectRetry && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3015
4076
|
"div",
|
|
@@ -3064,7 +4125,8 @@ function SplitCardFormInner({
|
|
|
3064
4125
|
onButtonClick,
|
|
3065
4126
|
onDecline,
|
|
3066
4127
|
runBeforeButtonClick,
|
|
3067
|
-
onLoadStateChange: setPaypalLoadState
|
|
4128
|
+
onLoadStateChange: setPaypalLoadState,
|
|
4129
|
+
placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
|
|
3068
4130
|
}
|
|
3069
4131
|
) }),
|
|
3070
4132
|
shouldRenderWallets && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_stripe_js.Elements, { stripe: stripeInstance, options: walletOptions, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -3073,18 +4135,49 @@ function SplitCardFormInner({
|
|
|
3073
4135
|
sessionId,
|
|
3074
4136
|
email: resolvedAccount.email,
|
|
3075
4137
|
billingApiUrl: resolvedBillingApiUrl,
|
|
3076
|
-
|
|
3077
|
-
|
|
4138
|
+
expressMethods: walletExpressMethods,
|
|
4139
|
+
onTokenizedBody: dispatchTokenizedBody,
|
|
4140
|
+
onErrorChange: updateError,
|
|
4141
|
+
onButtonClick,
|
|
4142
|
+
onDecline,
|
|
4143
|
+
runBeforeButtonClick,
|
|
4144
|
+
onLoadStateChange: setWalletLoadState,
|
|
4145
|
+
placeholderBorderRadius: bStyles.cardButton?.borderRadius ?? resolvedBorderRadius
|
|
4146
|
+
}
|
|
4147
|
+
) }),
|
|
4148
|
+
shouldRenderPaymentElement && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
4149
|
+
StripePaymentElementInner,
|
|
4150
|
+
{
|
|
4151
|
+
sessionId,
|
|
4152
|
+
email: resolvedAccount.email,
|
|
4153
|
+
billingName: [resolvedAccount.firstName, resolvedAccount.lastName].filter(Boolean).join(" ").trim() || void 0,
|
|
4154
|
+
billingApiUrl: resolvedBillingApiUrl,
|
|
4155
|
+
paymentElementMethods: paymentElementMethodsForCurrency,
|
|
4156
|
+
stripeInstance,
|
|
4157
|
+
paymentElementBaseOptions,
|
|
3078
4158
|
onTokenizedBody: dispatchTokenizedBody,
|
|
3079
4159
|
onErrorChange: updateError,
|
|
3080
4160
|
onButtonClick,
|
|
3081
4161
|
onDecline,
|
|
3082
4162
|
runBeforeButtonClick,
|
|
3083
|
-
onLoadStateChange:
|
|
4163
|
+
onLoadStateChange: setPaymentElementLoadState,
|
|
4164
|
+
isProcessing: isSubmitting,
|
|
4165
|
+
submitButtonColor: resolvedPrimaryColor,
|
|
4166
|
+
submitButtonBorderRadius: resolvedBorderRadius,
|
|
4167
|
+
submitButtonStyle: bStyles.submitButton,
|
|
4168
|
+
themeId: theme,
|
|
4169
|
+
onExpandApm: expandToApm,
|
|
4170
|
+
expandedApmMethod,
|
|
4171
|
+
buttonAppearance: {
|
|
4172
|
+
backgroundColor: bStyles.cardButton?.backgroundColor,
|
|
4173
|
+
borderColor: bStyles.cardInputBorder,
|
|
4174
|
+
textColor: bStyles.cardButton?.color,
|
|
4175
|
+
borderRadius: bStyles.cardButton?.borderRadius
|
|
4176
|
+
}
|
|
3084
4177
|
}
|
|
3085
|
-
)
|
|
4178
|
+
)
|
|
3086
4179
|
] }),
|
|
3087
|
-
(shouldDisplayWalletRow || shouldDisplayPayPalRow) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
4180
|
+
showStripe && (shouldDisplayWalletRow || shouldDisplayPayPalRow || shouldDisplayPaymentElementRow) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: {
|
|
3088
4181
|
display: "flex",
|
|
3089
4182
|
alignItems: "center",
|
|
3090
4183
|
gap: "0.75rem",
|
|
@@ -3096,7 +4189,7 @@ function SplitCardFormInner({
|
|
|
3096
4189
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "or pay with card" }),
|
|
3097
4190
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, height: 1, backgroundColor: "#ddd" } })
|
|
3098
4191
|
] }),
|
|
3099
|
-
cardFormBlock
|
|
4192
|
+
showStripe && cardFormBlock
|
|
3100
4193
|
] });
|
|
3101
4194
|
}
|
|
3102
4195
|
|
|
@@ -3629,6 +4722,7 @@ function hasInlineSessionPatchData(patch) {
|
|
|
3629
4722
|
}
|
|
3630
4723
|
function FloPayCheckout({
|
|
3631
4724
|
sessionId: sessionIdProp,
|
|
4725
|
+
nonce: nonceProp,
|
|
3632
4726
|
createSession: createSessionParams,
|
|
3633
4727
|
billingApiUrl,
|
|
3634
4728
|
appearance: appearanceOverride,
|
|
@@ -3642,6 +4736,7 @@ function FloPayCheckout({
|
|
|
3642
4736
|
onCountryChange,
|
|
3643
4737
|
onZipChange,
|
|
3644
4738
|
showPayPal = true,
|
|
4739
|
+
showStripe = true,
|
|
3645
4740
|
showApplePay = true,
|
|
3646
4741
|
showGooglePay = true,
|
|
3647
4742
|
debug = false,
|
|
@@ -3988,6 +5083,14 @@ function FloPayCheckout({
|
|
|
3988
5083
|
},
|
|
3989
5084
|
successUrl: params?.successUrl,
|
|
3990
5085
|
cancelUrl: params?.cancelUrl,
|
|
5086
|
+
// Unified products[] is the post-#760 wire shape. Must be included or
|
|
5087
|
+
// carts that only set `products` (no legacy items/subscriptions) all
|
|
5088
|
+
// hash to the same key — the bootstrap effect's `createSessionHash`
|
|
5089
|
+
// dep then never changes when products change, the session-id cache
|
|
5090
|
+
// hits the wrong session, and the backend's enabledPaymentMethods
|
|
5091
|
+
// result for the *previous* cart sticks until a fresh page-load with
|
|
5092
|
+
// the session cache cleared.
|
|
5093
|
+
p: params?.products?.map((x) => `${x.type ?? ""}:${x.code ?? x.providerItemId ?? x.providerPlanId ?? ""}:${x.totalAmount ?? ""}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
3991
5094
|
i: params?.items?.map((x) => `${x.code ?? x.providerItemId ?? ""}:${x.totalAmount ?? ""}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
3992
5095
|
s: params?.subscriptions?.map((x) => `${x.code ?? x.providerPlanId ?? ""}:${x.totalAmount ?? ""}:${x.overrideAmount ?? ""}:${x.quantity ?? 1}`).sort(),
|
|
3993
5096
|
couponCodes: params?.couponCodes,
|
|
@@ -4214,7 +5317,7 @@ function FloPayCheckout({
|
|
|
4214
5317
|
async function init() {
|
|
4215
5318
|
try {
|
|
4216
5319
|
const api = new import_js4.PaymentAPI(resolvedBillingUrl);
|
|
4217
|
-
const result = await api.getUnifiedCheckoutSession(activeSessionId);
|
|
5320
|
+
const result = await api.getUnifiedCheckoutSession(activeSessionId, nonceProp);
|
|
4218
5321
|
if (cancelled) return;
|
|
4219
5322
|
setUnified(result);
|
|
4220
5323
|
const sess = result.data.session ?? null;
|
|
@@ -4301,6 +5404,7 @@ function FloPayCheckout({
|
|
|
4301
5404
|
createSessionHash,
|
|
4302
5405
|
effectiveCreateSessionMode,
|
|
4303
5406
|
initSessionDependency,
|
|
5407
|
+
nonceProp,
|
|
4304
5408
|
runSavedPaymentFlow
|
|
4305
5409
|
]);
|
|
4306
5410
|
const handleConfirmCheckout = (0, import_react9.useCallback)(async () => {
|
|
@@ -4374,17 +5478,18 @@ function FloPayCheckout({
|
|
|
4374
5478
|
] });
|
|
4375
5479
|
}
|
|
4376
5480
|
if (layout === "buttons") {
|
|
5481
|
+
const bundleRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
|
|
4377
5482
|
const skeletonBar = (h) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4378
5483
|
height: h,
|
|
4379
|
-
borderRadius:
|
|
5484
|
+
borderRadius: bundleRadius,
|
|
4380
5485
|
background: "#e5e7eb",
|
|
4381
5486
|
animation: "flopay-loading-pulse 1.5s ease-in-out infinite"
|
|
4382
5487
|
} });
|
|
4383
5488
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
4384
5489
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
4385
5490
|
showPayPal && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
4386
|
-
(showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
4387
|
-
skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
5491
|
+
showStripe && (showApplePay || showGooglePay) && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
5492
|
+
showStripe && skeletonBar(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
4388
5493
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("style", { children: `@keyframes flopay-loading-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }` })
|
|
4389
5494
|
] }),
|
|
4390
5495
|
modeOverlay
|
|
@@ -4429,6 +5534,7 @@ function FloPayCheckout({
|
|
|
4429
5534
|
{
|
|
4430
5535
|
onButtonClick,
|
|
4431
5536
|
showPayPal,
|
|
5537
|
+
showStripe,
|
|
4432
5538
|
showApplePay,
|
|
4433
5539
|
showGooglePay,
|
|
4434
5540
|
theme,
|
|
@@ -4573,6 +5679,8 @@ function FloPayCheckout({
|
|
|
4573
5679
|
onErrorChange: setModeError,
|
|
4574
5680
|
onFullNameChange,
|
|
4575
5681
|
showPayPal,
|
|
5682
|
+
showStripe,
|
|
5683
|
+
enabledPaymentMethods: unified?.data.stripe?.enabledPaymentMethods,
|
|
4576
5684
|
showApplePay,
|
|
4577
5685
|
showGooglePay,
|
|
4578
5686
|
layout,
|
|
@@ -4637,6 +5745,7 @@ function InterimButtonsView({
|
|
|
4637
5745
|
cardOpen,
|
|
4638
5746
|
errorMessage,
|
|
4639
5747
|
showPayPal,
|
|
5748
|
+
showStripe = true,
|
|
4640
5749
|
showApplePay,
|
|
4641
5750
|
showGooglePay,
|
|
4642
5751
|
theme,
|
|
@@ -4668,9 +5777,10 @@ function InterimButtonsView({
|
|
|
4668
5777
|
title: { ...base.title, ...stylesOverride.title }
|
|
4669
5778
|
};
|
|
4670
5779
|
}, [themeBundle, buttonsTheme, stylesOverride]);
|
|
5780
|
+
const skeletonRadius = themeBundle?.buttonsLayout?.cardButton?.borderRadius ?? themeBundle?.appearance.variables?.borderRadius ?? 8;
|
|
4671
5781
|
const skeleton = (h) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4672
5782
|
height: h,
|
|
4673
|
-
borderRadius:
|
|
5783
|
+
borderRadius: skeletonRadius,
|
|
4674
5784
|
background: "#e5e7eb",
|
|
4675
5785
|
animation: "flopay-interim-pulse 1.5s ease-in-out infinite"
|
|
4676
5786
|
} });
|
|
@@ -4741,8 +5851,28 @@ function InterimButtonsView({
|
|
|
4741
5851
|
] }),
|
|
4742
5852
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderTopLeftRadius: 8, borderTopRightRadius: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { width: "60%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
4743
5853
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex" }, children: [
|
|
4744
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4745
|
-
|
|
5854
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
5855
|
+
flex: 1,
|
|
5856
|
+
backgroundColor: inputBg,
|
|
5857
|
+
borderTop: "none",
|
|
5858
|
+
borderRight: "none",
|
|
5859
|
+
borderBottom: `1px solid ${inputBorder}`,
|
|
5860
|
+
borderLeft: `1px solid ${inputBorder}`,
|
|
5861
|
+
borderBottomLeftRadius: 8,
|
|
5862
|
+
padding: 12,
|
|
5863
|
+
height: 45
|
|
5864
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { width: "50%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
5865
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
5866
|
+
flex: 1,
|
|
5867
|
+
backgroundColor: inputBg,
|
|
5868
|
+
borderTop: "none",
|
|
5869
|
+
borderRight: `1px solid ${inputBorder}`,
|
|
5870
|
+
borderBottom: `1px solid ${inputBorder}`,
|
|
5871
|
+
borderLeft: `1px solid ${inputBorder}`,
|
|
5872
|
+
borderBottomRightRadius: 8,
|
|
5873
|
+
padding: 12,
|
|
5874
|
+
height: 45
|
|
5875
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { width: "40%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) })
|
|
4746
5876
|
] }),
|
|
4747
5877
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { backgroundColor: inputBg, border: `1px solid ${inputBorder}`, borderRadius: 8, marginTop: 8, padding: 12, height: 45 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { width: "45%", height: 14, borderRadius: 4, background: "#e5e7eb", animation: "flopay-interim-pulse 1.5s ease-in-out infinite" } }) }),
|
|
4748
5878
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
@@ -4766,8 +5896,8 @@ function InterimButtonsView({
|
|
|
4766
5896
|
}
|
|
4767
5897
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
|
|
4768
5898
|
showPayPal && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
4769
|
-
(showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
4770
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
5899
|
+
showStripe && (showApplePay || showGooglePay) && skeleton(DEFAULT_BUTTONS_LAYOUT_BUTTON_HEIGHT2),
|
|
5900
|
+
showStripe && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4771
5901
|
"button",
|
|
4772
5902
|
{
|
|
4773
5903
|
type: "button",
|
|
@@ -4836,7 +5966,7 @@ var import_js5 = require("@flopay/js");
|
|
|
4836
5966
|
var import_shared9 = require("@flopay/shared");
|
|
4837
5967
|
var import_react10 = require("react");
|
|
4838
5968
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4839
|
-
var
|
|
5969
|
+
var WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
4840
5970
|
var CheckoutForm = (0, import_react10.forwardRef)(
|
|
4841
5971
|
function CheckoutForm2(props, ref) {
|
|
4842
5972
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CheckoutFormInner, { ...props, innerRef: ref });
|
|
@@ -4962,7 +6092,7 @@ function CheckoutFormInner({
|
|
|
4962
6092
|
onDecline?.(buildDeclineEvent("paypal", message));
|
|
4963
6093
|
return;
|
|
4964
6094
|
}
|
|
4965
|
-
localStorage.setItem(
|
|
6095
|
+
localStorage.setItem(WALLET_RESUME_KEY, JSON.stringify({
|
|
4966
6096
|
sessionId,
|
|
4967
6097
|
paymentIntentId: "",
|
|
4968
6098
|
tokenType: "card",
|
|
@@ -5038,12 +6168,12 @@ function CheckoutFormInner({
|
|
|
5038
6168
|
}), [flopay, dispatchTokenizedBody, onError, updateError, emitDecline]);
|
|
5039
6169
|
(0, import_react10.useEffect)(() => {
|
|
5040
6170
|
if (typeof window === "undefined") return;
|
|
5041
|
-
const stored = localStorage.getItem(
|
|
6171
|
+
const stored = localStorage.getItem(WALLET_RESUME_KEY);
|
|
5042
6172
|
if (!stored) return;
|
|
5043
6173
|
try {
|
|
5044
6174
|
const payload = JSON.parse(stored);
|
|
5045
6175
|
if (payload.sessionId === sessionId) {
|
|
5046
|
-
localStorage.removeItem(
|
|
6176
|
+
localStorage.removeItem(WALLET_RESUME_KEY);
|
|
5047
6177
|
dispatchTokenizedBody({
|
|
5048
6178
|
id: payload.tokenId,
|
|
5049
6179
|
type: payload.tokenType,
|
|
@@ -5051,7 +6181,7 @@ function CheckoutFormInner({
|
|
|
5051
6181
|
});
|
|
5052
6182
|
}
|
|
5053
6183
|
} catch {
|
|
5054
|
-
localStorage.removeItem(
|
|
6184
|
+
localStorage.removeItem(WALLET_RESUME_KEY);
|
|
5055
6185
|
}
|
|
5056
6186
|
}, [sessionId, dispatchTokenizedBody]);
|
|
5057
6187
|
const handleSubmit = (0, import_react10.useCallback)(
|
|
@@ -5396,10 +6526,12 @@ function resolveCreateSessionDraft(props) {
|
|
|
5396
6526
|
if (!props.clientId || !props.account || !props.successUrl || !props.cancelUrl) {
|
|
5397
6527
|
return null;
|
|
5398
6528
|
}
|
|
6529
|
+
const hasProducts = props.products != null;
|
|
5399
6530
|
return {
|
|
5400
6531
|
clientId: props.clientId,
|
|
5401
|
-
|
|
5402
|
-
|
|
6532
|
+
products: props.products,
|
|
6533
|
+
items: hasProducts ? void 0 : props.items,
|
|
6534
|
+
subscriptions: hasProducts ? void 0 : props.subscriptions,
|
|
5403
6535
|
account: props.account,
|
|
5404
6536
|
successUrl: props.successUrl,
|
|
5405
6537
|
cancelUrl: props.cancelUrl,
|
|
@@ -5417,6 +6549,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
5417
6549
|
paymentMethodId: _deprecatedPaymentMethodId,
|
|
5418
6550
|
checkoutMethod: _deprecatedCheckoutMethod,
|
|
5419
6551
|
clientId,
|
|
6552
|
+
products,
|
|
5420
6553
|
items,
|
|
5421
6554
|
subscriptions,
|
|
5422
6555
|
account,
|
|
@@ -5447,6 +6580,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
5447
6580
|
() => resolveCreateSessionDraft({
|
|
5448
6581
|
createSession,
|
|
5449
6582
|
clientId,
|
|
6583
|
+
products,
|
|
5450
6584
|
items,
|
|
5451
6585
|
subscriptions,
|
|
5452
6586
|
account,
|
|
@@ -5462,6 +6596,7 @@ function FloPayAutomaticPaymentButton({
|
|
|
5462
6596
|
clientId,
|
|
5463
6597
|
couponCodes,
|
|
5464
6598
|
createSession,
|
|
6599
|
+
products,
|
|
5465
6600
|
items,
|
|
5466
6601
|
subscriptions,
|
|
5467
6602
|
successUrl,
|
|
@@ -5750,10 +6885,20 @@ function FloPayAutomaticPaymentButton({
|
|
|
5750
6885
|
style: {
|
|
5751
6886
|
width: "100%",
|
|
5752
6887
|
...cardButtonSizing,
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
6888
|
+
// `bStyles.cardButton` first so the bundle's padding / typography
|
|
6889
|
+
// / radius / shadow come through, then `derivePrimaryTileStyle`
|
|
6890
|
+
// on top so the auto-pay button carries the *submit* button's
|
|
6891
|
+
// background and text colours — making it visually identical to
|
|
6892
|
+
// the "Confirm Payment" / "Pay with X" actions it stands in for.
|
|
6893
|
+
// Inline `style` consumer override stays at the end so explicit
|
|
6894
|
+
// per-button overrides still win.
|
|
6895
|
+
...bStyles.cardButton,
|
|
6896
|
+
...derivePrimaryTileStyle({
|
|
6897
|
+
themeBundle,
|
|
6898
|
+
resolvedPrimaryColor: themeBundle?.appearance.variables?.colorPrimary ?? "#4A49FF",
|
|
6899
|
+
resolvedBorderRadius: themeBundle?.appearance.variables?.borderRadius ?? "8px",
|
|
6900
|
+
submitButtonStyle: bStyles.submitButton
|
|
6901
|
+
}),
|
|
5757
6902
|
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
5758
6903
|
fontWeight: 600,
|
|
5759
6904
|
cursor: disabled || isProcessing ? "not-allowed" : "pointer",
|
|
@@ -5761,11 +6906,9 @@ function FloPayAutomaticPaymentButton({
|
|
|
5761
6906
|
alignItems: "center",
|
|
5762
6907
|
justifyContent: "center",
|
|
5763
6908
|
gap: "0.625rem",
|
|
5764
|
-
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
5765
6909
|
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
5766
6910
|
position: "relative",
|
|
5767
6911
|
opacity: disabled || isProcessing ? 0.6 : 1,
|
|
5768
|
-
...bStyles.cardButton,
|
|
5769
6912
|
...style
|
|
5770
6913
|
},
|
|
5771
6914
|
onMouseDown: (e) => {
|