You can request USDC from someone using any of the following methods:

Subdomain

If you are on EVM, you can allow your users to generate their own unique subdomain for receiving funds. Set it up using the Global Identity guide.

Dynamic UI

Once a user has created their subdomain, they can find it in their user profile section of the Dynamic Widget and copy it to share with others, who can then send USDC to them.
ENS subdomain in Dynamic Widget

Hooks Only (Headless)

Access the users subdomain by finding the VCs which have a name service, and then checking the nameService.name property:
const walletVCsWithNameService = user.verifiedCredentials.filter(
  vc => vc.nameService?.name
);

const nameService = walletVCsWithNameService[0].nameService.name;
If you have multiple wallet VCs with a name service, you can filter by the primary wallet:
const { primaryWallet } = useDynamicContext();

const primaryWalletVC = walletVCsWithNameService.find(vc => vc.address === primaryWallet.address);

const nameService = primaryWalletVC?.nameService.name;

QR Code

Dynamic UI

In the “Deposit” section of the Dynamic Widget UI, you’ll see a “Receive by QR button”, which will reveal a QR code that anyone can scan. It will provide the wallet address for the sender to send USDC to.
Deposit by QR code in Dynamic Widget

Hooks Only (Headless)

Simply access the users wallet address using the primaryWallet.address property, and use any QR code generator to create a QR code for said address.

Wallet Address

You can also allow the user to copy and paste their wallet address directly rather than via providing a QR code.

Dynamic UI

In the same “Receive by QR” section, you’ll see a partial wallet address along with a “Copy” button.

Hooks Only (Headless)

Simply access the users wallet address using the primaryWallet.address property and provide it to the user. You can build custom payment links that allow others to send you USDC with a preset amount. While Dynamic doesn’t provide a native payment link generator, you can create this functionality using the wallet’s direct transfer capabilities and URL parameters. Create a component that generates shareable URLs with preset payment parameters:
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { useState } from "react";

const PaymentLinkGenerator = () => {
  const [amount, setAmount] = useState("10");
  const [paymentLink, setPaymentLink] = useState("");
  const { primaryWallet } = useDynamicContext();

  const generatePaymentLink = async () => {
    if (!primaryWallet?.address) return;
    
    // Get current network
    const currentChainId = await getNetwork(primaryWallet.connector);
    
    // Create a payment link with preset parameters
    const baseUrl = window.location.origin;
    const params = new URLSearchParams({
      recipient: primaryWallet.address,
      amount: amount,
      token: "USDC",
      network: currentChainId?.toString() || "1", // Use current network
    });
    
    const link = `${baseUrl}/pay?${params.toString()}`;
    setPaymentLink(link);
  };

  const copyToClipboard = () => {
    navigator.clipboard.writeText(paymentLink);
  };

  return (
    <div>
      <h3>Generate Payment Link</h3>
      <div>
        <label>Amount (USDC):</label>
        <input 
          type="number" 
          value={amount} 
          onChange={(e) => setAmount(e.target.value)}
          min="0.01"
          step="0.01"
        />
      </div>
      <button onClick={generatePaymentLink}>Generate Link</button>
      
      {paymentLink && (
        <div>
          <p>Payment Link:</p>
          <input 
            type="text" 
            value={paymentLink} 
            readOnly 
            style={{ width: "100%", marginBottom: "10px" }}
          />
          <button onClick={copyToClipboard}>Copy Link</button>
        </div>
      )}
    </div>
  );
};
Create a payment page that handles incoming payment links and uses the wallet’s direct transfer capabilities:
import { useDynamicContext, getNetwork } from "@dynamic-labs/sdk-react-core";
import { isEthereumWallet } from "@dynamic-labs/ethereum";
import { parseUnits, erc20Abi } from 'viem';
import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";

