Payment Request (Braintree)
This use case explains how to request a custom payment (e.g., for physical goods, services, or dynamically priced items) using the Braintree platform via cordova-plugin-purchase
v13+ and its required Braintree extension (cordova-plugin-purchase-braintree
).
Unlike store products, payment requests typically involve a custom amount and require server-side processing of a payment method nonce generated by the Braintree SDK.
1. Platform Setup
First, ensure your Braintree account (Sandbox and Production), Cordova project, and native platforms (iOS/Android) are correctly configured.
Setup for Braintree Payments
This guide details the necessary steps to configure your development environment, Braintree account, and application settings before implementing custom payments using the Braintree platform via cordova-plugin-purchase
v13+ and its required Braintree extension.
Platform Interfaces Change Frequently!
The Braintree control panel interface, SDKs, and requirements change often. This guide provides a general overview but may become outdated.
Always refer to the official Braintree documentation as the primary source:
1. Braintree Account Setup
Sandbox: Sign up for a Braintree Sandbox account for development and testing. This provides test API keys and a simulated environment.
Production: When ready to accept real payments, apply for a Braintree Production account and complete the underwriting process.
2. Retrieve Braintree API Credentials
Log in to your Braintree Control Panel (Sandbox or Production):
Navigate to Settings (gear icon) -> API.
Note down your:
Merchant ID
Public Key
Private Key (Keep this secure! It's used server-side).
You will also find your Tokenization Key here. This key can be used directly in the client-side SDK for basic authorization, primarily suitable for testing or simple integrations. Using Client Tokens generated server-side is recommended for production.
3. Install Dependencies
Install the core purchase plugin AND the Braintree extension plugin.
Also ensure you have base Cordova/Node development tools installed:
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.
4. Create or Prepare Cordova Project
Set up your Cordova project and add the required platforms (iOS and/or Android).
Create Project: !INCLUDE "./create-cordova-project.md"
Add Platforms:
5. Configure Android Project (Gradle/Dependencies)
The cordova-plugin-purchase-braintree
extension uses Gradle to manage the native Braintree Android SDK dependencies.
Run
cordova prepare android
.Ensure your project builds successfully (
cordova build android
). Gradle should automatically download the required Braintree libraries.Check the plugin's
plugin.xml
for specific Android SDK version requirements if you encounter build issues related to dependencies.
6. Configure iOS Project (Cocoapods)
The Braintree extension uses Cocoapods for iOS dependencies.
Install Cocoapods: If you don't have it, run
sudo gem install cocoapods
.Navigate and Install: After adding the iOS platform and the plugin, go to your project's
platforms/ios/
directory in the terminal and run:Open Workspace: Always open the
.xcworkspace
file in Xcode, not the.xcodeproj
file, after runningpod install
.Build: Build the project in Xcode to ensure dependencies are linked correctly.
7. Initialize Braintree in Your App
In your application's JavaScript (after deviceready
), you need to initialize the CdvPurchase.store
including the Braintree platform adapter. This requires providing authorization.
Option 1: Using Client Token Provider (Recommended for Production)
This is the most secure method. Your server generates a short-lived Client Token using the Braintree Server SDK (with your Merchant ID, Public Key, and Private Key). Your app requests this token when needed.
Note: You must implement the
/api/braintree/client-token
endpoint on your server using Braintree's server SDKs.
Option 2: Using Tokenization Key (Simpler for Testing)
Use your Sandbox Tokenization Key directly. Do not use this method with your Production Tokenization Key in a publicly distributed app, as it's less secure than Client Tokens.
8. (Optional) Configure Apple Pay (iOS Only)
To enable Apple Pay via Braintree:
Configure Apple Pay in Braintree: Follow Braintree's guide to configure Apple Pay in your Braintree control panel (requires Apple Merchant ID setup).
Enable Apple Pay Capability: In Xcode, under "Signing & Capabilities", add the "Apple Pay" capability and configure your Merchant ID.
Provide Options: Add the
applePay
configuration within thebraintreeOptions
duringstore.initialize
. At minimum,companyName
is often needed.
9. (Optional) Configure Google Pay (Android Only)
To enable Google Pay via Braintree:
Configure Google Pay in Braintree: Follow Braintree's guide to enable Google Pay in your Braintree control panel (may require linking a Google Merchant ID).
Provide Options: Add the
googlePay
configuration within thebraintreeOptions
duringstore.initialize
.
10. (Optional) Configure 3D Secure
To add an extra layer of security for card payments:
Enable 3D Secure in Braintree: Ensure 3D Secure is enabled for your Braintree account.
Provide Options: Add the
threeDSecure
configuration within thebraintreeOptions
. You typically need to provide the amount and potentially billing/shipping details within thestore.requestPayment
call itself, but you can set defaults here.
After completing these steps, your Braintree account and application should be configured to process payments using the cordova-plugin-purchase-braintree
extension and the store.requestPayment()
method. Remember that the crucial step of processing the payment nonce must happen on your server.
2. Code Implementation
Implement the JavaScript code to initialize the Braintree adapter, display payment details, request the payment using store.requestPayment()
, and handle the resulting events.
This section details the code implementation steps for processing a custom payment using the Braintree platform via cordova-plugin-purchase
and its Braintree extension.
1. Base Framework
First, ensure you have the basic HTML structure and initial JavaScript setup as outlined in the Code Framework section. This includes waiting for deviceready
and basic plugin checks.
index.html
Assuming you're starting from a blank Cordova project, let's set up the minimal HTML needed for the tutorials.
Step 1: Modify www/index.html
Replace the default <body>
content with the following structure:
Explanation: We create a main container (
#app
) and add specificdiv
elements (#messages
,#product-details
,#user-status
,#management-buttons
) that subsequent code examples will use to display information dynamically.
Step 2: Adjust Content Security Policy (CSP)
In the <head>
of your www/index.html
, find the <meta http-equiv="Content-Security-Policy" ...>
tag. You need to modify the connect-src
directive to allow connections to your receipt validation server. Also, ensure 'unsafe-inline'
is present in script-src
or default-src
if your examples use inline onclick
handlers (though using addEventListener
in JavaScript is generally preferred).
Explanation:
Replace
https://your-validator-server.com
with the actual URL of your receipt validation service (e.g.,https://validator.iaptic.com
). If you don't use a validator initially, you might omit this, but you'll need it later for secure implementations.The example keeps other default Cordova CSP directives. Adjust them based on your app's needs.
Step 3: (Optional) Clean Up Default CSS
You might want to comment out or remove the default CSS (www/css/index.css
) from the Cordova template project to avoid style conflicts with the simple examples.
JavaScript (www/js/index.js
)
www/js/index.js
)This section provides the minimal JavaScript foundation needed to start using the cordova-plugin-purchase
plugin in your www/js/index.js
file (or equivalent).
Explanation:
Wait for
deviceready
(Line 2): Essential first step for any Cordova plugin interaction.Plugin Check (Lines 8-14): Verifies that
CdvPurchase.store
is available before proceeding.Basic Setup (Lines 17-29):
Aliases common plugin members (
store
,LogLevel
, etc.) for convenience.Sets
store.verbosity
toDEBUG
for detailed logging during development.Sets up a global
store.error
handler to catch and log general plugin errors.
Deferred Initialization (Line 35): Calls
initializeStoreAndSetupListeners()
. This function is intentionally left as a placeholder here. Specific use-case guides (like setting up subscriptions or consumables) will provide the implementation for this function, which will includestore.register()
,store.validator = ...
,store.when()...
, andstore.initialize()
.Initial UI Refresh (Line 38): Calls
refreshUI()
, another placeholder function that specific guides will implement to display product information and purchase status.Helper Functions (Lines 43-51): Includes a basic
setStatus
function as an example for updating the UI.Placeholders (Lines 54-72): Empty definitions for
initializeStoreAndSetupListeners
andrefreshUI
emphasize that their specific logic depends on the use case and will be provided in subsequent steps of the tutorials.
This minimal base ensures the plugin is loaded and basic logging/error handling is in place before diving into platform-specific or product-type-specific configurations in the main use-case guides.
2. Initialization (initializeStoreAndSetupListeners
)
initializeStoreAndSetupListeners
)Next, implement the initializeStoreAndSetupListeners
function. This involves:
Configuring Braintree options, crucially providing a
clientTokenProvider
(recommended) or atokenizationKey
(for testing).Setting up the mandatory
store.validator
. Your validator endpoint must be capable of receiving a Braintree payment method nonce and using the Braintree Server SDK to create atransaction.sale
.Setting up
store.when()
listeners to handle theapproved
(nonce received),verified
(server processed nonce successfully), andfinished
(acknowledged) states.Calling
store.initialize()
with the Braintree platform and options.
Explanation:
Lines 11-15: Define configuration constants (replace placeholders!).
Lines 21-25: (Optional) Instantiate Iaptic helper if using it for token/validation.
Lines 28-60: Define
braintreeOptions
. TheclientTokenProvider
(Lines 30-57) is the recommended way to authorize the client SDK. It fetches a short-lived token from your server. Alternatively, uncomment and usetokenizationKey
(Line 59) for sandbox testing. Optional Apple Pay/Google Pay/3DS settings can be added here.Lines 63-66: Crucially, set
store.validator
to your backend endpoint that processes Braintree nonces. Without this, payments cannot be completed.Lines 69-91: Set up
store.when()
listeners..approved()
: Triggered when the Braintree SDK successfully generates a nonce. You must calltransaction.verify()
here to send the nonce to your validator..verified()
: Triggered after your validator successfully processes the nonce (calls Braintree'stransaction.sale
) and returns a success response. Callreceipt.finish()
here and fulfill the order..unverified()
: Handles validation failures reported by your server..finished()
: Confirms the transaction is fully acknowledged by the plugin..cancelled()
: Handles cancellations from the Drop-in UI (though therequestPayment
promise.cancelled()
is often more direct).
Lines 94-110: Call
store.initialize()
to activate the Braintree adapter. Update UI state based on success or failure.
3. User Interface (refreshUI
)
refreshUI
)Implement the refreshUI
function to display the payment details and update the UI based on the payment state (LOADING
, BASKET
, IN_PROGRESS
, PAYMENT_INITIATED
, PAYMENT_APPROVED
, PAYMENT_FINISHED
).
Explanation:
This function manages showing/hiding elements and enabling/disabling the "Pay Now" button based on the
appState
variable.The
setAppState
helper updates the state and callsrefreshUI
.
4. Payment Request (requestBraintreePayment
)
requestBraintreePayment
)Implement the function triggered by your "Pay Now" button. This function uses store.requestPayment()
to initiate the Braintree flow.
Explanation:
Lines 10-29: Define payment details (items, total amount, currency) and optional billing/user info.
Line 32: Update UI state to show processing.
Lines 35-54: Call
store.requestPayment()
withplatform: Platform.BRAINTREE
and the payment details.Lines 55-end: Chain promise handlers to manage the UI state during the payment flow:
.cancelled()
: User closed the Drop-in UI..failed()
: An error occurred initiating the payment request..initiated()
: The Braintree Drop-in UI has likely been presented..approved()
: The Braintree SDK returned a nonce; verification is now happening via thestore.when().approved()
listener setup earlier..finished()
: Called after the entire flow (including verification andfinish()
) is complete.
With these pieces in place, your app can initialize Braintree, display payment options, request a payment, and handle the nonce processing via your backend validator.
3. Server-Side Nonce Processing (Mandatory)
The client-side flow only generates a payment method nonce. This nonce is temporary and represents the user's authorized payment method (e.g., card details, PayPal account).
Your backend server MUST receive this nonce (typically sent from the
.approved()
event via yourstore.validator
endpoint).Your server then uses the Braintree Server SDK (Node.js, PHP, Python, etc.) along with your Braintree Private Key to call the Braintree API (e.g.,
gateway.transaction.sale()
) with the nonce and the payment amount.This server-side call creates the actual charge on the payment method.
Your
store.validator
should return a success response only after the Braintree transaction is successfully created.
Never trust the client-side approved
event alone to fulfill an order.
4. Server-to-Server Webhooks (Recommended)
For robust fulfillment, especially for asynchronous payment methods or post-settlement events, configure webhooks in your Braintree control panel to notify your server about transaction status changes (e.g., settlement confirmation, disputes).
When using a validation service like Iaptic to process the Braintree payment nonce, the service typically handles the communication with the Braintree gateway (e.g., calling transaction.sale
). After a successful transaction on Braintree's side, Iaptic (or your custom validator) will notify your application backend via a server-to-server webhook.
This webhook informs your server that a specific payment (often linked to your internal user ID if provided during validation) has been successfully completed. Your server should then:
Verify the webhook's authenticity (e.g., check a secret signature provided by Iaptic).
Update the user's account status in your database (e.g., mark order as paid, grant access, credit virtual currency).
Respond to the webhook request with a success status (e.g., HTTP 200 OK) so the service knows it was received.
Here's an example structure of what a webhook payload might look like (actual format depends on your validator service):
Handling this webhook correctly on your server is crucial for reliable order fulfillment after a Braintree payment processed via a validator. Consult your chosen validation service's documentation for specific details on their webhook format and security recommendations.(Note: This section uses Iaptic as an example validator service; adapt if using your own backend).
5. Testing
Follow the specific testing procedures using your Braintree Sandbox account and Braintree's test card numbers.
After implementing the code from the previous sections, you can test the Braintree payment flow.
Prerequisites:
Braintree Sandbox Account: Ensure you have set up a Braintree Sandbox account.
Client Token/Tokenization Key: Your app must be initialized with a valid Sandbox Client Token or Tokenization Key. Using production keys will result in real charges or errors.
Validator Endpoint (Mock or Real): You need a backend endpoint configured as
store.validator
that can receive the payment nonce from the.approved()
step. For initial testing, this endpoint could simply log the received nonce and return a successful validation response to allow the.verified()
and.finished()
steps to proceed in the app. Do not fulfill orders based on this mock validation. Later, implement the actual Braintreetransaction.sale
call on your server.Platform Setup: Build and run the app on a device or emulator (Android or iOS).
Testing Steps:
Launch App: Start your application. Check the console logs to ensure the Braintree platform initializes successfully and the UI reaches the 'Ready to pay' state.
Initiate Payment: Tap the "Pay Now" button in your app.
Braintree Drop-in UI: The Braintree Drop-in UI should appear, presenting payment options.
Enter Test Card Details: Select the "Card" option (or another if configured). Use one of Braintree's official test card numbers. A common one for success is:
Card Number:
4111 1111 1111 1111
Expiry Date: Any date in the future (e.g., 12/2025)
CVV: Any 3 digits (e.g., 123)
Postal Code: Any 5 digits (e.g., 12345)
Confirm Payment: Tap the button to submit the payment (e.g., "Pay $XX.XX").
Observe App & Logs:
The Drop-in UI should close.
Your app's status message should update (e.g., "Payment approved. Verifying with server...").
Check console logs for the
approved
event, which includes the payment method nonce (transaction.transactionId
).Your validator endpoint should receive a request containing this nonce.
(If Validator Mocked/Successful): Your validator returns success. The app logs the
verified
event, callsreceipt.finish()
, logs thefinished
event, and updates the UI to "Payment Successful!".
Verify Server (Real Validator): If using a real validator, check your Braintree Sandbox control panel to confirm that a transaction corresponding to the nonce was successfully created (e.g., status "Submitted for Settlement"). Check your server logs to ensure fulfillment logic was triggered.
Testing Failures:
Use Braintree's specific test card numbers designed to trigger processor declines (e.g.,
4242...
often works, check Braintree docs for current decline cards).Test cancelling the Drop-in UI (should trigger the
.cancelled()
callback inrequestPayment
).Simulate failures in your validator endpoint to test the
.unverified()
handler.
This process allows you to verify the client-side flow and the crucial interaction with your backend for processing the payment nonce.
Last updated