Elavon Payment Gateway Integration – 3D Secure 2 (SCA)

A number of years ago I was tasked to integrate Elavon’s payment gateway into a WordPress site by extending a WordPress plugin. Yes before you ask it ships with Stripe/PayPal but the client is tied to Elavon as they have their card reader – so quit with the comments of why not just use Stripe.

At the end of last year the client was advised that it was going to be a requirement for all sites to implement 3D Secure 2 into the checkout process. This adds an extra layer of security as part of checkout process. I’m sure you’ll have come across this previously. This will become mandatory for U.K card payment on 14th September 2020 and is already required for cards within the E.U. You can read more here

Having read through Elavon’s “Quick start guide” although there are a number of steps, adding this looked fairly straight forward. I quoted for the work and set aside time to get this up and running before the end of last year.

Well that is what I thought... this was certainly not straightforward to implement. I now have an end-to-end test working (I used Cypress for the first time on this project – it’s a great tool which I am looking forward to using more in future) I am currently finalising testing.

Along the way I came across a few issues, so I thought I’d pull together a post in case they can help someone else not suffer the pain that I did. Please note that this is not an exhaustive guide of how to get this up and running. We’ll be using Javascipt and PHP throughout. It’s also a little peek into the world of web development and why sometimes things don’t always go as planned! For context here is a twitter thread of all my issues/rants along the way:

Here is their Quick Start guide (that I planned to follow):
http://developer.elavonpaymentgateway.com/#!/integration-api/3d-secure-2/java/html_js

The Elavon process – How it should work? #

When I mention “Javascript library" I used the most of the code from the example here and customised for my needs:
https://github.com/globalpayments/globalpayments-js/tree/master/packages/globalpayments-3ds. Server side we are using their PHP SDK alongside my own PHP implementation.

  • Customer enters their card Details
  • Send request to checkEnrollment
	$threeDSecureData = Secure3dService::checkEnrollment($card)
->execute('default', Secure3dVersion::TWO)
  • In the background an invisible iFrame is created by JavaScript library
  • The responses is output into the hidden iFrame. The main window is informed that iFrame has updated via postMessage.
    https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage and then closed – we proceed to the next step.
  • If card is enrolled to 3D Secure to we can then a send request to initiateAuthentication
	$threeDSecureData = Secure3dService::initiateAuthentication($card, $threeDSecureData)
->withAmount($this->amount)
->withCurrency("GBP")
->withOrderId($this->booking['booking_code'])
->withOrderCreateDate(date("Y-m-d H:i:s"))
->withCustomerEmail($this->invoice['print_customer_email'])
->withMobileNumber("44",$this->invoice['phone'])
->withAddress($billingAddress, AddressType::BILLING)
->withBrowserData($browserData)
->withMethodUrlCompletion(MethodUrlCompletion::YES)
->execute('default',Secure3dVersion::TWO);
  • This will return whether or not to present a challenge or if the transaction should be frictionless
  • If a challenge is required an iFrame is created and challenge presented in the browser. The Javascript library handles the opening of the iFrame and also handles the challenge response once completed by the user.
  • If a transaction is frictionless (no challenge required) we proceed to charge the card
  • When a challenge response passes or transaction is frictionless we can then progress to make a final request to an endpoint which will capture/authorise the final payment and setup a transaction within Elavon.
	$response = $this->card->authorize($this->amount)
->withCurrency("GBP")
->withOrderId($this->booking['booking_code'])
->withCustomerId($this->customer['customer_id'])
->execute('default', Secure3dVersion::TWO);

The issues I came across whilst setting the above up: #

Before you even get started #

This is where I lost a lot of time – trying to setup a quick test to check if a card was enrolled using Elavon’s Json example. I was getting a 401 unauthorised error and was getting nowhere. My first email to support (which took an age to get a response from). It turns out the 3DSv2 needs to be enabled at their end for this to work and also I think I was using the wrong URL (see below). So it is probably worth checking with Elavon before you get started to check if 3DSv2 is enabled on your account.

Note on their service URLs #

The docs do sometimes reference the below URL:
https://api.sandbox.elavonpaymentgateway.com/3ds2/authentications

