3D Secure 2.0 card payments with Braintree

Stephan Bester
9 min readJul 16, 2019

Braintree offers an online card payment service that integrates with .NET web applications (among others). It helps your application to comply with PCI and allows payments to be verified using 3D Secure. Furthermore, in light of PSD2 and Strong Customer Authentication (SCA) requirements, Braintree has adopted 3D Secure 2.0.

In this article, I demonstrate how to integrate a .NET Core web application with Braintree for 3D Secure 2.0 card payments using the Drop-in UI.

Get a sandbox environment

The sandbox allows developers to test Braintree integration in their dev and test environments.

Signing up for a sandbox is free. You can do this on Braintree’s Sandbox page. You will receive an email with instructions for activating your account. Once you’ve completed this step and signed in, you will see the following keys displayed on the home page:

  • Merchant ID
  • Public Key
  • Private Key

Take note of these keys, as they will be used in the sections below.

The client token

Before a payment can be made, the first step is to send a client token from the server to the front-end. The server obtains this token from Braintree.

The client token is generated and sent to the front-end

The token will be used by the front-end when it renders your card payment screen and is included in communications with Braintree.

Install the client library on your server

For the server to talk to Braintree, you need to install the Braintree client library. The client library for .NET is available as a NuGet package, which you can find below:

You can add a reference to your server project in Visual Studio Code using the following terminal command:

dotnet add package Braintree

Generate a client token

A gateway to Braintree is needed to generate tokens. On your server, create an instance of the BraintreeGateway class from the Braintree namespace, and set the three keys from your sandbox as well as the Environment property, e.g.

var gateway = new BraintreeGateway
{
Environment = Braintree.Environment.SANDBOX,
MerchantId = "Merchant ID",
PublicKey = "Public Key",
PrivateKey = "Private Key"
};

(These values will be different for the live system, so in the real world it makes sense to load them from a config file.)

The client token is a simple string and is generated by calling the Generate method on the ClientToken property of the gateway object, e.g.

var token = gateway.ClientToken.Generate();

Note that you can keep using the same gateway instance throughout the lifetime of your application. Pass the token to your front-end whenever it needs to render a new card payment page, e.g. by including it in the model to a Razor View.

The card payment screen

When the front-end renders the card payment screen, it uses the client token received from the server to initialize components from the Braintree SDK in JavaScript.

The front-end requests the payment method and verifies the card

Card details are captured in fields hosted by Braintree, which returns a payment method nonce to the front-end. This nonce is used in a subsequent call for the 3D Secure verification step, which returns a second nonce. The front-end will send this final nonce to the server to close the loop.

Include the JavaScript SDK

The web page that will be used for card payments will need to import three JavaScript files from Braintree. The first is the main SDK:

<script src="https://js.braintreegateway.com/web/3.47.0/js/client.min.js"></script>

Next is the Drop-in UI component:

<script src="https://js.braintreegateway.com/web/dropin/1.18.0/js/dropin.min.js"></script>

Finally, this script is for the 3D Secure component:

<script src="https://js.braintreegateway.com/web/3.47.0/js/three-d-secure.min.js"></script>

Note: 3D Secure 2.0 requires SDK version 3.47 or higher.

Initialize the Drop-in UI

Braintree offers two options for integrating your front-end — Hosted Fields and Drop-in UI. In this article, I will be using the Drop-in UI, but you will find a comparison of the two by following the first link.

The Drop-in UI component injects a div element with input fields hosted on Braintree’s site (achieved through the magic of the iframe). This <div> is identified at initialization using a CSS selector. Put the <div> where you want the UI to be rendered. By default, it looks something like this:

The Drop-in UI card payment form

Initialize the Drop-in UI by calling braintree.dropin.create. Your JavaScript will resemble the following:

braintree.dropin.create({
authorization: "client_token",
container: "#container_id"
},
function (dropInError, dropInInstance) {
if (dropInError) {
// Handle initialization errors here
return;
}
// Use the instance here
}

The first parameter is an object with two properties:

  • authorization is the client token from your server.
  • container is a CSS selector for the target <div>.

The second parameter is a callback function that Braintree will call once the component has been initialized. If an error occurs, an error object is passed as the first argument; otherwise, the error object is null and the new component instance is passed as the second argument. (This pattern is standard throughout the Braintree JavaScript SDK.)

As in the example above, the first thing you should do in the body of the callback is to handle errors. After that, you can proceed to make use of the new drop-in instance. In our case, before we touch the instance, we need to initialize the 3D Secure component as well.

Initialize the 3D Secure component

To initialize the 3D Secure component, call braintree.threeDSecure.create as in the example below:

braintree.threeDSecure.create({
authorization: "client_token",
version: 2
},
function (threeDSecureError, threeDSecureInstance) {
if (threeDSecureError) {
// Handle initialization errors here
return;
}
// Use the instance here
});

The first parameter is once again an object, with two parameters:

  • authorization is the client token from your server (the same as before).
  • version is 2 so that Braintree knows to use 3D Secure 2.0.

As before, the second parameter is a callback that takes two arguments — an error object and a new 3D Secure instance. Once you’ve provided for error handling, the next step is to create an event handler to request the payment method nonce from Braintree.

Request the payment method

If you test your page at this point, you’ll see the Drop-in UI appear shortly after the page is loaded. The user can capture their details, and there is built-in validation. What you need next is a way to trigger the transaction.

Add a button to your page and hook up a click event listener that calls the requestPaymentMethod function on your drop-in component (dropInInstance in the code above), e.g.

payButton.addEventListener('click', function () {
event.preventDefault();
dropInInstance.requestPaymentMethod(function (requestPaymentMethodErr, requestPaymentPayload) {
if (requestPaymentMethodErr) {
// Handle errors here
return;
}
// Use requestPaymentPayload.nonce here
});
});

This function takes only one parameter — a callback function. Braintree calls this to return the payment method, passing a potential error object and a payload object. The payload contains the payment method nonce, which you will use for the next step.

Verify the card using 3D Secure

The verifyCard method of the 3D Secure component (threeDSecureInstance in the code above) is used to verify the payment method, as in the example below:

threeDSecureInstance.verifyCard({
amount: 15,
nonce: payload.nonce,
bin: payload.details.bin,
email: "customer_email",
billingAddress: { /* address */ },
additionalInformation: { /* more info */ },
onLookupComplete: function (data, next) {
// Use look-up data here
next();
}
},
function (verifyError, verifyResponse) {
if (verifyError) {
// Handle errors here
return;
}
// Submit verifyResponse.nonce to your server
});

The first parameter is an object with many notable properties:

  • amount is the transaction amount.
  • nonce is the nonce from the payload returned by requestPaymentMethod in the section above.
  • bin is a Bank Identification Number and can be obtained from the same payload using details.bin.
  • billingAddress and additionalInformation are optional. You may omit these fields, but Braintree recommends including them in order to reduce the need for authentication challenges. For more detail, see the Braintree 3D Secure 2.0 adoption guide.
  • onLookupComplete is a callback that allows you to inspect the 3DS look-up data before calling the next function.

The second parameter is a callback that receives an error object and a payload that contains the new nonce, which shall be sent to your server to finalize the transaction. The callback is invoked by Braintree once the user has completed the 3D Secure challenge, which appears in an overlay on the card payment page.

Example of a 3D Secure challenge in the sandbox

Note: To test 3D Secure 2.0 in the sandbox environment, you need to use a very specific set of test values. Values that do not appear in this list will cause Braintree to default to an older version of 3DS.

Warning: Sending through an amount of zero results in an HTTP 422 error (unprocessable entity) in the Braintree API. I’m noting this down because this sort of thing can happen during development and it might not be obvious what has gone wrong.

After accounting for errors, pass the new nonce to your server, e.g. via an AJAX call, or by including the value in a hidden input field on a <form> and submitting it to the back-end.

Process the payment on the server-side

Having verified the payment method using 3D Secure, the front-end sends the nonce to the server.

The server receives the nonce from the front-end and makes the sale

The transaction is only created once the server sends this nonce to Braintree in a call to process the sale.

Verify that 3D Secure was used

Before putting the sale through, you probably want to confirm that the card was verified using 3D Secure. This is done by looking up information about the payment method using the Find method on the PaymentMethodNonce property of the Braintree gateway object you created before. Pass in the nonce string received from the front-end, e.g.

var paymentMethodNonce = gateway.PaymentMethodNonce.Find(nonce);if (paymentMethodNonce.ThreeDSecureInfo == null)
{
// 3D Secure was not used
return;
}

This returns a PaymentMethodNonce object. If its ThreeDSecureInfo property is null, the payment method was not verified using 3D Secure. In this case, you may want to reject the transaction, log an error, etc. Otherwise, this object contains useful information about the verification result.

Properties o the ThreeDSecureInfo object

If you are satisfied with the payment method, it’s time to use the nonce to create the transaction.

Create the transaction

A transaction is created by calling the Sale method on the Transaction property of the Braintree gateway, passing in a TransactionRequest instance:

var request = new TransactionRequest
{
Amount = amount,
PaymentMethodNonce = nonce,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = true
}
};
var result = _gateway.Transaction.Sale(request);

The Amount property is a decimal that represents the amount that will be charged. The PaymentMethodNonce is the nonce string that was received from the front-end and used in the 3D Secure verification step above. The Sale method returns a Result<Transaction> instance, which you can inspect to see whether or not the transaction was successful, e.g.

if (!result.IsSuccess())
{
// Handle transaction errors
// Check result.Transaction.Status
}

If the IsSuccess method returns false, you can look at the Status property of the Transaction for more detail.

View transactions

Transactions created in the sandbox can be viewed on the Braintree website.

An example of the transaction report

Sign in to your sandbox account and do a transaction search. If you have successfully created transactions during testing, they will appear in this report.

Used for this article

  • Visual Studio Code 1.36.1
  • .NET Core 2.2.300
  • Braintree .NET 4.12.0

--

--

Stephan Bester
Stephan Bester

Written by Stephan Bester

Software developer walking the edge between legacy systems and modern technology. I also make music: stephanbmusic.com

Responses (3)