@b3dotfun/sdk 0.1.68-alpha.3 → 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 (167) hide show
  1. package/dist/cjs/anyspend/platform/client.d.ts +35 -0
  2. package/dist/cjs/anyspend/platform/client.js +158 -0
  3. package/dist/cjs/anyspend/platform/errors.d.ts +38 -0
  4. package/dist/cjs/anyspend/platform/errors.js +77 -0
  5. package/dist/cjs/anyspend/platform/index.d.ts +87 -0
  6. package/dist/cjs/anyspend/platform/index.js +85 -0
  7. package/dist/cjs/anyspend/platform/resources/analytics.d.ts +7 -0
  8. package/dist/cjs/anyspend/platform/resources/analytics.js +12 -0
  9. package/dist/cjs/anyspend/platform/resources/checkout-sessions.d.ts +17 -0
  10. package/dist/cjs/anyspend/platform/resources/checkout-sessions.js +27 -0
  11. package/dist/cjs/anyspend/platform/resources/customers.d.ts +19 -0
  12. package/dist/cjs/anyspend/platform/resources/customers.js +34 -0
  13. package/dist/cjs/anyspend/platform/resources/discount-codes.d.ts +29 -0
  14. package/dist/cjs/anyspend/platform/resources/discount-codes.js +31 -0
  15. package/dist/cjs/anyspend/platform/resources/events.d.ts +14 -0
  16. package/dist/cjs/anyspend/platform/resources/events.js +16 -0
  17. package/dist/cjs/anyspend/platform/resources/notifications.d.ts +18 -0
  18. package/dist/cjs/anyspend/platform/resources/notifications.js +27 -0
  19. package/dist/cjs/anyspend/platform/resources/organization.d.ts +17 -0
  20. package/dist/cjs/anyspend/platform/resources/organization.js +15 -0
  21. package/dist/cjs/anyspend/platform/resources/payment-links.d.ts +21 -0
  22. package/dist/cjs/anyspend/platform/resources/payment-links.js +49 -0
  23. package/dist/cjs/anyspend/platform/resources/products.d.ts +27 -0
  24. package/dist/cjs/anyspend/platform/resources/products.js +31 -0
  25. package/dist/cjs/anyspend/platform/resources/transactions.d.ts +11 -0
  26. package/dist/cjs/anyspend/platform/resources/transactions.js +25 -0
  27. package/dist/cjs/anyspend/platform/resources/webhooks.d.ts +14 -0
  28. package/dist/cjs/anyspend/platform/resources/webhooks.js +33 -0
  29. package/dist/cjs/anyspend/platform/resources/widgets.d.ts +38 -0
  30. package/dist/cjs/anyspend/platform/resources/widgets.js +31 -0
  31. package/dist/cjs/anyspend/platform/types.d.ts +478 -0
  32. package/dist/cjs/anyspend/platform/types.js +5 -0
  33. package/dist/cjs/anyspend/platform/utils/idempotency.d.ts +4 -0
  34. package/dist/cjs/anyspend/platform/utils/idempotency.js +17 -0
  35. package/dist/cjs/anyspend/platform/utils/pagination.d.ts +12 -0
  36. package/dist/cjs/anyspend/platform/utils/pagination.js +22 -0
  37. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +3 -1
  38. package/dist/cjs/anyspend/react/components/AnySpend.js +128 -11
  39. package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
  40. package/dist/cjs/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  41. package/dist/cjs/anyspend/react/components/checkout/KycGate.js +181 -0
  42. package/dist/cjs/anyspend/react/hooks/index.d.ts +1 -0
  43. package/dist/cjs/anyspend/react/hooks/index.js +1 -0
  44. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
  45. package/dist/cjs/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  46. package/dist/cjs/anyspend/react/hooks/useKycStatus.js +113 -0
  47. package/dist/cjs/anyspend/services/anyspend.d.ts +3 -1
  48. package/dist/cjs/anyspend/services/anyspend.js +2 -1
  49. package/dist/cjs/global-account/react/components/B3DynamicModal.js +1 -1
  50. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
  51. package/dist/cjs/global-account/react/hooks/useAuth.js +1 -1
  52. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +4 -0
  53. package/dist/cjs/global-account/react/stores/useModalStore.js +2 -0
  54. package/dist/cjs/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  55. package/dist/cjs/global-account/react/utils/createWagmiConfig.js +17 -0
  56. package/dist/esm/anyspend/platform/client.d.ts +35 -0
  57. package/dist/esm/anyspend/platform/client.js +153 -0
  58. package/dist/esm/anyspend/platform/errors.d.ts +38 -0
  59. package/dist/esm/anyspend/platform/errors.js +67 -0
  60. package/dist/esm/anyspend/platform/index.d.ts +87 -0
  61. package/dist/esm/anyspend/platform/index.js +75 -0
  62. package/dist/esm/anyspend/platform/resources/analytics.d.ts +7 -0
  63. package/dist/esm/anyspend/platform/resources/analytics.js +8 -0
  64. package/dist/esm/anyspend/platform/resources/checkout-sessions.d.ts +17 -0
  65. package/dist/esm/anyspend/platform/resources/checkout-sessions.js +23 -0
  66. package/dist/esm/anyspend/platform/resources/customers.d.ts +19 -0
  67. package/dist/esm/anyspend/platform/resources/customers.js +30 -0
  68. package/dist/esm/anyspend/platform/resources/discount-codes.d.ts +29 -0
  69. package/dist/esm/anyspend/platform/resources/discount-codes.js +27 -0
  70. package/dist/esm/anyspend/platform/resources/events.d.ts +14 -0
  71. package/dist/esm/anyspend/platform/resources/events.js +12 -0
  72. package/dist/esm/anyspend/platform/resources/notifications.d.ts +18 -0
  73. package/dist/esm/anyspend/platform/resources/notifications.js +23 -0
  74. package/dist/esm/anyspend/platform/resources/organization.d.ts +17 -0
  75. package/dist/esm/anyspend/platform/resources/organization.js +11 -0
  76. package/dist/esm/anyspend/platform/resources/payment-links.d.ts +21 -0
  77. package/dist/esm/anyspend/platform/resources/payment-links.js +45 -0
  78. package/dist/esm/anyspend/platform/resources/products.d.ts +27 -0
  79. package/dist/esm/anyspend/platform/resources/products.js +27 -0
  80. package/dist/esm/anyspend/platform/resources/transactions.d.ts +11 -0
  81. package/dist/esm/anyspend/platform/resources/transactions.js +21 -0
  82. package/dist/esm/anyspend/platform/resources/webhooks.d.ts +14 -0
  83. package/dist/esm/anyspend/platform/resources/webhooks.js +29 -0
  84. package/dist/esm/anyspend/platform/resources/widgets.d.ts +38 -0
  85. package/dist/esm/anyspend/platform/resources/widgets.js +27 -0
  86. package/dist/esm/anyspend/platform/types.d.ts +478 -0
  87. package/dist/esm/anyspend/platform/types.js +4 -0
  88. package/dist/esm/anyspend/platform/utils/idempotency.d.ts +4 -0
  89. package/dist/esm/anyspend/platform/utils/idempotency.js +14 -0
  90. package/dist/esm/anyspend/platform/utils/pagination.d.ts +12 -0
  91. package/dist/esm/anyspend/platform/utils/pagination.js +19 -0
  92. package/dist/esm/anyspend/react/components/AnySpend.d.ts +3 -1
  93. package/dist/esm/anyspend/react/components/AnySpend.js +129 -12
  94. package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +13 -12
  95. package/dist/esm/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  96. package/dist/esm/anyspend/react/components/checkout/KycGate.js +145 -0
  97. package/dist/esm/anyspend/react/hooks/index.d.ts +1 -0
  98. package/dist/esm/anyspend/react/hooks/index.js +1 -0
  99. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +9 -0
  100. package/dist/esm/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  101. package/dist/esm/anyspend/react/hooks/useKycStatus.js +107 -0
  102. package/dist/esm/anyspend/services/anyspend.d.ts +3 -1
  103. package/dist/esm/anyspend/services/anyspend.js +2 -1
  104. package/dist/esm/global-account/react/components/B3DynamicModal.js +1 -1
  105. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +3 -3
  106. package/dist/esm/global-account/react/hooks/useAuth.js +2 -2
  107. package/dist/esm/global-account/react/stores/useModalStore.d.ts +4 -0
  108. package/dist/esm/global-account/react/stores/useModalStore.js +2 -0
  109. package/dist/esm/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  110. package/dist/esm/global-account/react/utils/createWagmiConfig.js +16 -0
  111. package/dist/styles/index.css +1 -1
  112. package/dist/types/anyspend/platform/client.d.ts +35 -0
  113. package/dist/types/anyspend/platform/errors.d.ts +38 -0
  114. package/dist/types/anyspend/platform/index.d.ts +87 -0
  115. package/dist/types/anyspend/platform/resources/analytics.d.ts +7 -0
  116. package/dist/types/anyspend/platform/resources/checkout-sessions.d.ts +17 -0
  117. package/dist/types/anyspend/platform/resources/customers.d.ts +19 -0
  118. package/dist/types/anyspend/platform/resources/discount-codes.d.ts +29 -0
  119. package/dist/types/anyspend/platform/resources/events.d.ts +14 -0
  120. package/dist/types/anyspend/platform/resources/notifications.d.ts +18 -0
  121. package/dist/types/anyspend/platform/resources/organization.d.ts +17 -0
  122. package/dist/types/anyspend/platform/resources/payment-links.d.ts +21 -0
  123. package/dist/types/anyspend/platform/resources/products.d.ts +27 -0
  124. package/dist/types/anyspend/platform/resources/transactions.d.ts +11 -0
  125. package/dist/types/anyspend/platform/resources/webhooks.d.ts +14 -0
  126. package/dist/types/anyspend/platform/resources/widgets.d.ts +38 -0
  127. package/dist/types/anyspend/platform/types.d.ts +478 -0
  128. package/dist/types/anyspend/platform/utils/idempotency.d.ts +4 -0
  129. package/dist/types/anyspend/platform/utils/pagination.d.ts +12 -0
  130. package/dist/types/anyspend/react/components/AnySpend.d.ts +3 -1
  131. package/dist/types/anyspend/react/components/checkout/KycGate.d.ts +11 -0
  132. package/dist/types/anyspend/react/hooks/index.d.ts +1 -0
  133. package/dist/types/anyspend/react/hooks/useKycStatus.d.ts +42 -0
  134. package/dist/types/anyspend/services/anyspend.d.ts +3 -1
  135. package/dist/types/global-account/react/stores/useModalStore.d.ts +4 -0
  136. package/dist/types/global-account/react/utils/createWagmiConfig.d.ts +18 -0
  137. package/package.json +7 -1
  138. package/src/anyspend/platform/client.ts +198 -0
  139. package/src/anyspend/platform/errors.ts +92 -0
  140. package/src/anyspend/platform/index.ts +129 -0
  141. package/src/anyspend/platform/resources/analytics.ts +10 -0
  142. package/src/anyspend/platform/resources/checkout-sessions.ts +36 -0
  143. package/src/anyspend/platform/resources/customers.ts +54 -0
  144. package/src/anyspend/platform/resources/discount-codes.ts +63 -0
  145. package/src/anyspend/platform/resources/events.ts +22 -0
  146. package/src/anyspend/platform/resources/notifications.ts +37 -0
  147. package/src/anyspend/platform/resources/organization.ts +24 -0
  148. package/src/anyspend/platform/resources/payment-links.ts +74 -0
  149. package/src/anyspend/platform/resources/products.ts +59 -0
  150. package/src/anyspend/platform/resources/transactions.ts +33 -0
  151. package/src/anyspend/platform/resources/webhooks.ts +47 -0
  152. package/src/anyspend/platform/resources/widgets.ts +63 -0
  153. package/src/anyspend/platform/types.ts +532 -0
  154. package/src/anyspend/platform/utils/idempotency.ts +15 -0
  155. package/src/anyspend/platform/utils/pagination.ts +32 -0
  156. package/src/anyspend/react/components/AnySpend.tsx +150 -13
  157. package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +16 -13
  158. package/src/anyspend/react/components/checkout/KycGate.tsx +351 -0
  159. package/src/anyspend/react/hooks/index.ts +1 -0
  160. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +10 -0
  161. package/src/anyspend/react/hooks/useKycStatus.ts +140 -0
  162. package/src/anyspend/services/anyspend.ts +4 -0
  163. package/src/global-account/react/components/B3DynamicModal.tsx +0 -2
  164. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +7 -7
  165. package/src/global-account/react/hooks/useAuth.ts +2 -2
  166. package/src/global-account/react/stores/useModalStore.ts +6 -0
  167. package/src/global-account/react/utils/createWagmiConfig.tsx +18 -0
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { eqci, getChainName, getDefaultToken, getExplorerTxUrl, getHyperliquidUSDCToken, HYPERLIQUID_CHAIN_ID, HYPERLIQUID_USDC_ADDRESS, USDC_BASE, ZERO_ADDRESS, } from "../../../anyspend/index.js";
4
4
  import { useAnyspendCreateOnrampOrder, useAnyspendCreateOrder, useAnyspendOrderAndTransactions, useAnyspendQuote, useGasPrice, useGeoOnrampOptions, } from "../../../anyspend/react/index.js";
