Skip to content

React Native Integration ​

Production-Ready Integration Pattern

This guide shows battle-tested integration patterns for Bridge Payments in React Native apps, based on real production implementations.

Bridge Payments is a production-ready REST API that you integrate directly using fetch. Official React Native SDK coming soon!

Installation ​

bash
# Install Stripe React Native (required for payment UI)
npm install @stripe/stripe-react-native

# Install dependencies
npx pod-install  # iOS only

Direct API Integration

Bridge Payments is a REST API - no Bridge Payments-specific packages needed! Just use fetch to make HTTP requests to your Bridge Payments instance. This gives you complete control and flexibility.

Configuration ​

1. Environment Variables ​

Create or update your .env file:

bash
# Bridge Payments instance URL
EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL=https://your-instance.pubflow.com

# Stripe publishable key
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...

# Optional: Enable debug logging
EXPO_PUBLIC_LOG_MODE=development

2. Initialize Stripe Provider ​

tsx
// App.tsx
import { StripeProvider } from '@stripe/stripe-react-native';

export default function App() {
  return (
    <StripeProvider publishableKey={process.env.EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY!}>
      {/* Your app content */}
    </StripeProvider>
  );
}

Basic Usage ​

Create Payment Client ​

typescript
import { BridgePaymentClient } from '@pubflow/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Get session ID from AsyncStorage
const sessionId = await AsyncStorage.getItem('pubflow_session_id');

// Initialize client
const client = new BridgePaymentClient({
  baseUrl: process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL!,
  sessionId: sessionId!
});

Complete Payment Flow ​

tsx
import { useState } from 'react';
import { View, Button, Alert } from 'react-native';
import { CardField, useStripe } from '@stripe/stripe-react-native';
import { BridgePaymentClient } from '@pubflow/react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

function CheckoutScreen() {
  const stripe = useStripe();
  const [loading, setLoading] = useState(false);
  
  const handlePayment = async () => {
    setLoading(true);
    
    try {
      // 1. Get session ID
      const sessionId = await AsyncStorage.getItem('pubflow_session_id');
      
      // 2. Initialize client
      const client = new BridgePaymentClient({
        baseUrl: process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL!,
        sessionId: sessionId!
      });
      
      // 3. Create payment intent
      const intent = await client.createPaymentIntent({
        subtotal_cents: 1800,
        tax_cents: 200,
        total_cents: 2000,
        currency: 'USD',
        concept: 'Premium Subscription',
        description: 'Monthly premium plan',
        provider_id: 'stripe'
      });
      
      // 4. Confirm payment with Stripe
      const { error, paymentIntent } = await stripe.confirmPayment(
        intent.client_secret,
        {
          paymentMethodType: 'Card'
        }
      );
      
      if (error) {
        Alert.alert('Payment Failed', error.message);
        return;
      }
      
      // 5. Sync status with backend
      const payment = await client.syncPaymentStatus(paymentIntent.id);
      
      if (payment.status === 'succeeded') {
        Alert.alert('Success', 'Payment completed successfully!');
      }
    } catch (error) {
      console.error('Payment error:', error);
      Alert.alert('Error', 'An error occurred. Please try again.');
    } finally {
      setLoading(false);
    }
  };
  
  return (
    <View style={{ padding: 20 }}>
      <CardField
        postalCodeEnabled={false}
        style={{ height: 50, marginBottom: 20 }}
      />
      <Button
        title={loading ? 'Processing...' : 'Pay $20.00'}
        onPress={handlePayment}
        disabled={loading}
      />
    </View>
  );
}

Payment Methods ​

List Saved Payment Methods ​

typescript
const client = new BridgePaymentClient({
  baseUrl: process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL!,
  sessionId: sessionId!
});

const methods = await client.getPaymentMethods();

// Display payment methods
methods.forEach(method => {
  console.log(`${method.card_brand} ending in ${method.card_last_four}`);
});

Save Payment Method During Payment ​

typescript
const intent = await client.createPaymentIntent({
  total_cents: 2000,
  currency: 'USD',
  concept: 'First Purchase',
  setup_future_usage: 'off_session' // Save for future use
});

