This tutorial is based on the latest version of Braintree.js i.e, Braintree.js v2.
This is a tutorial on deploying an in-app checkout page using Braintree.js to create subscriptions in Chargebee and directly send card information to Braintree. Braintree's embedded form ensures that sensitive credit card information does not pass through your server, thereby reducing your PCI compliance requirements.
Alternative Options
The simplest way to setup your checkout process with Chargebee is by using the hosted payment pages or the Hosted Pages + API integration method. Chargebee’s hosted pages are built using the Bootstrap themes.
Overview:
What is Braintree.js?
Braintree.js is a JavaScript library which you can wire into your checkout form, to directly handle secure credit card information. When a user signs up via this checkout form, sensitive information like card number,CVV, etc. is directly sent to Braintree. For further details, take a look at Braintree's documentation.
How exactly does this work with Chargebee?
Here's a detailed set of steps on how the entire checkout flow with Braintree & Chargebee works:
- After the customer confirms the plan and is led to the checkout page, a unique client token is embedded into the checkout page (this unique client token needs to be generated from the server using Braintree’s SDK).
- The customer then enters his/her subscription and card information in the checkout page.
- When the customer submits the payment form, Braintree encrypts the card information and returns it as a temporary token (payment method nonce).
- This temporary token (payment method nonce) is then passed to Chargebee along with other parameters to create a subscription using the create a subscription API.
Is this secure?
With Braintree.js, your server does not handle any sensitive credit card data, which reduces the PCI compliance burden to a greater extent. You could also use Chargebee's hosted payment pages which are also PCI compliant.
Honey Comics - Demo Application
'Honey Comics', our demo application, is a fictitious online comic book store providing a subscription service for comics. It sends comic books every week to subscribers via email. Users can sign up for a subscription from the website by providing account and payment information.
Prerequisites
Before trying out this tutorial, you would need to setup the following:
- A Chargebee account. Signup for a free trial if you don't have one.
- A plan in Chargebee for your customers to subscribe to. You can setup the plan for the demo using the "Setup Configuration" option in the index page if you have downloaded the code and started the tutorials locally.
- A Braintree account (sandbox for now, and a live one when you go into production).
- Your Braintree account integrated into your Chargebee account. Here is how you do it.
- You should have setup Braintree SDK in your server.
- Your Chargebee API key for your test site.
Client Side Implementation
Step 1: Build the checkout form
The client side implementation starts by building a form for users to sign up. The sample form we’ve used here contains fields for customer and card information.
The form snippet below shows the card number field for example.
<input type="text" className="card-number form-control" card-info="number" required data-msg-required="cannot be blank">
<input type="text" className="card-number form-control" card-info="number" required data-msg-required="cannot be blank">
<input type="text" className="card-number form-control" card-info="number" required data-msg-required="cannot be blank">
Warning
Do not include the 'name' attribute for the card data fields in the form. This is to ensure that sensitive card information is not passed even in case of an inadvertent bug at the client’s side. For the other non-card fields, ensure that the ‘name’ attribute is passed.
The form snippet below shows an account field. In this case the name attribute is set, as this has to be passed on to our demo application’s server.
<div className="col-sm-6">
<div className="form-group">
<label for="customer[email]">Email</label>
<input id="email" type="text" className="form-control" name="customer[email]" maxlength="50"
data-rule-required="true" data-rule-email="true"
data-msg-required="Please enter your email address"
data-msg-email="Please enter a valid email address">
<small for="customer[email]" className="text-danger"></small>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label for="customer[email]">Email</label>
<input id="email" type="text" className="form-control" name="customer[email]" maxlength="50"
data-rule-required="true" data-rule-email="true"
data-msg-required="Please enter your email address"
data-msg-email="Please enter a valid email address">
<small for="customer[email]" className="text-danger"></small>
</div>
</div>
<div className="col-sm-6">
<div className="form-group">
<label for="customer[email]">Email</label>
<input id="email" type="text" className="form-control" name="customer[email]" maxlength="50"
data-rule-required="true" data-rule-email="true"
data-msg-required="Please enter your email address"
data-msg-email="Please enter a valid email address">
<small for="customer[email]" className="text-danger"></small>
</div>
</div>
Step 2: Wire up Braintree.js
Now that the form is built, let's wire up Braintree.js into our checkout form. For Braintree.js to come into action, add it to the checkout page's header tag using the <script> tag.
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
Step 3: Set the client token from the server
A client token has to be embedded into the checkout form. This token is unique and has to be generated from the server using Braintree’s SDK. Here, we have used a sample dummy method as mentioned in Braintree’s docs. Please note that you will have to use Braintree’s SDK for generating the actual client token.
<script type="text/javascript">
//Replace it with your key
var client = new braintree.api.Client({ clientToken : "<%= getBraintreeClientToken() %>"});
</script>
<script type="text/javascript">
//Replace it with your key
var client = new braintree.api.Client({ clientToken : "<%= @client_token %>" });
</script>
<script type="text/javascript">
//Replace it with your key
var client = new braintree.api.Client({ clientToken : "<?php echo getBraintreeClientToken() ?>" });
</script>
Step 4 a. Send the card details to Braintree
So now that Braintree.js is all set in our web page, let's write up some javascript for our form. The javascript should be triggered when a user submits the form, after which card details should be passed on to Braintree. The response from Braintree should then be handled.
You will have to use Braintree's tokenizecard(cardobject,callback) method to pass the card details to Braintree. The first argument should be a JSON object containing the card information and the second argument refers to a callback function which is invoked once the card tokenization is complete. Here’s the sample of how the tokenizecard method has to be used.
<script type="text/javascript">
$(document).ready(function() {
$("#subscribe-form").on('submit', function(e) {
e.preventDefault();
var form = $('#subscribe-form')
if (!$(form).valid()) {
return false;
}
showProcessing();
client.tokenizeCard({
number: $('input[card-info=number]').val(),
expirationDate: $('select[card-info=expiry_month]').val()+ "/" + $('select[card-info=expiry_year]').val(),
cvv : $('input[card-info=cvv]').val()
},function (err, nonce) {
if(err){
$(".alert-danger").show().text("Couldn't process your card");
hideProcessing();
return;
}
if ($("input[name='braintreeToken']").length == 1) {
$("input[name='braintreeToken']").val(nonce);
} else {
form.append("<input type='hidden' name='braintreeToken' value='" + nonce + "' />");
}
var options = {
error: subscribeErrorHandler,
success: subscribeResponseHandler,
complete: hideProcessing,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json'
};
$(form).ajaxSubmit(options);
});
return false;
});
});
</script>
<script type="text/javascript">
$(document).ready(function() {
$("#subscribe-form").on('submit', function(e) {
e.preventDefault();
var form = $('#subscribe-form')
if (!$(form).valid()) {
return false;
}
showProcessing();
client.tokenizeCard({
number: $('input[card-info=number]').val(),
expirationDate: $('select[card-info=expiry_month]').val()+ "/" + $('select[card-info=expiry_year]').val(),
cvv : $('input[card-info=cvv]').val()
},function (err, nonce) {
if(err){
$(".alert-danger").show().text("Couldn't process your card");
hideProcessing();
return;
}
if ($("input[name='braintreeToken']").length == 1) {
$("input[name='braintreeToken']").val(nonce);
} else {
form.append("<input type='hidden' name='braintreeToken' value='" + nonce + "' />");
}
var options = {
error: subscribeErrorHandler,
success: subscribeResponseHandler,
complete: hideProcessing,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json'
};
$(form).ajaxSubmit(options);
});
return false;
});
});
</script>
<script type="text/javascript">
$(document).ready(function() {
$("#subscribe-form").on('submit', function(e) {
e.preventDefault();
var form = $('#subscribe-form')
if (!$(form).valid()) {
return false;
}
showProcessing();
client.tokenizeCard({
number: $('input[card-info=number]').val(),
expirationDate: $('select[card-info=expiry_month]').val()+ "/" + $('select[card-info=expiry_year]').val(),
cvv : $('input[card-info=cvv]').val()
},function (err, nonce) {
if(err){
$(".alert-danger").show().text("Couldn't process your card");
hideProcessing();
return;
}
if ($("input[name='braintreeToken']").length == 1) {
$("input[name='braintreeToken']").val(nonce);
} else {
form.append("<input type='hidden' name='braintreeToken' value='" + nonce + "' />");
}
var options = {
error: subscribeErrorHandler,
success: subscribeResponseHandler,
complete: hideProcessing,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json'
};
$(form).ajaxSubmit(options);
});
return false;
});
});
</script>
Step 4 b. Handle Braintree's response
Braintree triggers the callback function when the card tokenization operation is complete. The callback function's signature contains two parameters: the nonce and the error.
If the card was successfully tokenized, a valid temporary token is set and the error parameter is null.
However if the card tokenization fails, the temporary token is null and the error parameter contains the reason code for the error. Upon successful tokenization of the card, you will have to pass the nonce along with the customer information to your server.
Server Side Implementation
Step 1. Setup the Chargebee client library
You have to download and import the client library of our choice. Then, configure the client library with your test site and its api key.
For the tutorial, we have configured the site and the credentials in a separate properties file. When the webapp is initialized, the client library gets configured.
/**
* The credentials are stored in a properties file under WEB-INF
* The live site api keys should be stored securely. It should preferably
* be stored only in the production machine(s) and not hard coded
* in code or checked into a version control system by mistake.
*/
Properties credentials = read("WEB-INF/ChargeBeeCredentials.properties");
Environment.configure(credentials.getProperty("site"), credentials.getProperty("api_key"));
For the tutorial, we have configured the site credentials in config/environments/development.rb
ENV["CHARGEBEE_SITE"]="honeycomics-test"
ENV["CHARGEBEE_API_KEY"]="test_5LjFA6K6doB2EKRP7cufTd5TvT32a5BrT"
We setup the client library in config/initializers/chargebee.rb
ChargeBee.configure(:site => ENV["CHARGEBEE_SITE"], :api_key => ENV["CHARGEBEE_API_KEY"])
For the tutorial, we have configured the site credentials in Config.php
require_once(dirname(__FILE__) . "/lib/ChargeBee.php");
/*
* Sets the environment for calling the Chargebee API.
* You need to sign up at ChargeBee app to get this credential.
* It is better if you fetch configuration from the environment
* properties instead of hard coding it in code.
*/
ChargeBee_Environment::configure("honeycomics-test", "test_5LjFA6K6doB2EKRP7cufTd5TvT32a5BrT");
Step 2. Create the subscription
To create a subscription in Chargebee, the temporary token (payment nonce) fetched earlier has to be passed along with the other POST parameters (from the checkout page’s form submit event) using the Create Subscription API. Find language specific samples below:
- PHP
- RUBY
- JAVA
Result result = Subscription.create().planId(planId)
.customerFirstName(request.getParameter("customer[first_name]"))
.customerLastName(request.getParameter("customer[last_name"))
.customerEmail(request.getParameter("customer[email]"))
.customerPhone(request.getParameter("customer[phone]"))
.cardTmpToken(request.getParameter("braintreeToken"))
.request();
# Creating a subscription in ChargeBee by passing the encrypted
# card number and card cvv provided by Braintree Js.
create_subscription_params = {:plan_id => plan_id,
:customer => params['customer'],
:card => {"tmp_token" => params['braintreeToken'] }}
result = ChargeBee::Subscription.create(create_subscription_params)
/* Creating a subscription in ChargeBee by passing the encrypted
* card number and card cvv provided by Braintree Js.
*/
$createSubscriptionParams = array("planId" => $planId,
"customer" => $_POST['customer'],
"card" => array("tmpToken" => $_POST['braintreeToken']) );
$result = ChargeBee_Subscription::create($createSubscriptionParams);
Although the parameters have been validated at the client’s side, for additional security, we strongly recommend that you perform these validations on the server side as well.
For demonstrative purposes, we have skipped validating the parameters on the server’s side.
Redirecting to a thank you page (upon successful checkout)
So, what happens when a subscription is created successfully? Chargebee returns a success response in the json format which is wrapped in the form of a 'result' class by the client library. In case of an error, Chargebee returns an error response which is wrapped up as an exception and is thrown by the client library.
In case of successful checkout, redirect the user to a simple 'Thank You' page.
Validation and Error Handling
Here's how we validate user inputs and handle API call errors in this demo:
- Client Side Validation: Chargebee uses jQuery form validation plugin to check whether the user’s field inputs(email, zip code and phone number) are valid or not.
- Server Side Validation: As this is a demo application we have skipped the server side validation of all input parameters. But we recommend you to perform the validation at your end.
- Payment Errors: If a payment fails due to card verification or processing errors, Chargebee returns an error response which is thrown as a payment exception by the client library. We handle the exceptions in the demo application with appropriate error messages.
- General API Errors: Chargebee might return error responses due to various reasons such as invalid configuration, bad request etc. To identify specific reasons for all error responses you can check the API documentation. Also take a look at the error handler file to check how these errors can be handled.
Test cards
When you're all set, test your integration with some test transactions. Here are some credit card numbers that you can use to test your application:
Visa | 4111 1111 1111 1111 |
Discover | 6011 1111 1111 1117 |
JCB | 3530 1113 3330 0000 |
For more test cards for testing different scenarios click here.
Reference Links
We're always happy to help you with any questions you might have!
support@chargebee.com