Accepting Payments in a React Native App (Part 3)

Building the Payment Client Using React Native and JavaScript

Background

This article is part 3 of a series on integrating Braintree payments into a JavaScript only (no native code) React Native mobile application. The GitHub repository for this project is located here.

This is where we are in the movie

This article will cover building the first version of the client using React Native, JavaScript, and the version 3 of the Braintree JavaScript SDK.

The other articles can be found here:

1. Project Introduction. This section may not sound like much, but it’s a good explanation on how everything will fit together.

2. Application Server using Amazon Web Services. This portion explains how to interact with Braintree’s server in a secure manner.

3. Initial Version Using Vanilla-JavaScript discusses building an application that can receive payments.

4. React Component Based Version is about putting React inside a React Native application.

Introduction

In part 2 of this series, I presented the my of the Braintree payment flow:

  1. Mobile Application (the app) gets client token from our server
  2. Server gets token from Braintree and sends it back to our mobile application
  3. App uses that token in order to request a payment form UI element from Braintree
  4. Braintree provides a secure UI component for the mobile application to display
  5. The app displays that UI component to the user so that user can input payment information and commit the actual purchase at which point the payment information is sent to Braintree
  6. Braintree then provides a nonce (think of it as an authorization ticket) to the app that basically says “Okay, that user is good to make this purchase. Give this nonce to your server along with the purchase amount.
  7. The app then does just that. It sends the nonce to your server along with purchase information such as price, user name, etc.
  8. Your server’s primary step here is to send the nonce and sales information to Braintree which will in turn transfer money if the nonce is legitimate. It’s at this point that your serve will also record transaction data to a database, but that really has nothing to do with Braintree.

This article presents the design and creation of of an application that does steps 1, 3, 5, and 7.

Design

The mobile application will require a UI to interact with the user. It will also need to retrieve the client token from and post the purchase information to the application server and Braintree’s server.

The UI will will use the Braintree Drop-in UI component to provide the user with a secure form to input payment information. However, the catch is that the Drop-in UI interacts with the DOM. It requires a DOM <div> element to place it’s own UI in. This means a webpage is required since one can’t just stick a <div> into a RN <View> element. While I can’t force a <div> into a <View> , I can put an RN <WebView> into a <View>. And since the <WebView> is really just a browser, a <div> is an acceptable child element. The bottom line is that the mobile app will have a <View> that contains a <WebView> component that will encompass a <div> that the Drop-in UI will place the payment form in.

The <WebView>is capable of accepting and rendering an HTML file that is passed to it as prop. For ease of use, I’ll keep the JS separate from the HTML and use webpack to create a bundled file to pass to the <WebView> for rendering.

webpack bundles a template html file and its imported JavaScript to create an indexl.html to be used by RN webview

Since the <WebView>'s parent element will need to retrieve the client token upon mounting, I need some way to communicate that token to the Drop-in UI so that it can be used to request a nonce from the server.

It would be just as valid to avoid some <WebView>to RN communication by retrieving the client token in the JS file that contains the Drop-in code. However, there will have to be some communication between <WebView>and RN at some point. For example, we have to display purchase success or failure to the user. The communication problem can’t be avoided, so its best to get that out of the way early. Also, this separation of concerns will allow me to create a standalone component at some point.

The following diagram depicts the flow and component responsibilities described above:

Data flow between React Native parent component and it’s child webview

Implementation

A picture is worth a thousand words, and it’s the best way to describe the relationships between components.

The implementation starts off with a parent component in the BraintreePaymentScreen.js. This component is responsible for obtaining the client token from Braintree’s servers. It displays an <ActivityIndiator/> component until while the client token request is being processed. Once the token is received, it next passes the client token to the <BraintreeWebView/> component and displays that same component, while simultaneously hiding the <ActivityIndicator/> component. In addition to the client token, the <BraintreeWebView/> is also passed three other props

nonceObtainedCallback: a function that is executed one the nonce is received, which allows the BraintreePaymentScreen to make a purchase call to the server

paymentAPIResponse: holds the pending, and success or failure state of the response from Braintree once the nonce is sent to their servers. This allows us to pass that information to the webview and display feedback to the user

navigationBackCallback: an optional callback that is used to navigate to a different screen. A button on the webview activates this callback allowing the user to navigate to away from the webview to a screen of our choice

The <BrainTreeWebView/> component is a a custom component whose primary functions are to render a react-native-webview-messaging (rnwm) <WebView/> component that displays the a web page as its source, and to implement communications between that web page and BraintreePaymentScreen. I decided to use the rnwm component because it greatly simplified communication between the HTML rendered inside the webview and the BraintreePaymentScreen. All that is required is setting up listeners in the rnwm <WebView> and inside the JavaScript of the HTML that it renders. Then those two items can send events, strings, and even JSON objects to each other.

The use of rnwm’s <WebView/> component is similar to the standard React Native <WebView/>. It requires a source of HTML to display. In this case, I chose to do this by using a require statement. The require statement imports the index.html file created by webpack for use as the value for the <WebView/> source property.

The code for BraintreePayementWebview is shown below.

Code for WebView Component

I’ll be the first to admit that my webpack skills are probably not as strong as they should be. Fortunately, rnwm has an example that demonstrates how to combine an HTML file with JavaScript to create a bundled HTML file that can be included in the <WebView/> component. I recommend following the techniques shown in the example.

The index.js file contains all the JavaScript required to actually display the drop-in UI, retrieve a nonce from the Braintree server’s, send that nonce back to the parent component, and display payment processing feedback to the user.

The biggest problem I’ve encountered while using these techniques is that any changes made to the index.js file break Expo’s hot reloading. This means that every change requires repackaging the entire project. I hope that somebody reading this article can help me figure out a way to fix or avoid this issue. Please leave a comment below.

Conclusion

That’s it. In this article I demonstrated a non-native, purely JavaScript technique to display Braintree’s Drop-In UI to make payments in a React Native application.

In the next article in this series, I will replace the vanilla JS in index.js with React. I think you know what that means…

This one just wrote itself