Developing Stripe Payment and Subscription Service for React Native App with NodeJS

5 min readDec 12, 2019

Update 2022/05/12

Rejoice, Stripe has an official SDK for React Native! It looks like it went live on May 10, 2021, exactly a year ago today. Google has also announced custom code feature for Firebase extensions in today’s IO conference, so you can make use of Stripe API extension. You won’t have to go through this bend-over-backwards engineering laid out below anymore to implement PCI-DSS compliant in-app Stripe payment.


This is a personal development note and review for writing Node with Stripe API and Firebase for a React Native application. Key takeaways and learning points are:

  • Integrate DevOps with development as much as possible to avoid simple mistakes in productions (i.e deploying the wrong key). Let one developer deploy it through production and a separate DevOps / QA to check it’s implemented properly instead.
  • Must set up a recurring test and review schedule after production deployment of any code or project. Do not move on to a new project until KPIs are met for at least a week through post-production testing.
  • Contrary to solo development on a private repo, do not commit too often when working in teams. Take time to review the code for sensitive information before committing anything. Number of commits / LOC != productivity.


I worked as a remote backend programmer in a startup for about a year. The startup needed to integrate payment into the app for receiving one-off donations, and potentially implementing subscription services in the future.

Identifying business needs and limits

The first task I had to do was to research on regulations, security rules around online payments.

Apart from the fact that this is my first experience dealing with payments, there was a technical challenge particular to mobile payments.

  • Online payments need to follow PCI-DSS guideline, important key points being that payment processors must abide by strict guidelines (laid out in some 20+ pages) to protect consumer’s data privacy and security. As such, it is difficult and highly unrecommended for companies to store customer’s credit card information in their database, let alone caches in mobile apps. Well-known services that alleviate this burden are Paypal and Stripe.
  • After reviewing Paypal and Stripe, I recommended using Stripe API over Paypal because it had less commission fee and better documentation, support for developers.
  • One might expect there would be Stripe SDK for React Native (maybe there is now) — but there were no libraries that were PCI-DSS compliant. How were we supposed to use Stripe API and display the credit card payment process on React Native without breaking security compliance laws?


The backend solution

In the NodeJS app, we need to write a stripeController.js and stripeRoutes.js for handling all stripe payments, userController.js and userRoutes.js for retrieving user subscription expiry date from Firebase.

We assume that the front end browser will send API request to NodeJS server in the form of /donate?uid=???&amount=???, and subscribe .

I’ve uploaded NodeJS code to for those looking for code below.


Remember to add config.js for sensitive keys and firebase.js for starting Firebase app. See here for Node JS app Firebase setup.

As the existing NodeJS app was using ejs to render web pages, I had to adapt to using ejs, and the following solution worked. Whatever rendering engine you decide to use, hope the this will still guide you through loading Stripes Elements on the server-side.

Part of the folder structure

First, you need a folder named public with nested CSS, image (if needed), and js folders. I named files donation.css, donation.js within those folders for loading Stripe Elements.

donation.js — Take note of that red rectangle box. That later becomes the source of production screw up.

The Firebase DB will look like this in the end:

The frontend solution

While building backend we assumed the user would have to open a default browser on their phone to fill in credit card information. But the business side pushed for the best possible user experience (meaning, whatever it takes, find an in-app solution).

So I found a way: in-app pop up browsers. In expo, there’s a WebBrowser component.

Then call this _handlePressButtonAsync from Button component

The F@#!@ning

There’s always these production disaster episodes in dev communities. Here’s how we lost about USD 150 for donation: not swapping test key to prod key.

It’s such a dumb mistake that took hours of digging through Stripe logs because….

  • development was separated from DevOps
  • I am a newb to git collaboration, assumed people would read (please read me… T_T)

DevOps / deployment was handled by a senior engineer, which I thought was a good practice because I was afraid to break things in production. What happened instead was:

  • I left a Stripe test key in one of the files so that the NodeJS app would run. Should have left it empty instead to throw an error on purpose.
  • Wrote on README explaining variables needed to change before pushing to production
  • DevOps: oh, it runs -> deploy

Big thank you to Stripe team that foresaw these mistakes and kindly logged what keys were used in the API logs.