Accepting Payments in a React Native App (Part 3)
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.
4. React Component Based Version is about putting React inside a React Native application.
In part 2 of this series, I presented the my of the Braintree payment flow:
- Mobile Application (the app) gets client token from our server
- Server gets token from Braintree and sends it back to our mobile application
- App uses that token in order to request a payment form UI element from Braintree
- Braintree provides a secure UI component for the mobile application to display
- 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
- 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.
- The app then does just that. It sends the nonce to your server along with purchase information such as price, user name, etc.
- 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.
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.
<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.
<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:
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
<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
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.
<WebView/> component. I recommend following the techniques shown in the example.
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.
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…