@b3dotfun/sdk 0.0.79 → 0.0.80-alpha.0
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/AnySpendCollectorClubPurchase.js +9 -2
- package/dist/cjs/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +53 -13
- package/dist/cjs/anyspend/react/components/AnySpendNFT.js +6 -9
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +32 -12
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +6 -6
- package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
- package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/cjs/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
- package/dist/cjs/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/cjs/anyspend/react/hooks/useStripeSupport.js +1 -0
- package/dist/cjs/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
- package/dist/cjs/shared/utils/ipfs.d.ts +3 -3
- package/dist/cjs/shared/utils/ipfs.js +8 -8
- package/dist/esm/anyspend/react/components/AnySpendCollectorClubPurchase.js +9 -2
- package/dist/esm/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +53 -13
- package/dist/esm/anyspend/react/components/AnySpendNFT.js +7 -10
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +1 -3
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +32 -12
- package/dist/esm/anyspend/react/components/common/PanelOnramp.js +6 -6
- package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +5 -3
- package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/esm/anyspend/react/hooks/useGeoOnrampOptions.js +4 -2
- package/dist/esm/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/esm/anyspend/react/hooks/useStripeSupport.js +1 -0
- package/dist/esm/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +5 -3
- package/dist/esm/shared/utils/ipfs.d.ts +3 -3
- package/dist/esm/shared/utils/ipfs.js +8 -8
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpendCustom.d.ts +2 -0
- package/dist/types/anyspend/react/components/common/FiatPaymentMethod.d.ts +2 -1
- package/dist/types/anyspend/react/hooks/useGeoOnrampOptions.d.ts +2 -8
- package/dist/types/anyspend/react/hooks/useStripeSupport.d.ts +1 -0
- package/dist/types/shared/utils/ipfs.d.ts +3 -3
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendCollectorClubPurchase.tsx +9 -1
- package/src/anyspend/react/components/AnySpendCustom.tsx +71 -26
- package/src/anyspend/react/components/AnySpendNFT.tsx +49 -48
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +1 -4
- package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +45 -21
- package/src/anyspend/react/components/common/PanelOnramp.tsx +13 -13
- package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +53 -16
- package/src/anyspend/react/hooks/useGeoOnrampOptions.ts +5 -2
- package/src/anyspend/react/hooks/useStripeSupport.ts +1 -0
- package/src/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.tsx +5 -7
- package/src/shared/utils/ipfs.ts +8 -8
|
@@ -44,7 +44,6 @@ import { motion } from "motion/react";
|
|
|
44
44
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
45
45
|
|
|
46
46
|
import { base } from "viem/chains";
|
|
47
|
-
import { useFeatureFlags } from "../contexts/FeatureFlagsContext";
|
|
48
47
|
import { useAutoSetActiveWalletFromWagmi } from "../hooks/useAutoSetActiveWalletFromWagmi";
|
|
49
48
|
import { useCryptoPaymentMethodState } from "../hooks/useCryptoPaymentMethodState";
|
|
50
49
|
import { useRecipientAddressState } from "../hooks/useRecipientAddressState";
|
|
@@ -188,6 +187,8 @@ export function AnySpendCustom(props: {
|
|
|
188
187
|
onSuccess?: (txHash?: string) => void;
|
|
189
188
|
showRecipient?: boolean;
|
|
190
189
|
onShowPointsDetail?: () => void;
|
|
190
|
+
/** Fiat amount in USD for fiat payments */
|
|
191
|
+
srcFiatAmount?: string;
|
|
191
192
|
}) {
|
|
192
193
|
const fingerprintConfig = getFingerprintConfig();
|
|
193
194
|
|
|
@@ -215,6 +216,7 @@ function AnySpendCustomInner({
|
|
|
215
216
|
onSuccess,
|
|
216
217
|
showRecipient = true,
|
|
217
218
|
onShowPointsDetail,
|
|
219
|
+
srcFiatAmount: srcFiatAmountProps,
|
|
218
220
|
}: {
|
|
219
221
|
loadOrder?: string;
|
|
220
222
|
mode?: "modal" | "page";
|
|
@@ -238,9 +240,9 @@ function AnySpendCustomInner({
|
|
|
238
240
|
onSuccess?: (txHash?: string) => void;
|
|
239
241
|
showRecipient?: boolean;
|
|
240
242
|
onShowPointsDetail?: () => void;
|
|
243
|
+
srcFiatAmount?: string;
|
|
241
244
|
}) {
|
|
242
245
|
const hasMounted = useHasMounted();
|
|
243
|
-
const featureFlags = useFeatureFlags();
|
|
244
246
|
|
|
245
247
|
const searchParams = useSearchParamsSSR();
|
|
246
248
|
const router = useRouter();
|
|
@@ -402,14 +404,31 @@ function AnySpendCustomInner({
|
|
|
402
404
|
}
|
|
403
405
|
}, [activeTab, anyspendQuote?.data]);
|
|
404
406
|
const formattedSrcAmount = srcAmount ? formatTokenAmount(srcAmount, srcToken.decimals, 6, false) : null;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
407
|
+
// Calculate fiat amount for geo check (regardless of activeTab) to determine tab availability
|
|
408
|
+
const srcFiatAmountForGeoCheck = useMemo(() => {
|
|
409
|
+
// Use prop if provided
|
|
410
|
+
if (srcFiatAmountProps) {
|
|
411
|
+
return srcFiatAmountProps;
|
|
412
|
+
}
|
|
413
|
+
// Fallback to dstAmount if destination token is USDC
|
|
414
|
+
if (dstAmount && dstToken.address.toLowerCase() === USDC_BASE.address.toLowerCase()) {
|
|
415
|
+
return formatUnits(dstAmount, USDC_BASE.decimals);
|
|
416
|
+
}
|
|
417
|
+
// Use srcAmount if available (from quote)
|
|
418
|
+
if (srcAmount) {
|
|
419
|
+
return formatUnits(srcAmount.toString(), USDC_BASE.decimals);
|
|
420
|
+
}
|
|
421
|
+
return "0";
|
|
422
|
+
}, [srcAmount, srcFiatAmountProps, dstAmount, dstToken.address]);
|
|
423
|
+
|
|
424
|
+
const srcFiatAmount = useMemo(() => {
|
|
425
|
+
if (activeTab !== "fiat") return "0";
|
|
426
|
+
return srcFiatAmountForGeoCheck;
|
|
427
|
+
}, [activeTab, srcFiatAmountForGeoCheck]);
|
|
409
428
|
|
|
410
|
-
// Get geo data and onramp options (
|
|
411
|
-
const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeWeb2Support } =
|
|
412
|
-
useGeoOnrampOptions(
|
|
429
|
+
// Get geo data and onramp options (use srcFiatAmountForGeoCheck to check availability regardless of activeTab)
|
|
430
|
+
const { geoData, isOnrampSupported, coinbaseAvailablePaymentMethods, stripeOnrampSupport, stripeWeb2Support } =
|
|
431
|
+
useGeoOnrampOptions(srcFiatAmountForGeoCheck);
|
|
413
432
|
|
|
414
433
|
useEffect(() => {
|
|
415
434
|
if (oat?.data?.order.status === "executed" && !onSuccessCalled.current) {
|
|
@@ -521,9 +540,14 @@ function AnySpendCustomInner({
|
|
|
521
540
|
// Get the current geo data from the hook
|
|
522
541
|
const currentGeoData = geoData;
|
|
523
542
|
|
|
543
|
+
// Use total amount from quote (includes fees) for onramp, fallback to srcFiatAmount
|
|
544
|
+
const onrampAmount = anyspendQuote?.data?.currencyIn?.amountUsd
|
|
545
|
+
? anyspendQuote.data.currencyIn.amountUsd.toString()
|
|
546
|
+
: srcFiatAmount;
|
|
547
|
+
|
|
524
548
|
void createOnrampOrder({
|
|
525
549
|
...createOrderParams,
|
|
526
|
-
srcFiatAmount:
|
|
550
|
+
srcFiatAmount: onrampAmount,
|
|
527
551
|
onramp: {
|
|
528
552
|
vendor: onramp.vendor,
|
|
529
553
|
paymentMethod: onramp.paymentMethod,
|
|
@@ -601,12 +625,21 @@ function AnySpendCustomInner({
|
|
|
601
625
|
}
|
|
602
626
|
vendor = "coinbase";
|
|
603
627
|
paymentMethodString = coinbaseAvailablePaymentMethods[0]?.id || "";
|
|
604
|
-
} else if (paymentMethod === FiatPaymentMethod.
|
|
628
|
+
} else if (paymentMethod === FiatPaymentMethod.STRIPE_WEB2) {
|
|
629
|
+
// Stripe Web2 embedded payment
|
|
605
630
|
if (!stripeWeb2Support || !stripeWeb2Support.isSupport) {
|
|
606
|
-
toast.error("Stripe not available");
|
|
631
|
+
toast.error("Stripe embedded payment not available");
|
|
607
632
|
return;
|
|
608
633
|
}
|
|
609
|
-
vendor =
|
|
634
|
+
vendor = "stripe-web2";
|
|
635
|
+
paymentMethodString = "";
|
|
636
|
+
} else if (paymentMethod === FiatPaymentMethod.STRIPE) {
|
|
637
|
+
// Stripe redirect (one-click buy URL)
|
|
638
|
+
if (!stripeOnrampSupport) {
|
|
639
|
+
toast.error("Stripe redirect payment not available");
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
vendor = "stripe";
|
|
610
643
|
paymentMethodString = "";
|
|
611
644
|
} else {
|
|
612
645
|
toast.error("Please select a payment method");
|
|
@@ -803,7 +836,7 @@ function AnySpendCustomInner({
|
|
|
803
836
|
|
|
804
837
|
// Render points badge if conditions are met
|
|
805
838
|
const renderPointsBadge = () => {
|
|
806
|
-
if (
|
|
839
|
+
if (anyspendQuote?.data?.pointsAmount && anyspendQuote.data.pointsAmount > 0) {
|
|
807
840
|
return (
|
|
808
841
|
<PointsBadge
|
|
809
842
|
pointsAmount={anyspendQuote.data.pointsAmount}
|
|
@@ -1162,20 +1195,32 @@ function AnySpendCustomInner({
|
|
|
1162
1195
|
<div className="flex flex-col items-end gap-0.5">
|
|
1163
1196
|
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
1164
1197
|
{renderPointsBadge()}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1198
|
+
{isLoadingAnyspendQuote ? (
|
|
1199
|
+
<div className="bg-as-surface-secondary h-7 w-16 animate-pulse rounded" />
|
|
1200
|
+
) : (
|
|
1201
|
+
<span className="text-as-primary whitespace-nowrap text-xl font-semibold">
|
|
1202
|
+
{anyspendQuote?.data?.currencyIn?.amountUsd ? (
|
|
1203
|
+
`$${Number(anyspendQuote.data.currencyIn.amountUsd).toFixed(2)}`
|
|
1204
|
+
) : (
|
|
1205
|
+
<>
|
|
1206
|
+
${parseFloat(srcFiatAmount || "0").toFixed(2)}
|
|
1207
|
+
<span className="text-as-tertiarry text-base">+</span>
|
|
1208
|
+
</>
|
|
1209
|
+
)}
|
|
1210
|
+
</span>
|
|
1211
|
+
)}
|
|
1168
1212
|
</div>
|
|
1169
|
-
{
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
{(
|
|
1213
|
+
{(() => {
|
|
1214
|
+
if (anyspendQuote?.data?.fee?.type === "stripeweb2_fee" && anyspendQuote.data.fee.originalAmount) {
|
|
1215
|
+
const fee =
|
|
1173
1216
|
(Number(anyspendQuote.data.fee.originalAmount) - Number(anyspendQuote.data.fee.finalAmount)) /
|
|
1174
|
-
1e6
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1217
|
+
1e6;
|
|
1218
|
+
if (fee > 0) {
|
|
1219
|
+
return <span className="text-as-secondary text-xs">incl. ${fee.toFixed(2)} fee</span>;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return null;
|
|
1223
|
+
})()}
|
|
1179
1224
|
</div>
|
|
1180
1225
|
</motion.div>
|
|
1181
1226
|
</div>
|
|
@@ -7,7 +7,7 @@ import { getIpfsUrl } from "@b3dotfun/sdk/shared/utils/ipfs";
|
|
|
7
7
|
import { formatDisplayNumber, formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
|
|
8
8
|
import { MoreVertical } from "lucide-react";
|
|
9
9
|
import { AnimatePresence } from "motion/react";
|
|
10
|
-
import { useEffect, useState } from "react";
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
11
|
import { b3 } from "viem/chains";
|
|
12
12
|
import { GetQuoteResponse } from "../../types/api_req_res";
|
|
13
13
|
import { AnySpendCustom } from "./AnySpendCustom";
|
|
@@ -46,19 +46,19 @@ export function AnySpendNFT({
|
|
|
46
46
|
onShowPointsDetail?: () => void;
|
|
47
47
|
}) {
|
|
48
48
|
const [imageUrlWithFallback, setFallbackImageUrl] = useState<string | null>(nftContract.imageUrl);
|
|
49
|
-
const
|
|
49
|
+
const hasFetchedRef = useRef(false);
|
|
50
50
|
|
|
51
51
|
// Fetch contract metadata when imageUrl is empty
|
|
52
52
|
useEffect(() => {
|
|
53
53
|
async function fetchContractMetadata() {
|
|
54
54
|
// fetch image Uri if not provided
|
|
55
|
-
if (nftContract.imageUrl ||
|
|
55
|
+
if (nftContract.imageUrl || hasFetchedRef.current) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
setIsLoadingFallback(true);
|
|
59
|
+
hasFetchedRef.current = true;
|
|
61
60
|
|
|
61
|
+
try {
|
|
62
62
|
// Use the chainIdToPublicClient utility function
|
|
63
63
|
const publicClient = chainIdToPublicClient(nftContract.chainId);
|
|
64
64
|
|
|
@@ -96,56 +96,57 @@ export function AnySpendNFT({
|
|
|
96
96
|
}
|
|
97
97
|
} catch (error) {
|
|
98
98
|
console.error("Error fetching contract metadata:", error);
|
|
99
|
-
} finally {
|
|
100
|
-
setIsLoadingFallback(false);
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
fetchContractMetadata();
|
|
105
|
-
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId
|
|
106
|
-
|
|
107
|
-
const header = (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<div className="
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<div className="absolute inset-0 rounded-xl border border-white/10"></div>
|
|
122
|
-
</GlareCard>
|
|
123
|
-
|
|
124
|
-
<DropdownMenu nftContract={nftContract} />
|
|
125
|
-
</div>
|
|
126
|
-
<div className="from-b3-react-background to-as-on-surface-1 -mb-5 mt-[-100px] w-full rounded-t-lg bg-gradient-to-t">
|
|
127
|
-
<div className="h-[100px] w-full" />
|
|
128
|
-
<div className="mb-1 flex w-full flex-col items-center gap-2 p-5">
|
|
129
|
-
<span className="font-sf-rounded text-2xl font-semibold">{nftContract.name}</span>
|
|
130
|
-
|
|
131
|
-
<div className="flex w-fit items-center gap-1">
|
|
132
|
-
{anyspendPrice ? (
|
|
133
|
-
<AnimatePresence mode="wait">
|
|
134
|
-
<div
|
|
135
|
-
className={cn("text-as-primary group flex items-center text-3xl font-semibold transition-all", {
|
|
136
|
-
"opacity-0": isLoadingAnyspendPrice,
|
|
137
|
-
})}
|
|
138
|
-
>
|
|
139
|
-
{formatDisplayNumber(anyspendPrice?.data?.currencyIn?.amountUsd, { style: "currency" })}
|
|
140
|
-
</div>
|
|
141
|
-
</AnimatePresence>
|
|
142
|
-
) : (
|
|
143
|
-
<div className="h-[36px] w-full" />
|
|
103
|
+
}, [nftContract.contractAddress, nftContract.chainId, nftContract.imageUrl, nftContract.tokenId]);
|
|
104
|
+
|
|
105
|
+
const header = useCallback(
|
|
106
|
+
({
|
|
107
|
+
anyspendPrice,
|
|
108
|
+
isLoadingAnyspendPrice,
|
|
109
|
+
}: {
|
|
110
|
+
anyspendPrice: GetQuoteResponse | undefined;
|
|
111
|
+
isLoadingAnyspendPrice: boolean;
|
|
112
|
+
}) => (
|
|
113
|
+
<>
|
|
114
|
+
<div className="relative size-[200px]">
|
|
115
|
+
<div className="absolute inset-0 scale-95 bg-black/30 blur-md"></div>
|
|
116
|
+
<GlareCard className="overflow-hidden">
|
|
117
|
+
{imageUrlWithFallback && (
|
|
118
|
+
<img src={imageUrlWithFallback} alt={nftContract.name} className="size-full object-cover" />
|
|
144
119
|
)}
|
|
120
|
+
<div className="absolute inset-0 rounded-xl border border-white/10"></div>
|
|
121
|
+
</GlareCard>
|
|
122
|
+
|
|
123
|
+
<DropdownMenu nftContract={nftContract} />
|
|
124
|
+
</div>
|
|
125
|
+
<div className="from-b3-react-background to-as-on-surface-1 -mb-5 mt-[-100px] w-full rounded-t-lg bg-gradient-to-t">
|
|
126
|
+
<div className="h-[100px] w-full" />
|
|
127
|
+
<div className="mb-1 flex w-full flex-col items-center gap-2 p-5">
|
|
128
|
+
<span className="font-sf-rounded text-2xl font-semibold">{nftContract.name}</span>
|
|
129
|
+
|
|
130
|
+
<div className="flex w-fit items-center gap-1">
|
|
131
|
+
{anyspendPrice ? (
|
|
132
|
+
<AnimatePresence mode="wait">
|
|
133
|
+
<div
|
|
134
|
+
className={cn("text-as-primary group flex items-center text-3xl font-semibold transition-all", {
|
|
135
|
+
"opacity-0": isLoadingAnyspendPrice,
|
|
136
|
+
})}
|
|
137
|
+
>
|
|
138
|
+
{formatDisplayNumber(anyspendPrice?.data?.currencyIn?.amountUsd, { style: "currency" })}
|
|
139
|
+
</div>
|
|
140
|
+
</AnimatePresence>
|
|
141
|
+
) : (
|
|
142
|
+
<div className="h-[36px] w-full" />
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
145
|
</div>
|
|
146
146
|
</div>
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
</>
|
|
148
|
+
),
|
|
149
|
+
[imageUrlWithFallback, nftContract],
|
|
149
150
|
);
|
|
150
151
|
|
|
151
152
|
return (
|
|
@@ -5,7 +5,6 @@ import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
|
|
|
5
5
|
import { ChevronRight, Info } from "lucide-react";
|
|
6
6
|
import { motion } from "motion/react";
|
|
7
7
|
import { components } from "../../../types/api";
|
|
8
|
-
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext";
|
|
9
8
|
import { OrderTokenAmount } from "./OrderTokenAmount";
|
|
10
9
|
import { PointsBadge } from "./PointsBadge";
|
|
11
10
|
|
|
@@ -55,8 +54,6 @@ export function CryptoReceiveSection({
|
|
|
55
54
|
onShowPointsDetail,
|
|
56
55
|
onShowFeeDetail,
|
|
57
56
|
}: CryptoReceiveSectionProps) {
|
|
58
|
-
const featureFlags = useFeatureFlags();
|
|
59
|
-
|
|
60
57
|
return (
|
|
61
58
|
<motion.div
|
|
62
59
|
initial={{ opacity: 0, y: 20, filter: "blur(10px)" }}
|
|
@@ -184,7 +181,7 @@ export function CryptoReceiveSection({
|
|
|
184
181
|
);
|
|
185
182
|
})()}
|
|
186
183
|
</div>
|
|
187
|
-
{
|
|
184
|
+
{anyspendQuote?.data?.pointsAmount > 0 && (
|
|
188
185
|
<PointsBadge
|
|
189
186
|
pointsAmount={anyspendQuote.data.pointsAmount}
|
|
190
187
|
pointsMultiplier={anyspendQuote.data.pointsMultiplier}
|
|
@@ -7,7 +7,8 @@ import { ChevronLeft, ChevronRight, Loader2 } from "lucide-react";
|
|
|
7
7
|
export enum FiatPaymentMethod {
|
|
8
8
|
NONE = "none",
|
|
9
9
|
COINBASE_PAY = "coinbase_pay",
|
|
10
|
-
STRIPE = "stripe",
|
|
10
|
+
STRIPE = "stripe", // Stripe redirect (one-click buy URL)
|
|
11
|
+
STRIPE_WEB2 = "stripe_web2", // Stripe embedded payment
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
interface FiatPaymentMethodProps {
|
|
@@ -25,30 +26,34 @@ export function FiatPaymentMethodComponent({
|
|
|
25
26
|
onSelectPaymentMethod,
|
|
26
27
|
srcAmountOnRamp,
|
|
27
28
|
}: FiatPaymentMethodProps) {
|
|
29
|
+
// Load geo-based onramp options like in PanelOnramp
|
|
30
|
+
const {
|
|
31
|
+
coinbaseAvailablePaymentMethods,
|
|
32
|
+
stripeOnrampSupport,
|
|
33
|
+
stripeWeb2Support,
|
|
34
|
+
isLoading: isLoadingGeoOnramp,
|
|
35
|
+
} = useGeoOnrampOptions(srcAmountOnRamp);
|
|
36
|
+
|
|
28
37
|
// Helper function to get fees from API data
|
|
29
38
|
const getFeeFromApi = (paymentMethod: FiatPaymentMethod): string | null => {
|
|
30
39
|
switch (paymentMethod) {
|
|
31
40
|
case FiatPaymentMethod.COINBASE_PAY:
|
|
32
41
|
// Coinbase doesn't provide fee info in API, return null
|
|
33
42
|
return null;
|
|
34
|
-
case FiatPaymentMethod.
|
|
35
|
-
// Get fee from Stripe API response
|
|
43
|
+
case FiatPaymentMethod.STRIPE_WEB2:
|
|
44
|
+
// Get fee from Stripe Web2 API response
|
|
36
45
|
if (stripeWeb2Support && "formattedFeeUsd" in stripeWeb2Support) {
|
|
37
46
|
return stripeWeb2Support.formattedFeeUsd;
|
|
38
47
|
}
|
|
39
48
|
return null;
|
|
49
|
+
case FiatPaymentMethod.STRIPE:
|
|
50
|
+
// Stripe redirect doesn't have fee info from API
|
|
51
|
+
return null;
|
|
40
52
|
default:
|
|
41
53
|
return null; // No fee when no payment method selected
|
|
42
54
|
}
|
|
43
55
|
};
|
|
44
56
|
|
|
45
|
-
// Load geo-based onramp options like in PanelOnramp
|
|
46
|
-
const {
|
|
47
|
-
coinbaseAvailablePaymentMethods,
|
|
48
|
-
stripeWeb2Support,
|
|
49
|
-
isLoading: isLoadingGeoOnramp,
|
|
50
|
-
} = useGeoOnrampOptions(srcAmountOnRamp);
|
|
51
|
-
|
|
52
57
|
// Generate payment methods based on geo availability (like in PanelOnrampPayment)
|
|
53
58
|
const availablePaymentMethods = [];
|
|
54
59
|
|
|
@@ -65,15 +70,28 @@ export function FiatPaymentMethodComponent({
|
|
|
65
70
|
});
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
// Add Stripe if available
|
|
69
|
-
if (
|
|
70
|
-
const stripeFee = getFeeFromApi(FiatPaymentMethod.
|
|
73
|
+
// Add Stripe redirect (one-click) if available - primary option
|
|
74
|
+
if (stripeOnrampSupport) {
|
|
75
|
+
const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2); // Use same fee estimate
|
|
71
76
|
availablePaymentMethods.push({
|
|
72
77
|
id: FiatPaymentMethod.STRIPE,
|
|
73
|
-
name: "
|
|
74
|
-
description: "
|
|
75
|
-
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` :
|
|
76
|
-
badgeColor: "bg-
|
|
78
|
+
name: "Credit/Debit Card",
|
|
79
|
+
description: "Pay via Stripe checkout",
|
|
80
|
+
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
|
|
81
|
+
badgeColor: "bg-gray-100 text-gray-800",
|
|
82
|
+
available: true,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add Stripe Web2 (embedded) if available - secondary option
|
|
87
|
+
if (stripeWeb2Support && stripeWeb2Support.isSupport) {
|
|
88
|
+
const stripeFee = getFeeFromApi(FiatPaymentMethod.STRIPE_WEB2);
|
|
89
|
+
availablePaymentMethods.push({
|
|
90
|
+
id: FiatPaymentMethod.STRIPE_WEB2,
|
|
91
|
+
name: "Quick Pay",
|
|
92
|
+
description: "Credit or debit card",
|
|
93
|
+
badge: stripeFee ? `$${Number(stripeFee).toFixed(2)} fee` : undefined,
|
|
94
|
+
badgeColor: "bg-gray-100 text-gray-800",
|
|
77
95
|
available: true,
|
|
78
96
|
});
|
|
79
97
|
}
|
|
@@ -144,16 +162,22 @@ export function FiatPaymentMethodComponent({
|
|
|
144
162
|
>
|
|
145
163
|
{/* Icon - matching PanelOnramp style */}
|
|
146
164
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-blue-600 text-2xl text-white">
|
|
147
|
-
{method.id === FiatPaymentMethod.COINBASE_PAY
|
|
165
|
+
{method.id === FiatPaymentMethod.COINBASE_PAY
|
|
166
|
+
? "C"
|
|
167
|
+
: method.id === FiatPaymentMethod.STRIPE || method.id === FiatPaymentMethod.STRIPE_WEB2
|
|
168
|
+
? "S"
|
|
169
|
+
: "?"}
|
|
148
170
|
</div>
|
|
149
171
|
|
|
150
172
|
{/* Content */}
|
|
151
173
|
<div className="flex flex-1 flex-col items-start text-left">
|
|
152
174
|
<div className="flex items-center gap-2">
|
|
153
175
|
<span className="text-as-primary text-base font-semibold">{method.name}</span>
|
|
154
|
-
|
|
155
|
-
{method.
|
|
156
|
-
|
|
176
|
+
{method.badge && (
|
|
177
|
+
<span className={cn("rounded-full px-2 py-1 text-xs font-medium", method.badgeColor)}>
|
|
178
|
+
{method.badge}
|
|
179
|
+
</span>
|
|
180
|
+
)}
|
|
157
181
|
</div>
|
|
158
182
|
<span className="text-as-primary/60 text-sm">{method.description}</span>
|
|
159
183
|
</div>
|
|
@@ -8,7 +8,6 @@ import { formatAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
|
8
8
|
import { ChevronRight, Info, Wallet } from "lucide-react";
|
|
9
9
|
import { useRef } from "react";
|
|
10
10
|
|
|
11
|
-
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext";
|
|
12
11
|
import { FiatPaymentMethod } from "./FiatPaymentMethod";
|
|
13
12
|
import { OrderTokenAmountFiat } from "./OrderTokenAmountFiat";
|
|
14
13
|
import { PointsBadge } from "./PointsBadge";
|
|
@@ -54,8 +53,6 @@ export function PanelOnramp({
|
|
|
54
53
|
onShowFeeDetail?: () => void;
|
|
55
54
|
customUsdInputValues?: string[];
|
|
56
55
|
}) {
|
|
57
|
-
const featureFlags = useFeatureFlags();
|
|
58
|
-
|
|
59
56
|
// Helper function to get fees from anyspend quote
|
|
60
57
|
const getFeeFromApi = (paymentMethod: FiatPaymentMethod): number | null => {
|
|
61
58
|
// Try to get fee from anyspend quote first (most accurate)
|
|
@@ -84,11 +81,16 @@ export function PanelOnramp({
|
|
|
84
81
|
const getTotalAmount = (paymentMethod: FiatPaymentMethod): number => {
|
|
85
82
|
const baseAmount = parseFloat(srcAmountOnRamp) || 5;
|
|
86
83
|
|
|
87
|
-
//
|
|
84
|
+
// For stripeweb2_fee, use the originalAmount
|
|
88
85
|
if (anyspendQuote?.data?.fee?.type === "stripeweb2_fee") {
|
|
89
86
|
return Number(anyspendQuote.data.fee.originalAmount) / 1e6; // Convert from wei to USD
|
|
90
87
|
}
|
|
91
88
|
|
|
89
|
+
// Use currencyIn.amountUsd from quote when available (includes fees, most accurate for custom orders)
|
|
90
|
+
if (anyspendQuote?.data?.currencyIn?.amountUsd) {
|
|
91
|
+
return Number(anyspendQuote.data.currencyIn.amountUsd);
|
|
92
|
+
}
|
|
93
|
+
|
|
92
94
|
const fee = getFeeFromApi(paymentMethod);
|
|
93
95
|
|
|
94
96
|
// For Coinbase or when fee is available, calculate manually
|
|
@@ -279,15 +281,13 @@ export function PanelOnramp({
|
|
|
279
281
|
<Info className="h-4 w-4" />
|
|
280
282
|
</button>
|
|
281
283
|
)}
|
|
282
|
-
{
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
/>
|
|
290
|
-
)}
|
|
284
|
+
{anyspendQuote?.data?.pointsAmount && anyspendQuote?.data?.pointsAmount > 0 && (
|
|
285
|
+
<PointsBadge
|
|
286
|
+
pointsAmount={anyspendQuote.data.pointsAmount}
|
|
287
|
+
pointsMultiplier={anyspendQuote.data.pointsMultiplier}
|
|
288
|
+
onClick={() => onShowPointsDetail?.()}
|
|
289
|
+
/>
|
|
290
|
+
)}
|
|
291
291
|
</div>
|
|
292
292
|
<div className="flex flex-col items-end gap-0.5">
|
|
293
293
|
<span className="text-as-primary font-semibold">
|
|
@@ -65,6 +65,7 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
|
|
|
65
65
|
geoData,
|
|
66
66
|
coinbaseOnrampOptions,
|
|
67
67
|
coinbaseAvailablePaymentMethods,
|
|
68
|
+
stripeOnrampSupport,
|
|
68
69
|
stripeWeb2Support,
|
|
69
70
|
isLoading: isLoadingGeoOnramp,
|
|
70
71
|
} = useGeoOnrampOptions(srcAmountOnRamp);
|
|
@@ -201,16 +202,18 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
|
|
|
201
202
|
>
|
|
202
203
|
${parseFloat(srcAmountOnRamp).toFixed(2)}
|
|
203
204
|
</p>
|
|
204
|
-
{anyspendQuote?.data?.fee?.type === "standard_fee" &&
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
205
|
+
{anyspendQuote?.data?.fee?.type === "standard_fee" &&
|
|
206
|
+
anyspendQuote.data.currencyIn?.amountUsd &&
|
|
207
|
+
anyspendQuote.data.fee.finalFeeBps > 0 && (
|
|
208
|
+
<p className="text-b3-react-foreground/60 text-xs">
|
|
209
|
+
incl. $
|
|
210
|
+
{(
|
|
211
|
+
(Number(anyspendQuote.data.currencyIn.amountUsd) * anyspendQuote.data.fee.finalFeeBps) /
|
|
212
|
+
10000
|
|
213
|
+
).toFixed(2)}{" "}
|
|
214
|
+
fee
|
|
215
|
+
</p>
|
|
216
|
+
)}
|
|
214
217
|
</div>
|
|
215
218
|
</div>
|
|
216
219
|
</div>
|
|
@@ -311,7 +314,37 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
|
|
|
311
314
|
);
|
|
312
315
|
})()}
|
|
313
316
|
|
|
314
|
-
{/* Stripe Option -
|
|
317
|
+
{/* Stripe Redirect Option - Primary option */}
|
|
318
|
+
{stripeOnrampSupport && (
|
|
319
|
+
<button
|
|
320
|
+
onClick={() => handlePaymentMethodClick("stripe")}
|
|
321
|
+
className="bg-b3-react-background border-b3-react-border hover:border-as-brand group flex w-full items-center justify-between gap-4 rounded-xl border p-5 transition-all duration-200 hover:shadow-md"
|
|
322
|
+
>
|
|
323
|
+
<div className="flex items-center gap-4">
|
|
324
|
+
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-purple-50">
|
|
325
|
+
<img
|
|
326
|
+
src="https://raw.githubusercontent.com/stripe/stripe.github.io/455f506a628dc3f6c505e3001db45a64e29e9fc3/images/stripe-logo.svg"
|
|
327
|
+
alt="Stripe"
|
|
328
|
+
className="h-5"
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
<div className="flex flex-col items-start text-left">
|
|
332
|
+
<h4 className="text-b3-react-foreground text-lg font-semibold">Credit/Debit Card</h4>
|
|
333
|
+
<p className="text-b3-react-foreground/60 text-sm">Pay via Stripe checkout</p>
|
|
334
|
+
{stripeWeb2Support?.isSupport && stripeWeb2Support.formattedFeeUsd && (
|
|
335
|
+
<div className="mt-1">
|
|
336
|
+
<span className="text-xs text-gray-500">
|
|
337
|
+
${Number(stripeWeb2Support.formattedFeeUsd).toFixed(2)} fee
|
|
338
|
+
</span>
|
|
339
|
+
</div>
|
|
340
|
+
)}
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
<ChevronRight className="text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" />
|
|
344
|
+
</button>
|
|
345
|
+
)}
|
|
346
|
+
|
|
347
|
+
{/* Stripe Web2 Option - Embedded payment (secondary) */}
|
|
315
348
|
{stripeWeb2Support.isSupport && (
|
|
316
349
|
<button
|
|
317
350
|
onClick={() => handlePaymentMethodClick("stripe-web2")}
|
|
@@ -326,11 +359,15 @@ function PanelOnrampPaymentInner(props: PanelOnrampPaymentProps) {
|
|
|
326
359
|
/>
|
|
327
360
|
</div>
|
|
328
361
|
<div className="flex flex-col items-start text-left">
|
|
329
|
-
<h4 className="text-b3-react-foreground text-lg font-semibold">
|
|
330
|
-
<p className="text-b3-react-foreground/60 text-sm">Credit or debit card
|
|
331
|
-
|
|
332
|
-
<
|
|
333
|
-
|
|
362
|
+
<h4 className="text-b3-react-foreground text-lg font-semibold">Quick Pay</h4>
|
|
363
|
+
<p className="text-b3-react-foreground/60 text-sm">Credit or debit card</p>
|
|
364
|
+
{stripeWeb2Support.formattedFeeUsd && (
|
|
365
|
+
<div className="mt-1">
|
|
366
|
+
<span className="text-xs text-gray-500">
|
|
367
|
+
${Number(stripeWeb2Support.formattedFeeUsd).toFixed(2)} fee
|
|
368
|
+
</span>
|
|
369
|
+
</div>
|
|
370
|
+
)}
|
|
334
371
|
</div>
|
|
335
372
|
</div>
|
|
336
373
|
<ChevronRight className="text-b3-react-foreground/40 group-hover:text-b3-react-foreground/60 h-5 w-5 transition-colors" />
|
|
@@ -20,7 +20,7 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
|
|
|
20
20
|
const { geoData, loading: isLoadingGeo, error: geoError } = useGetGeo();
|
|
21
21
|
const { coinbaseOnrampOptions, isLoadingCoinbaseOnrampOptions, coinbaseOnrampOptionsError } =
|
|
22
22
|
useCoinbaseOnrampOptions(geoData?.country, visitorData);
|
|
23
|
-
const { stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(
|
|
23
|
+
const { stripeOnrampSupport, stripeWeb2Support, isLoadingStripeSupport, stripeSupportError } = useStripeSupport(
|
|
24
24
|
srcFiatAmount,
|
|
25
25
|
visitorData,
|
|
26
26
|
);
|
|
@@ -43,8 +43,10 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
|
|
|
43
43
|
geoData,
|
|
44
44
|
coinbaseOnrampOptions,
|
|
45
45
|
coinbaseAvailablePaymentMethods,
|
|
46
|
+
stripeOnrampSupport,
|
|
46
47
|
stripeWeb2Support,
|
|
47
|
-
isOnrampSupported:
|
|
48
|
+
isOnrampSupported:
|
|
49
|
+
coinbaseAvailablePaymentMethods.length > 0 || stripeOnrampSupport || stripeWeb2Support.isSupport,
|
|
48
50
|
isLoading: isLoadingGeo || isLoadingCoinbaseOnrampOptions || isLoadingStripeSupport || isLoadingVisitorData,
|
|
49
51
|
isLoadingGeo,
|
|
50
52
|
isLoadingCoinbaseOnrampOptions,
|
|
@@ -57,6 +59,7 @@ export function useGeoOnrampOptions(srcFiatAmount: string) {
|
|
|
57
59
|
geoData,
|
|
58
60
|
coinbaseOnrampOptions,
|
|
59
61
|
coinbaseAvailablePaymentMethods,
|
|
62
|
+
stripeOnrampSupport,
|
|
60
63
|
stripeWeb2Support,
|
|
61
64
|
isLoadingGeo,
|
|
62
65
|
isLoadingCoinbaseOnrampOptions,
|
|
@@ -12,6 +12,7 @@ export function useStripeSupport(usdAmount?: string, visitorData?: VisitorData,
|
|
|
12
12
|
|
|
13
13
|
return useMemo(
|
|
14
14
|
() => ({
|
|
15
|
+
stripeOnrampSupport: data?.stripeOnramp || false,
|
|
15
16
|
stripeWeb2Support: data?.stripeWeb2 || { isSupport: false },
|
|
16
17
|
isLoadingStripeSupport: isLoading,
|
|
17
18
|
stripeSupportError: error,
|