@b3dotfun/sdk 0.1.68-alpha.4 → 0.1.68-alpha.5
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/dist/cjs/anyspend/react/components/AnySpend.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/AnySpend.js +128 -11
- package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
- package/dist/cjs/anyspend/react/components/checkout/KycGate.d.ts +11 -0
- package/dist/cjs/anyspend/react/components/checkout/KycGate.js +181 -0
- package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/index.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
- package/dist/cjs/anyspend/react/hooks/useKycStatus.d.ts +42 -0
- package/dist/cjs/anyspend/react/hooks/useKycStatus.js +113 -0
- package/dist/cjs/anyspend/services/anyspend.d.ts +3 -1
- package/dist/cjs/anyspend/services/anyspend.js +2 -1
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +1 -1
- package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
- package/dist/cjs/global-account/react/hooks/useAuth.js +1 -1
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +4 -0
- package/dist/cjs/global-account/react/stores/useModalStore.js +2 -0
- package/dist/cjs/global-account/react/utils/createWagmiConfig.d.ts +18 -0
- package/dist/cjs/global-account/react/utils/createWagmiConfig.js +17 -0
- package/dist/esm/anyspend/react/components/AnySpend.d.ts +3 -1
- package/dist/esm/anyspend/react/components/AnySpend.js +129 -12
- package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
- package/dist/esm/anyspend/react/components/checkout/KycGate.d.ts +11 -0
- package/dist/esm/anyspend/react/components/checkout/KycGate.js +145 -0
- package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/index.js +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
- package/dist/esm/anyspend/react/hooks/useKycStatus.d.ts +42 -0
- package/dist/esm/anyspend/react/hooks/useKycStatus.js +107 -0
- package/dist/esm/anyspend/services/anyspend.d.ts +3 -1
- package/dist/esm/anyspend/services/anyspend.js +2 -1
- package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
- package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
- package/dist/esm/global-account/react/hooks/useAuth.js +2 -2
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +4 -0
- package/dist/esm/global-account/react/stores/useModalStore.js +2 -0
- package/dist/esm/global-account/react/utils/createWagmiConfig.d.ts +18 -0
- package/dist/esm/global-account/react/utils/createWagmiConfig.js +16 -0
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpend.d.ts +3 -1
- package/dist/types/anyspend/react/components/checkout/KycGate.d.ts +11 -0
- package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
- package/dist/types/anyspend/react/hooks/useKycStatus.d.ts +42 -0
- package/dist/types/anyspend/services/anyspend.d.ts +3 -1
- package/dist/types/global-account/react/stores/useModalStore.d.ts +4 -0
- package/dist/types/global-account/react/utils/createWagmiConfig.d.ts +18 -0
- package/package.json +2 -1
- package/src/anyspend/react/components/AnySpend.tsx +150 -13
- package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +16 -13
- package/src/anyspend/react/components/checkout/KycGate.tsx +351 -0
- package/src/anyspend/react/hooks/index.ts +1 -0
- package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +10 -0
- package/src/anyspend/react/hooks/useKycStatus.ts +140 -0
- package/src/anyspend/services/anyspend.ts +4 -0
- package/src/global-account/react/components/B3DynamicModal.tsx +0 -2
- package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +7 -7
- package/src/global-account/react/hooks/useAuth.ts +2 -2
- package/src/global-account/react/stores/useModalStore.ts +6 -0
- package/src/global-account/react/utils/createWagmiConfig.tsx +18 -0
|
@@ -18,7 +18,9 @@ export declare enum PanelView {
|
|
|
18
18
|
FIAT_PAYMENT_METHOD = 7,
|
|
19
19
|
POINTS_DETAIL = 8,
|
|
20
20
|
FEE_DETAIL = 9,
|
|
21
|
-
DIRECT_TRANSFER_SUCCESS = 10
|
|
21
|
+
DIRECT_TRANSFER_SUCCESS = 10,
|
|
22
|
+
FIAT_KYC = 11,
|
|
23
|
+
FIAT_AUTH = 12
|
|
22
24
|
}
|
|
23
25
|
export declare function AnySpend(props: {
|
|
24
26
|
mode?: "page" | "modal";
|
|
@@ -37,6 +37,9 @@ const FiatPaymentMethod_1 = require("./common/FiatPaymentMethod");
|
|
|
37
37
|
const GasIndicator_1 = require("./common/GasIndicator");
|
|
38
38
|
const OrderDetails_1 = require("./common/OrderDetails");
|
|
39
39
|
const OrderHistory_1 = require("./common/OrderHistory");
|
|
40
|
+
const KycGate_1 = require("./checkout/KycGate");
|
|
41
|
+
const useKycStatus_1 = require("../hooks/useKycStatus");
|
|
42
|
+
const LoginStep_1 = require("../../../global-account/react/components/SignInWithB3/steps/LoginStep");
|
|
40
43
|
const PanelOnramp_1 = require("./common/PanelOnramp");
|
|
41
44
|
const PanelOnrampPayment_1 = require("./common/PanelOnrampPayment");
|
|
42
45
|
const PointsDetailPanel_1 = require("./common/PointsDetailPanel");
|
|
@@ -57,6 +60,8 @@ var PanelView;
|
|
|
57
60
|
PanelView[PanelView["POINTS_DETAIL"] = 8] = "POINTS_DETAIL";
|
|
58
61
|
PanelView[PanelView["FEE_DETAIL"] = 9] = "FEE_DETAIL";
|
|
59
62
|
PanelView[PanelView["DIRECT_TRANSFER_SUCCESS"] = 10] = "DIRECT_TRANSFER_SUCCESS";
|
|
63
|
+
PanelView[PanelView["FIAT_KYC"] = 11] = "FIAT_KYC";
|
|
64
|
+
PanelView[PanelView["FIAT_AUTH"] = 12] = "FIAT_AUTH";
|
|
60
65
|
})(PanelView || (exports.PanelView = PanelView = {}));
|
|
61
66
|
const ANYSPEND_RECIPIENTS_KEY = "anyspend_recipients";
|
|
62
67
|
function AnySpend(props) {
|
|
@@ -70,6 +75,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
70
75
|
const { partnerId } = (0, react_2.useB3Config)();
|
|
71
76
|
const setB3ModalContentType = (0, react_2.useModalStore)(state => state.setB3ModalContentType);
|
|
72
77
|
const setB3ModalOpen = (0, react_2.useModalStore)(state => state.setB3ModalOpen);
|
|
78
|
+
const { isAuthenticated } = (0, react_2.useAuth)();
|
|
79
|
+
// KYC approval is tracked per-session so we only prompt the wallet
|
|
80
|
+
// signature when the user actually clicks Buy, not on panel mount.
|
|
81
|
+
// useRef so handleFiatOrder can read the updated value synchronously
|
|
82
|
+
// in the same frame that onStatusResolved sets it (setState is async).
|
|
83
|
+
const kycApprovedRef = (0, react_4.useRef)(false);
|
|
84
|
+
// Pre-warm wallet auth headers inside user-gesture context (button click)
|
|
85
|
+
// so the signing prompt fires before we navigate away — browsers block
|
|
86
|
+
// wallet popups triggered from async/non-gesture contexts (React Query queryFn).
|
|
87
|
+
const { getHeaders: getKycHeaders } = (0, useKycStatus_1.useWalletAuthHeaders)();
|
|
73
88
|
// Determine if we're in "buy mode" based on whether destination token props are provided
|
|
74
89
|
const isBuyMode = !!(destinationTokenAddress && destinationTokenChainId);
|
|
75
90
|
// Add refs to track URL state
|
|
@@ -79,7 +94,17 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
79
94
|
const animationDirection = (0, react_4.useRef)(null);
|
|
80
95
|
// Track previous panel for proper back navigation
|
|
81
96
|
const previousPanel = (0, react_4.useRef)(PanelView.MAIN);
|
|
82
|
-
const [activeTab, setActiveTab] = (0, react_4.useState)(
|
|
97
|
+
const [activeTab, setActiveTab] = (0, react_4.useState)(() => {
|
|
98
|
+
if (typeof window !== "undefined") {
|
|
99
|
+
const stored = sessionStorage.getItem("anyspend_active_tab");
|
|
100
|
+
if (stored === "crypto" || stored === "fiat")
|
|
101
|
+
return stored;
|
|
102
|
+
}
|
|
103
|
+
return defaultActiveTab;
|
|
104
|
+
});
|
|
105
|
+
(0, react_4.useEffect)(() => {
|
|
106
|
+
sessionStorage.setItem("anyspend_active_tab", activeTab);
|
|
107
|
+
}, [activeTab]);
|
|
83
108
|
const [orderId, setOrderId] = (0, react_4.useState)(loadOrder);
|
|
84
109
|
const [directTransferTxHash, setDirectTransferTxHash] = (0, react_4.useState)();
|
|
85
110
|
const { orderAndTransactions: oat, getOrderAndTransactionsError } = (0, react_1.useAnyspendOrderAndTransactions)(orderId);
|
|
@@ -100,9 +125,19 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
100
125
|
const [customRecipients, setCustomRecipients] = (0, react_4.useState)([]);
|
|
101
126
|
// Payment method state with dual-state system (auto + explicit user selection)
|
|
102
127
|
const { cryptoPaymentMethod, setCryptoPaymentMethod, selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod, effectiveCryptoPaymentMethod, resetPaymentMethods, } = (0, useCryptoPaymentMethodState_1.useCryptoPaymentMethodState)();
|
|
103
|
-
const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = (0, react_4.useState)(
|
|
128
|
+
const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = (0, react_4.useState)(() => {
|
|
129
|
+
if (typeof window !== "undefined") {
|
|
130
|
+
const stored = sessionStorage.getItem("anyspend_fiat_method");
|
|
131
|
+
if (stored && Object.values(FiatPaymentMethod_1.FiatPaymentMethod).includes(stored))
|
|
132
|
+
return stored;
|
|
133
|
+
}
|
|
134
|
+
return FiatPaymentMethod_1.FiatPaymentMethod.NONE;
|
|
135
|
+
});
|
|
104
136
|
// const [newRecipientAddress, setNewRecipientAddress] = useState("");
|
|
105
137
|
// const recipientInputRef = useRef<HTMLInputElement>(null);
|
|
138
|
+
(0, react_4.useEffect)(() => {
|
|
139
|
+
sessionStorage.setItem("anyspend_fiat_method", selectedFiatPaymentMethod);
|
|
140
|
+
}, [selectedFiatPaymentMethod]);
|
|
106
141
|
// Get initial chain IDs from URL or defaults
|
|
107
142
|
const initialSrcChainId = sourceChainId || parseInt(searchParams.get("fromChainId") || "0") || chains_1.mainnet.id;
|
|
108
143
|
const initialDstChainId = parseInt(searchParams.get("toChainId") || "0") || (isBuyMode ? destinationTokenChainId : chains_1.base.id);
|
|
@@ -119,10 +154,27 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
119
154
|
const [selectedSrcToken, setSelectedSrcToken] = (0, react_4.useState)(effectiveSrcToken);
|
|
120
155
|
const { data: srcTokenMetadata } = (0, react_2.useTokenData)(selectedSrcToken?.chainId, selectedSrcToken?.address);
|
|
121
156
|
const [srcAmount, setSrcAmount] = (0, react_4.useState)(searchParams.get("fromAmount") || "0");
|
|
122
|
-
// State for onramp amount
|
|
123
|
-
const [srcAmountOnRamp, setSrcAmountOnRamp] = (0, react_4.useState)(
|
|
124
|
-
|
|
125
|
-
|
|
157
|
+
// State for onramp amount — persisted in sessionStorage so it survives Persona KYC roundtrip
|
|
158
|
+
const [srcAmountOnRamp, setSrcAmountOnRamp] = (0, react_4.useState)(() => {
|
|
159
|
+
if (typeof window !== "undefined") {
|
|
160
|
+
const stored = sessionStorage.getItem("anyspend_fiat_amount");
|
|
161
|
+
if (stored)
|
|
162
|
+
return stored;
|
|
163
|
+
}
|
|
164
|
+
return searchParams.get("fromAmount") || "5";
|
|
165
|
+
});
|
|
166
|
+
(0, react_4.useEffect)(() => {
|
|
167
|
+
sessionStorage.setItem("anyspend_fiat_amount", srcAmountOnRamp);
|
|
168
|
+
}, [srcAmountOnRamp]);
|
|
169
|
+
// State for destination chain/token selection (sync effects come after state declarations below) — persisted in sessionStorage for Persona KYC roundtrip
|
|
170
|
+
const [selectedDstChainId, setSelectedDstChainId] = (0, react_4.useState)(() => {
|
|
171
|
+
if (!isBuyMode && typeof window !== "undefined") {
|
|
172
|
+
const stored = sessionStorage.getItem("anyspend_dst_chain_id");
|
|
173
|
+
if (stored)
|
|
174
|
+
return parseInt(stored, 10);
|
|
175
|
+
}
|
|
176
|
+
return initialDstChainId;
|
|
177
|
+
});
|
|
126
178
|
// Helper to check if address is Hyperliquid USDC (supports both 34-char and 42-char formats)
|
|
127
179
|
const isHyperliquidUSDCAddress = (address) => (0, anyspend_1.eqci)(address, anyspend_1.HYPERLIQUID_USDC_ADDRESS) || (0, anyspend_1.eqci)(address, anyspend_1.ZERO_ADDRESS);
|
|
128
180
|
const defaultDstToken = isBuyMode
|
|
@@ -142,9 +194,29 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
142
194
|
defaultToken: defaultDstToken,
|
|
143
195
|
prefix: "to",
|
|
144
196
|
});
|
|
145
|
-
const [selectedDstToken, setSelectedDstToken] = (0, react_4.useState)(
|
|
197
|
+
const [selectedDstToken, setSelectedDstToken] = (0, react_4.useState)(() => {
|
|
198
|
+
if (!isBuyMode && typeof window !== "undefined") {
|
|
199
|
+
const stored = sessionStorage.getItem("anyspend_dst_token");
|
|
200
|
+
if (stored) {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.parse(stored);
|
|
203
|
+
}
|
|
204
|
+
catch { }
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return isBuyMode ? defaultDstToken : dstTokenFromUrl;
|
|
208
|
+
});
|
|
146
209
|
const { data: dstTokenMetadata } = (0, react_2.useTokenData)(selectedDstToken?.chainId, selectedDstToken?.address);
|
|
147
210
|
const [dstAmount, setDstAmount] = (0, react_4.useState)(searchParams.get("toAmount") || "");
|
|
211
|
+
// Sync dst chain/token to sessionStorage so they survive Persona KYC roundtrip
|
|
212
|
+
(0, react_4.useEffect)(() => {
|
|
213
|
+
if (!isBuyMode)
|
|
214
|
+
sessionStorage.setItem("anyspend_dst_chain_id", selectedDstChainId.toString());
|
|
215
|
+
}, [selectedDstChainId, isBuyMode]);
|
|
216
|
+
(0, react_4.useEffect)(() => {
|
|
217
|
+
if (!isBuyMode)
|
|
218
|
+
sessionStorage.setItem("anyspend_dst_token", JSON.stringify(selectedDstToken));
|
|
219
|
+
}, [selectedDstToken, isBuyMode]);
|
|
148
220
|
const [isSrcInputDirty, setIsSrcInputDirty] = (0, react_4.useState)(true);
|
|
149
221
|
// Add refs to track if we've applied metadata
|
|
150
222
|
const appliedSrcMetadataRef = (0, react_4.useRef)(false);
|
|
@@ -497,6 +569,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
497
569
|
}, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
|
|
498
570
|
// Call onSuccess when order is executed
|
|
499
571
|
(0, useOnOrderSuccess_1.useOnOrderSuccess)({ orderData: oat, orderId, onSuccess });
|
|
572
|
+
// Clear all persisted selection state once an order is submitted — next open starts fresh
|
|
573
|
+
(0, react_4.useEffect)(() => {
|
|
574
|
+
if (orderId) {
|
|
575
|
+
sessionStorage.removeItem("anyspend_fiat_amount");
|
|
576
|
+
sessionStorage.removeItem("anyspend_active_tab");
|
|
577
|
+
sessionStorage.removeItem("anyspend_fiat_method");
|
|
578
|
+
sessionStorage.removeItem("anyspend_dst_chain_id");
|
|
579
|
+
sessionStorage.removeItem("anyspend_dst_token");
|
|
580
|
+
}
|
|
581
|
+
}, [orderId]);
|
|
500
582
|
const { createOrder, isCreatingOrder } = (0, react_1.useAnyspendCreateOrder)({
|
|
501
583
|
onSuccess: data => {
|
|
502
584
|
const orderId = data.data.id;
|
|
@@ -577,8 +659,8 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
577
659
|
if (selectedFiatPaymentMethod === FiatPaymentMethod_1.FiatPaymentMethod.NONE) {
|
|
578
660
|
return { text: "Select payment method", disable: false, error: false, loading: false };
|
|
579
661
|
}
|
|
580
|
-
// If payment method is selected, show "
|
|
581
|
-
return { text: "
|
|
662
|
+
// If payment method is selected, show "Continue"
|
|
663
|
+
return { text: "Continue", disable: false, error: false, loading: false };
|
|
582
664
|
}
|
|
583
665
|
if (activeTab === "crypto") {
|
|
584
666
|
// For crypto: check payment method first, then recipient
|
|
@@ -599,7 +681,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
599
681
|
return { text: "Continue to payment", disable: false, error: false, loading: false };
|
|
600
682
|
}
|
|
601
683
|
}
|
|
602
|
-
return { text: "
|
|
684
|
+
return { text: "Continue", disable: false, error: false, loading: false };
|
|
603
685
|
}, [
|
|
604
686
|
activeInputAmountInWei,
|
|
605
687
|
isSameChainSameToken,
|
|
@@ -761,7 +843,22 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
761
843
|
paymentMethodString = "";
|
|
762
844
|
}
|
|
763
845
|
else if (paymentMethod === FiatPaymentMethod_1.FiatPaymentMethod.STRIPE_WEB2) {
|
|
764
|
-
// Stripe embedded payment form
|
|
846
|
+
// Stripe embedded payment form requires authentication + KYC
|
|
847
|
+
// Read from store directly to avoid stale closure when called from onLoginSuccess callback
|
|
848
|
+
const currentlyAuthenticated = react_2.useAuthStore.getState().isAuthenticated;
|
|
849
|
+
if (!currentlyAuthenticated) {
|
|
850
|
+
navigateToPanel(PanelView.FIAT_AUTH, "forward");
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (!kycApprovedRef.current) {
|
|
854
|
+
// Pre-sign the KYC auth message NOW (user-gesture context) so the
|
|
855
|
+
// result is cached before useKycStatus fires its queryFn inside the
|
|
856
|
+
// FIAT_KYC panel. Wallets/browsers block signing prompts from async
|
|
857
|
+
// (non-gesture) contexts, which is exactly what React Query uses.
|
|
858
|
+
await getKycHeaders().catch(() => { });
|
|
859
|
+
navigateToPanel(PanelView.FIAT_KYC, "forward");
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
765
862
|
if (!stripeWeb2Support.isSupport) {
|
|
766
863
|
react_2.toast.error("Pay with Card not available");
|
|
767
864
|
return;
|
|
@@ -870,6 +967,14 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
870
967
|
window.removeEventListener("popstate", handlePopState);
|
|
871
968
|
};
|
|
872
969
|
}, [activePanel, navigateBack]);
|
|
970
|
+
// When auth completes while on the FIAT_AUTH panel, navigate back and retry the order
|
|
971
|
+
(0, react_4.useEffect)(() => {
|
|
972
|
+
if (isAuthenticated && activePanel === PanelView.FIAT_AUTH) {
|
|
973
|
+
navigateBack();
|
|
974
|
+
handleFiatOrder(selectedFiatPaymentMethod);
|
|
975
|
+
}
|
|
976
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
977
|
+
}, [isAuthenticated]);
|
|
873
978
|
const historyView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto flex w-[560px] max-w-full flex-col items-center", children: (0, jsx_runtime_1.jsx)(OrderHistory_1.OrderHistory, { mode: mode, onBack: navigateBack, onSelectOrder: onSelectOrder }) }));
|
|
874
979
|
const orderDetailsView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto w-[460px] max-w-full p-5", children: (0, jsx_runtime_1.jsx)("div", { className: "relative flex flex-col gap-4", children: oat && ((0, jsx_runtime_1.jsx)(OrderDetails_1.OrderDetails, { mode: mode, order: oat.data.order, depositTxs: oat.data.depositTxs, relayTxs: oat.data.relayTxs, executeTx: oat.data.executeTx, refundTxs: oat.data.refundTxs, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod, onPaymentMethodChange: method => {
|
|
875
980
|
// When user explicitly changes payment method, set it as selected
|
|
@@ -1004,6 +1109,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
1004
1109
|
setDirectTransferTxHash(undefined);
|
|
1005
1110
|
setB3ModalOpen(false);
|
|
1006
1111
|
}, className: "bg-as-brand hover:bg-as-brand/90 w-full text-white", children: resolvedReturnLabel || "Done" })) })] }));
|
|
1112
|
+
const kycView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto flex w-full max-w-[460px] flex-col gap-4 px-5 pt-5", children: (0, jsx_runtime_1.jsx)(KycGate_1.KycGate, { enabled: activePanel === PanelView.FIAT_KYC, onStatusResolved: approved => {
|
|
1113
|
+
if (approved) {
|
|
1114
|
+
kycApprovedRef.current = true;
|
|
1115
|
+
navigateBack();
|
|
1116
|
+
handleFiatOrder(selectedFiatPaymentMethod);
|
|
1117
|
+
}
|
|
1118
|
+
} }) }));
|
|
1119
|
+
const authView = ((0, jsx_runtime_1.jsx)("div", { className: "mx-auto w-full max-w-[460px]", children: (0, jsx_runtime_1.jsx)(LoginStep_1.LoginStep, { chain: baseChain, onSuccess: async () => {
|
|
1120
|
+
// isAuthenticated will be true at this point — the useEffect below handles navigation
|
|
1121
|
+
} }) }));
|
|
1007
1122
|
// Add tabs to the main component when no order is loaded
|
|
1008
1123
|
return ((0, jsx_runtime_1.jsx)(react_2.StyleRoot, { children: (0, jsx_runtime_1.jsx)("div", { className: classes?.container ||
|
|
1009
1124
|
(0, cn_1.cn)("anyspend-container font-inter mx-auto w-full max-w-[460px]", mode === "page" &&
|
|
@@ -1037,5 +1152,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
1037
1152
|
(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(mode === "page" && "p-6"), children: pointsDetailView }, "points-detail-view"),
|
|
1038
1153
|
(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(mode === "page" && "p-6"), children: feeDetailView }, "fee-detail-view"),
|
|
1039
1154
|
(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(mode === "page" && "p-6"), children: directTransferSuccessView }, "direct-transfer-success-view"),
|
|
1155
|
+
(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(mode === "page" && "p-6"), children: kycView }, "fiat-kyc-view"),
|
|
1156
|
+
(0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)(mode === "page" && "p-6"), children: authView }, "fiat-auth-view"),
|
|
1040
1157
|
] }) }) }));
|
|
1041
1158
|
}
|
|
@@ -13,6 +13,7 @@ const react_stripe_js_1 = require("@stripe/react-stripe-js");
|
|
|
13
13
|
const lucide_react_1 = require("lucide-react");
|
|
14
14
|
const react_3 = require("motion/react");
|
|
15
15
|
const react_4 = require("react");
|
|
16
|
+
const KycGate_1 = require("./KycGate");
|
|
16
17
|
function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }) {
|
|
17
18
|
// Stable refs for callback props to avoid re-triggering effects
|
|
18
19
|
const onErrorRef = (0, react_4.useRef)(onError);
|
|
@@ -51,18 +52,12 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
|
|
|
51
52
|
const raw = (0, number_1.formatUnits)(anyspendQuote.data.currencyIn.amount, constants_1.USDC_BASE.decimals);
|
|
52
53
|
return parseFloat(raw).toFixed(2);
|
|
53
54
|
}, [isStablecoin, formattedAmount, anyspendQuote]);
|
|
54
|
-
// Debug: log computed values for Stripe flow diagnostics
|
|
55
|
-
(0, react_4.useEffect)(() => {
|
|
56
|
-
console.log("@@fiat-checkout:debug", {
|
|
57
|
-
totalAmount,
|
|
58
|
-
formattedAmount,
|
|
59
|
-
isStablecoin,
|
|
60
|
-
isLoadingAnyspendQuote,
|
|
61
|
-
quoteAmount: anyspendQuote?.data?.currencyIn?.amount,
|
|
62
|
-
usdAmount,
|
|
63
|
-
});
|
|
64
|
-
}, [totalAmount, formattedAmount, isStablecoin, isLoadingAnyspendQuote, anyspendQuote, usdAmount]);
|
|
65
55
|
const { geoData, stripeOnrampSupport, stripeWeb2Support, isLoading: isLoadingGeo, } = (0, react_1.useGeoOnrampOptions)(usdAmount || "0");
|
|
56
|
+
// KYC state
|
|
57
|
+
const [kycApproved, setKycApproved] = (0, react_4.useState)(false);
|
|
58
|
+
const handleKycResolved = (0, react_4.useCallback)((approved) => {
|
|
59
|
+
setKycApproved(approved);
|
|
60
|
+
}, []);
|
|
66
61
|
// Order state
|
|
67
62
|
const [orderId, setOrderId] = (0, react_4.useState)(null);
|
|
68
63
|
const [stripePaymentIntentId, setStripePaymentIntentId] = (0, react_4.useState)(null);
|
|
@@ -85,13 +80,14 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
|
|
|
85
80
|
onErrorRef.current?.(error);
|
|
86
81
|
},
|
|
87
82
|
});
|
|
88
|
-
// Auto-create onramp order when Stripe Web2 is supported and all data is ready
|
|
83
|
+
// Auto-create onramp order when Stripe Web2 is supported, KYC approved, and all data is ready
|
|
89
84
|
(0, react_4.useEffect)(() => {
|
|
90
85
|
if (!isLoadingGeo &&
|
|
91
86
|
(!isStablecoin ? !isLoadingAnyspendQuote : true) &&
|
|
92
87
|
usdAmount &&
|
|
93
88
|
parseFloat(usdAmount) > 0 &&
|
|
94
89
|
stripeWeb2Support?.isSupport &&
|
|
90
|
+
kycApproved &&
|
|
95
91
|
!orderCreatedRef.current &&
|
|
96
92
|
!orderId &&
|
|
97
93
|
!isCreatingOrder &&
|
|
@@ -132,6 +128,7 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
|
|
|
132
128
|
isLoadingAnyspendQuote,
|
|
133
129
|
usdAmount,
|
|
134
130
|
stripeWeb2Support,
|
|
131
|
+
kycApproved,
|
|
135
132
|
orderId,
|
|
136
133
|
isCreatingOrder,
|
|
137
134
|
orderError,
|
|
@@ -155,6 +152,10 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
|
|
|
155
152
|
if (!hasStripeWeb2 && !hasStripeRedirect) {
|
|
156
153
|
return ((0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-fiat-unavailable py-4 text-center", classes?.fiatPanel), children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Card payments are not available in your region for this amount." }) }));
|
|
157
154
|
}
|
|
155
|
+
// KYC gate — shown before order creation when verification is needed
|
|
156
|
+
if (!kycApproved) {
|
|
157
|
+
return (0, jsx_runtime_1.jsx)(KycGate_1.KycGate, { themeColor: themeColor, classes: classes, enabled: true, onStatusResolved: handleKycResolved });
|
|
158
|
+
}
|
|
158
159
|
// Order creation error - show with retry
|
|
159
160
|
if (orderError) {
|
|
160
161
|
return ((0, jsx_runtime_1.jsxs)(react_3.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-fiat-error flex flex-col items-center gap-3 py-4", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: orderError }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
|
|
2
|
+
interface KycGateProps {
|
|
3
|
+
themeColor?: string;
|
|
4
|
+
classes?: AnySpendCheckoutClasses;
|
|
5
|
+
/** Only fetch KYC status (and prompt wallet signature) when true. */
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
/** Called when KYC status is resolved (approved or not required) */
|
|
8
|
+
onStatusResolved: (approved: boolean) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function KycGate({ themeColor, classes, enabled, onStatusResolved }: KycGateProps): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.KycGate = KycGate;
|
|
38
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
|
+
const cn_1 = require("../../../../shared/utils/cn");
|
|
40
|
+
const react_1 = require("../../../../global-account/react");
|
|
41
|
+
const b3Chain_1 = require("../../../../shared/constants/chains/b3Chain");
|
|
42
|
+
const lucide_react_1 = require("lucide-react");
|
|
43
|
+
const react_2 = require("motion/react");
|
|
44
|
+
const react_3 = require("react");
|
|
45
|
+
const useKycStatus_1 = require("../../hooks/useKycStatus");
|
|
46
|
+
function KycGate({ themeColor, classes, enabled = false, onStatusResolved }) {
|
|
47
|
+
const { isAuthenticated, isAuthenticating } = (0, react_1.useAuth)();
|
|
48
|
+
const { kycStatus, isLoadingKycStatus, refetchKycStatus } = (0, useKycStatus_1.useKycStatus)(enabled);
|
|
49
|
+
const { createInquiry, isCreatingInquiry } = (0, useKycStatus_1.useCreateKycInquiry)();
|
|
50
|
+
const { verifyKyc, isVerifying } = (0, useKycStatus_1.useVerifyKyc)();
|
|
51
|
+
const setB3ModalOpen = (0, react_1.useModalStore)(state => state.setB3ModalOpen);
|
|
52
|
+
const setB3ModalContentType = (0, react_1.useModalStore)(state => state.setB3ModalContentType);
|
|
53
|
+
const { partnerId } = (0, react_1.useB3Config)();
|
|
54
|
+
const [personaOpen, setPersonaOpen] = (0, react_3.useState)(false);
|
|
55
|
+
const [personaError, setPersonaError] = (0, react_3.useState)(null);
|
|
56
|
+
const [personaCancelled, setPersonaCancelled] = (0, react_3.useState)(false);
|
|
57
|
+
const prevStatusRef = (0, react_3.useRef)(null);
|
|
58
|
+
// Notify parent when status resolves
|
|
59
|
+
(0, react_3.useEffect)(() => {
|
|
60
|
+
if (!kycStatus)
|
|
61
|
+
return;
|
|
62
|
+
const currentStatus = kycStatus.status;
|
|
63
|
+
if (currentStatus === prevStatusRef.current)
|
|
64
|
+
return;
|
|
65
|
+
prevStatusRef.current = currentStatus;
|
|
66
|
+
if (!kycStatus.kycRequired || currentStatus === "approved") {
|
|
67
|
+
onStatusResolved(true);
|
|
68
|
+
}
|
|
69
|
+
}, [kycStatus, onStatusResolved]);
|
|
70
|
+
const openPersonaFlow = (0, react_3.useCallback)(async (config) => {
|
|
71
|
+
setPersonaOpen(true);
|
|
72
|
+
try {
|
|
73
|
+
// Dynamic import to keep bundle small
|
|
74
|
+
const { Client } = await Promise.resolve().then(() => __importStar(require("persona")));
|
|
75
|
+
const client = new Client({
|
|
76
|
+
inquiryId: config.inquiryId,
|
|
77
|
+
sessionToken: config.sessionToken,
|
|
78
|
+
environment: config.environment === "production" ? "production" : "sandbox",
|
|
79
|
+
onComplete: async ({ inquiryId }) => {
|
|
80
|
+
// Reopen the modal first so the user lands back in the checkout flow
|
|
81
|
+
setB3ModalOpen(true);
|
|
82
|
+
setPersonaOpen(false);
|
|
83
|
+
if (inquiryId) {
|
|
84
|
+
try {
|
|
85
|
+
const result = await verifyKyc(inquiryId);
|
|
86
|
+
if (result.status === "approved") {
|
|
87
|
+
onStatusResolved(true);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
setPersonaError(err instanceof Error ? err.message : "Verification check failed — please retry.");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
refetchKycStatus();
|
|
95
|
+
},
|
|
96
|
+
onCancel: () => {
|
|
97
|
+
// Reopen the modal so the user can retry or cancel the purchase
|
|
98
|
+
setB3ModalOpen(true);
|
|
99
|
+
setPersonaOpen(false);
|
|
100
|
+
setPersonaCancelled(true);
|
|
101
|
+
},
|
|
102
|
+
onError: error => {
|
|
103
|
+
// Reopen the modal so the user sees the error and can retry
|
|
104
|
+
setB3ModalOpen(true);
|
|
105
|
+
setPersonaOpen(false);
|
|
106
|
+
setPersonaError(error?.message || "Verification encountered an error");
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// Close the modal before opening Persona so its overlay is fully
|
|
110
|
+
// interactive — no Radix Dialog backdrop or z-index conflicts.
|
|
111
|
+
// The modal's contentType is preserved in Zustand and restored on reopen.
|
|
112
|
+
setB3ModalOpen(false);
|
|
113
|
+
client.open();
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
setPersonaOpen(false);
|
|
117
|
+
setB3ModalOpen(true);
|
|
118
|
+
setPersonaError("Failed to load verification module");
|
|
119
|
+
}
|
|
120
|
+
}, [verifyKyc, onStatusResolved, refetchKycStatus, setB3ModalOpen]);
|
|
121
|
+
const handleStartVerification = (0, react_3.useCallback)(async () => {
|
|
122
|
+
setPersonaError(null);
|
|
123
|
+
setPersonaCancelled(false);
|
|
124
|
+
try {
|
|
125
|
+
const { inquiryId, sessionToken } = await createInquiry();
|
|
126
|
+
openPersonaFlow({
|
|
127
|
+
inquiryId,
|
|
128
|
+
sessionToken,
|
|
129
|
+
environment: kycStatus?.config?.environment,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
setPersonaError(error instanceof Error ? error.message : "Failed to start verification");
|
|
134
|
+
}
|
|
135
|
+
}, [createInquiry, kycStatus, openPersonaFlow]);
|
|
136
|
+
const handleSignIn = (0, react_3.useCallback)(() => {
|
|
137
|
+
setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: b3Chain_1.thirdwebB3Chain, partnerId });
|
|
138
|
+
setB3ModalOpen(true);
|
|
139
|
+
}, [setB3ModalContentType, setB3ModalOpen, partnerId]);
|
|
140
|
+
const handleResumeVerification = (0, react_3.useCallback)(() => {
|
|
141
|
+
if (!kycStatus?.inquiry)
|
|
142
|
+
return;
|
|
143
|
+
setPersonaError(null);
|
|
144
|
+
setPersonaCancelled(false);
|
|
145
|
+
openPersonaFlow({
|
|
146
|
+
inquiryId: kycStatus.inquiry.inquiryId,
|
|
147
|
+
sessionToken: kycStatus.inquiry.sessionToken,
|
|
148
|
+
environment: kycStatus.config?.environment,
|
|
149
|
+
});
|
|
150
|
+
}, [kycStatus, openPersonaFlow]);
|
|
151
|
+
// Auth loading state
|
|
152
|
+
if (isAuthenticating) {
|
|
153
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-loading flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), (0, jsx_runtime_1.jsx)(react_1.TextShimmer, { duration: 1.5, className: "text-sm", children: "Checking authentication..." })] }));
|
|
154
|
+
}
|
|
155
|
+
// Not authenticated — prompt to login
|
|
156
|
+
if (!isAuthenticated) {
|
|
157
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-auth flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-8 w-8 text-gray-400" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Login required to pay with card" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: "Sign in to your B3 account to complete identity verification." })] }), (0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleSignIn, children: (0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-4 w-4" }), "Sign In"] }) })] }));
|
|
158
|
+
}
|
|
159
|
+
// Loading KYC status state
|
|
160
|
+
if (isLoadingKycStatus) {
|
|
161
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-loading flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), (0, jsx_runtime_1.jsx)(react_1.TextShimmer, { duration: 1.5, className: "text-sm", children: "Checking verification status..." })] }));
|
|
162
|
+
}
|
|
163
|
+
// Not required or already approved — render nothing
|
|
164
|
+
if (!kycStatus?.kycRequired || kycStatus.status === "approved") {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
// Persona flow is open - show loading
|
|
168
|
+
if (personaOpen) {
|
|
169
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-persona flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), (0, jsx_runtime_1.jsx)(react_1.TextShimmer, { duration: 1.5, className: "text-sm", children: "Identity verification in progress..." }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-gray-400 dark:text-gray-500", children: "Complete the verification in the popup window" })] }));
|
|
170
|
+
}
|
|
171
|
+
// Needs review or completed (submitted, awaiting Persona decision)
|
|
172
|
+
if (kycStatus.status === "needs_review" || kycStatus.status === "completed") {
|
|
173
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-review flex flex-col items-center gap-3 py-2", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Clock, { className: "h-8 w-8 text-amber-500" }), (0, jsx_runtime_1.jsx)("p", { className: "text-center text-sm font-medium text-amber-700 dark:text-amber-300", children: "Your verification is under review" }), (0, jsx_runtime_1.jsx)("p", { className: "text-center text-xs text-amber-600 dark:text-amber-400", children: "This usually takes a few minutes. Please check back shortly." }), (0, jsx_runtime_1.jsx)("button", { onClick: () => refetchKycStatus(), className: "mt-1 text-sm font-medium text-amber-700 underline hover:text-amber-800 dark:text-amber-300", children: "Check status" })] }));
|
|
174
|
+
}
|
|
175
|
+
// Pending (started before) - offer resume
|
|
176
|
+
if (kycStatus.status === "pending" && kycStatus.inquiry) {
|
|
177
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-resume flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-8 w-8 text-blue-500" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Continue verification" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: "You have an incomplete verification. Resume to continue." })] }), (0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleResumeVerification, disabled: isCreatingInquiry, children: (0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [isCreatingInquiry ? (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-4 w-4" }), "Resume Verification"] }) })] }));
|
|
178
|
+
}
|
|
179
|
+
// Not verified / declined / expired - show verification prompt
|
|
180
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-kyc-prompt flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-8 w-8 text-blue-500" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Identity verification required" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: "Card payments require a one-time identity check. This takes about 2 minutes." })] }), (0, jsx_runtime_1.jsxs)(react_2.AnimatePresence, { initial: false, children: [personaError && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, className: "w-full rounded-lg border border-red-200 bg-red-50 px-3 py-2 dark:border-red-800 dark:bg-red-900/20", children: (0, jsx_runtime_1.jsx)("p", { className: "text-center text-sm text-red-600 dark:text-red-400", children: personaError }) }, "kyc-error")), personaCancelled && ((0, jsx_runtime_1.jsx)(react_2.motion.div, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, className: "w-full rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 dark:border-amber-800 dark:bg-amber-900/20", children: (0, jsx_runtime_1.jsx)("p", { className: "text-center text-sm text-amber-600 dark:text-amber-400", children: "Verification cancelled. Click below to try again." }) }, "kyc-cancelled"))] }), kycStatus.status === "declined" && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5 rounded-lg border border-red-200 bg-red-50 px-3 py-2 dark:border-red-800 dark:bg-red-900/20", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.AlertTriangle, { className: "h-3.5 w-3.5 text-red-500" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-red-600 dark:text-red-400", children: "Previous verification was declined. You may try again." })] })), (0, jsx_runtime_1.jsx)(react_1.ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleStartVerification, disabled: isCreatingInquiry || isVerifying, children: (0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [isCreatingInquiry ? (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.ShieldCheck, { className: "h-4 w-4" }), isCreatingInquiry ? "Starting..." : "Verify Identity"] }) })] }));
|
|
181
|
+
}
|
|
@@ -34,3 +34,4 @@ __exportStar(require("./useSigMint"), exports);
|
|
|
34
34
|
__exportStar(require("./useStripeClientSecret"), exports);
|
|
35
35
|
__exportStar(require("./useStripeSupport"), exports);
|
|
36
36
|
__exportStar(require("./useWatchTransfer"), exports);
|
|
37
|
+
__exportStar(require("./useKycStatus"), exports);
|
|
@@ -10,6 +10,7 @@ const react_query_1 = require("@tanstack/react-query");
|
|
|
10
10
|
const react_2 = require("react");
|
|
11
11
|
const viem_1 = require("viem");
|
|
12
12
|
const chains_1 = require("viem/chains");
|
|
13
|
+
const useKycStatus_1 = require("./useKycStatus");
|
|
13
14
|
const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId");
|
|
14
15
|
/**
|
|
15
16
|
* Hook for creating onramp orders in the Anyspend protocol
|
|
@@ -18,6 +19,7 @@ const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId
|
|
|
18
19
|
function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
19
20
|
// Get B3 context values
|
|
20
21
|
const { partnerId } = (0, react_1.useB3Config)();
|
|
22
|
+
const { getHeaders: getWalletAuthHeaders } = (0, useKycStatus_1.useWalletAuthHeaders)();
|
|
21
23
|
// Get validated client reference ID from B3 context
|
|
22
24
|
const createValidatedClientReferenceId = (0, useValidatedClientReferenceId_1.useValidatedClientReferenceId)();
|
|
23
25
|
// Get fingerprint data
|
|
@@ -39,6 +41,12 @@ function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
|
39
41
|
const srcChain = chains_1.base.id;
|
|
40
42
|
// Create order with USDC on Base as source
|
|
41
43
|
const srcAmountOnRampInWei = (0, viem_1.parseUnits)(srcFiatAmount, constants_1.USDC_BASE.decimals);
|
|
44
|
+
// For card payments, include wallet auth headers so the backend can verify
|
|
45
|
+
// KYC by the signing wallet address (may differ from the B3 JWT address).
|
|
46
|
+
let kycWalletHeaders;
|
|
47
|
+
if (onramp.vendor === "stripe-web2") {
|
|
48
|
+
kycWalletHeaders = await getWalletAuthHeaders().catch(() => undefined);
|
|
49
|
+
}
|
|
42
50
|
return await anyspend_1.anyspendService.createOrder({
|
|
43
51
|
recipientAddress: (0, utils_1.normalizeAddress)(recipientAddress),
|
|
44
52
|
type: orderType,
|
|
@@ -76,6 +84,7 @@ function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
|
76
84
|
visitorData,
|
|
77
85
|
callbackMetadata: params.callbackMetadata,
|
|
78
86
|
feeOnTop: params.feeOnTop,
|
|
87
|
+
kycWalletHeaders,
|
|
79
88
|
});
|
|
80
89
|
}
|
|
81
90
|
catch (error) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface KycStatusResponse {
|
|
2
|
+
kycRequired: boolean;
|
|
3
|
+
status: "not_verified" | "pending" | "completed" | "approved" | "declined" | "needs_review" | "expired";
|
|
4
|
+
inquiry?: {
|
|
5
|
+
inquiryId: string;
|
|
6
|
+
sessionToken: string;
|
|
7
|
+
};
|
|
8
|
+
config?: {
|
|
9
|
+
templateId: string;
|
|
10
|
+
environment: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface KycInquiryResponse {
|
|
14
|
+
inquiryId: string;
|
|
15
|
+
sessionToken: string;
|
|
16
|
+
}
|
|
17
|
+
interface KycVerifyResponse {
|
|
18
|
+
status: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns a function that builds the wallet-signature auth headers.
|
|
22
|
+
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
23
|
+
*/
|
|
24
|
+
export declare function useWalletAuthHeaders(): {
|
|
25
|
+
address: `0x${string}` | undefined;
|
|
26
|
+
getHeaders: () => Promise<Record<string, string>>;
|
|
27
|
+
};
|
|
28
|
+
export declare function useKycStatus(enabled?: boolean): {
|
|
29
|
+
kycStatus: KycStatusResponse | null;
|
|
30
|
+
isLoadingKycStatus: boolean;
|
|
31
|
+
kycStatusError: Error | null;
|
|
32
|
+
refetchKycStatus: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<KycStatusResponse, Error>>;
|
|
33
|
+
};
|
|
34
|
+
export declare function useCreateKycInquiry(): {
|
|
35
|
+
createInquiry: import("@tanstack/react-query").UseMutateAsyncFunction<KycInquiryResponse, Error, void, unknown>;
|
|
36
|
+
isCreatingInquiry: boolean;
|
|
37
|
+
};
|
|
38
|
+
export declare function useVerifyKyc(): {
|
|
39
|
+
verifyKyc: import("@tanstack/react-query").UseMutateAsyncFunction<KycVerifyResponse, Error, string, unknown>;
|
|
40
|
+
isVerifying: boolean;
|
|
41
|
+
};
|
|
42
|
+
export {};
|