Skip to main content

Overview

Enable native Apple Pay onramp in your React Native app, allowing users to purchase crypto directly without leaving your app. This provides the fastest onramp experience available, with a fully native feel on iOS devices.
Native Apple Pay onramp is currently available for US users only on iOS devices via the React Native SDK. For web users, or to support other payment methods, see Cross-Platform Onramp.

Prerequisites

  • A free CDP Portal account and project
  • A React Native app using the CDP React Native SDK
  • iOS development environment (Xcode 16.1+, iOS 15.1+)
  • Users must be located in the United States with a valid US phone number

Testing and Production Access

You can get started testing the Apple Pay onramp using sandbox mode by setting isSandbox: true when creating orders. When you’re ready to test with real funds, contact us to get production access.

User Verification Requirements

Apple Pay onramp requires users to have verified contact information:
  • Email verification: Users must have a verified email address
  • SMS verification: Users must have a verified US phone number (a real mobile number, not VoIP).
CDP handles the verification process, but you are responsible for building the UI to prompt users when verification is needed. The useApplePay hook returns an error with a specific code when verification is required, allowing you to display the appropriate verification flow.

Integration

The React Native SDK provides three main components for Apple Pay integration:
ComponentDescription
ApplePayProviderContext provider that manages Apple Pay state and order creation
ApplePayButtonRenders the Apple Pay payment WebView when an order is created
useApplePayHook that provides order creation, payment status, and error handling

Installation

Install the @coinbase/cdp-react-native package, along with its peer dependency on react-native-webview (tested on version 13.13.5):
npm install @coinbase/cdp-react-native@latest @coinbase/cdp-hooks@latest [email protected]

Setup

Wrap your app or the relevant screen with the ApplePayProvider:
import { ApplePayProvider } from '@coinbase/cdp-react-native';

function ApplePayScreen() {
  return (
    <ApplePayProvider>
      <ApplePayFlow />
    </ApplePayProvider>
  );
}

Using the useApplePay Hook

The useApplePay hook provides access to order creation and payment status:
const { status, data, error, createOrder, reset } = useApplePay();

Hook Return Values

The useApplePay hook returns the following:
PropertyTypeDescription
createOrder(options: CreateOrderOptions) => Promise<void>Creates an Apple Pay onramp order
dataEndUserApplePayOnrampOrderCreateResponse | undefinedThe created order data, including the payment link
status'idle' | 'pending' | 'error' | 'success'Current status of the onramp flow
errorOnrampError | undefinedError details if status is 'error'
reset() => voidResets the state back to 'idle'

CreateOrderOptions

PropertyTypeDescription
destination.addressstringThe blockchain address receiving the purchased crypto
destination.networkNetworkThe blockchain network (e.g., 'base', 'ethereum')
purchase.amountstringThe crypto amount to purchase, exclusive of fees (e.g., '10.00')
purchase.currencystringThe cryptocurrency to purchase (e.g., 'usdc')
payment.currencystringThe fiat currency for payment (e.g., 'usd')
isSandboxbooleanSet to true for sandbox testing (default: false)

Error Codes

When status is 'error', check the error.code to determine the appropriate action:
Error CodeDescriptionAction Required
requires_emailUser needs to verify their emailDisplay email verification UI
requires_smsUser needs to verify their phone numberDisplay SMS verification UI
user_not_authenticatedUser is not logged inRedirect to authentication flow
api_errorAPI error occurredDisplay error message and retry option

Rendering the Apple Pay Button

The ApplePayButton component automatically reads the payment URL from the ApplePayProvider context and renders the Apple Pay WebView. It only renders when order data is available:
import { ApplePayButton } from '@coinbase/cdp-react-native';

function PaymentView() {
  const { data, reset } = useApplePay();

  // ApplePayButton only renders when data exists
  return (
    <View style={styles.container}>
      <ApplePayButton style={{ width: '100%', height: 48 }} />
      <Button title="Cancel" onPress={reset} />
    </View>
  );
}
The ApplePayButton returns null on non-iOS platforms. You should check Platform.OS === 'ios' and display an appropriate message for Android users.

Handling Verification