5
- import { Button, ShinyButton, StyleRoot, TabsPrimitive, toast, TransitionPanel, useAccountWallet, useB3Config, useModalStore, useProfile, useRouter, useSearchParamsSSR, useTokenBalanceDirect, useTokenData, useTokenFromUrl, } from "../../../global-account/react/index.js";
5
+ import { Button, ShinyButton, StyleRoot, TabsPrimitive, toast, TransitionPanel, useAccountWallet, useAuth, useAuthStore, useB3Config, useModalStore, useProfile, useRouter, useSearchParamsSSR, useTokenBalanceDirect, useTokenData, useTokenFromUrl, } from "../../../global-account/react/index.js";
6
6
  import BottomNavigation from "../../../global-account/react/components/ManageAccount/BottomNavigation.js";
7
7
  import { useAccountWalletImage } from "../../../global-account/react/hooks/useAccountWallet.js";
8
8
  import { getThirdwebChain } from "../../../shared/constants/chains/supported.js";
@@ -30,6 +30,9 @@ import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaym
30
30
  import { GasIndicator } from "./common/GasIndicator.js";
31
31
  import { OrderDetails, OrderDetailsLoadingView } from "./common/OrderDetails.js";
32
32
  import { OrderHistory } from "./common/OrderHistory.js";
