The MoonPay module provides seamless cryptocurrency purchasing capabilities directly within your app. Users can buy crypto using credit cards, debit cards, Apple Pay, and other payment methods through MoonPay’s trusted platform.

Core Functionalities

Crypto On-Ramp

Purchase cryptocurrencies directly using various payment methods with automatic wallet integration

Wallet Integration

Automatically populates the user’s connected wallet address as the destination for purchases

Customizable Widget

Configurable MoonPay widget supporting both sandbox and production environments

Balance Management

View wallet balances and seamlessly navigate to purchase flows when funds are needed

Installation & Setup

1

Install MoonPay SDK

The module uses the official MoonPay React Native SDK:

npm install @moonpay/react-native-moonpay-sdk
2

Configure API Key

Add your MoonPay API key to .env.local:

MOONPAY_API_KEY=pk_test_your_api_key_here
# For production: pk_live_your_production_key
3

Import Components

Import the components you need:

import { 
  MoonPayWidget, 
  OnrampScreen,
  WalletScreen 
} from '@/modules/moonpay';

API Keys: Use pk_test_ prefixed keys for testing and pk_live_ for production. The module automatically detects the environment.

Module Architecture

src/modules/moonpay/
├── components/             # UI components
│   └── MoonPayWidget/     # Core MoonPay WebView wrapper
├── screens/               # Complete screen implementations
├── services/              # MoonPay configuration and utilities
├── types/                 # TypeScript definitions
└── utils/                 # Helper functions

Core Components

MoonPayWidget - Core component that embeds MoonPay’s purchase flow

import { MoonPayWidget } from '@/modules/moonpay';

function PurchaseCrypto() {
  const handleWidgetOpen = () => {
    console.log('MoonPay widget opened');
  };
  
  const handleError = (error) => {
    console.error('MoonPay error:', error);
  };
  
  return (
    <MoonPayWidget
      apiKey={process.env.MOONPAY_API_KEY}
      environment="sandbox" // or "production"
      height={600}
      onOpen={handleWidgetOpen}
      onError={handleError}
      onRetry={() => console.log('Retrying...')}
    />
  );
}

Props:

  • apiKey - Your MoonPay API key
  • environment - ‘sandbox’ or ‘production’
  • height - Widget height in pixels
  • onOpen - Callback when widget loads
  • onError - Error handling callback
  • onRetry - Retry mechanism callback

Quick Start Example

import { OnrampScreen } from '@/modules/moonpay';
import { useWallet } from '@/modules/wallet-providers';

function BuyCryptoFlow() {
  const { wallet } = useWallet();
  
  if (!wallet) {
    return (
      <View style={styles.container}>
        <Text>Please connect your wallet first</Text>
      </View>
    );
  }
  
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Buy Cryptocurrency</Text>
      <Text style={styles.subtitle}>
        Funds will be sent to: {wallet.address}
      </Text>
      <OnrampScreen />
    </View>
  );
}

MoonPay Service Configuration

The moonpayService.ts provides configuration and validation utilities:

import { createMoonPayService } from '@/modules/moonpay';

// Create MoonPay service instance
const moonPayService = createMoonPayService({
  apiKey: process.env.MOONPAY_API_KEY,
  environment: 'sandbox', // or 'production'
});

// Validate configuration
const isValid = moonPayService.validateConfig();
if (!isValid) {
  console.error('MoonPay configuration is invalid');
}

// Get widget URL (if needed for custom implementations)
const widgetUrl = moonPayService.getWidgetUrl({
  walletAddress: userWallet.address,
  currencyCode: 'SOL',
  baseCurrencyCode: 'USD',
});

Utility Functions

address_formatting
object
error_handling
object
environment_detection
object

Advanced Usage Patterns

Custom Purchase Flow

function CustomPurchaseFlow() {
  const { wallet } = useWallet();
  const [purchaseAmount, setPurchaseAmount] = useState('');
  const [selectedCurrency, setSelectedCurrency] = useState('SOL');

  const initiateCustomPurchase = () => {
    // Custom logic before showing MoonPay widget
    analytics.track('purchase_initiated', {
      amount: purchaseAmount,
      currency: selectedCurrency,
      wallet: wallet.address
    });
    
    // Show MoonPay widget with pre-filled values
    navigation.navigate('OnrampScreen', {
      defaultAmount: purchaseAmount,
      defaultCurrency: selectedCurrency
    });
  };

  return (
    <View>
      <TextInput
        value={purchaseAmount}
        onChangeText={setPurchaseAmount}
        placeholder="Amount to purchase"
        keyboardType="numeric"
      />
      
      <Picker
        selectedValue={selectedCurrency}
        onValueChange={setSelectedCurrency}
      >
        <Picker.Item label="Solana (SOL)" value="SOL" />
        <Picker.Item label="USD Coin (USDC)" value="USDC" />
      </Picker>
      
      <Button
        title="Buy Crypto"
        onPress={initiateCustomPurchase}
      />
    </View>
  );
}

