@flopay/react 0.3.14 → 0.3.15
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 +1 -1
- package/dist/index.cjs +79 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +80 -113
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -178,7 +178,7 @@ Skip the backend API route — create the session directly in the component:
|
|
|
178
178
|
|
|
179
179
|
The component POSTs to the billing API, gets the full session back, and renders the form — zero backend code needed.
|
|
180
180
|
|
|
181
|
-
When you use `onBeforeButtonClick`, `createSession.account.email` can be filled in later for the card-button flow. PayPal and
|
|
181
|
+
When you use `onBeforeButtonClick`, `createSession.account.email` can still be filled in later for the card-button flow. PayPal and wallet buttons still bootstrap normally, but this hook does not run for them, so collect any data those methods require before the user uses them.
|
|
182
182
|
|
|
183
183
|
### Advanced: Manual Provider Setup
|
|
184
184
|
|
package/dist/index.cjs
CHANGED
|
@@ -351,15 +351,6 @@ function buildSyntheticSession(params, checkoutModeOverride) {
|
|
|
351
351
|
}))
|
|
352
352
|
};
|
|
353
353
|
}
|
|
354
|
-
function ensureInlineSessionReady(params) {
|
|
355
|
-
if (!params.account.email?.trim()) {
|
|
356
|
-
throw new import_shared3.FloPayError(
|
|
357
|
-
"Email is required before continuing with card checkout.",
|
|
358
|
-
"validation_error",
|
|
359
|
-
{ param: "createSession.account.email" }
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
354
|
function mergeAccountPatch(base, patch) {
|
|
364
355
|
if (!patch) return base;
|
|
365
356
|
return {
|
|
@@ -821,6 +812,7 @@ function SplitCardFormInner({
|
|
|
821
812
|
}) {
|
|
822
813
|
const flopay = useFloPay();
|
|
823
814
|
const elements = useElements();
|
|
815
|
+
const checkout = (0, import_react6.useContext)(CheckoutContext);
|
|
824
816
|
const contextBillingUrl = useBillingApiUrl();
|
|
825
817
|
const [processing, setProcessing] = (0, import_react6.useState)(false);
|
|
826
818
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
@@ -866,7 +858,8 @@ function SplitCardFormInner({
|
|
|
866
858
|
title: { ...base.title, ...buttonsStylesOverride.title }
|
|
867
859
|
};
|
|
868
860
|
}, [buttonsTheme, buttonsStylesOverride]);
|
|
869
|
-
const
|
|
861
|
+
const isInlineSessionPatchProcessing = checkout.inlineSessionPatchProcessing ?? false;
|
|
862
|
+
const isSubmitting = (externalProcessing ?? processing) || isInlineSessionPatchProcessing;
|
|
870
863
|
const isSelfContained = !onTokenizedBody;
|
|
871
864
|
const baseUrl = resolvedBillingApiUrl.replace(/\/+$/, "");
|
|
872
865
|
const resolvedAccount = (0, import_react6.useMemo)(() => mergeAccountPatch({
|
|
@@ -925,8 +918,11 @@ function SplitCardFormInner({
|
|
|
925
918
|
if (result === false) {
|
|
926
919
|
return false;
|
|
927
920
|
}
|
|
928
|
-
if (result
|
|
929
|
-
|
|
921
|
+
if (result && typeof result === "object") {
|
|
922
|
+
await checkout.applyInlineSessionPatch?.(result);
|
|
923
|
+
if (result.account) {
|
|
924
|
+
setAccountPatch((prev) => ({ ...prev ?? {}, ...result.account }));
|
|
925
|
+
}
|
|
930
926
|
}
|
|
931
927
|
return true;
|
|
932
928
|
} catch (err) {
|
|
@@ -938,7 +934,7 @@ function SplitCardFormInner({
|
|
|
938
934
|
onError?.(floPayErr);
|
|
939
935
|
return false;
|
|
940
936
|
}
|
|
941
|
-
}, [onBeforeButtonClick, sessionId, updateError, onError]);
|
|
937
|
+
}, [checkout.applyInlineSessionPatch, onBeforeButtonClick, sessionId, updateError, onError]);
|
|
942
938
|
const processPaymentInternal = (0, import_react6.useCallback)(
|
|
943
939
|
async (tokenizedBody) => {
|
|
944
940
|
if (processingRef.current) return;
|
|
@@ -1547,11 +1543,13 @@ function SplitCardFormInner({
|
|
|
1547
1543
|
{
|
|
1548
1544
|
type: "button",
|
|
1549
1545
|
onClick: async () => {
|
|
1546
|
+
if (isSubmitting) return;
|
|
1550
1547
|
const shouldContinue = await runBeforeCardButtonClick();
|
|
1551
1548
|
if (!shouldContinue) return;
|
|
1552
1549
|
onButtonClick?.("card");
|
|
1553
1550
|
expandToCard();
|
|
1554
1551
|
},
|
|
1552
|
+
disabled: isSubmitting,
|
|
1555
1553
|
style: {
|
|
1556
1554
|
width: "100%",
|
|
1557
1555
|
padding: "0.9rem 1rem",
|
|
@@ -1561,7 +1559,7 @@ function SplitCardFormInner({
|
|
|
1561
1559
|
borderRadius: "8px",
|
|
1562
1560
|
fontSize: bStyles.cardButtonFontSize ?? "0.95rem",
|
|
1563
1561
|
fontWeight: 600,
|
|
1564
|
-
cursor: "pointer",
|
|
1562
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
1565
1563
|
display: "flex",
|
|
1566
1564
|
alignItems: "center",
|
|
1567
1565
|
justifyContent: "center",
|
|
@@ -1569,6 +1567,7 @@ function SplitCardFormInner({
|
|
|
1569
1567
|
transition: "border-color 0.2s, box-shadow 0.2s, transform 0.1s",
|
|
1570
1568
|
boxShadow: "0 1px 2px rgba(0,0,0,0.04)",
|
|
1571
1569
|
position: "relative",
|
|
1570
|
+
opacity: isSubmitting ? 0.6 : 1,
|
|
1572
1571
|
...bStyles.cardButton
|
|
1573
1572
|
},
|
|
1574
1573
|
onMouseDown: (e) => {
|
|
@@ -1652,6 +1651,12 @@ function SplitCardFormInner({
|
|
|
1652
1651
|
|
|
1653
1652
|
// src/flopay-checkout.tsx
|
|
1654
1653
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1654
|
+
function hasInlineSessionPatchData(patch) {
|
|
1655
|
+
if (!patch) return false;
|
|
1656
|
+
return Boolean(
|
|
1657
|
+
patch.account && Object.keys(patch.account).length > 0 || patch.couponCodes !== void 0 || patch.tagsData !== void 0 || patch.utmMetadata !== void 0
|
|
1658
|
+
);
|
|
1659
|
+
}
|
|
1655
1660
|
function FloPayCheckout({
|
|
1656
1661
|
sessionId: sessionIdProp,
|
|
1657
1662
|
createSession: createSessionParams,
|
|
@@ -1699,7 +1704,6 @@ function FloPayCheckout({
|
|
|
1699
1704
|
const [createSessionPatch, setCreateSessionPatch] = (0, import_react7.useState)(void 0);
|
|
1700
1705
|
const [createSessionPatchBaseHash, setCreateSessionPatchBaseHash] = (0, import_react7.useState)("");
|
|
1701
1706
|
const [cardBootstrapPending, setCardBootstrapPending] = (0, import_react7.useState)(false);
|
|
1702
|
-
const [deferredCardOpen, setDeferredCardOpen] = (0, import_react7.useState)(false);
|
|
1703
1707
|
const autoCheckoutAttempted = (0, import_react7.useRef)(false);
|
|
1704
1708
|
const onCompleteRef = (0, import_react7.useRef)(onComplete);
|
|
1705
1709
|
onCompleteRef.current = onComplete;
|
|
@@ -1834,9 +1838,6 @@ function FloPayCheckout({
|
|
|
1834
1838
|
);
|
|
1835
1839
|
const inflightRef = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
1836
1840
|
const initializedHashRef = (0, import_react7.useRef)(null);
|
|
1837
|
-
const deferInlineSessionUntilCardClick = Boolean(
|
|
1838
|
-
effectiveCreateSession && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession.checkoutMode ?? checkoutModeProp ?? "full") === "full"
|
|
1839
|
-
);
|
|
1840
1841
|
function hashCreateParams(params) {
|
|
1841
1842
|
const key = JSON.stringify({
|
|
1842
1843
|
c: params?.clientId,
|
|
@@ -1866,7 +1867,6 @@ function FloPayCheckout({
|
|
|
1866
1867
|
setResolvedSessionId(sessionIdProp ?? "");
|
|
1867
1868
|
}, [sessionIdProp]);
|
|
1868
1869
|
async function resolveInlineSession(params, cacheKey) {
|
|
1869
|
-
ensureInlineSessionReady(params);
|
|
1870
1870
|
const api = new import_js.PaymentAPI(resolvedBillingUrl);
|
|
1871
1871
|
let sid = typeof window !== "undefined" ? window.sessionStorage.getItem(cacheKey) : null;
|
|
1872
1872
|
let realResult = null;
|
|
@@ -1911,104 +1911,64 @@ function FloPayCheckout({
|
|
|
1911
1911
|
} finally {
|
|
1912
1912
|
inflightRef.current.delete(cacheKey);
|
|
1913
1913
|
}
|
|
1914
|
-
if (patch) {
|
|
1915
|
-
setCreateSessionPatchBaseHash(baseCreateSessionHash);
|
|
1916
|
-
setCreateSessionPatch((prev) => mergeInlineSessionPatches(prev, patch));
|
|
1917
|
-
}
|
|
1918
|
-
const { sid, result: realResult } = resolved;
|
|
1919
|
-
setUnified(realResult);
|
|
1920
|
-
if (realResult.data.session) {
|
|
1921
|
-
setSession(realResult.data.session);
|
|
1922
|
-
}
|
|
1923
|
-
if (sid) {
|
|
1924
|
-
setResolvedSessionId(sid);
|
|
1925
|
-
}
|
|
1926
|
-
let publishableKey;
|
|
1927
|
-
if (realResult.provider === "stripe") {
|
|
1928
|
-
publishableKey = realResult.data.stripe?.publishableKey;
|
|
1929
|
-
}
|
|
1930
|
-
if (!publishableKey) publishableKey = fallbackPublishableKey;
|
|
1931
|
-
if (!publishableKey) {
|
|
1932
|
-
throw new import_shared6.FloPayError(
|
|
1933
|
-
"No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
|
|
1934
|
-
"validation_error"
|
|
1935
|
-
);
|
|
1936
|
-
}
|
|
1937
|
-
const instance = await (0, import_js.loadFloPay)(publishableKey, {
|
|
1938
|
-
billingApiUrl: resolvedBillingUrl,
|
|
1939
|
-
locale
|
|
1940
|
-
});
|
|
1941
|
-
flopayRef.current = instance;
|
|
1942
|
-
setFloPay(instance);
|
|
1943
1914
|
initializedHashRef.current = cacheKey;
|
|
1944
|
-
|
|
1915
|
+
try {
|
|
1916
|
+
if (patch) {
|
|
1917
|
+
setCreateSessionPatchBaseHash(baseCreateSessionHash);
|
|
1918
|
+
setCreateSessionPatch((prev) => mergeInlineSessionPatches(prev, patch));
|
|
1919
|
+
}
|
|
1920
|
+
const { sid, result: realResult } = resolved;
|
|
1921
|
+
setUnified(realResult);
|
|
1922
|
+
if (realResult.data.session) {
|
|
1923
|
+
setSession(realResult.data.session);
|
|
1924
|
+
}
|
|
1925
|
+
if (sid) {
|
|
1926
|
+
setResolvedSessionId(sid);
|
|
1927
|
+
}
|
|
1928
|
+
let publishableKey;
|
|
1929
|
+
if (realResult.provider === "stripe") {
|
|
1930
|
+
publishableKey = realResult.data.stripe?.publishableKey;
|
|
1931
|
+
}
|
|
1932
|
+
if (!publishableKey) publishableKey = fallbackPublishableKey;
|
|
1933
|
+
if (!publishableKey) {
|
|
1934
|
+
throw new import_shared6.FloPayError(
|
|
1935
|
+
"No publishable key found. Provide fallbackPublishableKey or ensure the session includes gatewayData.publishableKey.",
|
|
1936
|
+
"validation_error"
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
const instance = await (0, import_js.loadFloPay)(publishableKey, {
|
|
1940
|
+
billingApiUrl: resolvedBillingUrl,
|
|
1941
|
+
locale
|
|
1942
|
+
});
|
|
1943
|
+
flopayRef.current = instance;
|
|
1944
|
+
setFloPay(instance);
|
|
1945
|
+
setIsLoading(false);
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
if (initializedHashRef.current === cacheKey) {
|
|
1948
|
+
initializedHashRef.current = null;
|
|
1949
|
+
}
|
|
1950
|
+
throw err;
|
|
1951
|
+
}
|
|
1945
1952
|
return resolved;
|
|
1946
1953
|
},
|
|
1947
1954
|
[fallbackPublishableKey, locale, resolvedBillingUrl]
|
|
1948
1955
|
);
|
|
1949
|
-
const
|
|
1950
|
-
if (cardBootstrapPending) return;
|
|
1951
|
-
setLoadError(null);
|
|
1956
|
+
const handleInlineSessionPatch = (0, import_react7.useCallback)(async (patch) => {
|
|
1957
|
+
if (!hasInlineSessionPatchData(patch) || cardBootstrapPending) return;
|
|
1952
1958
|
setCardBootstrapPending(true);
|
|
1953
1959
|
try {
|
|
1954
|
-
const beforeClickResult = await onBeforeButtonClick?.({
|
|
1955
|
-
method: "card",
|
|
1956
|
-
createSession: effectiveCreateSession
|
|
1957
|
-
});
|
|
1958
|
-
if (beforeClickResult === false) {
|
|
1959
|
-
setDeferredCardOpen(false);
|
|
1960
|
-
return;
|
|
1961
|
-
}
|
|
1962
|
-
const patch = beforeClickResult && typeof beforeClickResult === "object" ? beforeClickResult : void 0;
|
|
1963
|
-
const baseParams = createSessionParamsRef.current;
|
|
1964
|
-
if (!baseParams) {
|
|
1965
|
-
throw new import_shared6.FloPayError(
|
|
1966
|
-
"createSession is required to bootstrap checkout.",
|
|
1967
|
-
"validation_error"
|
|
1968
|
-
);
|
|
1969
|
-
}
|
|
1970
|
-
ensureInlineSessionReady(mergeInlineSessionPatch(baseParams, patch));
|
|
1971
|
-
setDeferredCardOpen(true);
|
|
1972
|
-
onButtonClick?.("card");
|
|
1973
1960
|
await bootstrapInlineSession(patch);
|
|
1974
|
-
} catch (err) {
|
|
1975
|
-
setDeferredCardOpen(false);
|
|
1976
|
-
const floPayErr = err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(
|
|
1977
|
-
err instanceof Error ? err.message : "Failed to start card checkout.",
|
|
1978
|
-
"api_error"
|
|
1979
|
-
);
|
|
1980
|
-
setLoadError(floPayErr);
|
|
1981
|
-
onErrorRef.current?.(floPayErr);
|
|
1982
1961
|
} finally {
|
|
1983
1962
|
setCardBootstrapPending(false);
|
|
1984
1963
|
}
|
|
1985
1964
|
}, [
|
|
1986
1965
|
bootstrapInlineSession,
|
|
1987
|
-
|
|
1988
|
-
cardBootstrapPending,
|
|
1989
|
-
effectiveCreateSession,
|
|
1990
|
-
onButtonClick,
|
|
1991
|
-
onBeforeButtonClick
|
|
1966
|
+
cardBootstrapPending
|
|
1992
1967
|
]);
|
|
1993
1968
|
(0, import_react7.useEffect)(() => {
|
|
1994
1969
|
let cancelled = false;
|
|
1995
1970
|
setLoadError(null);
|
|
1996
1971
|
if (createSessionHash) {
|
|
1997
|
-
if (deferInlineSessionUntilCardClick) {
|
|
1998
|
-
if (initializedHashRef.current !== createSessionHash) {
|
|
1999
|
-
setSession(null);
|
|
2000
|
-
setUnified(null);
|
|
2001
|
-
setFloPay(null);
|
|
2002
|
-
setResolvedSessionId("");
|
|
2003
|
-
setDeferredCardOpen(false);
|
|
2004
|
-
flopayRef.current = null;
|
|
2005
|
-
}
|
|
2006
|
-
setCurrentMode(createSessionParamsRef.current?.checkoutMode ?? checkoutModeProp ?? "full");
|
|
2007
|
-
setIsLoading(false);
|
|
2008
|
-
return () => {
|
|
2009
|
-
cancelled = true;
|
|
2010
|
-
};
|
|
2011
|
-
}
|
|
2012
1972
|
if (initializedHashRef.current === createSessionHash) return;
|
|
2013
1973
|
const params = createSessionParamsRef.current;
|
|
2014
1974
|
setSession(buildSyntheticSession(params, checkoutModeProp));
|
|
@@ -2104,7 +2064,7 @@ function FloPayCheckout({
|
|
|
2104
2064
|
return () => {
|
|
2105
2065
|
cancelled = true;
|
|
2106
2066
|
};
|
|
2107
|
-
}, [resolvedSessionId, createSessionHash, checkoutModeProp,
|
|
2067
|
+
}, [resolvedSessionId, createSessionHash, checkoutModeProp, bootstrapInlineSession]);
|
|
2108
2068
|
const handleConfirmCheckout = (0, import_react7.useCallback)(async () => {
|
|
2109
2069
|
if (confirmProcessing || !session) return;
|
|
2110
2070
|
setConfirmProcessing(true);
|
|
@@ -2147,17 +2107,29 @@ function FloPayCheckout({
|
|
|
2147
2107
|
}
|
|
2148
2108
|
return opts;
|
|
2149
2109
|
}, [unified, session, appearance, resolvedBillingUrl]);
|
|
2110
|
+
const shouldHandleInlineSessionPatch = Boolean(
|
|
2111
|
+
createSessionParams && !children && layout === "buttons" && onBeforeButtonClick && (effectiveCreateSession?.checkoutMode ?? checkoutModeProp ?? "full") === "full"
|
|
2112
|
+
);
|
|
2150
2113
|
const checkoutValue = (0, import_react7.useMemo)(
|
|
2151
2114
|
() => ({
|
|
2152
2115
|
session,
|
|
2153
2116
|
loading: isLoading,
|
|
2154
2117
|
error: loadError,
|
|
2155
|
-
checkoutMode: currentMode
|
|
2118
|
+
checkoutMode: currentMode,
|
|
2119
|
+
applyInlineSessionPatch: shouldHandleInlineSessionPatch ? handleInlineSessionPatch : void 0,
|
|
2120
|
+
inlineSessionPatchProcessing: cardBootstrapPending
|
|
2156
2121
|
}),
|
|
2157
|
-
[
|
|
2122
|
+
[
|
|
2123
|
+
session,
|
|
2124
|
+
isLoading,
|
|
2125
|
+
loadError,
|
|
2126
|
+
currentMode,
|
|
2127
|
+
shouldHandleInlineSessionPatch,
|
|
2128
|
+
handleInlineSessionPatch,
|
|
2129
|
+
cardBootstrapPending
|
|
2130
|
+
]
|
|
2158
2131
|
);
|
|
2159
2132
|
const shouldShowInterimButtons = Boolean(createSessionParams) && layout === "buttons" && (!flopay || !providerOptions);
|
|
2160
|
-
const shouldKeepDeferredInterimVisible = shouldShowInterimButtons && deferInlineSessionUntilCardClick;
|
|
2161
2133
|
if (isLoading) {
|
|
2162
2134
|
if (loadingNode) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: loadingNode });
|
|
2163
2135
|
if (layout === "buttons") {
|
|
@@ -2186,7 +2158,7 @@ function FloPayCheckout({
|
|
|
2186
2158
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })
|
|
2187
2159
|
] });
|
|
2188
2160
|
}
|
|
2189
|
-
if (loadError
|
|
2161
|
+
if (loadError) {
|
|
2190
2162
|
if (errorNode) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: errorNode(loadError) });
|
|
2191
2163
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2192
2164
|
"div",
|
|
@@ -2206,13 +2178,9 @@ function FloPayCheckout({
|
|
|
2206
2178
|
InterimButtonsView,
|
|
2207
2179
|
{
|
|
2208
2180
|
onButtonClick,
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
errorMessage: shouldKeepDeferredInterimVisible ? loadError?.message ?? null : null,
|
|
2213
|
-
showPayPal: deferInlineSessionUntilCardClick ? false : showPayPal,
|
|
2214
|
-
showApplePay: deferInlineSessionUntilCardClick ? false : showApplePay,
|
|
2215
|
-
showGooglePay: deferInlineSessionUntilCardClick ? false : showGooglePay,
|
|
2181
|
+
showPayPal,
|
|
2182
|
+
showApplePay,
|
|
2183
|
+
showGooglePay,
|
|
2216
2184
|
buttonsTheme,
|
|
2217
2185
|
buttonsStyles,
|
|
2218
2186
|
cardButtonContent,
|
|
@@ -2313,7 +2281,6 @@ function FloPayCheckout({
|
|
|
2313
2281
|
enableAVS,
|
|
2314
2282
|
avsLayout,
|
|
2315
2283
|
country: session?.customer?.country,
|
|
2316
|
-
initialCardOpen: deferredCardOpen,
|
|
2317
2284
|
submitLabel,
|
|
2318
2285
|
className
|
|
2319
2286
|
}
|