33
+ import { KycGate } from "./checkout/KycGate.js";
34
+ import { useWalletAuthHeaders } from "../hooks/useKycStatus.js";
35
+ import { LoginStep } from "../../../global-account/react/components/SignInWithB3/steps/LoginStep.js";
33
36
  import { PanelOnramp } from "./common/PanelOnramp.js";
34
37
  import { PanelOnrampPayment } from "./common/PanelOnrampPayment.js";
35
38
  import { PointsDetailPanel } from "./common/PointsDetailPanel.js";
@@ -50,6 +53,8 @@ export var PanelView;
50
53
  PanelView[PanelView["POINTS_DETAIL"] = 8] = "POINTS_DETAIL";
51
54
  PanelView[PanelView["FEE_DETAIL"] = 9] = "FEE_DETAIL";
52
55
  PanelView[PanelView["DIRECT_TRANSFER_SUCCESS"] = 10] = "DIRECT_TRANSFER_SUCCESS";
56
+ PanelView[PanelView["FIAT_KYC"] = 11] = "FIAT_KYC";
57
+ PanelView[PanelView["FIAT_AUTH"] = 12] = "FIAT_AUTH";
53
58
  })(PanelView || (PanelView = {}));
54
59
  const ANYSPEND_RECIPIENTS_KEY = "anyspend_recipients";