Transaction Status Tracking

function useMoonPayTransactionStatus() {
  const [transactions, setTransactions] = useState([]);
  const [loading, setLoading] = useState(false);

  const trackTransaction = async (transactionId) => {
    setLoading(true);
    try {
      // Note: This would require backend integration with MoonPay's API
      const status = await fetch(`/api/moonpay/transaction/${transactionId}`);
      const data = await status.json();
      
      setTransactions(prev => 
        prev.map(tx => 
          tx.id === transactionId 
            ? { ...tx, status: data.status }
            : tx
        )
      );
    } catch (error) {
      console.error('Failed to track transaction:', error);
    } finally {
      setLoading(false);
    }
  };

  return { transactions, trackTransaction, loading };
}

Balance-Based Recommendations

function BalanceBasedOnramp() {
  const { wallet } = useWallet();
  const { balance } = useBalance(wallet?.address);
  const [showRecommendation, setShowRecommendation] = useState(false);

  useEffect(() => {
    // Show purchase recommendation if balance is low
    if (balance < 0.1) { // Less than 0.1 SOL
      setShowRecommendation(true);
    }
  }, [balance]);

  if (!showRecommendation) return null;

  return (
    <Card style={styles.recommendationCard}>
      <Text style={styles.recommendationTitle}>Low Balance Detected</Text>
      <Text style={styles.recommendationText}>
        Your SOL balance is low ({balance.toFixed(4)} SOL). 
        Consider adding funds to cover transaction fees.
      </Text>
      <Button
        title="Add Funds"
        onPress={() => navigation.navigate('OnrampScreen')}
      />
      <TouchableOpacity onPress={() => setShowRecommendation(false)}>
        <Text style={styles.dismissText}>Dismiss</Text>
      </TouchableOpacity>
    </Card>
  );
}

Integration with Other Modules

Combined Workflow Example

import { useWallet } from '@/modules/wallet-providers';
import { useFetchTokens } from '@/modules/data-module';
import { useSwap } from '@/modules/swap';
import { MoonPayWidget } from '@/modules/moonpay';

function BuyAndSwapFlow() {
  const { wallet } = useWallet();
  const { refetch: refetchBalance } = useFetchTokens(wallet?.address);
  const { executeSwap } = useSwap();
  const [purchaseComplete, setPurchaseComplete] = useState(false);

  const handlePurchaseComplete = async () => {
    setPurchaseComplete(true);
    
    // Refresh balance after purchase
    await refetchBalance();
    
    // Optional: Auto-swap purchased SOL to another token
    const shouldSwap = await askUserForSwap();
    if (shouldSwap) {
      navigation.navigate('SwapScreen', {
        inputToken: 'SOL',
        outputToken: 'USDC'
      });
    }
  };

  return (
    <View>
      {!purchaseComplete ? (
        <MoonPayWidget
          apiKey={process.env.MOONPAY_API_KEY}
          environment="production"
          onOpen={handlePurchaseComplete}
        />
      ) : (
        <PurchaseSuccessScreen onContinue={() => navigation.goBack()} />
      )}
    </View>
  );
}

Environment Configuration

# .env.local for development
MOONPAY_API_KEY=pk_test_your_sandbox_key_here
  • Uses sandbox environment automatically
  • Test transactions don’t involve real money
  • Full widget functionality for testing

Error Handling & Troubleshooting

common_issues
object
function RobustMoonPayWidget() {
  const [error, setError] = useState(null);
  const [retryCount, setRetryCount] = useState(0);
  const maxRetries = 3;

  const handleError = (err) => {
    console.error('MoonPay error:', err);
    setError(parseErrorMessage(err));
  };

  const handleRetry = () => {
    if (retryCount < maxRetries) {
      setRetryCount(prev => prev + 1);
      setError(null);
    } else {
      Alert.alert(
        'Connection Failed',
        'Please check your internet connection and try again later.'
      );
    }
  };

  return (
    <View>
      {error ? (
        <ErrorDisplay 
          message={error}
          onRetry={retryCount < maxRetries ? handleRetry : null}
        />
      ) : (
        <MoonPayWidget
          apiKey={process.env.MOONPAY_API_KEY}
          environment="production"
          onError={handleError}
          onRetry={handleRetry}
        />
      )}
    </View>
  );
}

Security Considerations

API Key Security: Never expose production API keys in client-side code. Consider using a backend proxy for additional security.

User Verification: MoonPay handles KYC/AML compliance, but ensure your app provides clear information about verification requirements.

API Reference

For detailed API documentation, see:


The MoonPay module provides a seamless bridge between traditional finance and crypto, enabling users to easily onboard into the Solana ecosystem with familiar payment methods.