const PaymentPage = () => {
  const [searchParams] = useSearchParams();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState(false);
  const { primaryWallet, user } = useDynamicContext();

  // Extract parameters from URL
  const recipientAddress = searchParams.get("recipient");
  const amount = searchParams.get("amount");
  const token = searchParams.get("token") || "USDC";
  const network = searchParams.get("network") || "1";

  const handlePayment = async () => {
    if (!recipientAddress || !amount) {
      setError("Invalid payment link parameters");
      return;
    }

    // Check if user is authenticated
    if (!user) {
      setError("Please log in to complete this payment");
      return;
    }

    if (!primaryWallet || !isEthereumWallet(primaryWallet)) {
      setError("Wallet not connected or not EVM compatible");
      return;
    }

    // Check if wallet is on the correct network for USDC
    const expectedChainId = parseInt(network); // From URL params
    const currentChainId = await getNetwork(primaryWallet.connector);
    
    if (currentChainId !== expectedChainId) {
      // Try to switch to the correct network
      if (primaryWallet.connector.supportsNetworkSwitching()) {
        try {
          setError(`Switching to ${getNetworkName(expectedChainId)}...`);
          await primaryWallet.switchNetwork(expectedChainId);
          // Verify the switch was successful
          const newChainId = await getNetwork(primaryWallet.connector);
          if (newChainId !== expectedChainId) {
            setError(`Failed to switch to ${getNetworkName(expectedChainId)}. Please switch manually.`);
            return;
          }
        } catch (switchError: any) {
          setError(`Failed to switch network: ${switchError.message}. Please switch to ${getNetworkName(expectedChainId)} manually.`);
          return;
        }
      } else {
        setError(`Please switch to ${getNetworkName(expectedChainId)}. Your wallet doesn't support automatic network switching.`);
        return;
      }
    }

    setIsLoading(true);
    setError(null);

    try {
      const walletClient = await primaryWallet.getWalletClient();
      
      // Get USDC contract address for the current network
      const usdcAddress = getUSDCAddress(currentChainId);
      
      // Convert amount to USDC units (6 decimals)
      const amountInUnits = parseUnits(amount, 6);

      // Send USDC transfer
      const hash = await walletClient.writeContract({
        address: usdcAddress as `0x${string}`,
        abi: erc20Abi,
        functionName: 'transfer',
        args: [recipientAddress as `0x${string}`, amountInUnits],
      });

      setSuccess(true);
      console.log("USDC transfer successful:", hash);
    } catch (err: any) {
      setError(err.message || "Payment failed");
    } finally {
      setIsLoading(false);
    }
  };

  // Helper function to get USDC contract address for different networks
  const getUSDCAddress = (chainId: number): string => {
    const usdcAddresses: Record<number, string> = {
      1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Ethereum mainnet
      137: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // Polygon
      42161: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // Arbitrum One
      10: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // Optimism
      8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base
      11155111: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", // Sepolia testnet
    };
    
    const address = usdcAddresses[chainId];
    if (!address) {
      throw new Error(`USDC not supported on network ${chainId}`);
    }
    
    return address;
  };

  // Helper function to get network name
  const getNetworkName = (chainId: number): string => {
    const networks: Record<number, string> = {
      1: "Ethereum Mainnet",
      137: "Polygon",
      42161: "Arbitrum One",
      10: "Optimism",
      8453: "Base",
      11155111: "Sepolia Testnet",
    };
    
    return networks[chainId] || `Network ${chainId}`;
  };

  if (!recipientAddress || !amount) {
    return <div>Invalid payment link</div>;
  }

  // Show login prompt if user is not authenticated
  if (!user) {
    return (
      <div style={{ padding: '20px', border: '1px solid #ffc107', borderRadius: '8px', backgroundColor: '#fff3cd' }}>
        <h2>Authentication Required</h2>
        <p>You need to log in to complete this payment.</p>
        <p><strong>Amount:</strong> {amount} {token}</p>
        <p><strong>Recipient:</strong> {recipientAddress.substring(0, 8)}...{recipientAddress.substring(recipientAddress.length - 6)}</p>
        
        <div style={{ marginTop: '20px' }}>
          <p>Please connect your wallet to continue with the payment.</p>
          <p style={{ fontSize: '14px', color: '#666' }}>
            Once you log in, you'll be able to complete the payment securely.
          </p>
        </div>
      </div>
    );
  }

  if (success) {
    return (
      <div>
        <h2>Payment Successful!</h2>
        <p>You have successfully sent {amount} {token} to {recipientAddress.substring(0, 8)}...</p>
      </div>
    );
  }

  return (
    <div>
      <h2>Complete Payment</h2>
      <p>Amount: {amount} {token}</p>
      <p>Recipient: {recipientAddress.substring(0, 8)}...{recipientAddress.substring(recipientAddress.length - 6)}</p>
      
      {isLoading && <p>Processing payment...</p>}
      {error && <p style={{ color: "red" }}>Error: {error}</p>}
      
      <button onClick={handlePayment} disabled={isLoading}>
        {isLoading ? "Processing..." : "Pay Now"}
      </button>
    </div>
  );
};
For more sophisticated payment links, you can include additional metadata:
const generateAdvancedPaymentLink = (amount: string, description?: string, reference?: string) => {
  if (!primaryWallet?.address) return;
  
  const baseUrl = window.location.origin;
  const params = new URLSearchParams({
    recipient: primaryWallet.address,
    amount: amount,
    token: "USDC",
    network: "1",
    ...(description && { description }),
    ...(reference && { reference }),
    timestamp: Date.now().toString(),
  });
  
  return `${baseUrl}/pay?${params.toString()}`;
};

// Example usage:
const link = generateAdvancedPaymentLink("25.50", "Coffee payment", "coffee-2024-01");
// Result: https://yourapp.com/pay?recipient=0x...&amount=25.50&token=USDC&network=1&description=Coffee%20payment&reference=coffee-2024-01&timestamp=1704067200000

Security Considerations

When implementing payment links:
  1. Validate Parameters: Always validate the recipient address and amount on the server side
  2. Rate Limiting: Implement rate limiting to prevent abuse
  3. Expiration: Consider adding expiration timestamps to payment links
  4. HTTPS Only: Ensure payment links are only served over HTTPS
  5. Amount Limits: Set reasonable minimum and maximum amounts for payments