55
60
  export function AnySpend(props) {
@@ -63,6 +68,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
63
68
  const { partnerId } = useB3Config();
64
69
  const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
65
70
  const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
71
+ const { isAuthenticated } = useAuth();
72
+ // KYC approval is tracked per-session so we only prompt the wallet
73
+ // signature when the user actually clicks Buy, not on panel mount.
74
+ // useRef so handleFiatOrder can read the updated value synchronously
75
+ // in the same frame that onStatusResolved sets it (setState is async).
76
+ const kycApprovedRef = useRef(false);
77
+ // Pre-warm wallet auth headers inside user-gesture context (button click)
78
+ // so the signing prompt fires before we navigate away — browsers block
79
+ // wallet popups triggered from async/non-gesture contexts (React Query queryFn).
80
+ const { getHeaders: getKycHeaders } = useWalletAuthHeaders();
66
81
  // Determine if we're in "buy mode" based on whether destination token props are provided
67
82
  const isBuyMode = !!(destinationTokenAddress && destinationTokenChainId);
68
83
  // Add refs to track URL state
@@ -72,7 +87,17 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
72
87
  const animationDirection = useRef(null);
73
88
  // Track previous panel for proper back navigation
74
89
  const previousPanel = useRef(PanelView.MAIN);
75
- const [activeTab, setActiveTab] = useState(defaultActiveTab);
90
+ const [activeTab, setActiveTab] = useState(() => {
91
+ if (typeof window !== "undefined") {
92
+ const stored = sessionStorage.getItem("anyspend_active_tab");
93
+ if (stored === "crypto" || stored === "fiat")
94
+ return stored;
95
+ }
96
+ return defaultActiveTab;
97
+ });
98
+ useEffect(() => {
99
+ sessionStorage.setItem("anyspend_active_tab", activeTab);
100
+ }, [activeTab]);
76
101
  const [orderId, setOrderId] = useState(loadOrder);
77
102
  const [directTransferTxHash, setDirectTransferTxHash] = useState();
78
103
  const { orderAndTransactions: oat, getOrderAndTransactionsError } = useAnyspendOrderAndTransactions(orderId);
@@ -93,9 +118,19 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
93
118
  const [customRecipients, setCustomRecipients] = useState([]);
94
119
  // Payment method state with dual-state system (auto + explicit user selection)
95
120
  const { cryptoPaymentMethod, setCryptoPaymentMethod, selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod, effectiveCryptoPaymentMethod, resetPaymentMethods, } = useCryptoPaymentMethodState();
96
- const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(FiatPaymentMethod.NONE);
121
+ const [selectedFiatPaymentMethod, setSelectedFiatPaymentMethod] = useState(() => {
122
+ if (typeof window !== "undefined") {
123
+ const stored = sessionStorage.getItem("anyspend_fiat_method");
124
+ if (stored && Object.values(FiatPaymentMethod).includes(stored))
125
+ return stored;
126
+ }
127
+ return FiatPaymentMethod.NONE;
128
+ });
97
129
  // const [newRecipientAddress, setNewRecipientAddress] = useState("");
98
130
  // const recipientInputRef = useRef<HTMLInputElement>(null);
131
+ useEffect(() => {
132
+ sessionStorage.setItem("anyspend_fiat_method", selectedFiatPaymentMethod);
133
+ }, [selectedFiatPaymentMethod]);
99
134
  // Get initial chain IDs from URL or defaults
100
135
  const initialSrcChainId = sourceChainId || parseInt(searchParams.get("fromChainId") || "0") || mainnet.id;
101
136
  const initialDstChainId = parseInt(searchParams.get("toChainId") || "0") || (isBuyMode ? destinationTokenChainId : base.id);
@@ -112,10 +147,27 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
112
147
  const [selectedSrcToken, setSelectedSrcToken] = useState(effectiveSrcToken);
113
148
  const { data: srcTokenMetadata } = useTokenData(selectedSrcToken?.chainId, selectedSrcToken?.address);
114
149
  const [srcAmount, setSrcAmount] = useState(searchParams.get("fromAmount") || "0");
115
- // State for onramp amount
116
- const [srcAmountOnRamp, setSrcAmountOnRamp] = useState(searchParams.get("fromAmount") || "5");
117
- // State for destination chain/token selection
118
- const [selectedDstChainId, setSelectedDstChainId] = useState(initialDstChainId);
150
+ // State for onramp amount — persisted in sessionStorage so it survives Persona KYC roundtrip
151
+ const [srcAmountOnRamp, setSrcAmountOnRamp] = useState(() => {
152
+ if (typeof window !== "undefined") {
153
+ const stored = sessionStorage.getItem("anyspend_fiat_amount");
154
+ if (stored)
155
+ return stored;
156
+ }
157
+ return searchParams.get("fromAmount") || "5";
158
+ });
159
+ useEffect(() => {
160
+ sessionStorage.setItem("anyspend_fiat_amount", srcAmountOnRamp);
161
+ }, [srcAmountOnRamp]);
162
+ // State for destination chain/token selection (sync effects come after state declarations below) — persisted in sessionStorage for Persona KYC roundtrip
163
+ const [selectedDstChainId, setSelectedDstChainId] = useState(() => {
164
+ if (!isBuyMode && typeof window !== "undefined") {
165
+ const stored = sessionStorage.getItem("anyspend_dst_chain_id");
166
+ if (stored)
167
+ return parseInt(stored, 10);
168
+ }
169
+ return initialDstChainId;
170
+ });
119
171
  // Helper to check if address is Hyperliquid USDC (supports both 34-char and 42-char formats)
120
172
  const isHyperliquidUSDCAddress = (address) => eqci(address, HYPERLIQUID_USDC_ADDRESS) || eqci(address, ZERO_ADDRESS);
121
173
  const defaultDstToken = isBuyMode
@@ -135,9 +187,29 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
135
187
  defaultToken: defaultDstToken,
136
188
  prefix: "to",
137
189
  });
138
- const [selectedDstToken, setSelectedDstToken] = useState(isBuyMode ? defaultDstToken : dstTokenFromUrl);
190
+ const [selectedDstToken, setSelectedDstToken] = useState(() => {
191
+ if (!isBuyMode && typeof window !== "undefined") {
192
+ const stored = sessionStorage.getItem("anyspend_dst_token");
193
+ if (stored) {
194
+ try {
195
+ return JSON.parse(stored);
196
+ }
197
+ catch { }
198
+ }
199
+ }
200
+ return isBuyMode ? defaultDstToken : dstTokenFromUrl;
201
+ });
139
202
  const { data: dstTokenMetadata } = useTokenData(selectedDstToken?.chainId, selectedDstToken?.address);
