> ## Documentation Index
> Fetch the complete documentation index at: https://docs.phoenix.market/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native Integration

> Learn how to integrate Phoenix payments in your React Native application

Follow these steps to integrate using our pre-built React Native component.

## Step 1: Obtain Access Token

<AccordionGroup>
  <Accordion title="Obtaining Access Token">
    Before using the Phoenix component, you need to obtain an access token from your backend.

    ```tsx theme={null}
    // Example of fetching the access token from your backend
    const [accessToken, setAccessToken] = useState('');

    useEffect(() => {
      // Replace with your actual backend endpoint that implements the token generation
      fetch('https://your-backend.com/api/token')
        .then(response => response.json())
        .then(data => setAccessToken(data.access_token))
        .catch(error => console.error('Error fetching access token:', error));
    }, []);
    ```

    See the [Obtaining Access Token](/api-reference/access-token) page for detailed instructions on setting up the backend endpoint.
  </Accordion>
</AccordionGroup>

<Note>
  This is example code for obtaining an access token. You will need to adapt the endpoint URL and request format based on your specific backend implementation. Refer to the [Obtaining Access Token](/api-reference/access-token) page for a complete example of setting up a backend service.
</Note>

## Step 2: Install Required Dependencies

```bash theme={null}
# Install the WebView package
npm install react-native-webview

# For iOS, install pods
cd ios && pod install && cd ..
```

## Step 3: Download the Component

You can view the component code or download it directly:

