Payment Request with Braintree
This guide uses the plugin at version 13 or later (which is in beta at this stage).
We will proceed in steps: setup, initialization and payment.
First we'll details the basic setup steps, of course you can skip the first few steps if you already have a working application you want to integrate the code into.
- 1.Install NodeJS and Cordova
- 2.Setup your Cordova project
- 3.Prepare an Account on Braintree
- 4.Install the In-App Purchases plugin and Braintree extension
- 5.
Once we have a Cordova application with Braintree support enabled and everything is in place on Braintree and Iaptic dashboards, we will move to coding a minimal demo app.
- 1.Initialize the in-app purchase plugin
- 2.Launch a payment request
- 3.Handle the purchase events
- 4.Finalize
Needless to say, make sure you have the tools installed on your machine. During the writing of this guide, I've been using the following environment:
- NodeJS v10.12.0
- Cordova v8.1.2
- macOS 10.14.1
I'm not saying it won't work with different version. If you start fresh, it might be a good idea to use an up-to-date environment.
Follow the instructions from Braintree to create a sandbox account. https://www.braintreepayments.com/sandbox
Markdown your API Credentials: Merchant ID, Public Key and Private Key.
The Braintree implementation requires a server component that interacts with Braintree to create and settle transactions. Server side integration with the Braintree platform is outside the scope for this tutorial. We'll use Iaptic's own integration that provides a simple platform neutral interface.
Go to iaptic.com to create an account (or login if you already have one). Fill in the Braintree section in iaptic's settings.
Also mark down your iaptic's App Name and Public Key.
Support for Braintree is added by installing an additional plugin with ID
cordova-plugin-purchase-braintree
.For the most up-to-date information on how to install and setup the plugin, you can head to the plugin's GitHub page.
Let's install it:
cordova plugin add cordova-plugin-purchase-braintree
For this guide, I'll use an android device. As mentioned in the plugin's documentation, we have to add this section to our application's
config.xml
.<platform name="android">
<preference name="android-minSdkVersion" value="21" />
</platform>
Assuming you're starting from a blank project, we'll add the minimal amount of HTML for the purpose of this tutorial. Let's replace the
<body>
from the www/index.html
file with the below.<body>
<div id="app"></div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
Let's also make sure to comment out Cordova template project's CSS.
You also need to enable the
'unsafe-inline'
Content-Security-Policy
by adding it to the default-src
section:<meta http-equiv="Content-Security-Policy"
content="default-src 'self' 'unsafe-inline' [...]" />
We will now create a new JavaScript file and load it from the HTML. The code below will initialize the plugin.
1
document.addEventListener('deviceready', onDeviceReady);
2
3
function onDeviceReady() {
4
5
if (!window.CdvPurchase) {
6
console.log('CdvPurchase is not available');
7
return;
8
}
9
const {store} = CdvPurchase;
10
11
store.error(function(error) {
12
console.log('ERROR ' + error.code + ': ' + error.message);
13
});
14
15
store.ready(function() {
16
console.log("CdvPurchase is ready");
17
});
18
19
initializeStore();
20
refreshUI();
21
}
22
23
function initializeStore() {
24
// We will implement this soon
25
}
26
27
function refreshUI() {
28
// Soon...
29
}
Here's a little explanation:
Line 1, it's important to wait for the "deviceready" event before using cordova plugins.
Lines 5-8, we check if the plugin was correctly loaded.
Lines 11-13, we setup an error handler. It just logs errors to the console.
Whatever your setup is, you should make sure this runs as soon as the javascript application starts. You have to be ready to handle IAP events as soon as possible.
As mentioned earlier, we'll use iaptic for the server side integration with Braintree.
We'll instantiate the iaptic component, and use the provided
braintreeClientTokenProvider
and validator
to handle the server-side part of the purchase process.1
function initializeStore() {
2
const {store, Platform, Iaptic} = CdvPurchase;
3
4
// create the link with Iaptic
5
const iaptic = new Iaptic({
6
apiKey: 'ffff0000ffff0000ffff0000',
7
appName: 'my.app',
8
});
9
store.validator = iaptic.validator;
10
11
// purchase events handlers
12
store.when()
13
.approved(transaction => transaction.verify())
14
.verified(receipt => receipt.finish());
15
16
// initialize the plugin with Braintree support
17
store.initialize([{
18
platform: Platform.BRAINTREE,
19
options: {
20
clientTokenProvider: iaptic.braintreeClientTokenProvider,
21
}
22
}]);
23
}
We add the standard purchase events handlers for when the transaction is
approved
and the receipt verified
, with the store.when()
block.In particular, it requires a Client Token provider. For this example, we'll use the implementation provided by iaptic.
You are responsible for creating a user interface that presents the detail concerning the upcoming payment. Let's create a very simple interface.
1
let appState = 'BASKET';
2
let appMessage = '';
3
4
function refreshUI() {
5
const el = document.getElementById('app');
6
if (!el) return;
7
if (appState === 'BASKET') {
8
el.innerHTML = `
9
<p>${appMessage}</p>
10
<p>Amount to pay for 1x Real Good: $9.99</p>
11
<div><button onclick="pay()">Proceed to Payment</button></div>
12
`;
13
}
14
else if (appState === 'PAYMENT_INITIATED') {
15
el.innerHTML = `<p>Select your payment method...</p>`;
16
}
17
else if (appState === 'PAYMENT_APPROVED') {
18
el.innerHTML = `<p>Verifying your payment...</p>`;
19
}
20
else if (appState === 'PAYMENT_FINISHED') {
21
el.innerHTML = `<p>Payment successful!</p>`;
22
}
23
}
This is a primitive state machine that displays the basket, then the progress of the payment flow. While in the basket, the "Proceed to Payment" button calls the
pay()
function.Let's implement that function.
1
function pay() {
2
const {store, Platform, ErrorCode} = CdvPurchase;
3
4
// Showing some feedback in the UI (cf refreshUI)
5
function setAppState(state, message) {
6
appState = state;
7
appMessage = message || '';
8
refreshUI();
9
}
10
11
setAppState('IN_PROGRESS', 'Please wait...');
12
store.requestPayment({
13
14
// required fields
15
platform: Platform.BRAINTREE,
16
amountMicros: 9.99 * 1000000,
17
currency: 'USD',
18
items: [{
19
id: 'REAL_GOOD',
20
title: '1x Real Good',
21
pricing: {
22
priceMicros: 5.99 * 1000000,
23
}
24
}, {
25
id: 'DELIVERY_STD',
26
title: 'Standard Delivery',
27
pricing: {
28
priceMicros: 4.00 * 1000000,
29
}
30
}],
31
32
// optional fields
33
billingAddress: {
34
givenName: 'John',
35
surname: 'Doe',
36
streetAddress1: '1200 3rd Ave',
37
locality: 'Seattle',
38
region: 'WA',
39
postalCode: '98101',
40
countryCode: 'US',
41
},
42
description: '1x Real Good',
43
email: '[email protected]',
44
})
45
.cancelled(() => setAppState('BASKET'))
46
.failed(error => setAppState('BASKET', 'Payment failed: ' + error.message))
47
.initiated(() => setAppState('PAYMENT_INITIATED'))
48
.approved(() => setAppState('PAYMENT_APPROVED'))
49
.finished(() => setAppState('PAYMENT_FINISHED'));
50
}
Let's build and test that!
The code above will work on both iOS and Android. We need to pick a test platform, so let's use Android.
We can build and run our app with:
npx cordova run android
You can then make a test payment using one of Braintree's test credit card numbers, like 4242 4242 4242 4242, with an expiry date in the future.
Here's the result:





Of course, your job doesn't end here. Iaptic will send a webhook notification to your server, indicating a payment for a given has been performed by the user. You should handle that webhook to deliver the purchase on your server.
When an identifier user makes a purchase, iaptic sends a notification to your server with the details of the transaction.
Here's an example body content.
{
"type": "purchases.updated",
"applicationUsername": "my_username",
"purchases": {
"REAL_GOOD": {
"platform": "braintree",
"purchaseId": "braintree:xxxxxxxx",
"transactionId": "braintree:xxxxxxxx",
"productId": "REAL_GOOD",
"purchaseDate": "2022-11-14T10:57:48.000Z",
"currency": "EUR",
"amountMicros": 9990000,
"sandbox": true,
"isPending": false,
"amountUSD": 9.77,
"raw": { /* ... */ }
}
},
"password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
You can process with enabling the feature for your user depending on this purchase status (in particular, check for a potential
cancelationReason
which would mean the purchase has been cancelled).Iaptic documentation related to server-to-server webhook contains all the appropriate details, which fall outside the scope of this guide.
Last modified 5mo ago