Building Complete App using React Native, Stripe and AdonisJS

Building Complete App using React Native, Stripe and AdonisJS

We walk through building a complete app, with authentication and payment processing, using React Native, UI Kitten, Stripe, and AdonisJs. Monetizing your React Native app with Stripe

We walk through building a complete app, with authentication and payment processing, using React Native (with Expo), UI Kitten, Stripe, and AdonisJs.

Preparation

Since you’ve chosen to read this post, I’m sure you are familiar with how fast the JavaScript ecosystem evolves. After conquering the web, it is now rampant in the native app industry as well, with tools like React Native for mobile apps and Electron for desktop apps.

These days, the gap between apps built with React Native and those built with true native stacks is only getting narrower. One of the major details that benefits both indie and big corporate app developers alike is being able to handle payment in their apps without a lot of fuss.

If you have a great idea for an app, it’s only a matter of time before you start looking into how to monetize it and receive payments directly from users with as little friction as possible. So in this post, I will walk you through building an entire app, with authentication and payment processing, using React Native (via Expo), UI Kitten, Stripe, and AdonisJs, a backend Node.js framework.

Before you start following along, please keep in mind that this post assumes you are familiar with the basics of Git, React Native, React, TypesScript, and REST APIs in general.

If you’re new to one or all of those, you’re still welcome to read along and learn what you can along the way. In order to keep this post on topic, however, a lot of the lower-level detail will not be explained throughout the post. Also, please make sure that you have your environment set up for React Native app development.

With applications like this, where you have a server app and a client app, I usually put them in a container directory and give it the name of the project. I’m really bad with names, so I couldn’t come up with anything better than payme, which is just short for payment and makes me sound like I’m asking for money. Oh well!

So first, create a new directory named payme and navigate inside it from your terminal:

mkdir payme
cd payme

If you’re impatient like me and want to see the code first before you commit to anything, here’s the GitHub repo containing the entire codebase — dig in! Or, if you just want to see the end result, here’s a quick video preview.

Expo app

Expo is a platform/toolkit built on top of React Native to make developer experience much easier and smoother. You can think of it as RN on steroids. It does have some limitations when it comes to native functionality, but there’s always a workaround for that kind of thing.

The first thing we’re gonna need is expo-cli installed globally, and using the CLI, we will generate a new boilerplate app:

npm install -g expo-cli
expo init payme

The second command will ask you to pick a template, and using your arrow key, select the second one from the list, as shown in the image below. This generates a blank TypeScript Expo app.

Generating a Blank TypeScript Expo App

Remember when I said there are some limitations with Expo? Sorry to say that we have hit one of those — and so soon into the post!

If you need to make it work with iOS, you will need to use the bare workflow instead of managed because Expo does not support the Stripe module on iOS yet. For more details, please read this documentation from Expo.

However, all the code we will be writing throughout this post will be compatible in both bare and managed workflow.

Once complete, you will be left with a new directory named payme. Since we’re going to have a server-side API app and a client-side Expo app, let’s rename the generated app folder from payme to app. Then, install a few of the packages that we will be using very soon:

mv payme app
cd app
expo install @react-native-community/async-storage @ui-kitten/eva-icons @ui-kitten/components @eva-design/eva react-native-svg expo-payments-stripe

While the installation is running, please go through this UI Kitten documentation on branding and how to generate a theme file with your preferred color palette for your app. Following the steps from the documentation, you should get a theme.json with a bunch of colors. Put that file into the root of the app directory.

Finally, we can get to coding! Open up the App.tsx file and replace the existing code with the following:

import { StatusBar } from 'expo-status-bar';
import React, {useEffect, useState} from 'react';
import * as eva from '@eva-design/eva';
import { ApplicationProvider, IconRegistry } from '@ui-kitten/components';
import { default as theme } from './theme.json';
import { EvaIconsPack } from '@ui-kitten/eva-icons';
import { AuthPage } from "./AuthPage";
import { ProductPage } from "./ProductPage";
import { Auth } from "./auth";
import { Payment } from "./payment";

