PayDirect

Embeddable Widgets

NEW

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.

Quick Start — 3 Steps
From zero to crypto + card checkout in under 5 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.

Where does the money go?
You almost never need to pass merchantWallet

When you don't pass merchantWallet, PayDirect resolves the destination in this order:

  1. Workspace settlement_address — set per-workspace at Dashboard → Workspaces.
  2. Account-level settlement_address — set at Settings → Settlement.
  3. 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
Required
Context provider that supplies API credentials to all child widgets
<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>
PropTypeDefaultDescription
apiKeystringPayDirect API key (pd_live_ or pd_test_)
baseUrlstring""API base URL. Empty = same-origin proxied calls
theme"light" | "dark" | "auto""auto"Widget theme (inherits from parent by default)
onError(err: string) => voidGlobal error callback for all widgets
CheckoutWidget
Recommended
Dual-rail
One widget. Crypto on Base and card via Stripe. Customer picks the rail at checkout.

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 }}
/>
PropTypeDefaultDescription
amountstringFixed amount. Omit + set showAmountForm for variable.
descriptionstringShown on the receipt and forwarded to your webhook.
merchantWalletstringoptionalOverride destination wallet. Almost never needed — see the “Where does the money go?” callout above.
metadataRecord<string, string>Arbitrary metadata stored on the payment + echoed in webhooks.
returnUrlstringURL the customer is redirected to after a successful payment (renders “Return to merchant” on the hosted checkout).
cancelUrlstringURL the customer is sent to if they cancel/back out of Stripe.
showAmountFormbooleantrueShow the amount input + token selector. Disable for fixed-price checkouts.
defaultToken"USDC" | "ETH" | "ADAO""USDC"Default crypto token to suggest.
onSuccess(payment) => voidFires when the payment is created (not settled — use webhooks for settlement).
onError(error: string) => voidFailure callback (network, validation, API).
styleReact.CSSPropertiesStyle overrides on the outer card (only in @paydirectv2/react-widgets).
classNamestringClass applied to the outer card (composes after default styles).
Two flavors of the same widget: the styled shadcn/Tailwind version lives at 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.
PaymentButton
Payments
Create payment invoices with a single click. Supports fixed-amount, form, and compact button modes. Crypto-only — use 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" />
PropTypeDescription
amountstringFixed payment amount
token"USDC" | "ETH" | "ADAO"Token to accept
showFormbooleanShow amount input + token selector
compactbooleanRender as inline button instead of card
descriptionstringPayment description
merchantWalletstringOptional. Override destination wallet. Defaults to the workspace's configured settlement address.
metadataRecord<string, string>Custom metadata attached to payment
returnUrlstringRedirect URL after a successful payment (renders “Return to merchant” on the hosted checkout).
cancelUrlstringRedirect URL if the customer cancels.
buttonTextstringCustom button label
onSuccess(payment) => voidCallback on successful payment creation
onError(error) => voidCallback on failure
SwapWidget
DEX Swaps
On-chain token swaps via Uniswap V3 with live quotes, slippage protection, and gasless execution.
<SwapWidget
  defaultTokenIn="USDC"
  defaultTokenOut="ETH"
  walletType="smart_wallet"
  onSuccess={(swap) => console.log("Swap tx:", swap.txHash)}
/>
PropTypeDescription
defaultTokenInstringInput token (USDC, ETH, ADAO)
defaultTokenOutstringOutput token
defaultAmountstringPre-filled amount
walletType"eoa" | "smart_wallet"Wallet for executing swap (smart_wallet = gasless)
titlestringWidget title
onSuccess / onErrorcallbacksSwap result / error callbacks
BalanceDisplay
Wallet
Real-time wallet balances for EOA and smart wallet, with auto-refresh and compact mode.

Full Card

<BalanceDisplay
  showSmartWallet
  showAddress
  autoRefresh={30}  // Refresh every 30 seconds
/>

Compact Inline (for navbars)

<BalanceDisplay compact />
PropTypeDescription
showSmartWalletbooleanShow smart wallet section (default: true)
showAddressbooleanShow wallet addresses (default: true)
autoRefreshnumberAuto-refresh interval in seconds
compactbooleanRender as inline pill (for navbars/headers)
onBalanceLoaded(balance) => voidCallback when balance data loads
PayoutForm
Payouts
Send crypto payouts to any wallet address. Gasless for smart wallet users.
<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"
/>
PropTypeDescription
defaultToken"USDC" | "ETH" | "ADAO"Pre-selected token
defaultDestinationstringPre-filled destination address
walletType"eoa" | "smart_wallet"Sending wallet type
titlestringWidget title
onSuccess / onErrorcallbacksResult / error callbacks
TransactionHistory
History
Live transaction feed showing payments and payouts with status, amounts, and block explorer links.
<TransactionHistory
  limit={20}
  showPayments
  showPayouts
  autoRefresh={15}
/>

// Compact mode (e.g., sidebar)
<TransactionHistory limit={5} compact />
PropTypeDescription
limitnumberMax transactions to show (default: 10)
showPaymentsbooleanInclude incoming payments (default: true)
showPayoutsbooleanInclude outgoing payouts (default: true)
autoRefreshnumberPolling interval in seconds
compactbooleanCompact view (hides dates and tx links)
ConnectBadge
Status
API connection status indicator in three variants: badge, status bar, and full card.
// Simple badge (for headers)
<ConnectBadge variant="badge" />

// Status bar (for dashboards)
<ConnectBadge variant="status-bar" />

// Full card with details
<ConnectBadge variant="card" showDetails />
PropTypeDescription
variant"badge" | "card" | "status-bar"Display variant
showDetailsbooleanShow API key, environment, rate limit, wallets (card variant)
OnrampWidget
Coinbase
Embed a crypto purchase button powered by Coinbase Onramp
import { OnrampWidget } from "@paydirectv2/react-widgets";

<OnrampWidget
  defaultAsset="USDC"
  defaultFiatAmount={50}
  mode="popup"
  onSuccess={({ onrampUrl }) => console.log("Onramp URL:", onrampUrl)}
/>
PropTypeDefaultDescription
defaultAsset"USDC" | "ETH""USDC"Pre-selected asset
defaultFiatAmountnumberPre-fill fiat amount
fiatCurrencystring"USD"Fiat currency code
mode"popup" | "inline""popup"Open in popup window or inline iframe
onSuccessfunctionCallback with { onrampUrl, channelId }
onErrorfunctionError callback
titlestring"Buy Crypto"Widget title
Complete Integration Examples
Copy-paste patterns for common use cases

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 Reference
All available widgets at a glance
WidgetAPI UsedVariantsUse Case
CheckoutWidgetPOST /paymentsCardDual-rail checkout (crypto + Stripe card)
PaymentButtonPOST /paymentsCard, Form, CompactCrypto-only checkout, donations, invoices
SwapWidgetGET /swap/quote, POST /swapCardToken exchange, OTC, rebalancing
BalanceDisplayGET /wallet/balanceCard, Compact pillWallet dashboard, navbar, sidebar
PayoutFormPOST /payoutsCardSend funds, vendor payouts, withdrawals
TransactionHistoryGET /payments, GET /payoutsCard, CompactActivity feed, audit trail
ConnectBadgeGET /wallet/balanceBadge, Status bar, CardConnection status, health check
OnrampWidgetPOST /onramp/sessionPopup, InlineBuy crypto with fiat via Coinbase Onramp
Using Widgets in External Projects
Three ways to integrate, from easiest to most flexible

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 skeleton

Option 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.
Python? Server-side integrations can use the paydirect Python SDK from PyPI — see SDKs & CLI.