Stripe with React Native (frontend) and Laravel 8 (backend) without Cashier — Simple Step by Step Tutorial with Example

Collecting payments in your React Native app consists of creating an object to track a payment on your server, collecting card information in your app, and submitting the payment to Stripe for processing.

Stripe uses a payment object, called a PaymentIntent, to track and handle all the states of the payment until it’s completed, including situations like two-factor authentication when the bank requires customer intervention.

1. Setup Stripe:


This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# Install the PHP library via Composer
composer require stripe/stripe-php
# Or download the source directly:


The React Native SDK is open source and fully documented. Internally, it makes use of native iOS and Android SDKs. Install the SDK by running:

yarn add @stripe/stripe-react-native

For iOS, run pod install in the ios directory to ensure that you also install the required native dependencies. Android doesn’t require any additional steps.

When your app starts, configure the SDK with your Stripe publishable key, so it can make requests to the Stripe API:

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

function App() {
return (
// required for 3D Secure and bank redirects
// required for Apple Pay
// Your app code here

Use your test mode keys while you test and develop, and your live mode keys when you publish your app.

2. Create your UI

Securely collect card information on the client with CardField, a UI component provided by the SDK.

Add the CardField component to your payment screen to securely collect card details from your customers. Use the onCardChange callback to inspect non-sensitive information about the card, like the brand, and whether the details are complete.

import { CardField, useStripe } from '@stripe/stripe-react-native';

function PaymentScreen() {
// ...
return (
number: '4242 4242 4242 4242',
backgroundColor: '#FFFFFF',
textColor: '#000000',
width: '100%',
height: 50,
marginVertical: 30,
onCardChange={(cardDetails) => {
console.log('cardDetails', cardDetails);
onFocus={(focusedField) => {
console.log('focusField', focusedField);

Run your app and make sure your checkout page shows the CardField component.

3. Create a PaymentIntent

Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process.


On your server, make an endpoint that creates a PaymentIntent with an amount and currency. Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

// Set your secret key. Remember to switch to your live secret key in production.
// See your keys here:
\Stripe\Stripe::setApiKey( 'sk_test_lR2sPJDzaXy8sZTlbgZzPqai');

$intent = \Stripe\PaymentIntent::create([
'amount' => 1099,
'currency' => 'gbp',
$client_secret = $intent->client_secret;
// Pass the client secret to the client

Instead of passing the entire PaymentIntent object to your app, only return its client secret. The PaymentIntent’s client secret is a unique key that lets you confirm the payment and update card details on the client, without allowing manipulation of sensitive information, like payment amount.


On the client, request a PaymentIntent from your server and store its client secret:

function PaymentScreen() {
// ...

const fetchPaymentIntentClientSecret = async () => {
const response = await fetch(`${API_URL}/create-payment-intent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
body: JSON.stringify({
currency: 'usd',
const {clientSecret} = await response.json();

return clientSecret;

const handlePayPress = async () => {
if (!card) {

// Fetch the intent client secret from the backend
const clientSecret = await fetchPaymentIntentClientSecret();

return (
<CardField onCardChange={(cardDetails) => console.log('cardDetails', cardDetails)} />
// In the onPress handler, request a PaymentIntent from your server and store
// its client secret
<Button onPress={handlePayPress} title="Pay" disabled={loading} />

4. Submit the payment to Stripe

When the customer taps Pay, confirm the PaymentIntent to complete the payment.

Rather than sending the entire PaymentIntent object to the client, use its client secret. This is different from your API keys that authenticate Stripe API requests. The client secret should be handled carefully because it can complete the charge. Don’t log it, embed it in URLs, or expose it to anyone but the customer.

Pass the customer card details and client secret to confirmPayment, which you can access with the useConfirmPayment hook. The hook also returns a loading value to indicate the state of the asynchronous action:

function PaymentScreen() {
const {confirmPayment, loading} = useConfirmPayment();

// ...

const handlePayPress = async () => {
// Gather the customer's billing information (e.g., email)
const billingDetails: BillingDetails = {
email: '',

// Fetch the intent client secret from the backend
const clientSecret = await fetchPaymentIntentClientSecret();

// Confirm the payment with the card details
const {paymentIntent, error} = await confirmPayment(clientSecret, {
type: 'Card',

if (error) {
console.log('Payment confirmation error', error);
} else if (paymentIntent) {
console.log('Success from promise', paymentIntent);

return <View />;

If authentication is required by regulation (e.g., Strong Customer Authentication), additional actions are presented to the customer to complete the payment process. See Card authentication and 3D Secure for more details.

When the payment completes successfully, the value of the returned PaymentIntent’s status property is succeeded. Check the status of a PaymentIntent in the Dashboard or by inspecting the status property on the object. If the payment isn’t successful, inspect the returned error to determine the cause.

5. Test the integration

By this point you should have a basic card integration that collects card details and makes a payment.

There are several test cards you can use in test mode to make sure this integration is ready. Use them with any CVC, postal code, and future expiration date.

Basic test card numbers

Genuine card information cannot be used in test mode. Instead, use any of the following test card numbers, a valid expiration date in the future, and any random CVC number, to create a successful payment. Each basic test card’s billing country is set to U.S. If you need to create test card payments using cards for other billing countries, use our international test cards.

Please let me know if this was a helpful post in your comments.




Imagination is the key to unlock the world. I am trying to unlock mine.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

NodeJS on App Engine Standard

Learn functions by a digital media agency

Javascript Essentials — Event Bubbling and Event Capturing

We Made It. My final project for Flatiron

JavaScript The Bully/The Samurai

ThreeJS objects reacting on audio

React Native using rn-nodeify

Performance Optimization with React Hooks and Memo

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


Imagination is the key to unlock the world. I am trying to unlock mine.

More from Medium

Know the 11 best frameworks for mobile app development

Flutter Vs React Native for Mobile App Development

Laravel Echo — Pusher and React Native

Deploy a React Native App to the Apple Play Store