Embeddable Widgets
Drop-in React components that bring full PayDirect functionality to any Next.js or React project. Wrap your app in a provider, pick the widgets you need, and you're live in minutes.
1. Install the widgets package
npm install @paydirectv2@paydirectv2/react-widgets
# Or, for headless / server-side use:
npm install @paydirectv2@paydirectv2/sdk@paydirectv2/react-widgets ships portable, self-styled React components — no Tailwind or shadcn/ui required.
2. Wrap your app with the Provider
// app/layout.tsx (Next.js 13+) or _app.tsx
"use client"
import { PayDirectProvider } from "@paydirectv2/react-widgets";
export default function Layout({ children }) {
return (
<PayDirectProvider
apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}
// Defaults to https://www.paydirect.com.
// Leave blank to call same-origin if you proxy the API.
// baseUrl=""
theme="auto"
>
{children}
</PayDirectProvider>
);
}3. Drop in a checkout widget
"use client"
import { CheckoutWidget } from "@paydirectv2/react-widgets";
export default function CheckoutPage() {
return (
<CheckoutWidget
amount="25.00"
description="Pro Plan — Monthly"
returnUrl="https://yourapp.com/billing/success"
cancelUrl="https://yourapp.com/billing"
onSuccess={(p) => console.log(p.paymentMethod, p.id)}
/>
);
}That's it — the customer sees both Pay with crypto and Pay with card options. No merchantWallet needed; PayDirect routes funds to the workspace's settlement address automatically.
merchantWalletWhen you don't pass merchantWallet, PayDirect resolves the destination in this order:
- Workspace
settlement_address— set per-workspace at Dashboard → Workspaces. - Account-level
settlement_address— set at Settings → Settlement. - If neither is set, the API responds with
No merchantWallet provided and no settlement address configured.
Configure the workspace settlement once and your <CheckoutWidget /> stays clean. Only pass merchantWallet explicitly if you have per-payment destinations (e.g., a marketplace where each seller gets paid into a different wallet).
<PayDirectProvider
apiKey="pd_live_..." // Your PayDirect API key
baseUrl="" // API base URL (default: same origin)
theme="auto" // "light" | "dark" | "auto"
onError={(err) => {}} // Global error handler
>
{children}
</PayDirectProvider>| Prop | Type | Default | Description |
|---|---|---|---|
| apiKey | string | — | PayDirect API key (pd_live_ or pd_test_) |
| baseUrl | string | "" | API base URL. Empty = same-origin proxied calls |
| theme | "light" | "dark" | "auto" | "auto" | Widget theme (inherits from parent by default) |
| onError | (err: string) => void | — | Global error callback for all widgets |
Fixed amount, both rails
<CheckoutWidget
amount="10.00"
description="Order #123"
returnUrl="https://yourapp.com/orders/123/success"
cancelUrl="https://yourapp.com/orders/123"
onSuccess={(p) => {
// p.paymentMethod is "crypto" | "stripe"
// p.id is the payment ID — store it, then verify via webhook
console.log(p.paymentMethod, p.id)
}}
/>Variable amount (tip jar / donation)
<CheckoutWidget
showAmountForm
defaultToken="USDC"
description="Support the show"
metadata={{ source: "youtube" }}
onSuccess={(p) => router.push(`/thanks?id=${p.id}`)}
/>Marketplace (per-payment destination)
<CheckoutWidget
amount={listing.price}
description={`Buy ${listing.title}`}
merchantWallet={listing.seller.wallet} // routes to this seller
metadata={{ listingId: listing.id, sellerId: listing.seller.id }}
/>| Prop | Type | Default | Description |
|---|---|---|---|
| amount | string | — | Fixed amount. Omit + set showAmountForm for variable. |
| description | string | — | Shown on the receipt and forwarded to your webhook. |
| merchantWallet | string | optional | Override destination wallet. Almost never needed — see the “Where does the money go?” callout above. |
| metadata | Record<string, string> | — | Arbitrary metadata stored on the payment + echoed in webhooks. |
| returnUrl | string | — | URL the customer is redirected to after a successful payment (renders “Return to merchant” on the hosted checkout). |
| cancelUrl | string | — | URL the customer is sent to if they cancel/back out of Stripe. |
| showAmountForm | boolean | true | Show the amount input + token selector. Disable for fixed-price checkouts. |
| defaultToken | "USDC" | "ETH" | "ADAO" | "USDC" | Default crypto token to suggest. |
| onSuccess | (payment) => void | — | Fires when the payment is created (not settled — use webhooks for settlement). |
| onError | (error: string) => void | — | Failure callback (network, validation, API). |
| style | React.CSSProperties | — | Style overrides on the outer card (only in @paydirectv2/react-widgets). |
| className | string | — | Class applied to the outer card (composes after default styles). |
components/widgets/checkout-widget inside the PayDirect repo; the portable, dependency-free version ships from @paydirectv2/react-widgets. Both call the same POST /api/v1/payments endpoint.CheckoutWidget for dual-rail.Fixed Amount (checkout button)
<PaymentButton
amount="25.00"
token="USDC"
description="Pro Plan — Monthly"
onSuccess={(payment) => console.log("Payment created:", payment.id)}
/>Custom Amount Form
<PaymentButton
showForm
token="USDC"
description="Donation"
buttonText="Donate with Crypto"
onSuccess={(p) => router.push(`/thank-you?id=${p.id}`)}
/>Compact Inline Button
<PaymentButton amount="5.00" token="USDC" compact buttonText="Pay" />| Prop | Type | Description |
|---|---|---|
| amount | string | Fixed payment amount |
| token | "USDC" | "ETH" | "ADAO" | Token to accept |
| showForm | boolean | Show amount input + token selector |
| compact | boolean | Render as inline button instead of card |
| description | string | Payment description |
| merchantWallet | string | Optional. Override destination wallet. Defaults to the workspace's configured settlement address. |
| metadata | Record<string, string> | Custom metadata attached to payment |
| returnUrl | string | Redirect URL after a successful payment (renders “Return to merchant” on the hosted checkout). |
| cancelUrl | string | Redirect URL if the customer cancels. |
| buttonText | string | Custom button label |
| onSuccess | (payment) => void | Callback on successful payment creation |
| onError | (error) => void | Callback on failure |
<SwapWidget
defaultTokenIn="USDC"
defaultTokenOut="ETH"
walletType="smart_wallet"
onSuccess={(swap) => console.log("Swap tx:", swap.txHash)}
/>| Prop | Type | Description |
|---|---|---|
| defaultTokenIn | string | Input token (USDC, ETH, ADAO) |
| defaultTokenOut | string | Output token |
| defaultAmount | string | Pre-filled amount |
| walletType | "eoa" | "smart_wallet" | Wallet for executing swap (smart_wallet = gasless) |
| title | string | Widget title |
| onSuccess / onError | callbacks | Swap result / error callbacks |
Full Card
<BalanceDisplay
showSmartWallet
showAddress
autoRefresh={30} // Refresh every 30 seconds
/>Compact Inline (for navbars)
<BalanceDisplay compact />| Prop | Type | Description |
|---|---|---|
| showSmartWallet | boolean | Show smart wallet section (default: true) |
| showAddress | boolean | Show wallet addresses (default: true) |
| autoRefresh | number | Auto-refresh interval in seconds |
| compact | boolean | Render as inline pill (for navbars/headers) |
| onBalanceLoaded | (balance) => void | Callback when balance data loads |
<PayoutForm
defaultToken="USDC"
walletType="smart_wallet"
onSuccess={(result) => console.log("Sent:", result.txHash)}
/>
// Pre-filled destination (e.g., vendor payouts)
<PayoutForm
defaultToken="ADAO"
defaultDestination="0xVendorWallet..."
title="Pay Vendor"
/>| Prop | Type | Description |
|---|---|---|
| defaultToken | "USDC" | "ETH" | "ADAO" | Pre-selected token |
| defaultDestination | string | Pre-filled destination address |
| walletType | "eoa" | "smart_wallet" | Sending wallet type |
| title | string | Widget title |
| onSuccess / onError | callbacks | Result / error callbacks |
<TransactionHistory
limit={20}
showPayments
showPayouts
autoRefresh={15}
/>
// Compact mode (e.g., sidebar)
<TransactionHistory limit={5} compact />| Prop | Type | Description |
|---|---|---|
| limit | number | Max transactions to show (default: 10) |
| showPayments | boolean | Include incoming payments (default: true) |
| showPayouts | boolean | Include outgoing payouts (default: true) |
| autoRefresh | number | Polling interval in seconds |
| compact | boolean | Compact view (hides dates and tx links) |
// Simple badge (for headers)
<ConnectBadge variant="badge" />
// Status bar (for dashboards)
<ConnectBadge variant="status-bar" />
// Full card with details
<ConnectBadge variant="card" showDetails />| Prop | Type | Description |
|---|---|---|
| variant | "badge" | "card" | "status-bar" | Display variant |
| showDetails | boolean | Show API key, environment, rate limit, wallets (card variant) |
import { OnrampWidget } from "@paydirectv2/react-widgets";
<OnrampWidget
defaultAsset="USDC"
defaultFiatAmount={50}
mode="popup"
onSuccess={({ onrampUrl }) => console.log("Onramp URL:", onrampUrl)}
/>| Prop | Type | Default | Description |
|---|---|---|---|
| defaultAsset | "USDC" | "ETH" | "USDC" | Pre-selected asset |
| defaultFiatAmount | number | — | Pre-fill fiat amount |
| fiatCurrency | string | "USD" | Fiat currency code |
| mode | "popup" | "inline" | "popup" | Open in popup window or inline iframe |
| onSuccess | function | — | Callback with { onrampUrl, channelId } |
| onError | function | — | Error callback |
| title | string | "Buy Crypto" | Widget title |
E-Commerce Checkout Page
"use client"
import { PayDirectProvider, PaymentButton, BalanceDisplay } from "@paydirectv2/react-widgets";
export default function Checkout({ plan, price }) {
return (
<PayDirectProvider apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}>
<div className="max-w-md mx-auto space-y-6">
<h1>Subscribe to {plan}</h1>
<PaymentButton
amount={price}
token="USDC"
description={`${plan} Plan Subscription`}
metadata={{ plan, userId: "user_123" }}
onSuccess={(payment) => {
// Redirect to success page
window.location.href = `/success?id=${payment.id}`;
}}
/>
</div>
</PayDirectProvider>
);
}Token Swap Platform
"use client"
import { PayDirectProvider, SwapWidget, BalanceDisplay } from "@paydirectv2/react-widgets";
export default function SwapPage() {
return (
<PayDirectProvider apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}>
<div className="max-w-md mx-auto space-y-6">
<BalanceDisplay compact />
<SwapWidget
defaultTokenIn="USDC"
defaultTokenOut="ETH"
walletType="smart_wallet"
onSuccess={(swap) => {
toast.success(`Swapped! Tx: ${swap.txHash.slice(0, 10)}...`);
}}
/>
</div>
</PayDirectProvider>
);
}Admin Dashboard
"use client"
import {
PayDirectProvider,
ConnectBadge,
BalanceDisplay,
PayoutForm,
TransactionHistory,
} from "@paydirectv2/react-widgets";
export default function Dashboard() {
return (
<PayDirectProvider apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}>
<div className="space-y-6">
<ConnectBadge variant="status-bar" />
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<BalanceDisplay showSmartWallet autoRefresh={30} />
<PayoutForm defaultToken="USDC" walletType="smart_wallet" />
</div>
<TransactionHistory limit={20} autoRefresh={15} />
</div>
</PayDirectProvider>
);
}AI Agent Payment Interface
"use client"
import {
PayDirectProvider,
BalanceDisplay,
SwapWidget,
TransactionHistory,
ConnectBadge,
} from "@paydirectv2/react-widgets";
export default function AgentWallet() {
return (
<PayDirectProvider apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}>
<div className="max-w-2xl mx-auto space-y-6">
<div className="flex items-center justify-between">
<h1>Agent Wallet</h1>
<ConnectBadge variant="badge" />
</div>
<BalanceDisplay showSmartWallet autoRefresh={10} />
<SwapWidget walletType="smart_wallet" title="Rebalance Portfolio" />
<TransactionHistory limit={10} autoRefresh={10} />
</div>
</PayDirectProvider>
);
}| Widget | API Used | Variants | Use Case |
|---|---|---|---|
| CheckoutWidget | POST /payments | Card | Dual-rail checkout (crypto + Stripe card) |
| PaymentButton | POST /payments | Card, Form, Compact | Crypto-only checkout, donations, invoices |
| SwapWidget | GET /swap/quote, POST /swap | Card | Token exchange, OTC, rebalancing |
| BalanceDisplay | GET /wallet/balance | Card, Compact pill | Wallet dashboard, navbar, sidebar |
| PayoutForm | POST /payouts | Card | Send funds, vendor payouts, withdrawals |
| TransactionHistory | GET /payments, GET /payouts | Card, Compact | Activity feed, audit trail |
| ConnectBadge | GET /wallet/balance | Badge, Status bar, Card | Connection status, health check |
| OnrampWidget | POST /onramp/session | Popup, Inline | Buy crypto with fiat via Coinbase Onramp |
Option 1 — @paydirectv2/react-widgets (recommended)
Portable, dependency-free React components. Works in any Next.js or React app — no Tailwind, no shadcn/ui, no global CSS required. Ships PayDirectProvider + CheckoutWidget today; additional widgets land in subsequent minor versions.
npm install @paydirectv2/react-widgets
# Style overrides via props
import { PayDirectProvider, CheckoutWidget } from "@paydirectv2/react-widgets";
<PayDirectProvider apiKey={process.env.NEXT_PUBLIC_PAYDIRECT_API_KEY!}>
<CheckoutWidget
amount="25.00"
description="Pro Plan"
style={{ maxWidth: 480, background: "#101418" }}
/>
</PayDirectProvider>Option 2 — Copy the styled shadcn widgets
If you already use Tailwind + shadcn/ui and want pixel-matched UI with the PayDirect dashboard, copy the components/widgets/ directory directly from the repo. All widgets (CheckoutWidget, PaymentButton, BalanceDisplay, SwapWidget, PayoutForm, TransactionHistory, ConnectBadge, OnrampWidget) are included.
# Copy to your project
cp -r paydirect/components/widgets/ your-app/components/widgets/
# Install required dependencies
npm install lucide-react
npx shadcn@latest add card button input select badge skeletonOption 3 — Headless SDK (@paydirectv2/sdk)
Skip the widgets entirely and bring your own UI. Full TypeScript types, works in browser and Node.
npm install @paydirectv2/sdk
import PayDirectClient from "@paydirectv2/sdk";
const client = new PayDirectClient({
apiKey: process.env.PAYDIRECT_API_KEY!,
});
// All SDK methods available:
// client.createPayment(...)
// client.getBalance()
// client.sendPayout(...)
// client.getSwapQuote(...)
// client.executeSwap(...)
// client.listPayments(...)
// etc.paydirect Python SDK from PyPI — see SDKs & CLI.