The BasePayButton is a ready-to-use React component that provides a seamless payment experience using Base Account. It handles the entire payment flow including user interaction, transaction processing, and result handling.

Please Follow the Brand Guidelines

If you intend on using the BasePayButton, please follow the Brand Guidelines to ensure consistency across your application.

Installation

npm install @base-org/account-ui

Basic Usage

import { BasePayButton } from '@base-org/account-ui/react';

function PaymentForm() {
  const handlePaymentResult = (result) => {
    if (result.success) {
      console.log('Payment successful!', result);
    } else {
      console.error('Payment failed:', result.error);
    }
  };

  return (
    <BasePayButton 
      paymentOptions={{
        amount: '10.00',
        to: 'your-wallet.eth',
        testnet: true
      }}
      colorScheme="light"
      onPaymentResult={handlePaymentResult}
    />
  );
}

Props

paymentOptions (required)

Payment configuration object with the following properties:

amount
string
required

The payment amount in USDC (e.g., “10.50” for $10.50)

to
string
required

The recipient wallet address or ENS name

testnet
boolean

Whether to use testnet for the payment (default: false)

payerInfo
object

Object containing information requests to collect during payment

Styling Props

colorScheme
'light' | 'dark' | 'system'

Color scheme for the button appearance (default: ‘system’)

size
'small' | 'medium' | 'large'

Button size (default: ‘medium’)

variant
'solid' | 'outline'

Button variant style (default: ‘solid’)

disabled
boolean

Whether the button is disabled (default: false)

Event Handlers

onPaymentResult
function

Callback function called when payment completes (success or failure)

onClick
function

Custom click handler (called before payment processing)

Payment Options

Basic Payment

const paymentOptions = {
  amount: '25.00',
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f6E456',
  testnet: true
};

<BasePayButton 
  paymentOptions={paymentOptions}
  colorScheme="light"
  onPaymentResult={handleResult}
/>

Payment with User Info Collection

const paymentOptions = {
  amount: '49.99',
  to: 'store.eth',
  payerInfo: {
    requests: [
      { type: 'email', optional: false },
      { type: 'name', optional: true },
      { type: 'physicalAddress', optional: false }
    ],
    callbackURL: 'https://api.example.com/validate' // Optional
  }
};

<BasePayButton 
  paymentOptions={paymentOptions}
  onPaymentResult={(result) => {
    if (result.success) {
      console.log('Payment successful!');
      console.log('User info:', result.payerInfoResponses);
    }
  }}
/>

Styling Options

Color Schemes

{/* Light theme */}
<BasePayButton 
  paymentOptions={paymentOptions}
  colorScheme="light"
/>

{/* Dark theme */}
<BasePayButton 
  paymentOptions={paymentOptions}
  colorScheme="dark"
/>

{/* System theme (follows user's system preference) */}
<BasePayButton 
  paymentOptions={paymentOptions}
  colorScheme="system"
/>

Sizes and Variants

{/* Different sizes */}
<BasePayButton size="small" paymentOptions={paymentOptions} />
<BasePayButton size="medium" paymentOptions={paymentOptions} />
<BasePayButton size="large" paymentOptions={paymentOptions} />

{/* Different variants */}
<BasePayButton variant="solid" paymentOptions={paymentOptions} />
<BasePayButton variant="outline" paymentOptions={paymentOptions} />

Event Handling

Payment Result Handling

const handlePaymentResult = (result) => {
  if (result.success) {
    // Payment successful
    console.log('Transaction hash:', result.transactionHash);
    console.log('Block number:', result.blockNumber);
    
    // Handle user info if collected
    if (result.userInfo) {
      console.log('User email:', result.userInfo.email);
      console.log('User name:', result.userInfo.name);
    }
    
    // Update UI, redirect, etc.
    showSuccessMessage();
    redirectToThankYouPage();
  } else {
    // Payment failed
    console.error('Payment error:', result.error);
    
    // Handle different error types
    if (result.error.includes('insufficient funds')) {
      showInsufficientFundsMessage();
    } else if (result.error.includes('user rejected')) {
      showUserCancelledMessage();
    } else {
      showGenericErrorMessage();
    }
  }
};

Custom Click Handler

const handleClick = () => {
  // Custom logic before payment
  console.log('User clicked pay button');
  
  // Analytics tracking
  trackEvent('payment_button_clicked', {
    amount: '10.00',
    product: 'subscription'
  });
  
  // Validation
  if (!isValidPayment()) {
    alert('Please complete the form first');
    return false; // Prevent payment
  }
  
  return true; // Continue with payment
};

