Skip to content

Commit bc51255

Browse files
gregfromstlclaudejoaquim-verges
authored
[SDK] Add enableCard prop to control fiat payment visibility (#7465)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Joaquim Verges <joaquim.verges@gmail.com>
1 parent 8e29d48 commit bc51255

File tree

15 files changed

+333
-140
lines changed

15 files changed

+333
-140
lines changed

.changeset/dull-breads-start.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Adds paymentMethods prop to BuyWidget, CheckoutWidget, and TransactionWidget to control available payment options. Accepts an array of "crypto" and/or "card" values.

apps/playground-web/src/app/connect/pay/components/CodeGen.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ function Example() {
7373
<${componentName}
7474
client={client}
7575
chain={defineChain(${options.payOptions.buyTokenChain.id})}
76-
amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${
76+
amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${
7777
options.payOptions.widget === "transaction"
7878
? `\n\t transaction={claimTo({
7979
contract: nftContract,

apps/playground-web/src/app/connect/pay/components/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { Address, Chain } from "thirdweb";
1+
import type { Chain } from "thirdweb/chains";
22
import type { ThemeOverrides } from "thirdweb/react";
3+
import type { Address } from "thirdweb/utils";
34

45
export type BridgeComponentsPlaygroundOptions = {
56
theme: {
@@ -22,5 +23,7 @@ export type BridgeComponentsPlaygroundOptions = {
2223

2324
// transaction mode options
2425
transactionData?: string; // Simplified for demo; could be more complex in real implementation
26+
27+
paymentMethods: ("crypto" | "card")[];
2528
};
2629
};

apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useId, useState } from "react";
1313
import type { Address } from "thirdweb";
1414
import { defineChain } from "thirdweb/chains";
1515
import { CustomRadioGroup } from "@/components/ui/CustomRadioGroup";
16+
import { Checkbox } from "@/components/ui/checkbox";
1617
import { Input } from "@/components/ui/input";
1718
import { Label } from "@/components/ui/label";
1819
import { cn } from "../../../../lib/utils";
@@ -53,6 +54,8 @@ export function LeftSection(props: {
5354
const modalTitleIconId = useId();
5455
const modalDescriptionId = useId();
5556
const themeId = useId();
57+
const cryptoPaymentId = useId();
58+
const cardPaymentId = useId();
5659

5760
return (
5861
<div className="flex flex-col gap-4">
@@ -161,6 +164,65 @@ export function LeftSection(props: {
161164
/>
162165
</div>
163166
</div>
167+
168+
{/* Payment Methods */}
169+
<div className="flex flex-col gap-3 pt-4">
170+
<Label>Payment Methods</Label>
171+
<div className="flex gap-4">
172+
<div className="flex items-center space-x-2">
173+
<Checkbox
174+
checked={payOptions.paymentMethods.includes(
175+
"crypto",
176+
)}
177+
id={cryptoPaymentId}
178+
onCheckedChange={(checked) => {
179+
setOptions((v) => ({
180+
...v,
181+
payOptions: {
182+
...v.payOptions,
183+
paymentMethods: checked
184+
? [
185+
...v.payOptions.paymentMethods.filter(
186+
(m) => m !== "crypto",
187+
),
188+
"crypto",
189+
]
190+
: v.payOptions.paymentMethods.filter(
191+
(m) => m !== "crypto",
192+
),
193+
},
194+
}));
195+
}}
196+
/>
197+
<Label htmlFor={cryptoPaymentId}>Crypto</Label>
198+
</div>
199+
<div className="flex items-center space-x-2">
200+
<Checkbox
201+
checked={payOptions.paymentMethods.includes("card")}
202+
id={cardPaymentId}
203+
onCheckedChange={(checked) => {
204+
setOptions((v) => ({
205+
...v,
206+
payOptions: {
207+
...v.payOptions,
208+
paymentMethods: checked
209+
? [
210+
...v.payOptions.paymentMethods.filter(
211+
(m) => m !== "card",
212+
),
213+
"card",
214+
]
215+
: v.payOptions.paymentMethods.filter(
216+
(m) => m !== "card",
217+
),
218+
},
219+
}));
220+
}}
221+
/>
222+
<Label htmlFor={cardPaymentId}>Card</Label>
223+
</div>
224+
</div>
225+
</div>
164226
</div>
165227
</div>
166228
</div>
@@ -249,6 +311,65 @@ export function LeftSection(props: {
249311
/>
250312
</div>
251313
</div>
314+
315+
{/* Payment Methods */}
316+
<div className="flex flex-col gap-3">
317+
<Label>Payment Methods</Label>
318+
<div className="flex gap-4">
319+
<div className="flex items-center space-x-2">
320+
<Checkbox
321+
checked={payOptions.paymentMethods.includes(
322+
"crypto",
323+
)}
324+
id={cryptoPaymentId}
325+
onCheckedChange={(checked) => {
326+
setOptions((v) => ({
327+
...v,
328+
payOptions: {
329+
...v.payOptions,
330+
paymentMethods: checked
331+
? [
332+
...v.payOptions.paymentMethods.filter(
333+
(m) => m !== "crypto",
334+
),
335+
"crypto",
336+
]
337+
: v.payOptions.paymentMethods.filter(
338+
(m) => m !== "crypto",
339+
),
340+
},
341+
}));
342+
}}
343+
/>
344+
<Label htmlFor={cryptoPaymentId}>Crypto</Label>
345+
</div>
346+
<div className="flex items-center space-x-2">
347+
<Checkbox
348+
checked={payOptions.paymentMethods.includes("card")}
349+
id={cardPaymentId}
350+
onCheckedChange={(checked) => {
351+
setOptions((v) => ({
352+
...v,
353+
payOptions: {
354+
...v.payOptions,
355+
paymentMethods: checked
356+
? [
357+
...v.payOptions.paymentMethods.filter(
358+
(m) => m !== "card",
359+
),
360+
"card",
361+
]
362+
: v.payOptions.paymentMethods.filter(
363+
(m) => m !== "card",
364+
),
365+
},
366+
}));
367+
}}
368+
/>
369+
<Label htmlFor={cardPaymentId}>Card</Label>
370+
</div>
371+
</div>
372+
</div>
252373
</div>
253374
</div>
254375
</div>

apps/playground-web/src/app/connect/pay/embed/RightSection.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export function RightSection(props: {
6060
client={THIRDWEB_CLIENT}
6161
description={props.options.payOptions.description}
6262
image={props.options.payOptions.image}
63+
paymentMethods={props.options.payOptions.paymentMethods}
6364
theme={themeObj}
6465
title={props.options.payOptions.title}
6566
tokenAddress={props.options.payOptions.buyTokenAddress}
@@ -81,6 +82,7 @@ export function RightSection(props: {
8182
getDefaultImage(props.options.theme.type)
8283
}
8384
name={props.options.payOptions.title || "Your Product Name"}
85+
paymentMethods={props.options.payOptions.paymentMethods}
8486
presetOptions={[1, 2, 3]}
8587
seller={props.options.payOptions.sellerAddress}
8688
theme={themeObj}
@@ -95,6 +97,7 @@ export function RightSection(props: {
9597
client={THIRDWEB_CLIENT}
9698
description={props.options.payOptions.description}
9799
image={props.options.payOptions.image}
100+
paymentMethods={props.options.payOptions.paymentMethods}
98101
theme={themeObj}
99102
title={props.options.payOptions.title}
100103
transaction={claimTo({

apps/playground-web/src/app/connect/pay/embed/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const defaultConnectOptions: BridgeComponentsPlaygroundOptions = {
1313
buyTokenChain: arbitrum,
1414
description: "",
1515
image: "",
16+
paymentMethods: ["crypto", "card"],
1617
sellerAddress: "0x0000000000000000000000000000000000000000",
1718
title: "",
1819
transactionData: "",

packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ export interface BridgeOrchestratorProps {
113113
* Quick buy amounts
114114
*/
115115
presetOptions: [number, number, number] | undefined;
116+
117+
/**
118+
* Allowed payment methods
119+
* @default ["crypto", "card"]
120+
*/
121+
paymentMethods?: ("crypto" | "card")[];
116122
}
117123

118124
export function BridgeOrchestrator({
@@ -127,6 +133,7 @@ export function BridgeOrchestrator({
127133
purchaseData,
128134
paymentLinkId,
129135
presetOptions,
136+
paymentMethods = ["crypto", "card"],
130137
}: BridgeOrchestratorProps) {
131138
// Initialize adapters
132139
const adapters = useMemo(
@@ -270,6 +277,7 @@ export function BridgeOrchestrator({
270277
}}
271278
onError={handleError}
272279
onPaymentMethodSelected={handlePaymentMethodSelected}
280+
paymentMethods={paymentMethods}
273281
receiverAddress={state.context.receiverAddress}
274282
/>
275283
)}

packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ export type BuyWidgetProps = {
160160
* @hidden
161161
*/
162162
paymentLinkId?: string;
163+
164+
/**
165+
* Allowed payment methods
166+
* @default ["crypto", "card"]
167+
*/
168+
paymentMethods?: ("crypto" | "card")[];
163169
};
164170

165171
// Enhanced UIOptions to handle unsupported token state
@@ -378,6 +384,7 @@ export function BuyWidget(props: BuyWidgetProps) {
378384
props.onError?.(err);
379385
}}
380386
paymentLinkId={props.paymentLinkId}
387+
paymentMethods={props.paymentMethods}
381388
presetOptions={props.presetOptions}
382389
purchaseData={props.purchaseData}
383390
receiverAddress={undefined}

packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,12 @@ export type CheckoutWidgetProps = {
166166
* @hidden
167167
*/
168168
paymentLinkId?: string;
169+
170+
/**
171+
* Allowed payment methods
172+
* @default ["crypto", "card"]
173+
*/
174+
paymentMethods?: ("crypto" | "card")[];
169175
};
170176

171177
// Enhanced UIOptions to handle unsupported token state
@@ -341,6 +347,7 @@ export function CheckoutWidget(props: CheckoutWidgetProps) {
341347
props.onError?.(err);
342348
}}
343349
paymentLinkId={props.paymentLinkId}
350+
paymentMethods={props.paymentMethods}
344351
presetOptions={props.presetOptions}
345352
purchaseData={props.purchaseData}
346353
receiverAddress={props.seller}

packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ export type TransactionWidgetProps = {
169169
* @hidden
170170
*/
171171
paymentLinkId?: string;
172+
173+
/**
174+
* Allowed payment methods
175+
* @default ["crypto", "card"]
176+
*/
177+
paymentMethods?: ("crypto" | "card")[];
172178
};
173179

174180
// Enhanced UIOptions to handle unsupported token state
@@ -400,6 +406,7 @@ export function TransactionWidget(props: TransactionWidgetProps) {
400406
props.onError?.(err);
401407
}}
402408
paymentLinkId={props.paymentLinkId}
409+
paymentMethods={props.paymentMethods}
403410
presetOptions={props.presetOptions}
404411
purchaseData={props.purchaseData}
405412
receiverAddress={undefined}

packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ export interface PaymentSelectionProps {
7171
* Whether to include the destination token in the payment methods
7272
*/
7373
includeDestinationToken?: boolean;
74+
75+
/**
76+
* Allowed payment methods
77+
* @default ["crypto", "card"]
78+
*/
79+
paymentMethods?: ("crypto" | "card")[];
7480
}
7581

7682
type Step =
@@ -90,6 +96,7 @@ export function PaymentSelection({
9096
connectOptions,
9197
connectLocale,
9298
includeDestinationToken,
99+
paymentMethods = ["crypto", "card"],
93100
}: PaymentSelectionProps) {
94101
const connectedWallets = useConnectedWallets();
95102
const activeWallet = useActiveWallet();
@@ -115,7 +122,7 @@ export function PaymentSelection({
115122
? currentStep.selectedWallet
116123
: activeWallet;
117124
const {
118-
data: paymentMethods,
125+
data: suitableTokenPaymentMethods,
119126
isLoading: paymentMethodsLoading,
120127
error: paymentMethodsError,
121128
} = usePaymentMethods({
@@ -248,6 +255,7 @@ export function PaymentSelection({
248255
onConnectWallet={handleConnectWallet}
249256
onFiatSelected={handleFiatSelected}
250257
onWalletSelected={handleWalletSelected}
258+
paymentMethods={paymentMethods}
251259
/>
252260
)}
253261

@@ -261,7 +269,7 @@ export function PaymentSelection({
261269
destinationToken={destinationToken}
262270
onBack={handleBackToWalletSelection}
263271
onPaymentMethodSelected={handlePaymentMethodSelected}
264-
paymentMethods={paymentMethods}
272+
paymentMethods={suitableTokenPaymentMethods}
265273
paymentMethodsLoading={paymentMethodsLoading}
266274
/>
267275
)}

0 commit comments

Comments
 (0)