140
203
  const [dstAmount, setDstAmount] = useState(searchParams.get("toAmount") || "");
204
+ // Sync dst chain/token to sessionStorage so they survive Persona KYC roundtrip
205
+ useEffect(() => {
206
+ if (!isBuyMode)
207
+ sessionStorage.setItem("anyspend_dst_chain_id", selectedDstChainId.toString());
208
+ }, [selectedDstChainId, isBuyMode]);
209
+ useEffect(() => {
210
+ if (!isBuyMode)
211
+ sessionStorage.setItem("anyspend_dst_token", JSON.stringify(selectedDstToken));
212
+ }, [selectedDstToken, isBuyMode]);
141
213
  const [isSrcInputDirty, setIsSrcInputDirty] = useState(true);
142
214
  // Add refs to track if we've applied metadata
143
215
  const appliedSrcMetadataRef = useRef(false);
@@ -490,6 +562,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
490
562
  }, [anyspendQuote, isSrcInputDirty, destinationTokenAmount]);
491
563
  // Call onSuccess when order is executed
492
564
  useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
565
+ // Clear all persisted selection state once an order is submitted — next open starts fresh
566
+ useEffect(() => {
567
+ if (orderId) {
568
+ sessionStorage.removeItem("anyspend_fiat_amount");
569
+ sessionStorage.removeItem("anyspend_active_tab");
570
+ sessionStorage.removeItem("anyspend_fiat_method");
571
+ sessionStorage.removeItem("anyspend_dst_chain_id");
572
+ sessionStorage.removeItem("anyspend_dst_token");
573
+ }
574
+ }, [orderId]);
493
575
  const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({
494
576
  onSuccess: data => {
495
577
  const orderId = data.data.id;
@@ -570,8 +652,8 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
570
652
  if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
571
653
  return { text: "Select payment method", disable: false, error: false, loading: false };
572
654
  }
573
- // If payment method is selected, show "Buy"
574
- return { text: "Buy", disable: false, error: false, loading: false };
655
+ // If payment method is selected, show "Continue"
656
+ return { text: "Continue", disable: false, error: false, loading: false };
575
657
  }
576
658
  if (activeTab === "crypto") {
577
659
  // For crypto: check payment method first, then recipient
@@ -592,7 +674,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
592
674
  return { text: "Continue to payment", disable: false, error: false, loading: false };
593
675
  }
594
676
  }
595
- return { text: "Buy", disable: false, error: false, loading: false };
677
+ return { text: "Continue", disable: false, error: false, loading: false };
596
678
  }, [
597
679
  activeInputAmountInWei,
598
680
  isSameChainSameToken,
@@ -754,7 +836,22 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
754
836
  paymentMethodString = "";
755
837
  }
756
838
  else if (paymentMethod === FiatPaymentMethod.STRIPE_WEB2) {
757
- // Stripe embedded payment form
839
+ // Stripe embedded payment form requires authentication + KYC
840
+ // Read from store directly to avoid stale closure when called from onLoginSuccess callback
841
+ const currentlyAuthenticated = useAuthStore.getState().isAuthenticated;
842
+ if (!currentlyAuthenticated) {
843
+ navigateToPanel(PanelView.FIAT_AUTH, "forward");
844
+ return;
845
+ }
846
+ if (!kycApprovedRef.current) {
847
+ // Pre-sign the KYC auth message NOW (user-gesture context) so the
848
+ // result is cached before useKycStatus fires its queryFn inside the
849
+ // FIAT_KYC panel. Wallets/browsers block signing prompts from async
850
+ // (non-gesture) contexts, which is exactly what React Query uses.
851
+ await getKycHeaders().catch(() => { });
852
+ navigateToPanel(PanelView.FIAT_KYC, "forward");
853
+ return;
854
+ }
758
855
  if (!stripeWeb2Support.isSupport) {
759
856
  toast.error("Pay with Card not available");
760
857
  return;
@@ -863,6 +960,14 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
863
960
  window.removeEventListener("popstate", handlePopState);
864
961
  };
865
962
  }, [activePanel, navigateBack]);
963
+ // When auth completes while on the FIAT_AUTH panel, navigate back and retry the order
964
+ useEffect(() => {
965
+ if (isAuthenticated && activePanel === PanelView.FIAT_AUTH) {
966
+ navigateBack();
967
+ handleFiatOrder(selectedFiatPaymentMethod);
968
+ }
969
+ // eslint-disable-next-line react-hooks/exhaustive-deps
970
+ }, [isAuthenticated]);
866
971
  const historyView = (_jsx("div", { className: "mx-auto flex w-[560px] max-w-full flex-col items-center", children: _jsx(OrderHistory, { mode: mode, onBack: navigateBack, onSelectOrder: onSelectOrder }) }));
867
972
  const orderDetailsView = (_jsx("div", { className: "mx-auto w-[460px] max-w-full p-5", children: _jsx("div", { className: "relative flex flex-col gap-4", children: oat && (_jsx(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 => {
868
973
  // When user explicitly changes payment method, set it as selected
@@ -997,6 +1102,16 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
997
1102
  setDirectTransferTxHash(undefined);
998
1103
  setB3ModalOpen(false);
999
1104
  }, className: "bg-as-brand hover:bg-as-brand/90 w-full text-white", children: resolvedReturnLabel || "Done" })) })] }));
