diff --git a/apps/dashboard/src/app/bridge/checkout-widget/CheckoutWidgetEmbed.client.tsx b/apps/dashboard/src/app/bridge/checkout-widget/CheckoutWidgetEmbed.client.tsx index 57bc6194fab..e78f6e01b43 100644 --- a/apps/dashboard/src/app/bridge/checkout-widget/CheckoutWidgetEmbed.client.tsx +++ b/apps/dashboard/src/app/bridge/checkout-widget/CheckoutWidgetEmbed.client.tsx @@ -34,6 +34,7 @@ export function CheckoutWidgetEmbed({ showThirdwebBranding, theme, currency, + paymentMethods, }: { chainId: number; amount: string; @@ -48,6 +49,7 @@ export function CheckoutWidgetEmbed({ showThirdwebBranding?: boolean; theme: "light" | "dark"; currency?: SupportedFiatCurrency; + paymentMethods?: ("crypto" | "card")[]; }) { const client = useMemo( () => @@ -79,6 +81,7 @@ export function CheckoutWidgetEmbed({ showThirdwebBranding={showThirdwebBranding} theme={theme} currency={currency} + paymentMethods={paymentMethods} connectOptions={{ wallets: bridgeWallets, appMetadata, diff --git a/apps/dashboard/src/app/bridge/checkout-widget/page.tsx b/apps/dashboard/src/app/bridge/checkout-widget/page.tsx index 57e6792ea31..6a4b26da661 100644 --- a/apps/dashboard/src/app/bridge/checkout-widget/page.tsx +++ b/apps/dashboard/src/app/bridge/checkout-widget/page.tsx @@ -68,6 +68,14 @@ export default async function Page(props: { isValidCurrency(v) ? (v as SupportedFiatCurrency) : undefined, ); + const paymentMethods = parseQueryParams(searchParams.paymentMethods, (v) => { + if (v === "crypto" || v === "card") { + return [v] as ("crypto" | "card")[]; + } + + return undefined; + }); + // Validate required params if (!chainId || !amount || !seller) { return ( @@ -125,6 +133,7 @@ export default async function Page(props: { showThirdwebBranding={showThirdwebBranding} theme={theme} currency={currency} + paymentMethods={paymentMethods} /> diff --git a/apps/playground-web/src/app/bridge/checkout-widget/CheckoutPlayground.tsx b/apps/playground-web/src/app/bridge/checkout-widget/CheckoutPlayground.tsx index b270b575265..f6b2b2ee7c6 100644 --- a/apps/playground-web/src/app/bridge/checkout-widget/CheckoutPlayground.tsx +++ b/apps/playground-web/src/app/bridge/checkout-widget/CheckoutPlayground.tsx @@ -1,12 +1,15 @@ "use client"; -import { useState } from "react"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; import { arbitrum } from "thirdweb/chains"; +import { TabButtons } from "@/components/ui/tab-buttons"; import { LeftSection } from "../components/LeftSection"; import { RightSection } from "../components/RightSection"; import type { BridgeComponentsPlaygroundOptions } from "../components/types"; const defaultOptions: BridgeComponentsPlaygroundOptions = { + integrationType: "react", payOptions: { buyTokenAddress: undefined, buyTokenAmount: "0.01", @@ -29,20 +32,74 @@ const defaultOptions: BridgeComponentsPlaygroundOptions = { }, }; -export function CheckoutPlayground() { - const [options, setOptions] = - useState(defaultOptions); +function updatePageUrl( + tab: BridgeComponentsPlaygroundOptions["integrationType"], +) { + const url = new URL(window.location.href); + if (tab === defaultOptions.integrationType) { + url.searchParams.delete("tab"); + } else { + url.searchParams.set("tab", tab || ""); + } + + window.history.replaceState({}, "", url.toString()); +} + +export function CheckoutPlayground(props: { defaultTab?: "iframe" | "react" }) { + const { theme } = useTheme(); + + const [options, setOptions] = useState( + () => ({ + ...defaultOptions, + integrationType: props.defaultTab || defaultOptions.integrationType, + }), + ); + + // Change theme on global theme change + useEffect(() => { + setOptions((prev) => ({ + ...prev, + theme: { + ...prev.theme, + type: theme === "dark" ? "dark" : "light", + }, + })); + }, [theme]); + + useEffect(() => { + updatePageUrl(options.integrationType); + }, [options.integrationType]); return ( -
-
- +
+ setOptions({ ...options, integrationType: "react" }), + isActive: options.integrationType === "react", + }, + { + name: "Iframe", + onClick: () => + setOptions({ ...options, integrationType: "iframe" }), + isActive: options.integrationType === "iframe", + }, + ]} + /> + +
+ +
+
+ +
+
-
); } diff --git a/apps/playground-web/src/app/bridge/checkout-widget/page.tsx b/apps/playground-web/src/app/bridge/checkout-widget/page.tsx index b336e64c80e..decdedc53dc 100644 --- a/apps/playground-web/src/app/bridge/checkout-widget/page.tsx +++ b/apps/playground-web/src/app/bridge/checkout-widget/page.tsx @@ -10,6 +10,9 @@ const description = const ogDescription = "Accept fiat or crypto payments on any chain—direct to your wallet. Instant checkout, webhook support, and full control over post-sale actions."; +const validTabs = ["iframe", "react"] as const; +type ValidTabs = (typeof validTabs)[number]; + export const metadata = createMetadata({ description: ogDescription, title, @@ -19,16 +22,28 @@ export const metadata = createMetadata({ }, }); -export default function Page() { +export default async function Page(props: { + searchParams: Promise<{ + tab?: string | undefined | string[]; + }>; +}) { + const searchParams = await props.searchParams; + const tab = + typeof searchParams.tab === "string" ? searchParams.tab : undefined; + + const validTab = validTabs.includes(tab as ValidTabs) + ? (tab as ValidTabs) + : undefined; + return ( - + ); diff --git a/apps/playground-web/src/app/bridge/components/CodeGen.tsx b/apps/playground-web/src/app/bridge/components/CodeGen.tsx index 14366a814a4..0578f2fa115 100644 --- a/apps/playground-web/src/app/bridge/components/CodeGen.tsx +++ b/apps/playground-web/src/app/bridge/components/CodeGen.tsx @@ -5,6 +5,7 @@ import { stringifyImports, stringifyProps, } from "../../../lib/code-gen"; +import { buildCheckoutIframeUrl } from "./buildCheckoutIframeUrl"; import type { BridgeComponentsPlaygroundOptions } from "./types"; const CodeClient = lazy(() => @@ -25,19 +26,38 @@ export function CodeGen(props: { widget: "buy" | "checkout" | "transaction"; options: BridgeComponentsPlaygroundOptions; }) { + const isIframe = + props.widget === "checkout" && props.options.integrationType === "iframe"; + return (
}>
); } +function getIframeCode(options: BridgeComponentsPlaygroundOptions) { + const src = buildCheckoutIframeUrl(options); + + return `\ +