However I found the above to be fairly problematic and I believe the SDK always uses a mixture of the below URLs and never uses an actual elavaongateway.com URL. If using the SDK it is best to leave any URL references out of the config as it figures the correct URLs out for you.
https://api.sandbox.globalpay-ecommerce.com/3ds2/ (For 3Ds2 integration)
https://api.realexpayments.com/epage-remote.cgi (For charging and authorising payments to a card)

Behind the scenes I am not sure how this all works – who owns what between Elavon, Realexpayments and Globalpayments. That said Globalpayments have similar but slightly improved documentation to Elavon, so if something isn’t clear on Elavon’s docs jump over to Globalpayments. I did end up using a mixture of both to get my head around things.
https://developer.globalpay.com/api/3d-secure-two

Notification URLs #

The examples on the Quick start guide are not very clear (or at least weren’t to me) as they’ve split out what you actually need into two blocks of code. Nothing is actually returned in their example just a comment of “// TODO: notify client-side that the Method URL step is complete” – useful. What is expected is HTML something like the below.

$threeDSMethodData = $request["threeDSMethodData"];

$decodedThreeDSMethodData = base64_decode($threeDSMethodData);
$convertedThreeDSMethodData = json_decode($decodedThreeDSMethodData, true);
$serverTransID = $convertedThreeDSMethodData['threeDSServerTransID'];

$html = "<script src='".$this->getScriptPath()."'></script>";
$html .= "<script>";
$html .= "GlobalPayments.ThreeDSecure.handleMethodNotification(";
$html .= "{ threeDSServerTransID: '". $serverTransID ."'}";
$html .= ");";
$html .= "</script>";

$this->returnHtml($html);

This Github issue (https://github.com/globalpayments/globalpayments-3ds-js/issues/7
) was a great helper in getting this working. If you’ve got issue with any Open Source project digging into the issues both open/closed can be a great help in figuring out issues.

An additional “gotcha” – if you are testing locally you will need to make sure these Notification URL’s are publicly accessible. I don’t think MethodNotification matters but ChallengeNotification will need to be public. I used ngrok (https://ngrok.com/) for this. This was a new discovery thank you to Ross Wintle for pointing me to it and also suggesting this may be why I was having issue).

Composer/PHP SDK #

You can grab the latest version from the below URLs.
https://github.com/globalpayments/php-sdk

Their tests can also be a helpful resource:
https://github.com/globalpayments/php-sdk/blob/master/test/Integration/Gateways/GpApiConnector/CreditCardNotPresentTest.php
https://github.com/globalpayments/php-sdk/blob/master/test/Integration/Gateways/GpApiConnector/CreditCardNotPresentTest.php

Their site suggests using the below, though this is now outdated. The latest version as of March 2021 is 2.29.

{
"require": {
"globalpayments/php-sdk": "2.0"
}
}

I used the below resulting in the below composer.json file.

composer require globalpayments/php-sdk
{
"require": {
"globalpayments/php-sdk": "^2.2"
}
}

Once using the latest version of the SDK you will come across issues if copying and pasting examples from Elavon’s documentation. Here are things to look out for:

Configuration now moved to separate config files #

$config = new ServicesConfig();

No longer work as the SDK has since been updated so that each provider has their own configuration class. For Elavon you will want to change this to:

$config = new GpEcomConfig();

Check execute() methods #

Similarly any execute() methods will need updating/checking. I believe you can now have multiple configurations by passing a name as the first parameter of the execute() method. So if we pass one parameter as per the first example this will cause the method to fail as it is expected two parameters – they’ll need updating to the second example.

$threeDSecureData = Secure3dService::getAuthenticationData()
->withServerTransactionId($serverTransactionId)
->execute(Secure3dVersion::TWO);
$threeDSecureData = Secure3dService::getAuthenticationData()
->withServerTransactionId($this->serverTransactionId)
->execute('default', Secure3dVersion::TWO);

I hope that above helps someone 🚀 I do like a challenge – but some small changes to their documentation could have made this process a whole lot easier. Also it’s not much to ask for a company like Elavon to at least provide up to date documentation is it?

I’d like to think I’m fairly experienced and think if their documentation was clearer it would have saved me many hours of trying to figure my issues out. Looking to setup Elavon payment gateway and struggling – get in touch I'll do my best to help.