<AccordionGroup>
  <Accordion title="View Component Code">
    ```tsx PhoenixPaymentNative.tsx theme={null}
    import React, { useRef, useEffect } from 'react';
    import { View, StyleSheet, TouchableOpacity, Text, Modal, Platform, Linking } from 'react-native';
    import { WebView } from 'react-native-webview';

    interface PhoenixPaymentNativeProps {
      walletAddress: string;
      amount: string;
      accessToken: string; // Access token from your backend
      onClose: () => void;
      onComplete: (success: boolean, transactionId?: string, data?: any) => void;
      onDone?: () => void; // New callback for handling done events
      onOrderCreated?: (data: any) => void; // Callback for handling order creation
      theme?: 'light' | 'dark'; // Optional theme parameter
    }

    export const PhoenixPaymentNative: React.FC<PhoenixPaymentNativeProps> = ({
      walletAddress,
      amount,
      accessToken,
      onClose,
      onComplete,
      onDone,
      onOrderCreated,
      theme,
    }) => {
      const webViewRef = useRef<WebView>(null);

      // Handle messages from WebView
      const handleWebViewMessage = (event: any) => {
        try {
          const data = JSON.parse(event.nativeEvent.data);
          
          // Handle transaction success message
          if (data?.type === 'TRANSACTION_SUCCESS') {
            const { amount, token, chain, walletAddress, sendAmount, transactionHash, basescanLink } = data.data;
            onComplete(true, transactionHash, { 
              amount, token, chain, walletAddress, sendAmount, transactionHash, basescanLink 
            });
          }
          // Handle order created message
          else if (data?.type === 'ORDER_CREATED') {
            const { order_id, ...orderData } = data.data;
            console.log('Order created:', data.data);
            
            if (onOrderCreated) {
              onOrderCreated({ order_id, data: orderData });
            }
          }
          // Handle cancel message
          else if (data?.type === 'CANCEL') {
            console.log('Payment canceled by user');
            onClose();
          }
          // Handle open explorer URL message
          else if (data?.type === 'OPEN_EXPLORER_URL') {
            const url = data.url;
            console.log('Opening explorer URL:', url);
            
            // Always open in browser using Linking
            Linking.canOpenURL(url).then(supported => {
              if (supported) {
                Linking.openURL(url);
              } else {
                console.log("Don't know how to open URI: " + url);
              }
            });
          }
          // Handle done message
          else if (data?.type === 'USER_DONE') {
            console.log('Payment process completed');
            
            if (onDone) {
              onDone();
            } else {
              // Default behavior: close the window
              onClose();
            }
          }
        } catch (error) {
          console.error('Error parsing WebView message:', error);
        }
      };

      // Build WebView URL with all parameters
      const getWebViewUrl = () => {
        let url = `https://app-partner.phoenix.market/?address=${walletAddress}&amount=${amount}&access_token=${accessToken}`;
        if (theme) {
          url += `&theme=${theme}`;
        }
        return url;
      };

      // Inject JavaScript to listen for postMessage events and forward them to React Native
      const injectedJavaScript = `
        window.addEventListener('message', function(event) {
          if (event.origin === 'https://app-partner.phoenix.market') {
            window.ReactNativeWebView.postMessage(JSON.stringify(event.data));
          }
        });
        true; // note: this is required, or you'll sometimes get silent failures
      `;

      return (
        <Modal
          visible={true}
          animationType="slide"
          transparent={true}
          onRequestClose={onClose}
        >
          <View style={styles.container}>
            <View style={styles.paymentModal}>
              <TouchableOpacity 
                style={styles.closeButton} 
                onPress={onClose}
                accessibilityLabel="Close payment window"
              >
                <Text style={styles.closeButtonText}>✕</Text>
              </TouchableOpacity>
              <WebView
                ref={webViewRef}
                source={{ uri: getWebViewUrl() }}
                style={styles.webView}
                javaScriptEnabled={true}
                domStorageEnabled={true}
                originWhitelist={['*']}
                injectedJavaScript={injectedJavaScript}
                onMessage={handleWebViewMessage}
                incognito={false}
                thirdPartyCookiesEnabled={true}
                allowsInlineMediaPlayback={true}
                androidLayerType="hardware"
              />
            </View>
          </View>
        </Modal>
      );
    };

    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: 'rgba(0, 0, 0, 0.5)',
        justifyContent: 'center',
        alignItems: 'center',
      },
      paymentModal: {
        backgroundColor: 'transparent',
        borderRadius: 10,
        width: 350,
        height: 600,
        overflow: 'visible',
        position: 'relative',
        ...Platform.select({
          ios: {
            shadowColor: 'transparent',
            shadowOffset: { width: 0, height: 0 },
            shadowOpacity: 0,
            shadowRadius: 0,
          },
          android: {
            elevation: 0,
          },
        }),
      },
      closeButton: {
        position: 'absolute',
        top: -15,
        right: -15,
        zIndex: 10,
        backgroundColor: '#333',
        width: 30,
        height: 30,
        borderRadius: 15,
        alignItems: 'center',
        justifyContent: 'center',
        padding: 0,
        ...Platform.select({
          ios: {
            shadowColor: '#000',
            shadowOffset: { width: 0, height: 2 },
            shadowOpacity: 0.25,
            shadowRadius: 4,
          },
          android: {
            elevation: 4,
          },
        }),
      },
      closeButtonText: {
        fontSize: 18,
        fontWeight: 'bold',
        color: 'white',
        lineHeight: 18,
        textAlign: 'center',
      },
      webView: {
        flex: 1,
        borderRadius: 8,
        overflow: 'hidden',
        backgroundColor: 'transparent',
        opacity: 0.99, // Fix for black borders on Android
      },
    });

    export default PhoenixPaymentNative;
    ```
  </Accordion>
</AccordionGroup>

<div style={{ marginTop: '24px', marginBottom: '24px' }}>
  <CardGroup>
    <a href="https://app-partner.phoenix.market/download-phoenix-native.html" download style={{ textDecoration: 'none', minWidth: '400px', display: 'block' }} target="_blank" rel="noopener noreferrer">
      <Card title="Download Component" icon="download">
        Click to download PhoenixPaymentNative.tsx
      </Card>
    </a>
  </CardGroup>
</div>

Save this file to your components directory, for example:
`components/PhoenixPaymentNative.tsx`

## Step 4: Implement the Component

Follow these steps to implement the component in your application:

### Step 4.1: Import the Component

```tsx theme={null}
import { PhoenixPaymentNative } from './components/PhoenixPaymentNative';
```

### Step 4.2: Prepare Required and Optional Parameters

Before adding the component, you need to collect the following information:

#### Required Parameters

* `walletAddress`: The Ethereum wallet address where USDC will be sent
* `amount`: The amount in USD to convert to USDC
* `accessToken`: The access token retrieved from your backend
* `onClose`: Function to handle when the user closes the payment modal
* `onComplete`: Callback function to handle payment completion events

#### Optional Parameters

* `onDone`: Callback function to handle when the payment process is complete
* `onOrderCreated`: Callback function to handle order creation events
* `theme`: Optional UI theme, can be set to either 'light' or 'dark'

You should create form inputs in your UI to collect the required information:

<AccordionGroup>
  <Accordion title="View Input Collection Example">
    ```tsx theme={null}
    // Example of collecting user input
    const [walletAddress, setWalletAddress] = useState('');
    const [amount, setAmount] = useState('');
    const [showPayment, setShowPayment] = useState(false);
    const [accessToken, setAccessToken] = useState('');

    useEffect(() => {
      // Fetch the access token from your backend
      fetch('https://your-backend.com/api/token')
        .then(response => response.json())
        .then(data => setAccessToken(data.access_token))
        .catch(error => console.error('Error fetching access token:', error));
    }, []);
    ```
  </Accordion>
</AccordionGroup>

### Step 4.3: Add the Component to Your JSX

Once you have collected these values, you can pass them to the PhoenixPaymentNative component:

```tsx theme={null}
<PhoenixPaymentNative
  walletAddress={walletAddress}
  amount={amount}
  accessToken={accessToken}
  onClose={() => setShowPayment(false)}
  onComplete={handlePaymentComplete}
  onDone={handlePaymentDone}
  onOrderCreated={handleOrderCreated}
  theme="dark" // Optional: specify 'light' or 'dark' theme
