In this article, I’ll guide you through the process of adding payments to your React Native app.
But let's make it even easier and narrow down payment options. There are three ways of paying in mobile applications: paid app, in-app payments, and paying in app for services not related to the app itself (ex. e-commerce). Today we will focus on the third option, the one that gives us the most flexibility. In our case, we will implement paying via a card functionality using Stripe.
But before we dig into coding, let me answer a few questions that probably popped for some of you.
- Can I use Expo?
- Yes, you can! And you will if you decide to join me today.
- Do I need any complicated setup on my machine?
- No, we will be using Expo Snack and Glitch.io. So a browser is the only thing you need.
- Is any Stripe account needed, how can I test payments?
- If you want to properly test your payments implementation, it’s worth having a Stripe account. You can easily create it for free here. For testing, we will be using test card numbers provided by Stripe.
For those who don’t know, Expo Snack is a React Native Playground in your browser. With it, you can play with React Native and test it even on your device without leaving the browser. It is based on Expo.
Glitch is an online playground but for the web with a possibility to create an instantly deployed backend service. No setup is required which means this is the perfect tool for us to create a test server for our payments.
Final examples can be found here:
- Expo Snack → https://snack.expo.dev/@souhe/stripe-card-payments
- Glitch.io → https://glitch.com/edit/#!/shy-opalescent-bactrosaurus
React Native Setup
To create a new Snack project just open this link and start coding.
Expo has built-in support for the <rte-code>@stripe/stripe-react-native library<rte-code>. This means you can freely use it without ejecting or creating a custom EAS plugin. If these things aren’t familiar to you, don’t worry! You won’t need them for this tutorial.
To use <rte-code>@stripe/stripe-react-native<rte-code> in Snack, you need to add the line below to your dependencies in the <rte-code>package.json<rte-code> file:
After adding the dependency, let’s replace unnecessary code from App.js with our StripeProvider component:
StripeProvider is used to initialize Stripe in your app. It should be used in the root component of your application. The only required prop is publishableKey. You can find this key in your Stripe account settings.
For simplicity, we are passing hardcoded publishable key but if possible it is worth creating an API endpoint that will return it to use with <rte-code>StripeProvider<rte-code>.
<p-bg-col>💡 If you’re like me and prefer using TS instead of JS you can just change the file extension in Snack to .tsx <p-bg-col>
Now we are ready to go!
Ok, almost ready to go. Remember when I was writing about the backend? So it wasn’t a mistake or a typo. In order to implement payments in our React Native app, we need a server. By using a server weincrease the security of our app, which is quite important when dealing with payments of any kind.
But since this is a “React Native Payment with Stripe” tutorial, we can cheat a little and use pre-built backend. The only thing you have to know about its implementation is that we need:
- <rte-code>/create-payment-intent<rte-code> endpoint that creates a payment intent. Under the hood it uses <rte-code>stripe-node<rte-code> library to create <rte-code>paymentIntent<rte-code> to start payment flow.
- Webhooks setup - an endpoint that will be automatically called by Stripe after the payment is completed.
<p-bg-col> 👉 PaymentIntent - Stripe uses this to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process. <p-bg-col>
To customize the backend to work with your Stripe account first open this project in Glitch.io. It is the working implementation you need. Then just press the “Remix” button, open the .env file, and modify the following key:
- <rte-code>STRIPE_SECRET_KEY<rte-code> - you can find your secret key here
From now on you will be using your own cloned Glitch server as your backend. To retrieve the URL of this service press “Preview” and copy the address of the opened page.
💡 Webhooks: For your final backend, you would probably need a webhooks setup. It is a mechanism that allows Stripe to inform you backend that payment was proceeded successfully (or not). For simplicity, we won’t use it in our test app but you can find more details here.
Screen with CardInput
Now we are ready to create a screen with card input so we can collect card data. There is a <rte-code>CardInput<rte-code> component in <rte-code>@stripe/stripe-react-native<rte-code> for that. Some of the props you can pass include: placeholder, autofocus info, or styles to it (the list is longer, see the documentation for more details). We are only going to pass <rte-code>style<rte-code>, <rte-code>postalCodeEnabled<rte-code>, and <rte-code>cardStyle<rte-code>.
Below the input, we can render a button that will start our payment process.
The lines below added to <rte-code>App.js<rte-code> file should render an autofocused card input, ready to interact together with a “Pay” button.
You probably already realized that there is no logic to keep our card details available in state. That’s not a mistake! To make the card payment even more secure and PCI compliant, it is up to the <rte-code>CardField<rte-code> component itself, and Stripe’s React Native library, to store card details natively behind the scenes, and share them with the other Stripe functions when needed.
Processing Card Payment
To process your payments after pressing “Pay” button, we need to follow four easy steps. All of those steps should be done in the <rte-code>onPress<rte-code> callback of our button.
💡 You can write code required in those steps inside your main component function in App.js file. After fallowing all steps your code will look like this.
Step 1 - create a payment intent
Create a payment intent in the backend and fetch its secret. Simply post to your endpoint, and save the returned payment intent client secret. We will use this secret in step 3.
Step 2 - customer data
Gather customer information. In our case let’s just mock it. In a real application you would have to collect these from a user via a form.
Step 3 - confirm payment
After gathering the required details, we are ready to finalize the payment. This is a step when we are confirming the payment intent created in step 1. Card information is needed in order to call this step but it will be passed automatically from the CardInput component.
To confirm the payment, you can use the <rte-code>useConfirmPayment<rte-code> hook from <rte-code>@stripe/stripe-react-native<rte-code> library. The hook returns an object with the <rte-code>confirmPayment<rte-code> method, and information about the loading state. In order to finalize the payment you need to call it with <rte-code>clientSecret<rte-code> retrieved in step 1 and payment configuration. Since we paying with a card, the object we pass will contain the field <rte-code>type<rte-code> with the value <rte-code>'Card'<rte-code> and the field <rte-code>billingDetails<rte-code> which contains the data we collected in step 2.
Step 4 - error handling
At this stage our payment should have completed successfully. But, we cannot forget about error handling. The <rte-code>confirmPayment<rte-code> method used in the previous step returns an object with <rte-code>error<rte-code> and <rte-code>paymentIntent fields<rte-code>. The first one can be used in order to check for any error information (if one occurred). The second one is needed to check details about our succeeded payment intent.
Connecting and testing everything
That’s it. Our card payments implementation is complete. The last thing to do is test it. To do that, you can open the app you created and proceed with payment using one of the test cards provided by Stripe.
💡 You can even test error scenarios just by using different card numbers. More information can be found here.
React Native Expo, around 70 lines of code, one additional dependency, one simple backend, and one free Stripe account - this is everything you need to implement working card payments in a mobile application. Easy, right?What’s great is that after implementing this one simple payment method it is not that hard to add more. Setting up future payments, 3D Secure, drop-in UI Payment Element with support for >10 different payment methods, Google Pay, Apple Pay, and many more features are waiting for you to use from <rte-code>@stripe/stripe-react-native<rte-code>. Probably the best place to start will be the official Stripe documentation.