When the useApplePay hook returns an error indicating missing verification, guide users through the verification process. You can use the CDP hooks to handle email and SMS verification:
import { useApplePay } from '@coinbase/cdp-react-native';
import { Platform } from 'react-native';

function ApplePayFlow() {
  const { status, error, reset } = useApplePay();

  // Platform check - Apple Pay is iOS only
  if (Platform.OS !== 'ios') {
    return (
      <View>
        <Text>Apple Pay is only available on iOS devices.</Text>
      </View>
    );
  }

  if (status === 'error') {
    if (error?.code === 'requires_email') {
      // Render your email verification component
      // After successful verification, the user can retry
      return <EmailVerificationFlow onComplete={reset} />;
    }

    if (error?.code === 'requires_sms') {
      // Render your SMS verification component
      // After successful verification, the user can retry
      return <SmsVerificationFlow onComplete={reset} />;
    }

    // Handle other errors
    return (
      <View>
        <Text>Error: {error?.message || 'An error occurred'}</Text>
        <Button title="Try Again" onPress={reset} />
      </View>
    );
  }

  // Continue with Apple Pay flow...
}
Here’s a complete example showing the full Apple Pay onramp flow with verification handling:
import { useState, useCallback } from 'react';
import { View, Text, TextInput, Button, Platform } from 'react-native';
import { ApplePayProvider, ApplePayButton, useApplePay } from '@coinbase/cdp-react-native';
import { useEvmAddress } from '@coinbase/cdp-hooks';

export default function ApplePayScreen() {
  return (
    <ApplePayProvider>
      <ApplePayFlow />
    </ApplePayProvider>
  );
}

function ApplePayFlow() {
  const { status, data, error, createOrder, reset } = useApplePay();
  const { evmAddress } = useEvmAddress();
  const [amount, setAmount] = useState('');

  const handleCreateOrder = useCallback(async () => {
    const parsedAmount = parseFloat(amount);
    if (parsedAmount < 1 || parsedAmount > 10000 || !evmAddress) {
      return;
    }

    await createOrder({
      destination: { address: evmAddress, network: 'base' },
      purchase: { amount, currency: 'usdc' },
      payment: { currency: 'usd' },
      isSandbox: true,
    });
  }, [amount, evmAddress, createOrder]);

  const handleDone = useCallback(() => {
    reset();
    setAmount('');
  }, [reset]);

  // Platform check - Apple Pay is iOS only
  if (Platform.OS !== 'ios') {
    return (
      <View>
        <Text>Apple Pay is only available on iOS devices.</Text>
      </View>
    );
  }

  // Handle errors including verification requirements
  if (status === 'error') {
    if (error?.code === 'requires_email') {
      return <EmailVerificationView />;
    }
    if (error?.code === 'requires_sms') {
      return <SmsVerificationView />;
    }
    return (
      <View>
        <Text>Error: {error?.message || 'An error occurred'}</Text>
        <Button title="Try Again" onPress={reset} />
      </View>
    );
  }

  // Show success state
  if (status === 'success') {
    return (
      <View>
        <Text>Purchase complete!</Text>
        <Button title="Done" onPress={handleDone} />
      </View>
    );
  }

  // Show Apple Pay button when order is ready
  if (status === 'pending' && data) {
    return (
      <View>
        <Text>Complete your purchase</Text>
        <ApplePayButton />
        <Button title="Cancel" onPress={reset} />
      </View>
    );
  }

  // Show order form
  return (
    <View>
      <TextInput
        value={amount}
        onChangeText={setAmount}
        placeholder="Amount in USD"
        keyboardType="decimal-pad"
      />
      <Button
        title={status === 'pending' ? 'Creating order...' : 'Buy USDC with Apple Pay'}
        onPress={handleCreateOrder}
        disabled={status === 'pending' || !amount}
      />
    </View>
  );
}
Your users must accept Coinbase’s legal agreements before using Apple Pay onramp: Ensure your app clearly informs users that by proceeding with payment, they are agreeing to these policies.

Reference Implementation

For a complete React Native reference implementation showcasing the Apple Pay integration with CDP Embedded Wallets, check out the example app on GitHub.