Back

How to Set Up Stripe Terminal in React Native with Expo

Preston KellyThursday, August 7, 2025
How to Set Up Stripe Terminal in React Native with Expo

In this guide, we'll walk through the high level steps you'll need to build a mobile payment app using Stripe Terminal and React Native. This guide will be the framework for a complete solution, you'll need to add your own UI, styling, error handling, etc. We'll focus on iOS, the principles are similar but not identical for Android. This guide uses Expo to simplify development.

Why Stripe Terminal?

Stripe Terminal provides a solution for accepting in-person payments using your device. It's ideal for:

  • Small businesses looking to accept card payments
  • Service providers who need to accept payments on the go
  • Developers building custom point-of-sale solutions
  • Retail stores requiring reliable payment processing

Prerequisites

Before we dive in, make sure you have:

  • iOS 16.7 or later
  • Xcode 14 or later
  • An Apple Developer account
  • A Stripe account
  • A compatible Bluetooth card reader
  • Node.js and Expo CLI installed (npm install -g expo-cli)
  • A physical iOS device

Implementation Guide

1. Create a new Expo app

Create an Expo app with the following command:

npx create-expo-app@latest

2. Setting Up Dependencies

Let's install the necessary packages:

npm install @stripe/stripe-terminal-react-native@0.0.1-beta.24 expo-build-properties@0.14.6

3. Configuring Your App

Create or update your app.config.ts with the following configuration:

import type { ConfigContext, ExpoConfig } from 'expo/config'; export default ({ config }: ConfigContext): ExpoConfig => { return { name: 'stripe-terminal-app', slug: 'stripe-terminal-app', version: '1.0.0', orientation: 'portrait', icon: './assets/images/icon.png', userInterfaceStyle: 'automatic', ...config, ios: { ...config.ios, bundleIdentifier: 'com.yourcompany.stripe-terminal-app', }, plugins: [ ...(config.plugins || []), [ 'expo-build-properties', { ios: { deploymentTarget: '16.7', useFrameworks: 'static', }, }, ], [ '@stripe/stripe-terminal-react-native', { bluetoothBackgroundMode: true, locationWhenInUsePermission: 'Location access is required for payments.', }, ], ], }; };

4. Setting Up the Backend

First, we need to set up a backend server to handle connection tokens. Connection tokens are used by the Stripe SDK to connect to a reader.

Create a simple Express server to handle connection tokens:

const express = require('express'); const stripe = require('stripe')('YOUR_STRIPE_SECRET_KEY'); const app = express(); app.use(express.json()); app.post('/connection_token', async (req, res) => { try { const token = await stripe.terminal.connectionTokens.create(); res.json({ secret: token.secret }); } catch (error) { res.status(500).json({ error: error.message }); } }); app.listen(3000, () => console.log('Server running on port 3000'));

5. Creating the Terminal Provider

Now that we have our backend server running, we can create a Terminal Provider that will use the connection token endpoint:

import { StripeTerminalProvider } from '@stripe/stripe-terminal-react-native'; import { ReactElement } from 'react'; const fetchTokenProvider = async () => { const response = await fetch('YOUR_BACKEND_URL/connection_token', { method: 'POST', headers: { 'Content-Type': 'application/json', }, }); const { secret } = await response.json(); return secret; }; export function TerminalProvider({ children }: { children: ReactElement }) { return ( <StripeTerminalProvider logLevel="verbose" tokenProvider={fetchTokenProvider} > {children} </StripeTerminalProvider> ); }

6. Building the Payment Screen

Create a payment screen that allows the user to discover readers, connect to a reader, and process a payment. For a more complete solution, you'll likely want a separate screen for reader management. For our high level example, we'll create a single screen that handles discovery, connection, and payment. Here are the essential code snippets:

Initialize Stripe Terminal:

const { initialize, discoverReaders, connectReader, createPaymentIntent, collectPaymentMethod, confirmPaymentIntent } = useStripeTerminal(); useEffect(() => { initialize(); }, [initialize]);

Discover Readers:

const handleDiscoverReaders = async () => { await discoverReaders({ discoveryMethod: 'bluetoothScan' }); };

Note: The useStripeTerminal hook provides several callbacks, such as onUpdateDiscoveredReaders. You can use this callback to update your component state with the list of discovered readers, allowing you to display them in your UI and let the user select which reader to connect to.

Connect to a Reader:

const handleConnectReader = async (reader: Reader.Type) => { await connectReader({ reader }, 'bluetoothScan'); };

Process a Payment:

const handlePayment = async () => { // Create the payment intent const { error: intentError, paymentIntent } = await createPaymentIntent({ amount: 1000, // $10.00 currency: 'usd', captureMethod: 'automatic', }); if (intentError) return; // Collect the payment method const { error: collectError, paymentIntent: collectedIntent } = await collectPaymentMethod({ paymentIntent, updatePaymentIntent: true, }); if (collectError) return; // Confirm the payment await confirmPaymentIntent({ paymentIntent: collectedIntent }); };

UI Example:

return ( <View> <Button title="Discover Readers" onPress={handleDiscoverReaders} /> <Button title="Process $10.00 Payment" onPress={handlePayment} /> </View> );

7. Running the App on a Physical Device

To test the Stripe Terminal integration, you'll need to run the app on a physical device. This is because:

  • Bluetooth functionality requires a real device to work properly
  • The iOS Simulator doesn't support Bluetooth connections
  • Stripe Terminal SDK requires actual hardware to communicate with card readers

Run the following command to build and install the app on your connected device:

npx expo run:ios --device

Make sure your device is:

  • Connected to your Mac via USB
  • Unlocked and trusted
  • Developer mode enabled in (Settings > Privacy & Security)

Next Steps

  1. Add loading states and progress indicators
  2. Implement reader reconnection logic
  3. Add payment confirmation and receipt handling
  4. Implement proper state management
  5. Add error recovery mechanisms
  6. Reach out to Echobind for a complete solution

Resources

Production-Ready Solutions with Echobind

Connecting to a reader is just the beginning. While this guide covers the basics of implementing Stripe Terminal in a React Native app, building a production-ready payment solution requires additional considerations such as handling for edge cases where a reader may disconnect unexpectedly, software updates, loading states, and location management. At Echobind, we have experience working with Stripe Terminal and can help you build a complete payment solution.

Advanced Features

Here are some examples of advanced features you might want for a complete solution

  1. Stripe Location Management

    • Dynamic location registration and updates
    • Multi-location support for businesses
    • Location-based reader assignment
    • Automated location verification
  2. Reader Management

    • Automatic reader updates and firmware management
    • Reader health monitoring and diagnostics
    • Battery level tracking and notifications
    • Reader registration and deactivation workflows
  3. Error Handling

    • Automatic reconnection strategies
    • Offline mode support
    • Transaction recovery mechanisms
    • Comprehensive error logging and monitoring
  4. Analytics and Reporting

    • Real-time transaction monitoring
    • Custom reporting dashboards
    • Performance analytics
    • Error rate tracking

Contact Echobind to discuss how we can help build your complete payment solution.

Share this post

twitterfacebooklinkedin

Interested in working with us?

Give us some details about your project, and our team will be in touch with how we can help.

Get in Touch