Skip to content

[SDK] Feature: Allow for supported providers to be passed to PayEmbed #7108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/small-boxes-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

Add support for filtering fiat payment providers in PayEmbed
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export type PayUIOptions = Prettify<
currency?: CurrencyMeta["shorthand"];
};
preferredProvider?: FiatProvider;
supportedProviders?: FiatProvider[];
}
| false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ChevronDownIcon } from "@radix-ui/react-icons";
import type { FiatProvider } from "../../../../../../pay/utils/commonTypes.js";
import { iconSize, spacing } from "../../../../../core/design-system/index.js";
import { Container } from "../../../components/basic.js";
import { Button } from "../../../components/buttons.js";
import { Text } from "../../../components/text.js";
import { getProviderLabel } from "./utils.js";

/**
* Shows the selected payment provider based on the preferred provider OR the quoted provider.
* @internal
*/
export const PayProviderSelection = (props: {
onShowProviders: () => void;
quotedProvider?: FiatProvider;
preferredProvider?: FiatProvider;
supportedProviders: FiatProvider[];
}) => {
const ProviderItem = (
<Container
flex="row"
center="y"
gap="xxs"
color="secondaryText"
style={{ padding: spacing.md }}
>
<Text size="xs">
{getProviderLabel(
props.preferredProvider ?? props.quotedProvider ?? "",
)}
</Text>
{props.supportedProviders.length > 1 && (
<ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
)}
</Container>
);

return (
<Container
bg="tertiaryBg"
flex="row"
borderColor="borderColor"
style={{
paddingLeft: spacing.md,
justifyContent: "space-between",
alignItems: "center",
borderWidth: "1px",
borderStyle: "solid",
borderBottom: "none",
}}
>
<Text size="xs" color="secondaryText">
Provider
</Text>
{props.supportedProviders.length > 1 ? (
<Button
variant="ghost"
onClick={props.onShowProviders}
style={{ padding: 0 }} // Padding is managed within children as the button is conditional
>
{ProviderItem}
</Button>
) : (
ProviderItem
)}
</Container>
);
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { useState } from "react";
import { trackPayEvent } from "../../../../../../../analytics/track/pay.js";
import type { Chain } from "../../../../../../../chains/types.js";
import type { ThirdwebClient } from "../../../../../../../client/client.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
import type { FiatProvider } from "../../../../../../../pay/utils/commonTypes.js";
import {
type Theme,
iconSize,
spacing,
} from "../../../../../../core/design-system/index.js";
type FiatProvider,
FiatProviders,
} from "../../../../../../../pay/utils/commonTypes.js";
import type { Theme } from "../../../../../../core/design-system/index.js";
import type { PayUIOptions } from "../../../../../../core/hooks/connection/ConnectButtonProps.js";
import { useBuyWithFiatQuote } from "../../../../../../core/hooks/pay/useBuyWithFiatQuote.js";
import { PREFERRED_FIAT_PROVIDER_STORAGE_KEY } from "../../../../../../core/utils/storage.js";
Expand All @@ -26,6 +24,7 @@ import { Button } from "../../../../components/buttons.js";
import { Text } from "../../../../components/text.js";
import { type ERC20OrNativeToken, isNativeToken } from "../../nativeToken.js";
import { EstimatedTimeAndFees } from "../EstimatedTimeAndFees.js";
import { PayProviderSelection } from "../PayProviderSelection.js";
import { PayWithCreditCard } from "../PayWIthCreditCard.js";
import type { SelectedScreen } from "../main/types.js";
import { FiatFees } from "../swap/Fees.js";
Expand Down Expand Up @@ -84,6 +83,19 @@ export function FiatScreenContent(props: {
: undefined,
);

const supportedProviders = (() => {
if (!buyWithFiatOptions) return [...FiatProviders];
const options = buyWithFiatOptions?.supportedProviders ?? [];
const optionsWithPreferred =
options.length > 0
? new Set([
...(preferredProvider ? [preferredProvider] : []),
...options,
])
: FiatProviders;
return Array.from(optionsWithPreferred);
})();

const fiatQuoteQuery = useBuyWithFiatQuote(
buyWithFiatOptions !== false && tokenAmount
? {
Expand All @@ -98,8 +110,8 @@ export function FiatScreenContent(props: {
isTestMode: buyWithFiatOptions?.testMode,
purchaseData: props.payOptions.purchaseData,
fromAddress: payer.account.address,
preferredProvider: preferredProvider,
paymentLinkId: paymentLinkId,
preferredProvider: preferredProvider ?? supportedProviders[0],
}
: undefined,
);
Expand Down Expand Up @@ -159,6 +171,7 @@ export function FiatScreenContent(props: {
</Text>
<Spacer y="lg" />
<Providers
supportedProviders={supportedProviders}
preferredProvider={
preferredProvider || fiatQuoteQuery.data?.provider
}
Expand Down Expand Up @@ -188,35 +201,13 @@ export function FiatScreenContent(props: {
currency={selectedCurrency}
onSelectCurrency={showCurrencySelector}
/>
<Container
bg="tertiaryBg"
flex="row"
borderColor="borderColor"
style={{
paddingLeft: spacing.md,
justifyContent: "space-between",
alignItems: "center",
borderWidth: "1px",
borderStyle: "solid",
borderBottom: "none",
}}
>
<Text size="xs" color="secondaryText">
Provider
</Text>
<Button variant="ghost" onClick={showProviders}>
<Container flex="row" center="y" gap="xxs" color="secondaryText">
<Text size="xs">
{preferredProvider
? `${preferredProvider.charAt(0).toUpperCase() + preferredProvider.slice(1).toLowerCase()}`
: fiatQuoteQuery.data?.provider
? `${fiatQuoteQuery.data?.provider.charAt(0).toUpperCase() + fiatQuoteQuery.data?.provider.slice(1).toLowerCase()}`
: ""}
</Text>
<ChevronDownIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
</Button>
</Container>
{/** Shows preferred or quoted provider and conditional selection */}
<PayProviderSelection
onShowProviders={showProviders}
quotedProvider={fiatQuoteQuery.data?.provider}
preferredProvider={preferredProvider}
supportedProviders={supportedProviders}
/>
{/* Estimated time + View fees button */}
<EstimatedTimeAndFees
quoteIsLoading={fiatQuoteQuery.isLoading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { StepConnectorArrow } from "../swap/StepConnector.js";
import { WalletRow } from "../swap/WalletRow.js";
import { addPendingTx } from "../swap/pendingSwapTx.js";
import type { PayerInfo } from "../types.js";
import { getProviderLabel } from "../utils.js";
import { StepContainer } from "./FiatSteps.js";

type OnRampScreenState = {
Expand Down Expand Up @@ -213,7 +214,7 @@ function StepUI(props: {
/>
<Container flex="column" gap="3xs" center="y" style={{ flex: "1" }}>
<Text size="sm" color="primaryText">
{step.action.charAt(0).toUpperCase() + step.action.slice(1)}
{getProviderLabel(step.action)}
</Text>

<Container
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
type FiatProvider,
FiatProviders,
} from "../../../../../../../pay/utils/commonTypes.js";
import type { FiatProvider } from "../../../../../../../pay/utils/commonTypes.js";
import { Container } from "../../../../components/basic.js";
import { Button } from "../../../../components/buttons.js";
import { Link } from "../../../../components/text.js";
import { getProviderLabel } from "../utils.js";
/**
* @internal
*/

export function Providers(props: {
supportedProviders: FiatProvider[];
preferredProvider?: FiatProvider;
onSelect: (provider: FiatProvider) => void;
}) {
Expand All @@ -21,37 +21,34 @@ export function Providers(props: {
alignItems: "flex-start",
}}
>
{FiatProviders.map((provider) => {
return (
<Container
key={provider}
flex="row"
expand
style={{
justifyContent: "space-between",
}}
{props.supportedProviders.map((provider) => (
<Container
key={provider}
flex="row"
expand
style={{
justifyContent: "space-between",
}}
>
<Button
fullWidth
onClick={() => props.onSelect(provider)}
variant={"link"}
>
<Button
fullWidth
onClick={() => props.onSelect(provider)}
variant={"link"}
<Link
color={
props.preferredProvider === provider
? "primaryText"
: "secondaryText"
}
size="sm"
hoverColor="primaryText"
>
<Link
color={
props.preferredProvider === provider
? "primaryText"
: "secondaryText"
}
size="sm"
hoverColor="primaryText"
>
{provider.charAt(0).toUpperCase() +
provider.slice(1).toLowerCase()}
</Link>
</Button>
</Container>
);
})}
{getProviderLabel(provider)}
</Link>
</Button>
</Container>
))}
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
export function getBuyTokenAmountFontSize(value: string) {
return value.length > 10 ? "26px" : value.length > 6 ? "34px" : "50px";
}

/**
*
* @param str accepts any string but expects a fully upppercased string / type FiatProvider
* @returns Fiat provider label to be rendered used within presentation logic
*/
export function getProviderLabel(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}
Loading