@b3dotfun/sdk 0.1.66-alpha.1 → 0.1.66-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.d.ts +50 -0
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.js +30 -0
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckoutTrigger.d.ts +47 -0
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckoutTrigger.js +45 -0
- package/dist/cjs/anyspend/react/components/checkout/CartItemRow.d.ts +8 -0
- package/dist/cjs/anyspend/react/components/checkout/CartItemRow.js +9 -0
- package/dist/cjs/anyspend/react/components/checkout/CartSummary.d.ts +8 -0
- package/dist/cjs/anyspend/react/components/checkout/CartSummary.js +9 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +12 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutCartPanel.js +19 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutLayout.d.ts +10 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutLayout.js +25 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +20 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.js +45 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutSuccess.d.ts +10 -0
- package/dist/cjs/anyspend/react/components/checkout/CheckoutSuccess.js +11 -0
- package/dist/cjs/anyspend/react/components/checkout/CoinbaseCheckoutPanel.d.ts +16 -0
- package/dist/cjs/anyspend/react/components/checkout/CoinbaseCheckoutPanel.js +27 -0
- package/dist/cjs/anyspend/react/components/checkout/CryptoCheckoutPanel.d.ts +33 -0
- package/dist/cjs/anyspend/react/components/checkout/CryptoCheckoutPanel.js +317 -0
- package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +16 -0
- package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +215 -0
- package/dist/cjs/anyspend/react/components/checkout/PoweredByBranding.d.ts +8 -0
- package/dist/cjs/anyspend/react/components/checkout/PoweredByBranding.js +9 -0
- package/dist/cjs/anyspend/react/components/checkout/QRCheckoutPanel.d.ts +17 -0
- package/dist/cjs/anyspend/react/components/checkout/QRCheckoutPanel.js +148 -0
- package/dist/cjs/anyspend/react/components/index.d.ts +5 -1
- package/dist/cjs/anyspend/react/components/index.js +6 -1
- package/dist/cjs/anyspend/react/components/types/classes.d.ts +32 -0
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +5 -1
- package/dist/cjs/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
- package/dist/cjs/global-account/react/components/ui/command.d.ts +7 -7
- package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
- package/dist/cjs/global-account/react/hooks/useAuth.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useUser.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useUserQuery.d.ts +1 -1
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +53 -1
- package/dist/cjs/shared/constants/chains/b3Chain.d.ts +2 -2
- package/dist/cjs/shared/constants/chains/supported.d.ts +3 -3
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.d.ts +50 -0
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.js +27 -0
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckoutTrigger.d.ts +47 -0
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckoutTrigger.js +42 -0
- package/dist/esm/anyspend/react/components/checkout/CartItemRow.d.ts +8 -0
- package/dist/esm/anyspend/react/components/checkout/CartItemRow.js +6 -0
- package/dist/esm/anyspend/react/components/checkout/CartSummary.d.ts +8 -0
- package/dist/esm/anyspend/react/components/checkout/CartSummary.js +6 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +12 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutCartPanel.js +16 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutLayout.d.ts +10 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutLayout.js +22 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +20 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.js +42 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutSuccess.d.ts +10 -0
- package/dist/esm/anyspend/react/components/checkout/CheckoutSuccess.js +8 -0
- package/dist/esm/anyspend/react/components/checkout/CoinbaseCheckoutPanel.d.ts +16 -0
- package/dist/esm/anyspend/react/components/checkout/CoinbaseCheckoutPanel.js +24 -0
- package/dist/esm/anyspend/react/components/checkout/CryptoCheckoutPanel.d.ts +33 -0
- package/dist/esm/anyspend/react/components/checkout/CryptoCheckoutPanel.js +313 -0
- package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +16 -0
- package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +212 -0
- package/dist/esm/anyspend/react/components/checkout/PoweredByBranding.d.ts +8 -0
- package/dist/esm/anyspend/react/components/checkout/PoweredByBranding.js +6 -0
- package/dist/esm/anyspend/react/components/checkout/QRCheckoutPanel.d.ts +17 -0
- package/dist/esm/anyspend/react/components/checkout/QRCheckoutPanel.js +145 -0
- package/dist/esm/anyspend/react/components/index.d.ts +5 -1
- package/dist/esm/anyspend/react/components/index.js +3 -0
- package/dist/esm/anyspend/react/components/types/classes.d.ts +32 -0
- package/dist/esm/global-account/react/components/B3DynamicModal.js +5 -1
- package/dist/esm/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
- package/dist/esm/global-account/react/components/ui/command.d.ts +7 -7
- package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
- package/dist/esm/global-account/react/hooks/useAuth.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useUser.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useUserQuery.d.ts +1 -1
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +53 -1
- package/dist/esm/shared/constants/chains/b3Chain.d.ts +2 -2
- package/dist/esm/shared/constants/chains/supported.d.ts +3 -3
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/checkout/AnySpendCheckout.d.ts +50 -0
- package/dist/types/anyspend/react/components/checkout/AnySpendCheckoutTrigger.d.ts +47 -0
- package/dist/types/anyspend/react/components/checkout/CartItemRow.d.ts +8 -0
- package/dist/types/anyspend/react/components/checkout/CartSummary.d.ts +8 -0
- package/dist/types/anyspend/react/components/checkout/CheckoutCartPanel.d.ts +12 -0
- package/dist/types/anyspend/react/components/checkout/CheckoutLayout.d.ts +10 -0
- package/dist/types/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +20 -0
- package/dist/types/anyspend/react/components/checkout/CheckoutSuccess.d.ts +10 -0
- package/dist/types/anyspend/react/components/checkout/CoinbaseCheckoutPanel.d.ts +16 -0
- package/dist/types/anyspend/react/components/checkout/CryptoCheckoutPanel.d.ts +33 -0
- package/dist/types/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +16 -0
- package/dist/types/anyspend/react/components/checkout/PoweredByBranding.d.ts +8 -0
- package/dist/types/anyspend/react/components/checkout/QRCheckoutPanel.d.ts +17 -0
- package/dist/types/anyspend/react/components/index.d.ts +5 -1
- package/dist/types/anyspend/react/components/types/classes.d.ts +32 -0
- package/dist/types/global-account/react/components/WalletImage/WalletImage.d.ts +1 -1
- package/dist/types/global-account/react/components/ui/command.d.ts +7 -7
- package/dist/types/global-account/react/hooks/useAuth.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useAuthentication.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useUser.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useUserQuery.d.ts +1 -1
- package/dist/types/global-account/react/stores/useModalStore.d.ts +53 -1
- package/dist/types/shared/constants/chains/b3Chain.d.ts +2 -2
- package/dist/types/shared/constants/chains/supported.d.ts +3 -3
- package/package.json +1 -1
- package/src/anyspend/react/components/checkout/AnySpendCheckout.tsx +127 -0
- package/src/anyspend/react/components/checkout/AnySpendCheckoutTrigger.tsx +166 -0
- package/src/anyspend/react/components/checkout/CartItemRow.tsx +43 -0
- package/src/anyspend/react/components/checkout/CartSummary.tsx +23 -0
- package/src/anyspend/react/components/checkout/CheckoutCartPanel.tsx +60 -0
- package/src/anyspend/react/components/checkout/CheckoutLayout.tsx +72 -0
- package/src/anyspend/react/components/checkout/CheckoutPaymentPanel.tsx +320 -0
- package/src/anyspend/react/components/checkout/CheckoutSuccess.tsx +91 -0
- package/src/anyspend/react/components/checkout/CoinbaseCheckoutPanel.tsx +90 -0
- package/src/anyspend/react/components/checkout/CryptoCheckoutPanel.tsx +643 -0
- package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +519 -0
- package/src/anyspend/react/components/checkout/PoweredByBranding.tsx +32 -0
- package/src/anyspend/react/components/checkout/QRCheckoutPanel.tsx +320 -0
- package/src/anyspend/react/components/index.ts +7 -0
- package/src/anyspend/react/components/types/classes.ts +48 -0
- package/src/global-account/react/components/B3DynamicModal.tsx +5 -0
- package/src/global-account/react/components/ui/dialog.tsx +1 -1
- package/src/global-account/react/stores/useModalStore.ts +52 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.CryptoCheckoutPanel = CryptoCheckoutPanel;
|
|
5
|
+
exports.TokenSelectorModal = TokenSelectorModal;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const useAnyspendQuote_1 = require("../../../../anyspend/react/hooks/useAnyspendQuote");
|
|
8
|
+
const useAnyspendCreateOrder_1 = require("../../../../anyspend/react/hooks/useAnyspendCreateOrder");
|
|
9
|
+
const useAnyspendOrderAndTransactions_1 = require("../../../../anyspend/react/hooks/useAnyspendOrderAndTransactions");
|
|
10
|
+
const useAnyspendTokens_1 = require("../../../../anyspend/react/hooks/useAnyspendTokens");
|
|
11
|
+
const useOnOrderSuccess_1 = require("../../../../anyspend/react/hooks/useOnOrderSuccess");
|
|
12
|
+
const anyspend_1 = require("../../../../anyspend");
|
|
13
|
+
const chain_1 = require("../../../../anyspend/utils/chain");
|
|
14
|
+
const react_1 = require("../../../../global-account/react");
|
|
15
|
+
const b3Chain_1 = require("../../../../shared/constants/chains/b3Chain");
|
|
16
|
+
const number_1 = require("../../../../shared/utils/number");
|
|
17
|
+
const token_1 = require("../../../../anyspend/utils/token");
|
|
18
|
+
const cn_1 = require("../../../../shared/utils/cn");
|
|
19
|
+
const react_2 = require("../../../../global-account/react");
|
|
20
|
+
const react_3 = require("../../../../global-account/react");
|
|
21
|
+
const dialog_1 = require("../../../../global-account/react/components/ui/dialog");
|
|
22
|
+
const drawer_1 = require("../../../../global-account/react/components/ui/drawer");
|
|
23
|
+
const lucide_react_1 = require("lucide-react");
|
|
24
|
+
const viem_1 = require("viem");
|
|
25
|
+
const react_4 = require("motion/react");
|
|
26
|
+
const react_5 = require("react");
|
|
27
|
+
const ChainTokenIcon_1 = require("../common/ChainTokenIcon");
|
|
28
|
+
function CryptoCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText = "Pay", themeColor, onSuccess, onError, callbackMetadata, classes, }) {
|
|
29
|
+
const [selectedSrcChainId, setSelectedSrcChainId] = (0, react_5.useState)(destinationTokenChainId);
|
|
30
|
+
const [selectedSrcToken, setSelectedSrcToken] = (0, react_5.useState)(null);
|
|
31
|
+
const [showTokenSelector, setShowTokenSelector] = (0, react_5.useState)(false);
|
|
32
|
+
const [tokenSearchQuery, setTokenSearchQuery] = (0, react_5.useState)("");
|
|
33
|
+
// Get wallet & modal
|
|
34
|
+
const { address: walletAddress } = (0, react_1.useAccountWallet)();
|
|
35
|
+
const { partnerId } = (0, react_1.useB3Config)();
|
|
36
|
+
const setB3ModalOpen = (0, react_1.useModalStore)(state => state.setB3ModalOpen);
|
|
37
|
+
const setB3ModalContentType = (0, react_1.useModalStore)(state => state.setB3ModalContentType);
|
|
38
|
+
// Get destination token data
|
|
39
|
+
const { data: dstTokenData } = (0, react_1.useTokenData)(destinationTokenChainId, destinationTokenAddress);
|
|
40
|
+
// Get token list for source chain
|
|
41
|
+
const { data: tokenList, isLoading: isLoadingTokens } = (0, useAnyspendTokens_1.useAnyspendTokenList)(selectedSrcChainId, tokenSearchQuery);
|
|
42
|
+
// Set default source token to destination token (same-chain, no swap needed)
|
|
43
|
+
(0, react_5.useEffect)(() => {
|
|
44
|
+
if (!selectedSrcToken && tokenList && tokenList.length > 0) {
|
|
45
|
+
// Try to find the destination token in the list
|
|
46
|
+
const dstToken = tokenList.find((t) => t.address.toLowerCase() === destinationTokenAddress.toLowerCase() && t.chainId === destinationTokenChainId);
|
|
47
|
+
if (dstToken) {
|
|
48
|
+
setSelectedSrcToken(dstToken);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Default to first token
|
|
52
|
+
setSelectedSrcToken(tokenList[0]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, [tokenList, selectedSrcToken, destinationTokenAddress, destinationTokenChainId]);
|
|
56
|
+
// Compute source amount from destination amount using quote
|
|
57
|
+
const isSameToken = selectedSrcToken &&
|
|
58
|
+
selectedSrcToken.address.toLowerCase() === destinationTokenAddress.toLowerCase() &&
|
|
59
|
+
selectedSrcToken.chainId === destinationTokenChainId;
|
|
60
|
+
const { anyspendQuote, isLoadingAnyspendQuote } = (0, useAnyspendQuote_1.useAnyspendQuote)({
|
|
61
|
+
type: "swap",
|
|
62
|
+
srcChain: selectedSrcChainId,
|
|
63
|
+
dstChain: destinationTokenChainId,
|
|
64
|
+
srcTokenAddress: selectedSrcToken?.address || "",
|
|
65
|
+
dstTokenAddress: destinationTokenAddress,
|
|
66
|
+
tradeType: "EXACT_OUTPUT",
|
|
67
|
+
amount: totalAmount,
|
|
68
|
+
});
|
|
69
|
+
// Get balance
|
|
70
|
+
const tokenAddress = selectedSrcToken
|
|
71
|
+
? (0, token_1.isNativeToken)(selectedSrcToken.address)
|
|
72
|
+
? "native"
|
|
73
|
+
: selectedSrcToken.address
|
|
74
|
+
: undefined;
|
|
75
|
+
const { data: balanceData } = (0, react_1.useSimTokenBalance)(walletAddress, tokenAddress, selectedSrcChainId);
|
|
76
|
+
const balance = (0, react_5.useMemo)(() => {
|
|
77
|
+
const b = balanceData?.balances?.[0];
|
|
78
|
+
if (!b?.amount)
|
|
79
|
+
return { raw: BigInt(0), formatted: "0", decimals: 18 };
|
|
80
|
+
return {
|
|
81
|
+
raw: BigInt(b.amount),
|
|
82
|
+
formatted: (0, number_1.formatTokenAmount)(BigInt(b.amount), b.decimals),
|
|
83
|
+
decimals: b.decimals,
|
|
84
|
+
};
|
|
85
|
+
}, [balanceData]);
|
|
86
|
+
// Determine the amount to pay in source token
|
|
87
|
+
const srcAmount = (0, react_5.useMemo)(() => {
|
|
88
|
+
if (isSameToken)
|
|
89
|
+
return totalAmount;
|
|
90
|
+
return anyspendQuote?.data?.currencyIn?.amount || "0";
|
|
91
|
+
}, [isSameToken, totalAmount, anyspendQuote]);
|
|
92
|
+
const srcAmountFormatted = (0, react_5.useMemo)(() => {
|
|
93
|
+
if (!selectedSrcToken)
|
|
94
|
+
return "0";
|
|
95
|
+
const decimals = selectedSrcToken.decimals || 18;
|
|
96
|
+
return (0, number_1.formatTokenAmount)(BigInt(srcAmount || "0"), decimals);
|
|
97
|
+
}, [srcAmount, selectedSrcToken]);
|
|
98
|
+
// Check if user has enough balance
|
|
99
|
+
const hasEnoughBalance = balance.raw >= BigInt(srcAmount || "0");
|
|
100
|
+
// Order tracking state
|
|
101
|
+
const [orderId, setOrderId] = (0, react_5.useState)();
|
|
102
|
+
const [isSendingDeposit, setIsSendingDeposit] = (0, react_5.useState)(false);
|
|
103
|
+
const depositSentRef = (0, react_5.useRef)(false);
|
|
104
|
+
// Wallet transaction execution
|
|
105
|
+
const { switchChainAndExecute } = (0, react_1.useUnifiedChainSwitchAndExecute)();
|
|
106
|
+
// Create order
|
|
107
|
+
const { createOrder, isCreatingOrder } = (0, useAnyspendCreateOrder_1.useAnyspendCreateOrder)({
|
|
108
|
+
onSuccess: (data) => {
|
|
109
|
+
const id = data?.data?.id;
|
|
110
|
+
if (id) {
|
|
111
|
+
setOrderId(id);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
onError: (error) => {
|
|
115
|
+
setIsSendingDeposit(false);
|
|
116
|
+
onError?.(error);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
// Poll order status until executed
|
|
120
|
+
const { orderAndTransactions: oat } = (0, useAnyspendOrderAndTransactions_1.useAnyspendOrderAndTransactions)(orderId);
|
|
121
|
+
// Send deposit transaction once order is created and ready
|
|
122
|
+
(0, react_5.useEffect)(() => {
|
|
123
|
+
if (!oat?.data?.order || depositSentRef.current)
|
|
124
|
+
return;
|
|
125
|
+
const order = oat.data.order;
|
|
126
|
+
if (order.status !== "scanning_deposit_transaction")
|
|
127
|
+
return;
|
|
128
|
+
if (oat.data.depositTxs?.length)
|
|
129
|
+
return; // Already deposited
|
|
130
|
+
depositSentRef.current = true;
|
|
131
|
+
const sendDeposit = async () => {
|
|
132
|
+
try {
|
|
133
|
+
setIsSendingDeposit(true);
|
|
134
|
+
const amount = BigInt(order.srcAmount);
|
|
135
|
+
if ((0, token_1.isNativeToken)(order.srcTokenAddress)) {
|
|
136
|
+
await switchChainAndExecute(order.srcChain, {
|
|
137
|
+
to: order.globalAddress,
|
|
138
|
+
value: amount,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const data = (0, viem_1.encodeFunctionData)({
|
|
143
|
+
abi: viem_1.erc20Abi,
|
|
144
|
+
functionName: "transfer",
|
|
145
|
+
args: [order.globalAddress, amount],
|
|
146
|
+
});
|
|
147
|
+
await switchChainAndExecute(order.srcChain, {
|
|
148
|
+
to: order.srcTokenAddress,
|
|
149
|
+
data,
|
|
150
|
+
value: BigInt(0),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
depositSentRef.current = false;
|
|
156
|
+
onError?.(error instanceof Error ? error : new Error(error?.message || "Transaction rejected"));
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
setIsSendingDeposit(false);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
sendDeposit();
|
|
163
|
+
}, [oat, switchChainAndExecute, onError]);
|
|
164
|
+
// Only call onSuccess when order is actually executed with a real txHash
|
|
165
|
+
(0, useOnOrderSuccess_1.useOnOrderSuccess)({
|
|
166
|
+
orderData: oat,
|
|
167
|
+
orderId,
|
|
168
|
+
onSuccess: (txHash) => {
|
|
169
|
+
onSuccess?.({ orderId, txHash });
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
const isWaitingForExecution = !!orderId && oat?.data?.order.status !== "executed";
|
|
173
|
+
const handlePay = (0, react_5.useCallback)(async () => {
|
|
174
|
+
if (!selectedSrcToken || !walletAddress)
|
|
175
|
+
return;
|
|
176
|
+
depositSentRef.current = false;
|
|
177
|
+
const dstToken = {
|
|
178
|
+
address: destinationTokenAddress,
|
|
179
|
+
chainId: destinationTokenChainId,
|
|
180
|
+
decimals: dstTokenData?.decimals || 18,
|
|
181
|
+
symbol: dstTokenData?.symbol || "",
|
|
182
|
+
name: dstTokenData?.name || "",
|
|
183
|
+
metadata: {
|
|
184
|
+
logoURI: dstTokenData?.logoURI || "",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
createOrder({
|
|
188
|
+
recipientAddress,
|
|
189
|
+
orderType: "swap",
|
|
190
|
+
srcChain: selectedSrcChainId,
|
|
191
|
+
dstChain: destinationTokenChainId,
|
|
192
|
+
srcToken: selectedSrcToken,
|
|
193
|
+
dstToken,
|
|
194
|
+
srcAmount,
|
|
195
|
+
expectedDstAmount: totalAmount,
|
|
196
|
+
callbackMetadata,
|
|
197
|
+
});
|
|
198
|
+
}, [
|
|
199
|
+
selectedSrcToken,
|
|
200
|
+
walletAddress,
|
|
201
|
+
recipientAddress,
|
|
202
|
+
selectedSrcChainId,
|
|
203
|
+
destinationTokenChainId,
|
|
204
|
+
destinationTokenAddress,
|
|
205
|
+
dstTokenData,
|
|
206
|
+
srcAmount,
|
|
207
|
+
totalAmount,
|
|
208
|
+
callbackMetadata,
|
|
209
|
+
createOrder,
|
|
210
|
+
]);
|
|
211
|
+
const handleSelectToken = (token) => {
|
|
212
|
+
setSelectedSrcToken(token);
|
|
213
|
+
setSelectedSrcChainId(token.chainId);
|
|
214
|
+
setShowTokenSelector(false);
|
|
215
|
+
setTokenSearchQuery("");
|
|
216
|
+
};
|
|
217
|
+
const isLoading = isLoadingAnyspendQuote || isLoadingTokens;
|
|
218
|
+
const isPending = isCreatingOrder || isSendingDeposit || isWaitingForExecution;
|
|
219
|
+
const canPay = walletAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
|
|
220
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-crypto-panel flex flex-col gap-4", classes?.cryptoPanel), children: [(0, jsx_runtime_1.jsxs)("div", { className: "anyspend-token-selector", children: [(0, jsx_runtime_1.jsx)("label", { className: "anyspend-token-label mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300", children: "Pay with" }), (0, jsx_runtime_1.jsxs)("button", { onClick: () => setShowTokenSelector(true), className: (0, cn_1.cn)("anyspend-token-btn flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:hover:border-gray-600", classes?.tokenSelector), children: [selectedSrcToken ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), (0, jsx_runtime_1.jsxs)("div", { className: "text-left", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-400", children: "Select token" })), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "h-4 w-4 text-gray-400" })] })] }), (0, jsx_runtime_1.jsx)(TokenSelectorModal, { open: showTokenSelector, onClose: () => {
|
|
221
|
+
setShowTokenSelector(false);
|
|
222
|
+
setTokenSearchQuery("");
|
|
223
|
+
}, tokenList: tokenList, isLoadingTokens: isLoadingTokens, tokenSearchQuery: tokenSearchQuery, onSearchChange: setTokenSearchQuery, onSelectToken: handleSelectToken, selectedToken: selectedSrcToken, walletAddress: walletAddress, chainId: selectedSrcChainId, onChainChange: chainId => {
|
|
224
|
+
setSelectedSrcChainId(chainId);
|
|
225
|
+
setSelectedSrcToken(null);
|
|
226
|
+
setTokenSearchQuery("");
|
|
227
|
+
} }), (0, jsx_runtime_1.jsx)(react_4.motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-quote-display rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-800/50", classes?.quoteDisplay), children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), (0, jsx_runtime_1.jsx)(react_4.AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? ((0, jsx_runtime_1.jsx)(react_4.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: (0, jsx_runtime_1.jsx)(react_2.TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : ((0, jsx_runtime_1.jsxs)(react_4.motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), (0, jsx_runtime_1.jsx)(react_4.AnimatePresence, { children: walletAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && ((0, jsx_runtime_1.jsxs)(react_4.motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "anyspend-balance-warning text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !walletAddress ? ((0, jsx_runtime_1.jsx)("button", { onClick: () => {
|
|
228
|
+
setB3ModalContentType({ type: "signInWithB3", showBackButton: false, chain: b3Chain_1.thirdwebB3Chain, partnerId });
|
|
229
|
+
setB3ModalOpen(true);
|
|
230
|
+
}, className: (0, cn_1.cn)("anyspend-crypto-pay-btn w-full rounded-xl px-4 py-3.5 text-sm font-semibold text-white transition-all", "bg-blue-600 hover:bg-blue-700 active:scale-[0.98]", classes?.payButton), style: themeColor ? { backgroundColor: themeColor } : undefined, children: "Connect Wallet to Pay" })) : ((0, jsx_runtime_1.jsx)("button", { onClick: handlePay, disabled: !canPay, className: (0, cn_1.cn)("anyspend-crypto-pay-btn w-full rounded-xl px-4 py-3.5 text-sm font-semibold text-white transition-all", canPay ? "bg-blue-600 hover:bg-blue-700 active:scale-[0.98]" : "cursor-not-allowed bg-blue-600 opacity-50", classes?.payButton), style: !canPay ? undefined : themeColor ? { backgroundColor: themeColor } : undefined, children: isPending ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingOrder
|
|
231
|
+
? "Creating order..."
|
|
232
|
+
: isSendingDeposit
|
|
233
|
+
? "Confirm in wallet..."
|
|
234
|
+
: "Confirming transaction..."] })) : (buttonText) }))] }));
|
|
235
|
+
}
|
|
236
|
+
const SOURCE_CHAINS = Object.values(chain_1.EVM_MAINNET).map(c => ({ id: c.id, name: c.name, logoUrl: c.logoUrl }));
|
|
237
|
+
function TokenSelectorModal({ open, onClose, tokenList, isLoadingTokens, tokenSearchQuery, onSearchChange, onSelectToken, selectedToken, walletAddress, chainId, onChainChange, }) {
|
|
238
|
+
const isMobile = (0, react_3.useIsMobile)();
|
|
239
|
+
const searchInputRef = (0, react_5.useRef)(null);
|
|
240
|
+
// Fetch all balances for the wallet on this chain
|
|
241
|
+
const { data: balanceData } = (0, react_1.useSimBalance)(walletAddress, [chainId]);
|
|
242
|
+
// Build a lookup map: lowercase token address -> balance info
|
|
243
|
+
const balanceMap = (0, react_5.useMemo)(() => {
|
|
244
|
+
const map = new Map();
|
|
245
|
+
if (!balanceData?.balances)
|
|
246
|
+
return map;
|
|
247
|
+
for (const b of balanceData.balances) {
|
|
248
|
+
if (b.amount && BigInt(b.amount) > BigInt(0)) {
|
|
249
|
+
map.set(b.address.toLowerCase(), {
|
|
250
|
+
raw: BigInt(b.amount),
|
|
251
|
+
formatted: (0, number_1.formatTokenAmount)(BigInt(b.amount), b.decimals),
|
|
252
|
+
decimals: b.decimals,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return map;
|
|
257
|
+
}, [balanceData]);
|
|
258
|
+
// Sort tokens: tokens with balance first (sorted by balance desc), then the rest
|
|
259
|
+
const sortedTokenList = (0, react_5.useMemo)(() => {
|
|
260
|
+
if (!tokenList)
|
|
261
|
+
return undefined;
|
|
262
|
+
const withBalance = [];
|
|
263
|
+
const withoutBalance = [];
|
|
264
|
+
for (const token of tokenList) {
|
|
265
|
+
const bal = balanceMap.get(token.address.toLowerCase());
|
|
266
|
+
if (bal) {
|
|
267
|
+
withBalance.push(token);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
withoutBalance.push(token);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
withBalance.sort((a, b) => {
|
|
274
|
+
const balA = balanceMap.get(a.address.toLowerCase())?.raw || BigInt(0);
|
|
275
|
+
const balB = balanceMap.get(b.address.toLowerCase())?.raw || BigInt(0);
|
|
276
|
+
if (balB > balA)
|
|
277
|
+
return 1;
|
|
278
|
+
if (balB < balA)
|
|
279
|
+
return -1;
|
|
280
|
+
return 0;
|
|
281
|
+
});
|
|
282
|
+
return [...withBalance, ...withoutBalance];
|
|
283
|
+
}, [tokenList, balanceMap]);
|
|
284
|
+
// Keep showing the previous list while new chain tokens are loading
|
|
285
|
+
const prevListRef = (0, react_5.useRef)(undefined);
|
|
286
|
+
if (sortedTokenList && sortedTokenList.length > 0) {
|
|
287
|
+
prevListRef.current = sortedTokenList;
|
|
288
|
+
}
|
|
289
|
+
const displayList = sortedTokenList && sortedTokenList.length > 0
|
|
290
|
+
? sortedTokenList
|
|
291
|
+
: isLoadingTokens
|
|
292
|
+
? prevListRef.current
|
|
293
|
+
: sortedTokenList;
|
|
294
|
+
// Focus search input when modal opens
|
|
295
|
+
(0, react_5.useEffect)(() => {
|
|
296
|
+
if (open) {
|
|
297
|
+
const timer = setTimeout(() => searchInputRef.current?.focus(), 100);
|
|
298
|
+
return () => clearTimeout(timer);
|
|
299
|
+
}
|
|
300
|
+
}, [open]);
|
|
301
|
+
const ModalComponent = isMobile ? drawer_1.Drawer : dialog_1.Dialog;
|
|
302
|
+
const ModalContent = isMobile ? drawer_1.DrawerContent : dialog_1.DialogContent;
|
|
303
|
+
const ModalTitle = isMobile ? drawer_1.DrawerTitle : dialog_1.DialogTitle;
|
|
304
|
+
const ModalDescription = isMobile ? drawer_1.DrawerDescription : dialog_1.DialogDescription;
|
|
305
|
+
return ((0, jsx_runtime_1.jsxs)(ModalComponent, { open: open, onOpenChange: (v) => {
|
|
306
|
+
if (!v)
|
|
307
|
+
onClose();
|
|
308
|
+
}, children: [(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: {
|
|
309
|
+
__html: `.anyspend-token-modal .b3-modal-ga-branding { display: none; } .anyspend-token-modal .modal-inner-content { margin-bottom: 0; }`,
|
|
310
|
+
} }), (0, jsx_runtime_1.jsxs)(ModalContent, { className: "anyspend-token-modal flex max-h-[80dvh] flex-col overflow-hidden rounded-2xl bg-white p-0 shadow-xl sm:max-h-[70dvh] dark:bg-gray-900", children: [(0, jsx_runtime_1.jsx)(ModalTitle, { className: "sr-only", children: "Select token" }), (0, jsx_runtime_1.jsx)(ModalDescription, { className: "sr-only", children: "Choose a token to pay with" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between px-5 py-4", children: (0, jsx_runtime_1.jsx)("h3", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "Select token" }) }), (0, jsx_runtime_1.jsx)("div", { className: "anyspend-chain-selector flex items-center gap-2 px-5 pb-3", children: SOURCE_CHAINS.map(chain => ((0, jsx_runtime_1.jsxs)("button", { onClick: () => onChainChange(chain.id), title: chain.name, className: "relative shrink-0 rounded-full transition-opacity", style: { opacity: chain.id === chainId ? 1 : 0.4 }, children: [(0, jsx_runtime_1.jsx)("img", { src: chain.logoUrl, alt: chain.name, className: "h-7 w-7 rounded-full" }), chain.id === chainId && ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 rounded-full", style: { boxShadow: "0 0 0 2px #3b82f6" } }))] }, chain.id))) }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-token-search flex items-center gap-2 border-b border-gray-100 px-5 py-2.5 dark:border-gray-800", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "h-4 w-4 shrink-0 text-gray-400" }), (0, jsx_runtime_1.jsx)("input", { ref: searchInputRef, type: "text", value: tokenSearchQuery, onChange: e => onSearchChange(e.target.value), placeholder: "Search tokens...", className: "anyspend-token-search-input w-full bg-transparent text-sm outline-none placeholder:text-gray-400 dark:text-gray-100" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-token-list relative flex-1 overflow-y-auto", style: { minHeight: 300 }, children: [displayList?.map((token) => {
|
|
311
|
+
const isSelected = selectedToken &&
|
|
312
|
+
selectedToken.address.toLowerCase() === token.address.toLowerCase() &&
|
|
313
|
+
selectedToken.chainId === token.chainId;
|
|
314
|
+
const tokenBalance = balanceMap.get(token.address.toLowerCase());
|
|
315
|
+
return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => onSelectToken(token), className: (0, cn_1.cn)("anyspend-token-option flex w-full items-center gap-3 px-5 py-3 text-left transition-colors hover:bg-gray-50 dark:hover:bg-gray-800", isSelected && "bg-blue-50 dark:bg-blue-900/20"), children: [(0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[token.chainId]?.logoUrl || "", tokenUrl: token.metadata?.logoURI, className: "h-8 w-8" }), (0, jsx_runtime_1.jsxs)("div", { className: "min-w-0 flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: token.symbol }), (0, jsx_runtime_1.jsx)("p", { className: "truncate text-xs text-gray-500 dark:text-gray-400", children: token.name })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [tokenBalance && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-300", children: tokenBalance.formatted })), isSelected && (0, jsx_runtime_1.jsx)("div", { className: "h-2 w-2 rounded-full bg-blue-600" })] })] }, `${token.chainId}-${token.address}`));
|
|
316
|
+
}), !isLoadingTokens && displayList && displayList.length === 0 && ((0, jsx_runtime_1.jsx)("div", { className: "px-5 py-8 text-center text-sm text-gray-400", children: "No tokens found" }))] })] })] })] }));
|
|
317
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
|
|
2
|
+
interface FiatCheckoutPanelProps {
|
|
3
|
+
recipientAddress: string;
|
|
4
|
+
destinationTokenAddress: string;
|
|
5
|
+
destinationTokenChainId: number;
|
|
6
|
+
totalAmount: string;
|
|
7
|
+
themeColor?: string;
|
|
8
|
+
onSuccess?: (result: {
|
|
9
|
+
txHash?: string;
|
|
10
|
+
orderId?: string;
|
|
11
|
+
}) => void;
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
classes?: AnySpendCheckoutClasses;
|
|
14
|
+
}
|
|
15
|
+
export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onError, classes, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FiatCheckoutPanel = FiatCheckoutPanel;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("../../../../anyspend/react");
|
|
7
|
+
const cn_1 = require("../../../../shared/utils/cn");
|
|
8
|
+
const number_1 = require("../../../../shared/utils/number");
|
|
9
|
+
const payment_utils_1 = require("../../../../shared/utils/payment.utils");
|
|
10
|
+
const react_2 = require("../../../../global-account/react");
|
|
11
|
+
const react_stripe_js_1 = require("@stripe/react-stripe-js");
|
|
12
|
+
const lucide_react_1 = require("lucide-react");
|
|
13
|
+
const react_3 = require("motion/react");
|
|
14
|
+
const react_4 = require("react");
|
|
15
|
+
function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onError, classes, }) {
|
|
16
|
+
const { data: tokenData } = (0, react_2.useTokenData)(destinationTokenChainId, destinationTokenAddress);
|
|
17
|
+
const { theme, stripePublishableKey } = (0, react_2.useB3Config)();
|
|
18
|
+
const formattedAmount = (0, react_4.useMemo)(() => {
|
|
19
|
+
const decimals = tokenData?.decimals || 18;
|
|
20
|
+
return (0, number_1.formatTokenAmount)(BigInt(totalAmount), decimals);
|
|
21
|
+
}, [totalAmount, tokenData]);
|
|
22
|
+
const { geoData, stripeOnrampSupport, stripeWeb2Support, isLoading: isLoadingGeo, } = (0, react_1.useGeoOnrampOptions)(formattedAmount);
|
|
23
|
+
// Order state
|
|
24
|
+
const [orderId, setOrderId] = (0, react_4.useState)(null);
|
|
25
|
+
const [stripePaymentIntentId, setStripePaymentIntentId] = (0, react_4.useState)(null);
|
|
26
|
+
const [orderError, setOrderError] = (0, react_4.useState)(null);
|
|
27
|
+
const orderCreatedRef = (0, react_4.useRef)(false);
|
|
28
|
+
const { createOrder, isCreatingOrder } = (0, react_1.useAnyspendCreateOnrampOrder)({
|
|
29
|
+
onSuccess: (data) => {
|
|
30
|
+
const id = data?.data?.id;
|
|
31
|
+
const intentId = data?.data?.stripePaymentIntentId;
|
|
32
|
+
if (id && intentId) {
|
|
33
|
+
setOrderId(id);
|
|
34
|
+
setStripePaymentIntentId(intentId);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
setOrderError("Failed to initialize payment. Please try again.");
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
onError: (error) => {
|
|
41
|
+
setOrderError(error.message || "Failed to create payment order.");
|
|
42
|
+
onError?.(error);
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
// Auto-create onramp order when Stripe Web2 is supported and all data is ready
|
|
46
|
+
(0, react_4.useEffect)(() => {
|
|
47
|
+
if (!isLoadingGeo &&
|
|
48
|
+
stripeWeb2Support?.isSupport &&
|
|
49
|
+
!orderCreatedRef.current &&
|
|
50
|
+
!orderId &&
|
|
51
|
+
!isCreatingOrder &&
|
|
52
|
+
!orderError &&
|
|
53
|
+
tokenData &&
|
|
54
|
+
recipientAddress) {
|
|
55
|
+
orderCreatedRef.current = true;
|
|
56
|
+
const dstToken = {
|
|
57
|
+
address: destinationTokenAddress,
|
|
58
|
+
chainId: destinationTokenChainId,
|
|
59
|
+
decimals: tokenData.decimals || 18,
|
|
60
|
+
symbol: tokenData.symbol || "",
|
|
61
|
+
name: tokenData.name || "",
|
|
62
|
+
metadata: {
|
|
63
|
+
logoURI: tokenData.logoURI || "",
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
createOrder({
|
|
67
|
+
recipientAddress,
|
|
68
|
+
orderType: "swap",
|
|
69
|
+
dstChain: destinationTokenChainId,
|
|
70
|
+
dstToken,
|
|
71
|
+
srcFiatAmount: formattedAmount,
|
|
72
|
+
onramp: {
|
|
73
|
+
vendor: "stripe-web2",
|
|
74
|
+
paymentMethod: "",
|
|
75
|
+
country: geoData?.country || "US",
|
|
76
|
+
redirectUrl: window.location.origin,
|
|
77
|
+
},
|
|
78
|
+
expectedDstAmount: totalAmount,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}, [
|
|
82
|
+
isLoadingGeo,
|
|
83
|
+
stripeWeb2Support,
|
|
84
|
+
orderId,
|
|
85
|
+
isCreatingOrder,
|
|
86
|
+
orderError,
|
|
87
|
+
tokenData,
|
|
88
|
+
recipientAddress,
|
|
89
|
+
destinationTokenAddress,
|
|
90
|
+
destinationTokenChainId,
|
|
91
|
+
formattedAmount,
|
|
92
|
+
totalAmount,
|
|
93
|
+
geoData,
|
|
94
|
+
createOrder,
|
|
95
|
+
]);
|
|
96
|
+
// Loading geo/stripe support check
|
|
97
|
+
if (isLoadingGeo) {
|
|
98
|
+
return ((0, jsx_runtime_1.jsxs)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-fiat-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_2.TextShimmer, { duration: 1.5, className: "text-sm", children: "Loading payment form..." })] }));
|
|
99
|
+
}
|
|
100
|
+
const hasStripeWeb2 = stripeWeb2Support && stripeWeb2Support.isSupport;
|
|
101
|
+
const hasStripeRedirect = !!stripeOnrampSupport;
|
|
102
|
+
// Not available in region
|
|
103
|
+
if (!hasStripeWeb2 && !hasStripeRedirect) {
|
|
104
|
+
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." }) }));
|
|
105
|
+
}
|
|
106
|
+
// Order creation error - show with retry
|
|
107
|
+
if (orderError) {
|
|
108
|
+
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: () => {
|
|
109
|
+
setOrderError(null);
|
|
110
|
+
orderCreatedRef.current = false;
|
|
111
|
+
}, className: "anyspend-fiat-retry text-sm font-medium text-blue-600 hover:text-blue-700 dark:text-blue-400", children: "Try again" })] }));
|
|
112
|
+
}
|
|
113
|
+
// Creating order / waiting for PaymentIntent
|
|
114
|
+
if (hasStripeWeb2 && (isCreatingOrder || !stripePaymentIntentId)) {
|
|
115
|
+
return ((0, jsx_runtime_1.jsxs)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-fiat-initializing 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_2.TextShimmer, { duration: 1.5, className: "text-sm", children: "Initializing secure payment..." })] }));
|
|
116
|
+
}
|
|
117
|
+
// Stripe Web2 embedded form
|
|
118
|
+
if (hasStripeWeb2 && stripePaymentIntentId && orderId) {
|
|
119
|
+
return ((0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3, ease: "easeOut" }, className: (0, cn_1.cn)("anyspend-fiat-stripe", classes?.fiatPanel), children: (0, jsx_runtime_1.jsx)(StripeCheckout, { stripePaymentIntentId: stripePaymentIntentId, stripePublishableKey: stripePublishableKey, theme: theme, themeColor: themeColor, orderId: orderId, onSuccess: onSuccess, onError: onError, classes: classes }) }));
|
|
120
|
+
}
|
|
121
|
+
// Fallback: Stripe redirect flow (only if web2 not available but redirect is)
|
|
122
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-fiat-redirect flex flex-col gap-3 py-2", classes?.fiatPanel), children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: "You'll be redirected to Stripe to complete your payment securely." }), (0, jsx_runtime_1.jsxs)("button", { className: (0, cn_1.cn)("anyspend-fiat-redirect-btn flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3.5 text-sm font-semibold text-white transition-all active:scale-[0.98]", "bg-blue-600 hover:bg-blue-700"), style: themeColor ? { backgroundColor: themeColor } : undefined, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "h-3.5 w-3.5" }), "Pay with Card"] }), (0, jsx_runtime_1.jsxs)("p", { className: "anyspend-fiat-secured flex items-center justify-center gap-1 text-xs text-gray-400", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "h-3 w-3" }), "Secured by Stripe"] })] }));
|
|
123
|
+
}
|
|
124
|
+
function StripeCheckout({ stripePaymentIntentId, stripePublishableKey, theme, themeColor, orderId, onSuccess, onError, classes, }) {
|
|
125
|
+
const { clientSecret, isLoadingStripeClientSecret, stripeClientSecretError } = (0, react_1.useStripeClientSecret)(stripePaymentIntentId);
|
|
126
|
+
if (isLoadingStripeClientSecret) {
|
|
127
|
+
return ((0, jsx_runtime_1.jsxs)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: "anyspend-stripe-loading flex flex-col items-center gap-3 py-6", 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_2.TextShimmer, { duration: 1.5, className: "text-sm", children: "Loading payment form..." })] }));
|
|
128
|
+
}
|
|
129
|
+
if (stripeClientSecretError || !clientSecret) {
|
|
130
|
+
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: "anyspend-stripe-error py-4 text-center", children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-500", children: stripeClientSecretError?.message || "Failed to load payment form. Please try again." }) }));
|
|
131
|
+
}
|
|
132
|
+
return ((0, jsx_runtime_1.jsx)(react_stripe_js_1.Elements, { stripe: (0, payment_utils_1.getStripePromise)(stripePublishableKey), options: {
|
|
133
|
+
clientSecret,
|
|
134
|
+
appearance: {
|
|
135
|
+
theme: theme === "light" ? "stripe" : "night",
|
|
136
|
+
variables: {
|
|
137
|
+
borderRadius: "8px",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
}, children: (0, jsx_runtime_1.jsx)(StripeCheckoutForm, { themeColor: themeColor, orderId: orderId, onSuccess: onSuccess, onError: onError, classes: classes }) }));
|
|
141
|
+
}
|
|
142
|
+
function StripeCheckoutForm({ themeColor, orderId, onSuccess, onError, classes }) {
|
|
143
|
+
const stripe = (0, react_stripe_js_1.useStripe)();
|
|
144
|
+
const elements = (0, react_stripe_js_1.useElements)();
|
|
145
|
+
const [loading, setLoading] = (0, react_4.useState)(false);
|
|
146
|
+
const [message, setMessage] = (0, react_4.useState)(null);
|
|
147
|
+
const [stripeReady, setStripeReady] = (0, react_4.useState)(false);
|
|
148
|
+
const [showAddressElement, setShowAddressElement] = (0, react_4.useState)(false);
|
|
149
|
+
(0, react_4.useEffect)(() => {
|
|
150
|
+
if (stripe && elements) {
|
|
151
|
+
setStripeReady(true);
|
|
152
|
+
}
|
|
153
|
+
}, [stripe, elements]);
|
|
154
|
+
const handlePaymentElementChange = (0, react_4.useCallback)((event) => {
|
|
155
|
+
setShowAddressElement(event.value?.type === "card");
|
|
156
|
+
}, []);
|
|
157
|
+
const handleSubmit = async (e) => {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
if (!stripe || !elements) {
|
|
160
|
+
setMessage("Payment system is not ready. Please wait.");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
setLoading(true);
|
|
164
|
+
setMessage(null);
|
|
165
|
+
try {
|
|
166
|
+
const result = (await stripe.confirmPayment({
|
|
167
|
+
elements,
|
|
168
|
+
redirect: "if_required",
|
|
169
|
+
}));
|
|
170
|
+
if (result.error) {
|
|
171
|
+
setMessage(result.error.message || "Payment failed. Please try again.");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// Payment succeeded
|
|
175
|
+
onSuccess?.({ orderId, txHash: undefined });
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const errorMessage = error?.message || "Payment failed. Please try again.";
|
|
179
|
+
setMessage(errorMessage);
|
|
180
|
+
onError?.(error instanceof Error ? error : new Error(errorMessage));
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
setLoading(false);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const stripeElementOptions = {
|
|
187
|
+
layout: "tabs",
|
|
188
|
+
fields: {
|
|
189
|
+
billingDetails: "auto",
|
|
190
|
+
},
|
|
191
|
+
wallets: {
|
|
192
|
+
applePay: "auto",
|
|
193
|
+
googlePay: "auto",
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
if (!stripeReady) {
|
|
197
|
+
return ((0, jsx_runtime_1.jsxs)(react_3.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.2, ease: "easeOut" }, className: "anyspend-stripe-loading flex flex-col items-center gap-3 py-6", 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_2.TextShimmer, { duration: 1.5, className: "text-sm", children: "Loading payment form..." })] }));
|
|
198
|
+
}
|
|
199
|
+
return ((0, jsx_runtime_1.jsxs)(react_3.motion.form, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3, ease: "easeOut" }, onSubmit: handleSubmit, className: "anyspend-stripe-form flex flex-col gap-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "anyspend-stripe-payment-element", children: (0, jsx_runtime_1.jsx)(react_stripe_js_1.PaymentElement, { onChange: handlePaymentElementChange, options: stripeElementOptions }) }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { initial: false, children: showAddressElement && ((0, jsx_runtime_1.jsx)(react_3.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.25, ease: "easeOut" }, style: { overflow: "hidden" }, className: "anyspend-stripe-address-element", children: (0, jsx_runtime_1.jsx)(react_stripe_js_1.AddressElement, { options: {
|
|
200
|
+
mode: "billing",
|
|
201
|
+
fields: {
|
|
202
|
+
phone: "always",
|
|
203
|
+
},
|
|
204
|
+
display: {
|
|
205
|
+
name: "split",
|
|
206
|
+
},
|
|
207
|
+
validation: {
|
|
208
|
+
phone: {
|
|
209
|
+
required: "always",
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
} }) }, "address-element")) }), (0, jsx_runtime_1.jsx)(react_3.AnimatePresence, { initial: false, children: message && ((0, jsx_runtime_1.jsx)(react_3.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: "anyspend-stripe-error 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-sm text-red-600 dark:text-red-400", children: message }) }, "stripe-error")) }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: !stripe || !elements || loading, className: (0, cn_1.cn)("anyspend-stripe-submit flex w-full items-center justify-center gap-2 rounded-xl px-4 py-3.5 text-sm font-semibold text-white transition-all", !stripe || !elements || loading
|
|
213
|
+
? "cursor-not-allowed bg-gray-300 dark:bg-gray-600"
|
|
214
|
+
: "bg-blue-600 hover:bg-blue-700 active:scale-[0.98]", classes?.payButton), style: themeColor && !loading ? { backgroundColor: themeColor } : undefined, children: loading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin" }), "Processing..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "h-3.5 w-3.5" }), "Complete Payment"] })) }), (0, jsx_runtime_1.jsxs)("p", { className: "anyspend-fiat-secured flex items-center justify-center gap-1 text-xs text-gray-400", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "h-3 w-3" }), "Secured by Stripe"] })] }));
|
|
215
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
|
|
2
|
+
interface PoweredByBrandingProps {
|
|
3
|
+
organizationName?: string;
|
|
4
|
+
organizationLogo?: string;
|
|
5
|
+
classes?: AnySpendCheckoutClasses;
|
|
6
|
+
}
|
|
7
|
+
export declare function PoweredByBranding({ organizationName, organizationLogo, classes }: PoweredByBrandingProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PoweredByBranding = PoweredByBranding;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const cn_1 = require("../../../../shared/utils/cn");
|
|
7
|
+
function PoweredByBranding({ organizationName, organizationLogo, classes }) {
|
|
8
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex items-center justify-between pt-4", classes?.poweredBy), children: [organizationLogo || organizationName ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [organizationLogo && ((0, jsx_runtime_1.jsx)("img", { src: organizationLogo, alt: organizationName || "Organization", className: "h-5 w-5 rounded-full" })), organizationName && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-400", children: organizationName }))] })) : ((0, jsx_runtime_1.jsx)("div", {})), (0, jsx_runtime_1.jsxs)("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: ["powered by ", (0, jsx_runtime_1.jsx)("span", { className: "font-medium text-gray-500 dark:text-gray-400", children: "anyspend" })] })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
|
|
2
|
+
interface QRCheckoutPanelProps {
|
|
3
|
+
recipientAddress: string;
|
|
4
|
+
destinationTokenAddress: string;
|
|
5
|
+
destinationTokenChainId: number;
|
|
6
|
+
totalAmount: string;
|
|
7
|
+
themeColor?: string;
|
|
8
|
+
onSuccess?: (result: {
|
|
9
|
+
txHash?: string;
|
|
10
|
+
orderId?: string;
|
|
11
|
+
}) => void;
|
|
12
|
+
onError?: (error: Error) => void;
|
|
13
|
+
callbackMetadata?: Record<string, unknown>;
|
|
14
|
+
classes?: AnySpendCheckoutClasses;
|
|
15
|
+
}
|
|
16
|
+
export declare function QRCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onError, callbackMetadata: _callbackMetadata, classes, }: QRCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export {};
|