1105
+ const kycView = (_jsx("div", { className: "mx-auto flex w-full max-w-[460px] flex-col gap-4 px-5 pt-5", children: _jsx(KycGate, { enabled: activePanel === PanelView.FIAT_KYC, onStatusResolved: approved => {
1106
+ if (approved) {
1107
+ kycApprovedRef.current = true;
1108
+ navigateBack();
1109
+ handleFiatOrder(selectedFiatPaymentMethod);
1110
+ }
1111
+ } }) }));
1112
+ const authView = (_jsx("div", { className: "mx-auto w-full max-w-[460px]", children: _jsx(LoginStep, { chain: baseChain, onSuccess: async () => {
1113
+ // isAuthenticated will be true at this point — the useEffect below handles navigation
1114
+ } }) }));
1000
1115
  // Add tabs to the main component when no order is loaded
1001
1116
  return (_jsx(StyleRoot, { children: _jsx("div", { className: classes?.container ||
1002
1117
  cn("anyspend-container font-inter mx-auto w-full max-w-[460px]", mode === "page" &&
@@ -1030,5 +1145,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
1030
1145
  _jsx("div", { className: cn(mode === "page" && "p-6"), children: pointsDetailView }, "points-detail-view"),
1031
1146
  _jsx("div", { className: cn(mode === "page" && "p-6"), children: feeDetailView }, "fee-detail-view"),
1032
1147
  _jsx("div", { className: cn(mode === "page" && "p-6"), children: directTransferSuccessView }, "direct-transfer-success-view"),
1148
+ _jsx("div", { className: cn(mode === "page" && "p-6"), children: kycView }, "fiat-kyc-view"),
1149
+ _jsx("div", { className: cn(mode === "page" && "p-6"), children: authView }, "fiat-auth-view"),
1033
1150
  ] }) }) }));
1034
1151
  }
@@ -10,6 +10,7 @@ import { AddressElement, Elements, PaymentElement, useElements, useStripe } from
10
10
  import { Loader2, Lock } from "lucide-react";
11
11
  import { AnimatePresence, motion } from "motion/react";
12
12
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
13
+ import { KycGate } from "./KycGate.js";
13
14
  export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }) {
14
15
  // Stable refs for callback props to avoid re-triggering effects
15
16
  const onErrorRef = useRef(onError);
@@ -48,18 +49,12 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
48
49
  const raw = formatUnits(anyspendQuote.data.currencyIn.amount, USDC_BASE.decimals);
49
50
  return parseFloat(raw).toFixed(2);
50
51
  }, [isStablecoin, formattedAmount, anyspendQuote]);
51
- // Debug: log computed values for Stripe flow diagnostics
52
- useEffect(() => {
53
- console.log("@@fiat-checkout:debug", {
54
- totalAmount,
55
- formattedAmount,
56
- isStablecoin,
57
- isLoadingAnyspendQuote,
58
- quoteAmount: anyspendQuote?.data?.currencyIn?.amount,
59
- usdAmount,
60
- });
61
- }, [totalAmount, formattedAmount, isStablecoin, isLoadingAnyspendQuote, anyspendQuote, usdAmount]);
62
52
  const { geoData, stripeOnrampSupport, stripeWeb2Support, isLoading: isLoadingGeo, } = useGeoOnrampOptions(usdAmount || "0");
53
+ // KYC state
54
+ const [kycApproved, setKycApproved] = useState(false);
55
+ const handleKycResolved = useCallback((approved) => {
56
+ setKycApproved(approved);
57
+ }, []);
63
58
  // Order state
64
59
  const [orderId, setOrderId] = useState(null);
65
60
  const [stripePaymentIntentId, setStripePaymentIntentId] = useState(null);
@@ -82,13 +77,14 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
82
77
  onErrorRef.current?.(error);
83
78
  },
84
79
  });
85
- // Auto-create onramp order when Stripe Web2 is supported and all data is ready
80
+ // Auto-create onramp order when Stripe Web2 is supported, KYC approved, and all data is ready
86
81
  useEffect(() => {
87
82
  if (!isLoadingGeo &&
88
83
  (!isStablecoin ? !isLoadingAnyspendQuote : true) &&
89
84
  usdAmount &&
90
85
  parseFloat(usdAmount) > 0 &&
91
86
  stripeWeb2Support?.isSupport &&
87
+ kycApproved &&
92
88
  !orderCreatedRef.current &&
93
89
  !orderId &&
94
90
  !isCreatingOrder &&
@@ -129,6 +125,7 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
129
125
  isLoadingAnyspendQuote,
130
126
  usdAmount,
131
127
  stripeWeb2Support,
128
+ kycApproved,
132
129
  orderId,
133
130
  isCreatingOrder,
134
131
  orderError,
@@ -152,6 +149,10 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
152
149
  if (!hasStripeWeb2 && !hasStripeRedirect) {
153
150
  return (_jsx(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-fiat-unavailable py-4 text-center", classes?.fiatPanel), children: _jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Card payments are not available in your region for this amount." }) }));
154
151
  }
152
+ // KYC gate — shown before order creation when verification is needed
153
+ if (!kycApproved) {
154
+ return _jsx(KycGate, { themeColor: themeColor, classes: classes, enabled: true, onStatusResolved: handleKycResolved });
155
+ }
155
156
  // Order creation error - show with retry