<BasePayButton 
  paymentOptions={paymentOptions}
  onClick={handleClick}
  onPaymentResult={handlePaymentResult}
/>

Complete Example

import React, { useState } from 'react';
import { BasePayButton } from '@base-org/account-ui/react';

export default function CheckoutPage() {
  const [loading, setLoading] = useState(false);
  const [paymentStatus, setPaymentStatus] = useState(null);

  const paymentOptions = {
    amount: '29.99',
    to: 'merchant.eth',
    testnet: true,
    payerInfo: {
      requests: [
        { type: 'email', optional: false },
        { type: 'name', optional: true },
        { type: 'physicalAddress', optional: false }
      ]
    }
  };

  const handlePaymentResult = (result) => {
    setLoading(false);
    
    if (result.success) {
      setPaymentStatus({
        type: 'success',
        message: 'Payment successful!',
        transactionHash: result.transactionHash,
        userInfo: result.userInfo
      });
      
      // Send confirmation email
      if (result.userInfo?.email) {
        sendConfirmationEmail(result.userInfo.email, result.transactionHash);
      }
    } else {
      setPaymentStatus({
        type: 'error',
        message: `Payment failed: ${result.error}`
      });
    }
  };

  const handleClick = () => {
    setLoading(true);
    setPaymentStatus(null);
  };

  return (
    <div className="checkout-page">
      <h2>Complete Your Purchase</h2>
      <div className="product-info">
        <h3>Premium Subscription</h3>
        <p>$29.99/month</p>
      </div>
      
      <BasePayButton 
        paymentOptions={paymentOptions}
        colorScheme="light"
        size="large"
        disabled={loading}
        onClick={handleClick}
        onPaymentResult={handlePaymentResult}
      />
      
      {loading && (
        <div className="loading">
          Processing payment...
        </div>
      )}
      
      {paymentStatus && (
        <div className={`status ${paymentStatus.type}`}>
          <p>{paymentStatus.message}</p>
          {paymentStatus.transactionHash && (
            <p>Transaction: {paymentStatus.transactionHash}</p>
          )}
          {paymentStatus.userInfo && (
            <div>
              <p>Email: {paymentStatus.userInfo.email}</p>
              {paymentStatus.userInfo.name && (
                <p>Name: {paymentStatus.userInfo.name.firstName} {paymentStatus.userInfo.name.lastName}</p>
              )}
            </div>
          )}
        </div>
      )}
      
      <style jsx>{`
        .checkout-page {
          max-width: 400px;
          margin: 0 auto;
          padding: 20px;
        }
        
        .product-info {
          background: #f5f5f5;
          padding: 20px;
          border-radius: 8px;
          margin-bottom: 20px;
        }
        
        .loading {
          text-align: center;
          margin-top: 15px;
          color: #666;
        }
        
        .status {
          margin-top: 20px;
          padding: 15px;
          border-radius: 8px;
        }
        
        .status.success {
          background: #d4edda;
          border: 1px solid #c3e6cb;
          color: #155724;
        }
        
        .status.error {
          background: #f8d7da;
          border: 1px solid #f5c6cb;
          color: #721c24;
        }
      `}</style>
    </div>
  );
}

TypeScript Support

The component is fully typed when using TypeScript:

import { BasePayButton, PaymentOptions, PaymentResult } from '@base-org/account-ui/react';

interface CheckoutProps {
  amount: string;
  recipient: string;
}

function Checkout({ amount, recipient }: CheckoutProps) {
  const paymentOptions: PaymentOptions = {
    amount,
    to: recipient,
    testnet: true
  };

  const handleResult = (result: PaymentResult) => {
    // TypeScript provides full type safety
    if (result.success) {
      console.log(result.transactionHash); // ✅ Type-safe
    }
  };

  return (
    <BasePayButton 
      paymentOptions={paymentOptions}
      onPaymentResult={handleResult}
    />
  );
}

Testing

For testing your integration:

  1. Use testnet mode: Set testnet: true in payment options
  2. Get test USDC: Use Circle’s faucet on Base Sepolia
  3. Test different scenarios: Try successful payments, cancellations, and errors
  4. Verify user info collection: Test with different payerInfo configurations

The BasePayButton provides a complete, production-ready payment solution that handles all the complexity of crypto payments while providing a familiar user experience.