const auth = new Auth();
const payment = new Payment(auth);
export default function App() {
    const [isLoggedIn, setIsLoggedIn] = useState(false);
    const [isLoggingIn, setIsLoggingIn] = useState(true);

    useEffect(() => {
        auth.getToken().then((token) => {
            setIsLoggingIn(false);
            if (!!token) setIsLoggedIn(true);
        });
    });

    return (
        <>
            <IconRegistry icons={EvaIconsPack} />
            <ApplicationProvider {...eva} theme={{...eva.light, ...theme}}>
                <StatusBar style="auto" />
                { isLoggedIn
                    ? <ProductPage {...{payment}} />
                    : <AuthPage {...{auth, setIsLoggedIn, setIsLoggingIn, isLoggingIn}} />
                }
            </ApplicationProvider>
        </>
    );
}

Some of this code is just boilerplate, but let’s break it down a bit. To set up UI Kitten with the custom theme that we generated, we are importing the theme.json file and passing the theme object to ApplicationProvider.

We are importing and instantiating two classes, Auth and Payment, from their designated files. The auth object is injected into the Payment class as a dependency. These encapsulate the business logic and communicate with the server API.

We have two state variables in this component, one representing whether the user is already logged in and the other representing whether the login operation is currently happening

Initially, we load the UI in login mode, and on load, we do a check for existing authentication info using auth.getToken(). This is necessary because if the user logs in once, subsequently opening the app shouldn’t show the login screen every time.

Then, we register the icon set from UI Kitten, set up the layout, show the status bar, yada, yada, yada… just more boilerplate stuff that I copy/pasted in from the UI Kitten’s official documentation. If you’re reading this a long time after this post was published, please make sure you follow and match the doc with this setup.