Use Saved Payment Method ​

typescript
const intent = await client.createPaymentIntent({
  total_cents: 2000,
  currency: 'USD',
  concept: 'Subscription Renewal',
  payment_method_id: savedMethod.id // Use saved method
});

Delete Payment Method ​

typescript
await client.deletePaymentMethod(methodId);

Guest Checkout ​

tsx
function GuestDonationScreen() {
  const stripe = useStripe();
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [amount, setAmount] = useState(50);
  
  const handleDonation = async () => {
    try {
      // No session ID required for guest checkout
      const client = new BridgePaymentClient({
        baseUrl: process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL!
      });
      
      const intent = await client.createPaymentIntent({
        total_cents: amount * 100,
        currency: 'USD',
        concept: 'Donation',
        provider_id: 'stripe',
        guest_data: {
          email,
          name
        }
      });
      
      const { error } = await stripe.confirmPayment(
        intent.client_secret,
        {
          paymentMethodType: 'Card'
        }
      );
      
      if (!error) {
        Alert.alert('Thank You!', 'Your donation was successful.');
      }
    } catch (error) {
      console.error('Donation error:', error);
    }
  };
  
  return (
    <View style={{ padding: 20 }}>
      <TextInput
        placeholder="Email"
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
      />
      <TextInput
        placeholder="Name"
        value={name}
        onChangeText={setName}
      />
      <TextInput
        placeholder="Amount"
        value={String(amount)}
        onChangeText={(text) => setAmount(Number(text))}
        keyboardType="numeric"
      />
      <CardField style={{ height: 50, marginVertical: 20 }} />
      <Button title={`Donate $${amount}`} onPress={handleDonation} />
    </View>
  );
}

Addresses ​

Create Address ​

typescript
const address = await client.createAddress({
  line1: '123 Main St',
  line2: 'Apt 4B',
  city: 'New York',
  state: 'NY',
  postal_code: '10001',
  country: 'US'
});

List Addresses ​

typescript
const addresses = await client.getAddresses();

Update Address ​

typescript
await client.updateAddress(addressId, {
  line1: '456 Oak Ave'
});

Delete Address ​

typescript
await client.deleteAddress(addressId);

Subscriptions ​

Create Subscription ​

typescript
const subscription = await client.createSubscription({
  product_id: 'prod_premium',
  payment_method_id: savedMethodId,
  trial_days: 14,
  concept: 'Premium Monthly Plan'
});

List Subscriptions ​

typescript
const subscriptions = await client.getSubscriptions();

Cancel Subscription ​

typescript
await client.cancelSubscription(subscriptionId);

Error Handling ​

typescript
try {
  const intent = await client.createPaymentIntent({
    total_cents: 2000,
    currency: 'USD'
  });
} catch (error) {
  if (error.response?.status === 401) {
    // Session expired, redirect to login
    navigation.navigate('Login');
  } else if (error.response?.status === 400) {
    // Validation error
    Alert.alert('Error', error.response.data.message);
  } else {
    // Network or server error
    Alert.alert('Error', 'An unexpected error occurred');
  }
}

Caching & Performance ​

The client automatically caches responses for better performance:

typescript
// Configure cache
const client = new BridgePaymentClient({
  baseUrl: process.env.EXPO_PUBLIC_BRIDGE_BASE_PAYMENT_URL!,
  sessionId: sessionId!,
  cache: {
    enabled: true,
    ttl: 300000 // 5 minutes
  }
});

// Force refresh (bypass cache)
const methods = await client.getPaymentMethods({ forceRefresh: true });

TypeScript Support ​

Full TypeScript support with type definitions:

typescript
import type {
  PaymentIntent,
  PaymentMethod,
  Address,
  Subscription,
  CreatePaymentIntentRequest
} from '@pubflow/react-native';

const request: CreatePaymentIntentRequest = {
  total_cents: 2000,
  currency: 'USD',
  concept: 'Premium Plan'
};

const intent: PaymentIntent = await client.createPaymentIntent(request);

Next Steps ​