156
157
  if (orderError) {
157
158
  return (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-fiat-error flex flex-col items-center gap-3 py-4", classes?.fiatPanel), children: [_jsx("p", { className: "text-sm text-red-500", children: orderError }), _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,145 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../../../../shared/utils/cn.js";
4
+ import { ShinyButton, TextShimmer, useAuth, useB3Config, useModalStore } from "../../../../global-account/react/index.js";
5
+ import { thirdwebB3Chain } from "../../../../shared/constants/chains/b3Chain.js";
6
+ import { Loader2, ShieldCheck, AlertTriangle, Clock } from "lucide-react";
7
+ import { AnimatePresence, motion } from "motion/react";
8
+ import { useCallback, useEffect, useRef, useState } from "react";
9
+ import { useCreateKycInquiry, useKycStatus, useVerifyKyc } from "../../hooks/useKycStatus.js";
10
+ export function KycGate({ themeColor, classes, enabled = false, onStatusResolved }) {
11
+ const { isAuthenticated, isAuthenticating } = useAuth();
12
+ const { kycStatus, isLoadingKycStatus, refetchKycStatus } = useKycStatus(enabled);
13
+ const { createInquiry, isCreatingInquiry } = useCreateKycInquiry();
14
+ const { verifyKyc, isVerifying } = useVerifyKyc();
15
+ const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
16
+ const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
17
+ const { partnerId } = useB3Config();
18
+ const [personaOpen, setPersonaOpen] = useState(false);
19
+ const [personaError, setPersonaError] = useState(null);
20
+ const [personaCancelled, setPersonaCancelled] = useState(false);
21
+ const prevStatusRef = useRef(null);
22
+ // Notify parent when status resolves
23
+ useEffect(() => {
24
+ if (!kycStatus)
25
+ return;
26
+ const currentStatus = kycStatus.status;
27
+ if (currentStatus === prevStatusRef.current)
28
+ return;
29
+ prevStatusRef.current = currentStatus;
30
+ if (!kycStatus.kycRequired || currentStatus === "approved") {
31
+ onStatusResolved(true);
32
+ }
33
+ }, [kycStatus, onStatusResolved]);
34
+ const openPersonaFlow = useCallback(async (config) => {
35
+ setPersonaOpen(true);
36
+ try {
37
+ // Dynamic import to keep bundle small
38
+ const { Client } = await import("persona");
39
+ const client = new Client({
40
+ inquiryId: config.inquiryId,
41
+ sessionToken: config.sessionToken,
42
+ environment: config.environment === "production" ? "production" : "sandbox",
43
+ onComplete: async ({ inquiryId }) => {
44
+ // Reopen the modal first so the user lands back in the checkout flow
45
+ setB3ModalOpen(true);
46
+ setPersonaOpen(false);
47
+ if (inquiryId) {
48
+ try {
49
+ const result = await verifyKyc(inquiryId);
50
+ if (result.status === "approved") {
51
+ onStatusResolved(true);
52
+ }
53
+ }
54
+ catch (err) {
55
+ setPersonaError(err instanceof Error ? err.message : "Verification check failed — please retry.");
56
+ }
57
+ }
58
+ refetchKycStatus();
59
+ },
60
+ onCancel: () => {
61
+ // Reopen the modal so the user can retry or cancel the purchase
62
+ setB3ModalOpen(true);
63
+ setPersonaOpen(false);
64
+ setPersonaCancelled(true);
65
+ },
66
+ onError: error => {
67
+ // Reopen the modal so the user sees the error and can retry
68
+ setB3ModalOpen(true);
69
+ setPersonaOpen(false);
70
+ setPersonaError(error?.message || "Verification encountered an error");
71
+ },
72
+ });
73
+ // Close the modal before opening Persona so its overlay is fully
74
+ // interactive — no Radix Dialog backdrop or z-index conflicts.
75
+ // The modal's contentType is preserved in Zustand and restored on reopen.
76
+ setB3ModalOpen(false);
77
+ client.open();
78
+ }
79
+ catch (error) {
80
+ setPersonaOpen(false);
81
+ setB3ModalOpen(true);
82
+ setPersonaError("Failed to load verification module");
83
+ }
84
+ }, [verifyKyc, onStatusResolved, refetchKycStatus, setB3ModalOpen]);
85
+ const handleStartVerification = useCallback(async () => {
86
+ setPersonaError(null);
87
+ setPersonaCancelled(false);
88
+ try {
89
+ const { inquiryId, sessionToken } = await createInquiry();
90
+ openPersonaFlow({
91
+ inquiryId,
92
+ sessionToken,
93
+ environment: kycStatus?.config?.environment,
94
+ });
95
+ }
96
+ catch (error) {
97
+ setPersonaError(error instanceof Error ? error.message : "Failed to start verification");
98
+ }
99
+ }, [createInquiry, kycStatus, openPersonaFlow]);
100
+ const handleSignIn = useCallback(() => {
101
+ setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: thirdwebB3Chain, partnerId });
102
+ setB3ModalOpen(true);
103
+ }, [setB3ModalContentType, setB3ModalOpen, partnerId]);
104
+ const handleResumeVerification = useCallback(() => {
105
+ if (!kycStatus?.inquiry)
106
+ return;
107
+ setPersonaError(null);
108
+ setPersonaCancelled(false);
109
+ openPersonaFlow({
110
+ inquiryId: kycStatus.inquiry.inquiryId,
111
+ sessionToken: kycStatus.inquiry.sessionToken,
112
+ environment: kycStatus.config?.environment,
113
+ });
114
+ }, [kycStatus, openPersonaFlow]);
115
+ // Auth loading state
116
+ if (isAuthenticating) {
117
+ return (_jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: cn("anyspend-kyc-loading flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), _jsx(TextShimmer, { duration: 1.5, className: "text-sm", children: "Checking authentication..." })] }));
118
+ }
119
+ // Not authenticated — prompt to login
120
+ if (!isAuthenticated) {
121
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-kyc-auth flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [_jsx(ShieldCheck, { className: "h-8 w-8 text-gray-400" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Login required to pay with card" }), _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." })] }), _jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleSignIn, children: _jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(ShieldCheck, { className: "h-4 w-4" }), "Sign In"] }) })] }));
122
+ }
123
+ // Loading KYC status state
124
+ if (isLoadingKycStatus) {
125
+ return (_jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: cn("anyspend-kyc-loading flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), _jsx(TextShimmer, { duration: 1.5, className: "text-sm", children: "Checking verification status..." })] }));
126
+ }
127
+ // Not required or already approved — render nothing
128
+ if (!kycStatus?.kycRequired || kycStatus.status === "approved") {
129
+ return null;
130
+ }
131
+ // Persona flow is open - show loading
132
+ if (personaOpen) {
133
+ return (_jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: cn("anyspend-kyc-persona flex flex-col items-center gap-3 py-6", classes?.fiatPanel), children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }), _jsx(TextShimmer, { duration: 1.5, className: "text-sm", children: "Identity verification in progress..." }), _jsx("p", { className: "text-xs text-gray-400 dark:text-gray-500", children: "Complete the verification in the popup window" })] }));
134
+ }
135
+ // Needs review or completed (submitted, awaiting Persona decision)
136
+ if (kycStatus.status === "needs_review" || kycStatus.status === "completed") {
137
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-kyc-review flex flex-col items-center gap-3 py-2", classes?.fiatPanel), children: [_jsx(Clock, { className: "h-8 w-8 text-amber-500" }), _jsx("p", { className: "text-center text-sm font-medium text-amber-700 dark:text-amber-300", children: "Your verification is under review" }), _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." }), _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" })] }));
138
+ }
139
+ // Pending (started before) - offer resume
140
+ if (kycStatus.status === "pending" && kycStatus.inquiry) {
141
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-kyc-resume flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [_jsx(ShieldCheck, { className: "h-8 w-8 text-blue-500" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Continue verification" }), _jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: "You have an incomplete verification. Resume to continue." })] }), _jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleResumeVerification, disabled: isCreatingInquiry, children: _jsxs("span", { className: "flex items-center justify-center gap-2", children: [isCreatingInquiry ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : _jsx(ShieldCheck, { className: "h-4 w-4" }), "Resume Verification"] }) })] }));
142
+ }
143
+ // Not verified / declined / expired - show verification prompt
144
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("anyspend-kyc-prompt flex flex-col items-center gap-4 py-2", classes?.fiatPanel), children: [_jsx(ShieldCheck, { className: "h-8 w-8 text-blue-500" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Identity verification required" }), _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." })] }), _jsxs(AnimatePresence, { initial: false, children: [personaError && (_jsx(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: _jsx("p", { className: "text-center text-sm text-red-600 dark:text-red-400", children: personaError }) }, "kyc-error")), personaCancelled && (_jsx(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: _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" && (_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: [_jsx(AlertTriangle, { className: "h-3.5 w-3.5 text-red-500" }), _jsx("p", { className: "text-xs text-red-600 dark:text-red-400", children: "Previous verification was declined. You may try again." })] })), _jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", className: "w-full", textClassName: "text-white", onClick: handleStartVerification, disabled: isCreatingInquiry || isVerifying, children: _jsxs("span", { className: "flex items-center justify-center gap-2", children: [isCreatingInquiry ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : _jsx(ShieldCheck, { className: "h-4 w-4" }), isCreatingInquiry ? "Starting..." : "Verify Identity"] }) })] }));
145
+ }
@@ -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";
@@ -18,3 +18,4 @@ export * from "./useSigMint.js";
18
18
  export * from "./useStripeClientSecret.js";