Finally, based on the login state, if the user is not logged in, we render the AuthPage component; otherwise, we render the ProductPage component. The ProductPage component only takes in the payment object as [rp[.

The AuthPage component, however, needs to be able to change the global state based on interactions that happen inside the component itself. This is why we pass all the state variables and the setter functions to modify those variables.

As your app grows, this kind of quick-and-dirty way of managing state might not cut it anymore, and you will have to pick up tools like React Context or more advanced state management libraries like Redux or Recoil, but for the limited scope of this post, this should do just fine.

Authentication page

Authentication is probably one of the first things you’d work on when starting a project — and yet, it is one of the most complex things to get done, with a lot of edge cases and your app’s contextual dependencies.

For the purpose of this post, we will keep it simple and concise. As soon as the app opens, we want to show a login screen to the user where they can sign up with their email address and password, or log in if they already have an account.

FIrst off, create a new file named AuthPage.tsx in the root of the app and drop in the code below:

import {Layout, Icon, Button} from "@ui-kitten/components";
import { Layout, Card, Button, Input, Text } from "@ui-kitten/components";
import { StyleSheet, View } from "react-native";
import React, { useState } from "react";

import { Auth } from "./auth";

const styles = StyleSheet.create({
    page: {
        flex: 1,
        padding: 15,
        alignItems: 'center',
        justifyContent: 'center',
    },
    card: {
      alignSelf: 'stretch',
    },
    formInput: {
        marginTop: 16,
    },
    footer: {
        marginTop: 10,
        alignSelf: 'stretch',
        flexDirection: 'row',
        justifyContent: 'space-between',
    },
    statusContainer: {
        alignSelf: 'center',
    },
    actionsContainer: {
        flexDirection: 'row-reverse',
    },
    button: {
        marginLeft: 10,
    }
});

type AuthPageProps = {
    auth: Auth,
    isLoggingIn: boolean,
    setIsLoggedIn: (isLoggedIn: boolean) => any,
    setIsLoggingIn: (isLoggedIn: boolean) => any,
};

export const AuthPage = ({ auth, isLoggingIn, setIsLoggedIn, setIsLoggingIn }: AuthPageProps) => {
    const [password, setPassword] = useState<string>();
    const [email, setEmail] = useState<string>();
    const [errors, setErrors] = useState<string[]>([]);

    const handlePrimaryButtonPress = async (action = 'signup') => {
        setIsLoggingIn(true);
        // when signing up, we want to use the signup method from auth class, otherwise, use the login method
        try {
            const { success, errors } = await auth.request(action, {email, password});
            setIsLoggedIn(success);
            setErrors(errors);
        } catch (err) {
            console.log(err);
        }

        setIsLoggingIn(false);
    };

    return (
        <Layout style={styles.page}>
            <Card style={styles.card} status='primary'>
                <Input
                    style={styles.formInput}
                    label='EMAIL'
                    value={email}
                    onChangeText={setEmail}
                />
                <Input
                    style={styles.formInput}
                    label='PASSWORD'
                    secureTextEntry={true}
                    value={password}
                    onChangeText={setPassword}
                />
                {errors.length > 0 && errors.map(message =>
                    <Text key={`auth_error_${message}`} status='danger'>{message}</Text>
                )}
            </Card>
            <View style={styles.footer}>
                <View style={styles.statusContainer}>
                    <Text>{isLoggingIn ? 'Authenticating...' : ''}</Text>
                </View>
                <View style={styles.actionsContainer}>
                    <Button
                        size="small"
                        style={styles.button}
                        disabled={isLoggingIn}
                        onPress={() => handlePrimaryButtonPress('signup')}>
                        Sign Up
                    </Button>
                    <Button
                        size="small"
                        status="info"
                        appearance="outline"
                        disabled={isLoggingIn}
                        onPress={() => handlePrimaryButtonPress('login')}>
                        Log In
                    </Button>
                </View>
            </View>
        </Layout>
    );
}

Ideally, in a real-world application, you would have your own architecture and folder structure, but for the purpose and scope of this post, we will just drop everything in the root of the app folder.

That seems like a lot of code, but trust me: in a real-world authentication page, you will have at least five times more code than this. Fear not — it’s fairly easy to break down and consume it in chunks.

First, we have some stylesheet definitions. Since UI building is not in the primary scope of this post, I will not go into much detail on why and how this works. If you’re completely new to this, please read up on it here. To summarize, we’re just adding some styles that we will later apply to our various elements rendered by the AuthPage component.

Then we create a type, defining all the different props this component expects to receive from its renderer.

Within the component itself, we have three state variables. The first two are email and password, which will be strings that the user inputs through the UI. Then we have an array of strings containing error messages. These will be errors that we might receive from the server side in response to the authentication requests. Imagine errors like duplicate email address for signup, wrong password for login, etc.

Next, we have an event handler function that will be triggered for either login or signup actions, depending on which button the user hits. Inside the body of the function, we call the various state setter functions passed to the component to let the parent know what’s happening.

Finally, we call the auth.request(action, {email, password}) method with the email and password from the state. Imagine this will just send out the data to our API server and get some response back.

Now, let’s get to what the user will actually see. To make that nice and pretty, we use the Layout and Card components from UI Kitten and place the card right in the center of the screen with two inputs inside — one for email and one for password. Let’s analyze the password field, for example:

<Input
   style={styles.formInput}
   label='PASSWORD'
   secureTextEntry={true}
   value={password}
   onChangeText={setPassword}
/>

We use the formInput style, which gives it a nice margin on top. Then the label prop adds a text on top of the input field. The secureTextEntry prop makes the text hide as the user types in the password. As the handler for the onChangeText event, we pass the setPassword function, which will update the state value. That value is then set as the value of the input field itself.

Pretty standard React stuff with a sprinkle of UI Kitten magic. There’s a lot more you can do to make these inputs look and feel according to your test by simply passing some props, read this to learn more about that.

Underneath, we loop through all the error message strings from state and render a Text component showing the message itself, if there is any. The status='danger' part will make the text color red thanks to UI Kitten.

{errors.length > 0 && errors.map(message =>
                    <Text key={`auth_error_${message}`} status='danger'>{message}</Text>
                )}

Finally, we get to the footer. Divided into two horizontal sections, on the right, it shows two buttons: one for Log in and the other for Sign up. On the left, it is an empty area by default, but when we are communicating with the server, text saying Authenticating… will be displayed there to let the user know that we are processing their request.

Notice that the two buttons call the same function but with different parameters. They also have slightly different props — for instance, one has appearance='outline', which gives it a border and makes the background color a bit faded. We are also passing disabled={isLoggingIn}, which makes sure that after the user presses a button and we send the data over to the server, both the buttons will be disabled to avoid multiple submissions.

Phew! First component of the app done — not bad, right? Well, the bad news is that you can’t see it in action yet because of the missing pieces here like the auth object and the PaymentPage component. However, to thank you for your hard work so far, I will reward you with a screenshot of how it will look once the app does render the page in all its glory! Lo and behold!

Preview of Our Login/Signup Page

OK, sorry, I probably hyped it up a bit too much. It’s not that amazing, but hey, it’s not that bad either, right?

Product page

Just like we did for the auth page, let’s create a new file named ProductPage.tsx in the root of the app and drop the below code in there:

import {Layout, Icon, Button} from "@ui-kitten/components";
import { StyleSheet } from "react-native";
import React, {useEffect, useState} from "react";
import {Payment, PaymentData} from "./payment";

const styles = StyleSheet.create({
    container: {
        flex: 1,
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'center',
    },
    button: {
        margin: 2
    }
});

const products = [{
    status: 'primary',
    text: 'Buy Video',
    product: 'video',
    icon: 'film',
    price: 50,
}, {
    price: 30,
    status: 'info',
    product: 'audio',
    text: 'Buy Audio',
    icon: 'headphones',
}];

type ProductPageProps = {
    payment: Payment,
};

export const ProductPage = ({ payment }: ProductPageProps) => {
    const [paymentReady, setPaymentReady] = useState(false);
    const [paymentHistory, setPaymentHistory] = useState<PaymentData[]>([]);

    // Initialize the payment module, on android, this MUST be inside the useEffect hook
    // on iOS, the initialization can happen anywhere
    useEffect(() => {
        payment.init().then(() => {
            setPaymentReady(true);
            setPaymentHistory(payment.history);
        });
    });

    const handlePayment = async (price: number, product: string) => {
        await payment.request(price, product);
        setPaymentHistory(payment.history);
    };

    const hasPurchased = (product: string) => !!paymentHistory.find(item => item.product === product);

    return (
        <Layout style={styles.container}>
            {products.map(({product, status, icon, text, price}) => (
                <Button
                    status={status}
                    style={styles.button}
                    disabled={!paymentReady || hasPurchased(product)}
                    onPress={() => handlePayment(price, product)}
                    key={`${status}_${icon}_${text}`}
                    accessoryLeft={props => <Icon {...props} name={icon}/>}>
                    {`${text} $${price}`}
                </Button>
            ))}
        </Layout>
    );
};

Let’s take a closer look at the code here. Just like before, we have some style definitions that we will add to different components later on.

Then we have an array containing two product entries. Each entry has a status, price, product, icon, and text properties. Except for the price and product properties, these are all mainly for the UI, whereas the former two will be later used in API communication. In a real-world app, you will probably get this list from the server instead of hardcoding it into the app’s code.

In the definition of the component itself, we set it up to receive the payment object as a prop, which we passed from the App component, remember? On load, we initialize the payment module by calling init(), and to keep the UI in sync with the state of the payment module loading, we introduce a state variable that is set to false initially and, upon initialization of the payment module, is toggled on.

Also, there’s another state variable that is supposed to contain all the previously made payments by the user. After calling init() on the payment object, we reset that state variable with the history property from the payment object. As if, after initializing, the history property will contain the payment history. We will see how that is done later.

We also have an event handler function, handlePayment, that gets triggered with price and product data and calls the request method from the payment module. After the call ends, we are re-adjusting the paymentHistory state with the history property from the payment object as if, after making a payment, the history property will contain different data then before.

We are blindly trusting the payment object to contain all these methods for the time being, but worry not, we will soon build it ourselves.

Then comes the UI rendering. Inside a Layout component from UI Kitten, we are simply looping through both our products and rendering one button for each of them. This is where you see the properties like status, text, etc. set in the product object are coming in handy.

UI Kitten lets us easily manipulate the appearance of various components like buttons through simple properties. For instance, status="primary" will give the button a distinct style based on the primary color from your theme’s definition.

We are also disabling the button if the payment initialization is not complete or there is already a payment entry in the history for the same product. You can play around with the UI look and feel by changing/adding various props documented here.

Notice that we’re using our previously created handlePayment function as the listener for onPress events, and we’re also using the various styles we created at the beginning to add some spacing between the buttons and center them vertically within the screen.

To give you a glimpse of how all of this looks, here’s a screenshot:

Preview of Our Product Page

With that, we will take a short break from the frontend side of things and build out the API. Once that’s ready, we will come back to this to connect the two and wrap things up!

react-native stripe adonis developer

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Hire Dedicated React Native Developer in India | React Native Development

Hire dedicated React Native developers for your next project. As the top react native development company we offer you the best service as per your business needs.

A Short Guide to React Native App Development

React Native is undoubtedly one of the most widely used cross-platform frameworks for creating native-like apps. This framework can be easily used for developing brand-new apps from scratch and even in existing iOS or Android projects.easily used for developing brand-new apps from scratch and even in existing iOS or Android projects.

Which is the best React Native app development company in New York?

Hire top react native app development company in New York to build and develop custom react native mobile apps for Android & iOS with the latest features.