/>
```

### Step 4.4: Handle Order Created Events

The `onOrderCreated` callback is triggered when a new order is created in the Phoenix system. This happens before the payment process begins. The callback receives an object containing:

* `order_id`: Unique identifier for the order
* `data`: Object containing additional order details:
  * `created_at`: Timestamp of order creation
  * `buyer_address`: The buyer's wallet address
  * `seller_address`: The seller's wallet address
  * `send_amount`: The amount to be sent
  * `receive_amount`: The amount to be received
  * `uniqueness_amount`: The amount of cents added to the order for uniqueness
  * `receiving_chain_id`: The chain ID where the payment will be received
  * `zelle_address`: The Zelle address for payment

```tsx theme={null}
const handleOrderCreated = ({ order_id, data }) => {
  console.log('Order created:', { order_id, data });
  // You can store the order details for reference
};
```

### Step 4.5: Handle Payment Completion

The `onComplete` callback receives three parameters:

1. `success`: Boolean indicating if the operation succeeded
2. `transactionId`: Transaction hash (only for successful payments)
3. `data`: Object containing additional information

#### Transaction Success Data

When a transaction is successful, the data object will contain:

* `amount`: The USD amount
* `token`: The token type (e.g., "USDC")
* `chain`: The blockchain network
* `walletAddress`: The recipient wallet
* `sendAmount`: The amount of tokens sent
* `transactionHash`: The blockchain transaction hash
* `basescanLink`: Link to view the transaction

```tsx theme={null}
const handlePaymentComplete = (success, transactionId, data) => {
  if (success) {
    // For transaction success
    if (data?.transactionHash) {
      console.log('Transaction completed:', data.transactionHash);
      
      // Uncomment the following line if you want to close the window after transaction success
      // setShowPayment(false);
    }
  } else {
    console.log('Payment failed or cancelled');
    
    // Uncomment the following line if you want to close the window after payment failure/cancellation
    // setShowPayment(false);
  }
};
```

### Step 4.6: Handle Done Events

```tsx theme={null}
const handlePaymentDone = () => {
  // Close the payment window
  setShowPayment(false);
};
```

### Platform Configuration

#### iOS

Add to your `Info.plist`:

```xml theme={null}
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>app-partner.phoenix.market</key>
    <dict>
      <key>NSExceptionAllowsInsecureHTTPLoads</key>
      <false/>
      <key>NSIncludesSubdomains</key>
      <true/>
    </dict>
  </dict>
