Idea

An implementation that demonstrates Dynamic’s unified payment flow, allowing users to:
  • Fund transactions using external wallets
  • Transfer funds from exchange accounts (Coinbase, Kraken)
  • Purchase crypto through onramp providers (Coinbase, Banxa)
  • Choose their preferred payment method through a unified interface

Key Components

  • Dynamic MPC Wallets - Embedded, non-custodial wallets with seamless auth
  • Unified Payment Interface - Single UI for all payment methods
  • External Wallet Funding - Direct transfers from connected wallets
  • Exchange Integration - Coinbase and Kraken account transfers
  • Onramp Providers - Fiat-to-crypto purchases via Coinbase and Banxa

Configure Dynamic Environment

Create a new environment in the Dynamic dashboard.
  1. Navigate to the Dynamic Dashboard > Developers
  2. Go to “SDK and API Keys” under the Developers section
  3. Copy your Environment ID and create a new API Token - you’ll need these to authenticate your requests to Dynamic’s API
Environment ID and API Token

Enable Funding Methods

Before using payWithDynamic, you need to enable the funding methods you want to support:

1. External Wallet Funding

  1. Go to your Dynamic dashboard
  2. Navigate to FundingExternal Wallet
  3. Enable Funding with External Wallets
More complete docs on enabling External Wallet Funding can be found here.

2. Exchange Funding

  1. Go to your Dynamic dashboard
  2. Navigate to FundingExchanges & Onramps
  3. Enable Coinbase and/or Kraken

3. Onramp Providers

  1. Go to your Dynamic dashboard
  2. Navigate to FundingExchanges & Onramps
  3. Enable Coinbase and/or Banxa
  4. Accept terms and conditions for each provider if necessary
More complete docs on enabling Onramps can be found here.

Basic Implementation

Create a simple payment component that uses the usePayWithDynamic hook: You can find more information about the hook here.
components/PaymentForm.tsx
import { usePayWithDynamic, ChainEnum } from "@dynamic-labs/sdk-react-core";
import { useState } from "react";

export const PaymentForm = () => {
  const [amount, setAmount] = useState("");
  const [destination, setDestination] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [result, setResult] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  const pay = usePayWithDynamic();

  const handlePayment = async (e: React.FormEvent) => {
    e.preventDefault();
    setIsLoading(true);
    setError(null);
    setResult(null);

    try {
      if (!amount || parseFloat(amount) <= 0) {
        throw new Error("Please provide a valid amount greater than 0");
      }

      if (!destination || !destination.startsWith("0x")) {
        throw new Error("Please provide a valid destination address");
      }

      await pay({
        destinationAddress: destination,
        tokenAmount: parseFloat(amount),
        tokenSymbol: "ETH",
        network: 1, // Ethereum mainnet
        chainName: ChainEnum.EVM,
      });

    } catch (err: any) {
      setError(err.message || "Payment failed");
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
      <h2 className="text-2xl font-bold mb-4">Send Payment</h2>
      
      <form onSubmit={handlePayment} className="space-y-4">
        <div>
          <label className="block text-sm font-medium mb-2">
            Destination Address
          </label>
          <input
            type="text"
            value={destination}
            onChange={(e) => setDestination(e.target.value)}
            placeholder="0x..."
            className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
            required
          />
        </div>

        <div>
          <label className="block text-sm font-medium mb-2">
            Amount (ETH)
          </label>
          <input
            type="number"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
            placeholder="0.1"
            step="0.001"
            min="0"
            className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
            required
          />
        </div>

        <button
          type="submit"
          disabled={isLoading}
          className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
        >
          {isLoading ? "Processing..." : "Send Payment"}
        </button>
      </form>

      {error && (
        <div className="mt-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded">
          {error}
        </div>
      )}

      {result && (
        <div className="mt-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded">
          {result}
        </div>
      )}
    </div>
  );
};

How It Works

Unified Payment Flow

The usePayWithDynamic hook provides a unified interface that:
  1. Accepts Payment Parameters - Destination address, amount, token, and network
  2. Shows Payment Options - Displays available funding methods based on your configuration
  3. Handles User Selection - Lets users choose their preferred payment method
  4. Orchestrates Transaction - Executes the payment through the selected method

Available Payment Methods

External Wallet Funding

  • Users can fund transactions from any connected external wallet
  • Supports all major wallet providers (MetaMask, WalletConnect, etc.)
  • Direct blockchain transactions with gas fee handling

Exchange Funding

  • Coinbase: Transfer funds from Coinbase accounts to wallets
  • Kraken: Transfer funds from Kraken accounts to wallets
  • Requires OAuth setup
  • Required destination address whitelisting for Kraken

Onramp Providers

  • Coinbase: Buy crypto with fiat using credit cards, bank transfers
  • Banxa: Global onramp with multiple payment methods

Error Handling

The payWithDynamic system handles various error scenarios:
  • Insufficient Funds: Clear messaging when user doesn’t have enough balance
  • Network Issues: Connection problems and transaction failures
  • Whitelisting Requirements: Exchange-specific address verification needs
  • Invalid Amounts: Amount validation and minimum requirements

Use Cases

E-commerce Integration

const handleProductPurchase = async (productId: string, amount: number) => {
  const pay = usePayWithDynamic();
  
  await pay({
    destinationAddress: MERCHANT_WALLET_ADDRESS,
    tokenAmount: amount,
    tokenSymbol: "USDC",
    network: 1,
    chainName: ChainEnum.EVM,
  });
};

Subscription Payments

const handleSubscriptionPayment = async (subscriptionId: string) => {
  const pay = usePayWithDynamic();
  
  await pay({
    destinationAddress: SUBSCRIPTION_CONTRACT_ADDRESS,
    tokenAmount: 99.99,
    tokenSymbol: "USDC",
    network: 1,
    chainName: ChainEnum.EVM,
  });
};