@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.
Files changed (59) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +3 -1
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +128 -11
  3. package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
  4. package/dist/cjs/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  5. package/dist/cjs/anyspend/react/components/checkout/KycGate.js +181 -0
  6. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  7. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  8. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
  9. package/dist/cjs/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  10. package/dist/cjs/anyspend/react/hooks/useKycStatus.js +113 -0
  11. package/dist/cjs/anyspend/services/anyspend.d.ts +3 -1
  12. package/dist/cjs/anyspend/services/anyspend.js +2 -1
  13. package/dist/cjs/global-account/react/components/B3DynamicModal.js +1 -1
  14. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
  15. package/dist/cjs/global-account/react/hooks/useAuth.js +1 -1
  16. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +4 -0
  17. package/dist/cjs/global-account/react/stores/useModalStore.js +2 -0
  18. package/dist/cjs/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  19. package/dist/cjs/global-account/react/utils/createWagmiConfig.js +17 -0
  20. package/dist/esm/anyspend/react/components/AnySpend.d.ts +3 -1
  21. package/dist/esm/anyspend/react/components/AnySpend.js +129 -12
  22. package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
  23. package/dist/esm/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  24. package/dist/esm/anyspend/react/components/checkout/KycGate.js +145 -0
  25. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  26. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  27. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
  28. package/dist/esm/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  29. package/dist/esm/anyspend/react/hooks/useKycStatus.js +107 -0
  30. package/dist/esm/anyspend/services/anyspend.d.ts +3 -1
  31. package/dist/esm/anyspend/services/anyspend.js +2 -1
  32. package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
  33. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
  34. package/dist/esm/global-account/react/hooks/useAuth.js +2 -2
  35. package/dist/esm/global-account/react/stores/useModalStore.d.ts +4 -0
  36. package/dist/esm/global-account/react/stores/useModalStore.js +2 -0
  37. package/dist/esm/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  38. package/dist/esm/global-account/react/utils/createWagmiConfig.js +16 -0
  39. package/dist/styles/index.css +1 -1
  40. package/dist/types/anyspend/react/components/AnySpend.d.ts +3 -1
  41. package/dist/types/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  42. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  43. package/dist/types/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  44. package/dist/types/anyspend/services/anyspend.d.ts +3 -1
  45. package/dist/types/global-account/react/stores/useModalStore.d.ts +4 -0
  46. package/dist/types/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  47. package/package.json +2 -1
  48. package/src/anyspend/react/components/AnySpend.tsx +150 -13
  49. package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +16 -13
  50. package/src/anyspend/react/components/checkout/KycGate.tsx +351 -0
  51. package/src/anyspend/react/hooks/index.ts +1 -0
  52. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +10 -0
  53. package/src/anyspend/react/hooks/useKycStatus.ts +140 -0
  54. package/src/anyspend/services/anyspend.ts +4 -0
  55. package/src/global-account/react/components/B3DynamicModal.tsx +0 -2
  56. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +7 -7
  57. package/src/global-account/react/hooks/useAuth.ts +2 -2
  58. package/src/global-account/react/stores/useModalStore.ts +6 -0
  59. 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)(defaultActiveTab);
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)(FiatPaymentMethod_1.FiatPaymentMethod.NONE);
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)(searchParams.get("fromAmount") || "5");
124
- // State for destination chain/token selection
125
- const [selectedDstChainId, setSelectedDstChainId] = (0, react_4.useState)(initialDstChainId);
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)(isBuyMode ? defaultDstToken : dstTokenFromUrl);
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 "Buy"
581
- return { text: "Buy", disable: false, error: false, loading: false };
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: "Buy", disable: false, error: false, loading: false };
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
+ }
@@ -18,3 +18,4 @@ export * from "./useSigMint";
18
18
  export * from "./useStripeClientSecret";
19
19
  export * from "./useStripeSupport";
20
20
  export * from "./useWatchTransfer";
21
+ export * from "./useKycStatus";
@@ -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 {};