</dict>
```

#### Android

Add to your `AndroidManifest.xml`:

```xml theme={null}
<uses-permission android:name="android.permission.INTERNET" />
```

## Complete Implementation Example

<AccordionGroup>
  <Accordion title="View Complete Example">
    Here's a complete example showing how to implement the Phoenix payment in a React Native app:

    ```tsx theme={null}
    import React, { useState, useEffect } from 'react';
    import { View, Text, TextInput, Button, StyleSheet, Linking } from 'react-native';
    import { PhoenixPaymentNative } from './components/PhoenixPaymentNative';

    export default function PaymentScreen() {
      const [walletAddress, setWalletAddress] = useState('');
      const [amount, setAmount] = useState('');
      const [showPayment, setShowPayment] = useState(false);
      const [accessToken, setAccessToken] = useState('');
      
      useEffect(() => {
        // Fetch the access token from your backend
        fetch('https://your-backend.com/api/token')
          .then(response => response.json())
          .then(data => setAccessToken(data.access_token))
          .catch(error => console.error('Error fetching access token:', error));
      }, []);
      
      const handlePaymentComplete = (success, transactionId, data) => {
        if (success) {
          // For transaction success
          if (data?.transactionHash) {
            console.log('Transaction completed:', data.transactionHash);
            
            // Uncomment the following line if you want to close the window after transaction success
            // setShowPayment(false);
          }
        } else {
          console.log('Payment failed or cancelled');
          
          // Uncomment the following line if you want to close the window after payment failure/cancellation
          // setShowPayment(false);
        }
      };
      
      const handlePaymentDone = () => {
        // Close the payment window
        setShowPayment(false);
      };

      const handleOrderCreated = ({ order_id, data }) => {
        console.log('Order created:', { order_id, data });
        // You can store the order details for reference
      };

      return (
        <View style={styles.container}>
          <Text style={styles.label}>Wallet Address:</Text>
          <TextInput
            style={styles.input}
            value={walletAddress}
            onChangeText={setWalletAddress}
            placeholder="0x..."
          />
          
          <Text style={styles.label}>Amount (USD):</Text>
          <TextInput
            style={styles.input}
            value={amount}
            onChangeText={setAmount}
            keyboardType="numeric"
            placeholder="100"
          />
          
          <Button
            title="Make Payment"
            onPress={() => setShowPayment(true)}
            disabled={!accessToken} // Disable if access token isn't loaded
          />
          
          {showPayment && accessToken && (
            <PhoenixPaymentNative
              walletAddress={walletAddress}
              amount={amount}
              accessToken={accessToken}
              onClose={() => setShowPayment(false)}
              onComplete={handlePaymentComplete}
              onDone={handlePaymentDone}
              onOrderCreated={handleOrderCreated}
              theme="dark" // Optional: specify 'light' or 'dark' theme
            />
          )}
        </View>
      );
    }

    const styles = StyleSheet.create({
      container: {
        flex: 1,
        padding: 20,
      },
      label: {
        fontSize: 16,
        fontWeight: 'bold',
        marginBottom: 8,
      },
      input: {
        borderWidth: 1,
        borderColor: '#ddd',
        borderRadius: 4,
        padding: 8,
        marginBottom: 16,
      },
    });
    ```
  </Accordion>
</AccordionGroup>