19
19
  export * from "./useStripeSupport.js";
20
20
  export * from "./useWatchTransfer.js";
21
+ export * from "./useKycStatus.js";
@@ -7,6 +7,7 @@ import { useMutation } from "@tanstack/react-query";
7
7
  import { useMemo } from "react";
8
8
  import { parseUnits } from "viem";
9
9
  import { base } from "viem/chains";
10
+ import { useWalletAuthHeaders } from "./useKycStatus.js";
10
11
  import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.js";
11
12
  /**
12
13
  * Hook for creating onramp orders in the Anyspend protocol
@@ -15,6 +16,7 @@ import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.j
15
16
  export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
16
17
  // Get B3 context values
17
18
  const { partnerId } = useB3Config();
19
+ const { getHeaders: getWalletAuthHeaders } = useWalletAuthHeaders();
18
20
  // Get validated client reference ID from B3 context
19
21
  const createValidatedClientReferenceId = useValidatedClientReferenceId();
20
22
  // Get fingerprint data
@@ -36,6 +38,12 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
36
38
  const srcChain = base.id;
37
39
  // Create order with USDC on Base as source
38
40
  const srcAmountOnRampInWei = parseUnits(srcFiatAmount, USDC_BASE.decimals);
41
+ // For card payments, include wallet auth headers so the backend can verify
42
+ // KYC by the signing wallet address (may differ from the B3 JWT address).
43
+ let kycWalletHeaders;
44
+ if (onramp.vendor === "stripe-web2") {
45
+ kycWalletHeaders = await getWalletAuthHeaders().catch(() => undefined);
46
+ }
39
47
  return await anyspendService.createOrder({
40
48
  recipientAddress: normalizeAddress(recipientAddress),
41
49
  type: orderType,
@@ -73,6 +81,7 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
73
81
  visitorData,
74
82
  callbackMetadata: params.callbackMetadata,
75
83
  feeOnTop: params.feeOnTop,
84
+ kycWalletHeaders,
76
85
  });
77
86
  }
78
87
  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 {};