3D Secure (3DS) is the authentication protocol used by card brands, such as Visa and Mastercard, to enable merchants to gain protection from fraud in a card-not-present environment and to comply with European strong customer authentication (SCA) regulations set in PSD2.
Before starting the steps in this guide, you must do the following:
Before testing or implementing 3D Secure 2, you first need to set up two endpoint URLs in your application or website so that data and event notifications can be received from the issuer’s ACS. Each endpoint must be configured to accept an HTTP POST with Base64 encoded values.
The issuer’s ACS uses notifications at two important points during authentication:
The following guides assume you will be using the 3DS library as part of your integration.
The globalpayments-3ds.js library does not have a CDN. The merchant can install it via npm (npm i globalpayments-3ds), then they can find the final build in node_modules/globalpayments-3ds/dist/ - this needs to be added somewhere publicly available and referenced in the scripts below.
We consume the POST sent by the issuer’s ACS, decode it, and prepare the string to be returned to the client side and return it to the client as HTML.
We consume the POST sent by the issuer’s ACS, decode it, and prepare the string to be returned to the client side and return it to the client as HTML.
In this step, we create a single-use token to use in place of the card details for the 3D Secure authentication steps and when creating the sale transaction. Single-use tokens can be created directly via our API.
This step assumes that you are using your own PCI-compliant card form. Alternatively, we can look after the PCI security compliance for you via our Hosted Fields or Drop-In UI solution, which captures the customer card details on your behalf.
The first API call in this step is to request a single-permission access token that allows you to request and create your single-use token.
{
"id": "PMT_1c22346c-aafe-47f3-a1c5-231bdb734521",
"time_created": "2022-10-06T11:36:44.316Z",
"status": "ACTIVE",
"usage_mode": "SINGLE",
"merchant_id": "MER_7e3e2c7df34f42819b3edee31022ee3f",
"merchant_name": "Sandbox_merchant_3",
"account_id": "TKA_b3a46f0f351f43cfad20acf5c32fea50",
"account_name": "tokenization",
"reference": "2f2462be-74c4-4a63-ad5c-8da350f5f60c",
"card": {
"masked_number_last4": "XXXXXXXXXXXX5344",
"brand": "VISA",
"expiry_month": "12",
"expiry_year": "25"
},
"action": {
"id": "ACT_hboxaxUONzfojemgWfDoAE7O0JK7qO",
"type": "PAYMENT_METHOD_CREATE",
"time_created": "2022-10-06T11:36:44.316Z",
"result_code": "SUCCESS",
"app_id": "grf9Q3tGYqglXOmYXYGAeYfFvsmnXAGe",
"app_name": "demo_app"
}
}Now that you have your single-use token stored, you need to use the /accesstoken endpoint again to generate a full permission access token to proceed on your 3D Secure integration. Below are sample request and expected response to allow you move to Step 3.
curl --location 'https://apis.sandbox.globalpay.com/ucp/accesstoken' \
--header 'Content-Type: application/json' \
--header 'X-GP-Version: 2021-03-22' \
--data '{
"app_id": "T6og1tbECpHFeO104qUM383oq5bOJ12r",
"secret": "9e6ab70379a06aa41aebe3aac68100254fec77b338b579ae40ee8b45a9452d405e1b990ce942d63cd3c63ea2ff6c34bf563b57ff4e05cca32eeda2f670e5f966",
"grant_type": "client_credentials",
"nonce": "2024-09-25T19:25:35.813Z"
}'{
"token": "yuolhwoZKLmPSFK04rEoZIEwfZyZ",
"type": "Bearer",
"scope": {
"merchant_id": "MER_c5d37eaf0e3841e083c232b2318af55c",
"merchant_name": "Sandbox_Merchant_4",
"accounts": [
{
"id": "TRA_27c901760029495f813afbb0dab197ef",
"name": "transaction_processing",
"permissions": [
"TRN_POST_Authorize",
"TRN_POST_Force",
"TRN_POST_Refund",
"TRN_POST_Capture",
"TRN_POST_Reverse",
"TRN_POST_Adjust",
"TRN_POST_Initiate",
"BAT_POST_Close",
"TRN_POST_Refund_Standalone",
"VER_POST_Verify",
"TRN_POST_Reauthorize",
"INS_POST_Query",
"TRN_GET_Single",
"TRN_POST_Confirm",
"DEV_POST_Sync",
"ACT_GET_Single",
"ACT_GET_List",
"TRN_GET_List",
"TRN_POST_Capture_Multiple",
"ACT_POST_Multiple",
"APP_GET_Single",
"AUT_POST_Initiate",
"CCS_POST_DCC",
"ACC_GET_Single_Platform",
"ACC_GET_Single",
"APP_POST_Search_PFCValidate",
"ACT_POST_Auto_Action",
"PFC_GET_List",
"PFC_GET_Single",
"TRN_POST_Incremental",
"AUT_POST_Results",
"BAT_GET_Single",
"ACC_GET_List",
"GET_Single",
"AUT_POST_Check_Availability",
"APP_GET_List",
"TRN_POST_Split",
"AUT_GET_Single",
"AUT_GET_List",
"TRN_POST_Hold",
"TRN_POST_Release"
]
},
{
"id": "TKA_c1de85a8a5844c56abd9ca07bbd792c8",
"name": "tokenization",
"permissions": [
"PMT_POST_Create",
"PMT_PATCH_Edit",
"PMT_Delete",
"PMT_GET_List",
"PMT_GET_Single",
"PMT_POST_Search",
"ACT_POST_Multiple",
"PMT_POST_Create_Single",
"ACC_GET_Single",
"GET_Single",
"ACT_POST_Auto_Action",
"APP_POST_Search_PFCValidate",
"ACC_GET_List",
"ACT_GET_List",
"ACT_GET_Single",
"PYR_GET_Single",
"PYR_POST_Edit",
"PYR_GET_List",
"PYR_POST_Create"
]
}
]
},
"app_id": "T6og1tbECpHFeO104qUM383oq5bOJ12r",
"app_name": "Test_APP",
"time_created": "2024-09-25T19:25:43.008Z",
"seconds_to_expire": 86399,
"email": "your.name@email.com"
}The authentication process begins with determining if the card is enrolled in 3D Secure 2. Authentication is not completed during this step. When Step 5.1 frictionless or Step 5.2 challenge is completed, the relevant authentication fields are returned by the issuing bank. Please consult the Check Availability method in our API Explorer for more details.
If the card is enrolled, the exact version of 3D Secure 2 will be returned (2.x.x) along with the necessary Method URL to facilitate the gathering of device data by the issuer’s ACS (if supported by the issuer).
The Check Version method facilitates requests to your server-side code to check the enrolled version of 3D Secure for the customer’s card. This method also automatically handles the response from the version check. When a card is enrolled and a method URL is present, the method opens a hidden iFrame to the issuer’s ACS to facilitate device-profiling requirements.
The method requires two parameters: the server-side endpoint responsible for checking the enrollment of the card and the card data. In our example, we pass the single-use token we created in Step 1. Optionally, we can also send any data from the client-side. For additional details on what can be sent from the client-side, see the 3D Secure Helper Library in our SDK Reference.
<script src="globalpayments-3dsecure.js"></script>
<script>
const {
checkVersion,
initiateAuthentication,
ChallengeWindowSize
} = GlobalPayments.ThreeDSecure;
// Add submit click event handler
document.addEventListener("DOMContentLoaded", () => {
const checkVersionButton = document.getElementById("start");
if (!checkVersionButton) {
return;
}
checkVersionButton.addEventListener("click", start3DS);
});
const start3DS = async (e) => {
e.preventDefault();
try {
const versionCheckData = await checkVersion('/ThreeDSecure/CheckEnrollment/', {
card: {
reference: document.getElementById("payment-token").value,
}
});
if (versionCheckData.status === "NOT_ENROLLED" && versionCheckData.liabilityShift !== "YES") {
// TODO: do not proceed to authorization and ask the customer to try another card instead
return false;
}
if (versionCheckData.liabilityShift === "YES") {
// TODO: proceed with authorization
paymentForm.submit();
return true;
}
// continued in next Step 4 - Initiate Authentication On the server side, we need to consume the data sent by the JavaScript Library. In our example, we add the single-use token created in Step 1 to our card object.
If the Check Version returns true for 3D Secure 2, we return the enrollment status to the client-side, along with the Authentication ID (AUT_ID) on our system. In addition, (if supported by the ACS) we pass back the Method URL and Encoded Method Data required by the JavaScript Library to facilitate the device-profiling step.
If the issuer’s ACS supports the Method URL step, we need to open a hidden iFrame targeting the URL returned in the Check Enrollment response. The JavaScript Library automatically performs this step, and the device profiling should complete in seconds. Once it does, the issuer’s ACS sends a form POST and initiates a redirect inside the hidden iFrame to the endpoint we set up previously. When the JavaScript Library receives the response from the Method Notification endpoint, it closes the hidden iFrame and proceeds to the next step.
Also, we must format the JSON response back to the client-side so that the JavaScript Library can understand it and proceed.
The JavaScript Library expects a JSON string from the Check Version method. An example is provided in the code sample.
{
"id": "AUT_9601d398-6a11-4b83-9b4e-86cdfa448990",
"time_created": "2022-06-01T09:34:14.068Z",
"transaction_type": "SALE",
"status": "AVAILABLE",
"channel": "CNP",
"amount": "1999",
"currency": "EUR",
"country": "IE",
"merchant_id": "MER_7e3e2c7df34f42819b3edee31022ee3f",
"merchant_name": "Sandbox_merchant_3",
"account_id": "TRA_c9967ad7d8ec4b46b6dd44a61cde9a91",
"account_name": "transaction_processing",
"reference": "ad643798-1a80-4384-bef1-262f98fae825",
"three_ds": {
"server_trans_ref": "9601d398-6a11-4b83-9b4e-86cdfa448990",
"message_version": "2.2.0",
"ds_protocol_version_start": "2.1.0",
"ds_protocol_version_end": "2.2.0",
"acs_protocol_version_start": "2.1.0",
"acs_protocol_version_end": "2.2.0",
"acs_info_indicator": [
"NOT_AVAILABLE"
],
"enrolled_status": "ENROLLED",
"eci": "",
"liability_shift": "",
"challenge_model": "",
"challenge_status": "",
"session_data_field_name": "threeDSSessionData",
"message_type": "creq",
"challenge_value": "",
"redirect_url": "",
"acs_challenge_request_url": "",
"method_url": "https://test.portal.gpwebpay.com/pay-sim/sim/acs",
"method_data": {
"three_ds_server_trans_id": "9601d398-6a11-4b83-9b4e-86cdfa448990",
"three_ds_method_return_url": "https://example.com/methodnotification",
"encoded_method_data": "ewogICJ0aHJlZURTU2VydmVyVHJhbnNJRCIgOiAiOTYwMWQzOTgtNmExMS00YjgzLTliNGUtODZjZGZhNDQ4OTkwIiwKICAidGhyZWVEU01ldGhvZE5vdGlmaWNhdGlvblVSTCIgOiAiaHR0cHM6Ly9lbnNpODA4bzg1emEueC5waXBlZHJlYW0ubmV0LyIKfQ"
}
},
"notifications": {
"challenge_return_url": "https://example.com/ChallengeNotification"
},
"action": {
"id": "ACT_bmueQM0quYPruTCFCE0c8ihq0XHo01",
"type": "CHECK_AVAILABILITY",
"time_created": "2022-06-01T09:34:14.068Z",
"result_code": "SUCCESS",
"app_id": "grf9Q3tGYqglXOmYXYGAeYfFvsmnXAGe",
"app_name": "demo_app"
}
}Now that we know if the card is enrolled for 3D Secure 2 and (conditionally) the Method URL process is complete, we can send the authentication request to the issuer. For this, we use the Initiate Authentication method of the JavaScript Library. In our example, we also call an Initiate Authentication endpoint on the server side.
Some devices may return a color depth that is not in the list of accepted values. In this scenario, the EMVCo recommendation is to submit the closest lower value. For example, if the color depth returned by the browser is 30, submit 24 as this is the closest lower value.
3D Secure 2 provides a framework for merchants to benefit from SCA exemptions under certain low-risk conditions. Exemptions may be applied by the issuer based on the transaction details or may be specifically requested by the merchant, with their acquirer’s permission. In the authentication message, you can request an exemption by including it in the Challenge Request Indicator field.
When a merchant requests an exemption (whether via authentication or directly in authorization) and it's successfully applied, they will no longer be able to avail of a liability shift in the event of a fraud-related chargeback. For Mastercard exemption requests, see our Mastercard Message Extension article.
The initiateAuthentication method facilitates requests to your server-side code to initiate 3DS authentication flows with the customer. This method also handles the response from your server-side endpoint. If a challenge is required, the method will open the issuer's ACS in an iFrame to facilitate the challenge.
The method requires that you pass the server-side endpoint responsible for initiating the authentication request as well as the challengeWindow. By default the challengeWindow is set to display in a Lightbox and 600x400 pixel size, but you can customize this. The method also automatically passes the browserData. You should also pass whether the Method URL (device profiling) step occurred.
In our example, we pass the single-use token again. Alternatively, you could also temporarily store it on the server side in the previous step. We also pass the Version Check data so we can obtain the unique ID for the 3D Secure authentication.
Optionally, we can also send any relevant data from the client side, such as the customer (payer) billing and shipping details as well as any required order information. In our example, all data is obtained on the server side, such as from the database or available variables. For more information on all of the initiateAuthentication method parameters, see the 3D Secure Helper Library in our SDK Reference.
The method also expects a JSON string response from the Initiate Authentication server-side endpoint. An example is provided in the code sample below.
// continued from previous step 3 - Check version and enrollment
const authenticationData = await initiateAuthentication('/ThreeDSecure/InitiateAuthentication/', {
card: {
reference: document.getElementById('payment-token').value,
},
methodUrlComplete: true,
versionCheckData: versionCheckData,
challengeWindow: {
windowSize: ChallengeWindowSize.Windowed600x400,
displayMode: 'lightbox',
}
/*
Optionally set order detail parameters
order: {
amount: "1000",
currency: "eur",
},
Optionally set payer details
payer: {
email: "john.smith@example.com",
},
*/
});
// continued in next stepThe authentication request also requires browser data, which the JavaScript Library collects automatically and passes in the request to the server side. We must also obtain the browser IP address and Accept Header using server-side methods. Finally, we add all mandatory and recommended data to the request, including the Server Transaction ID (AUT_ID) returned in the Check Version response, and execute the Initiate Authentication method.
Note: We recommend "NO_CHALLENGE_REQUESTED" as it has been shown to reduce the number of challenges a merchant gets.
In our example, we're adding only mandatory and recommended fields to the request. For the full list of optional fields, see Authentications the API Explorer.
{
"id": "AUT_a08c2bad-59b0-403e-bf6d-1787129e44d6",
"time_created": "2023-02-15T10:13:22.090Z",
"time_last_updated": "2023-02-15T10:01:00.155Z",
"transaction_type": "SALE",
"status": "CHALLENGE_REQUIRED",
"channel": "CNP",
"amount": "1999",
"currency": "EUR",
"country": "IE",
"source": "BROWSER",
"three_ds": {
"server_trans_ref": "a08c2bad-59b0-403e-bf6d-1787129e44d6",
"acs_trans_ref": "60388d60-ad19-41ed-8d04-76e5e00edb9c",
"acs_reference_number": "3DS_LOA_ACS_INTE_020200_00293",
"ds_trans_ref": "0da9c676-3659-4eda-bf9c-6df32aeec109",
"eci": "",
"liability_shift": "",
"status": "CHALLENGE_REQUIRED",
"status_reason": "",
"authentication_source": "BROWSER",
"message_version": "2.1.0",
"authentication_value": "",
"cardholder_response_info": "",
"message_category": "PAYMENT_AUTHENTICATION",
"redirect_url": "https://acs2p.test.gpe.cz/tds/challenge/brw/60388d60-ad19-41ed-8d04-76e5e00edb9c",
"acs_challenge_request_url": "https://acs2p.test.gpe.cz/tds/challenge/brw/60388d60-ad19-41ed-8d04-76e5e00edb9c",
"challenge_status": "MANDATED",
"challenge_model": "OUT_OF_BAND_CHALLENGE",
"session_data_field_name": "threeDSSessionData",
"message_type": "creq",
"challenge_value": "ewogICJtZXNzYWdlVHlwZSIgOiAiQ1JlcSIsCiAgIm1lc3NhZ2VWZXJzaW9uIiA6ICIyLjEuMCIsCiAgInRocmVlRFNTZXJ2ZXJUcmFuc0lEIiA6ICJhMDhjMmJhZC01OWIwLTQwM2UtYmY2ZC0xNzg3MTI5ZTQ0ZDYiLAogICJhY3NUcmFuc0lEIiA6ICI2MDM4OGQ2MC1hZDE5LTQxZWQtOGQwNC03NmU1ZTAwZWRiOWMiLAogICJjaGFsbGVuZ2VXaW5kb3dTaXplIiA6ICIwNSIKfQ"
},
"notifications": {
"challenge_return_url": "https://www.example.com/ChallengeNotification"
},
"action": {
"id": "ACT_CIlS98KAq4aeYJhdwjw695x0uwW6V7",
"type": "INITIATE",
"time_created": "2022-06-01T09:34:14.068Z",
"result_code": "SUCCESS",
"app_id": "grf9Q3tGYqglXOmYXYGAeYfFvsmnXAGe",
"app_name": "demo_app"
}
}At this point, the issuer analyzes the transaction based on the data your application provided as well as historical “normal” customer behavior and transaction analysis. If further authentication is not needed to decide whether the transaction should proceed or not, a challenge is not mandated (frictionless flow). If the Issuer decides that further authentication is needed before the transaction can proceed, it will mandate a challenge (challenge flow).
In a frictionless flow, the issuer determines that either no challenge is required to authenticate the transaction or that the transaction should not proceed any further because it is considered high risk or fraud is suspected. For European merchants and transactions in scope of PSD2, the issuer will also determine if a valid exemption can be applied.
Depending on the outcome and the relevant electronic commerce indicator (ECI) value, your application can now proceed to authorization.
The following diagram shows the logic for a frictionless flow.

In our example below, we return the outcome to the client side. Hence, the Initiate Authentication method of the JavaScript Library receives the authentication response, and all that remains is to proceed to authorization (see Step 6).
In the event of the ECI value indicating no liability shift away from the merchant, we return an authentication failed message to the client-side. In our client-side example above (initiate authentication), we included logic to handle each potential outcome:
You can tailor this flow to your application as needed. For example, you may choose to simply return the frictionless authentication outcome to the JavaScript Library and proceed to authorization from a different endpoint initiated by the client side. The JavaScript Library and SDK are completely flexible in this regard.
In a challenge flow, the issuer determines that further authentication is required for the transaction to proceed. Reasons can include a high-value transaction, unusual payment behavior for this particular customer (time of day, type of merchant, and so on), mismatching data, or insufficient data to make a determination.
The following diagram shows the logic for a challenge flow.

In our example, we return the necessary variables to the client side for the JavaScript Library to display the challenge to the customer. This includes the Issuer's ACS URL and the Encoded Challenge Request (creq). The challenge request is made up of various transaction variables, including the identifiers for the authentication (Server Transaction ID, ACS Transaction ID, and the Challenge Window Size).
To facilitate authentication, the JavaScript Library opens the issuer's ACS URL in an iFrame while sending the challenge request to it. The content of the iFrame is formatted per the challenge-window size set in the library (WINDOWED_600X400 is the default). The maximum amount of time that the iFrame should remain open to display the challenge is 600 seconds (this is set by the Issuer’s ACS).
The type of challenge displayed to the customer is determined by the Issuer’s ACS and aligns with at least two of the three elements of SCA:
In our Sandbox environment, we provides an issuer's ACS simulator that allows you to test different challenge outcomes.
Once the challenge loads, after 10 seconds, the simulator automatically completes the authentication and generates an Authentication Successful response (transStatus = “Y”). This is intended to mimic a scenario in which a customer received a notification on their phone to authenticate using their banking app.
Alternatively, clicking the CANCEL button generates an Authentication Failed response (transStatus = “N”). In either scenario, the response is sent in the Challenge Response message (CRes) to the Challenge Notification endpoint.

When the challenge is complete and the JavaScript Library receives the response from the Challenge Notification endpoint, the iFrame closes and we proceed to the next step. As outlined previously, our Challenge Notification endpoint passes back the transaction status (transStatus) to the client side, notifying us if the authentication was successful or not, along with the Server Transaction ID on our system.
On the client side, if the challenge completes successfully, we proceed to the next step by submitting our form to the authorization endpoint after appending the Server Transaction ID to it. For challenge scenarios, we need to execute one more request before sending the authorization request.
// continued from previous step
// Add serverTransactionId to form as a hidden input
const serverTransactionId = document.createElement("input");
serverTransactionId.type = "hidden";
serverTransactionId.name = "serverTransactionId";
serverTransactionId.value = authenticationData.serverTransactionId || authenticationData.challenge.response.data.threeDSServerTransID || versionCheckData.serverTransactionId
// Submit data to authorization endpoint for processing
paymentForm.appendChild(serverTransactionId);
paymentForm.submit();
return true;
} catch (e) {
console.log(e);
console.error('An error occurred', e.reasons);
return false;
}
return false;
};
</script >At this point, we know the customer has successfully completed the challenge. However, unlike in a frictionless scenario, our application doesn’t have all the data we need to proceed to authorization.
When the customer completes the challenge, the Issuer’s ACS also sends the outcome to our 3D Secure solution, with the full authentication data. The Get Result method in the API allows us to retrieve the necessary data for authorization, including:
If the customer canceled during the challenge or the authentication did not proceed for another reason, the summary endpoint will include this information. You can then update your application or website accordingly.
If you decide not to use our 3D Secure Authentication resource and do not send the Authentication ID (AUT_ID) returned by Authentication resource in the Authorization request or you are using a third party 3D Secure provider, you will be required to include the following fields in the Authorization request.
Please refer to the Sample Request for Third-party 3DS Authentication in Step 8 - Authorization with 3DS2 data.
When a liability shift is obtained via the 3D Secure process then these values above or the AUT_ID must be passed in the Authorization request. If not submitted, then the liability shift will not be granted in the event of a chargeback.
In our example, we only need to submit the REST API reference for the transaction, the AUT_ID. This returns all the information required to complete a 3D Secure authorization.
{
"id": "AUT_3df53b41-22eb-4db8-9861-56c28e0a37fb",
"time_created": "2022-06-03T15:53:01.974",
"status": "SUCCESS_AUTHENTICATED",
"channel": "CNP",
"amount": "1999",
"currency": "EUR",
"country": "IE",
"source": "BROWSER",
"three_ds": {
"acs_trans_ref": "e0d13dd0-e354-41ec-8c03-435b033a82d6",
"ds_trans_ref": "3d2f6746-6e41-4136-9650-4929c3932801",
"server_trans_ref": "3df53b41-22eb-4db8-9861-56c28e0a37fb",
"acs_reference_number": "",
"liability_shift": "YES",
"authentication_type": "OUT_OF_BAND_CHALLENGE",
"authentication_value": "AAkBBVKDWAAAACcQl4FUdAoPFg0=",
"eci": "05",
"status": "AUTHENTICATION_SUCCESSFUL",
"status_reason": "",
"redirect_url": "https://apis.sandbox.globalpay.com/ucp/authentications/redirect/eyJpZCI6IkFVVF8zZGY1M2I0MS0yMmViLTRkYjgtOTg2MS01NmMyOGUwYTM3ZmIiLCJtZXJjaGFudF9tYW5hZ2VtZW50X2lkIjpudWxsLCJtZXJjaGFudF9pZCI6Ik1FUl83ZTNlMmM3ZGYzNGY0MjgxOWIzZWRlZTMxMDIyZWUzZiIsImFjY291bnRfaWQiOiJUUkFfYzk5NjdhZDdkOGVjNGI0NmI2ZGQ0NGE2MWNkZTlhOTEiLCJtZXJjaGFudF9uYW1lIjoiU2FuZGJveF9tZXJjaGFudF8zIiwiYWNjb3VudF9uYW1lIjoidHJhbnNhY3Rpb25fcHJvY2Vzc2luZyIsImFjc191cmwiOiJodHRwczovL2FjczJwLnRlc3QuZ3BlLmN6L3Rkcy9jaGFsbGVuZ2UvYnJ3L2UwZDEzZGQwLWUzNTQtNDFlYy04YzAzLTQzNWIwMzNhODJkNiIsIm1lc3NhZ2VfdmVyc2lvbiI6IjIuMS4wIiwiYXBwX2lkIjoiZ3JmOVEzdEdZcWdsWE9tWVhZR0FlWWZGdnNtblhBR2UiLCJhcHBfbmFtZSI6Imhvc3RlZF9maWVsZHMiLCJhcHBfZGV2ZWxvcGVyIjoiamFzb24uYmFqYXJpYXNAZ2xvYmFscGF5LmNvbSJ9",
"acs_challenge_request_url": "https://acs2p.test.gpe.cz/tds/challenge/brw/e0d13dd0-e354-41ec-8c03-435b033a82d6",
"authentication_source": "BROWSER",
"challenge_value": "ewogICJtZXNzYWdlVHlwZSIgOiAiQ1JlcSIsCiAgIm1lc3NhZ2VWZXJzaW9uIiA6ICIyLjEuMCIsCiAgInRocmVlRFNTZXJ2ZXJUcmFuc0lEIiA6ICIzZGY1M2I0MS0yMmViLTRkYjgtOTg2MS01NmMyOGUwYTM3ZmIiLAogICJhY3NUcmFuc0lEIiA6ICJlMGQxM2RkMC1lMzU0LTQxZWMtOGMwMy00MzViMDMzYTgyZDYiLAogICJjaGFsbGVuZ2VXaW5kb3dTaXplIiA6ICIwNCIKfQ",
"message_category": "PAYMENT_AUTHENTICATION",
"message_version": "2.1.0",
"challenge_status": "NOT_MANDATED",
"authentication_request_type": "",
"acs_decoupled_response_indicator": "",
"whitelist_status": "",
"message_extension": []
},
"action": {
"id": "ACT_LmP0hFBVsqmimUGlBHwhAUaCYDQE3I",
"type": "GET_CHALLENGE_RESULT",
"time_created": "2022-06-03T15:53:01.974",
"result_code": "SUCCESS",
"app_id": "U1lRHKomEn7DN907RCDPxVhyMfiMLcfy",
"app_name": "demo_app"
}
}Now that the 3D Secure 2 process is complete, and depending on the ECI value returned, we can proceed to authorization while including the authentication data.
Like we did in a frictionless scenario, here we check the ECI value to ensure it affords the merchant a liability shift. If it does, we proceed to authorization. At this point, the transaction is processed as normal and may be successful or declined based on standard criteria: sufficient funds, correct security code entered, and so forth.
curl --location --request POST 'https://apis.sandbox.globalpay.com/ucp/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer yuolhwoZKLmPSFK04rEoZIEwfZyZ' \
--header 'Accept: application/json' \
--header 'X-GP-Version: 2021-03-22' \
--data-raw '{
"account_name": "transaction_processing",
"channel": "CNP",
"type": "SALE",
"amount": "1999",
"currency": "EUR",
"reference": "Test_1234sdfsd",
"country": "IE",
"payment_method": {
"name": "James Mason",
"entry_mode": "ECOM",
"id": "PMT_1c22346c-aafe-47f3-a1c5-231bdb734521",
"authentication": {
"three_ds": {
"message_version": "2.2.0",
"eci": "05",
"value": "ODQzNjgwNjU0ZjM3N2JmYTg0NTM=",
"server_trans_ref": "ad0fffeb-bfff-44d0-881f-b857fe77c5a2",
"ds_trans_ref": "c272b04f-6e7b-43a2-bb78-90f4fb94aa25",
}
}
}
}'"id": "TRN_pVOW1K2sc8hRP4Nymi0qveBP7gcFGb_st_1234sdfsd",
"time_created": "2022-06-01T09:42:42.252Z",
"type": "SALE",
"status": "CAPTURED",
"channel": "CNP",
"capture_mode": "AUTO",
"amount": "1999",
"currency": "EUR",
"country": "IE",
"merchant_id": "MER_7e3e2c7df34f42819b3edee31022ee3f",
"merchant_name": "Sandbox_merchant_3",
"account_id": "TRA_c9967ad7d8ec4b46b6dd44a61cde9a91",
"account_name": "transaction_processing",
"reference": "Test_1234sdfsd",
"payment_method": {
"result": "00",
"message": "[ test system ] AUTHORISED",
"entry_mode": "ECOM",
"authentication": {
"cavv_result": ""
},
"card": {
"funding": "CREDIT",
"brand": "VISA",
"masked_number_last4": "XXXXXXXXXXXX5262",
"authcode": "12345",
"brand_reference": "DxKmQE8NmDuXC1uV",
"brand_time_created": "",
"tag_response": "",
"cvv_result": "MATCHED",
"avs_address_result": "MATCHED",
"avs_postal_code_result": "MATCHED",
"avs_action": "",
"provider": {
"result": "00",
"cvv_result": "M",
"avs_address_result": "M",
"avs_postal_code_result": "M"
}
}
},
"batch_id": "BAT_1099150",
"action": {
"id": "ACT_pVOW1K2sc8hRP4Nymi0qveBP7gcFGb",
"type": "AUTHORIZE",
"time_created": "2022-06-01T09:42:42.252Z",
"result_code": "SUCCESS",
"app_id": "grf9Q3tGYqglXOmYXYGAeYfFvsmnXAGe",
"app_name": "demo_app"
}
}The following is a complete client-side example for Hosted Fields and 3D Secure 2.
<form id="payment-form" name="checkout" action="PlaceOrder.php" method="post">
<!-- Target for the credit card form -->
<div id="credit-card"></div>
<input id="amount" name="amount" value="1999" type="text" hidden="hidden">
<input id="currency" name="currency" value="EUR" type="text" hidden="hidden">
<div><button id="start" type="button" hidden="hidden">Pay</button></div>
</form>
// Configure account
window.GlobalPayments.configure({
accessToken: gpApiAccessToken,
env: 'sandbox',
apiVersion: '2021-03-22',
});
const paymentForm = document.getElementById("payment-form");
// Create Form
const cardForm = GlobalPayments.creditCard.form("#credit-card", {
style: "gp-default"
});
cardForm.on("token-success", (response) => {
// Add payment token to form as a hidden input
const token = document.createElement("input");
token.type = "hidden";
token.id = "payment-token";
token.name = "paymentToken";
token.value = response.paymentReference;
paymentForm.appendChild(token);
const start3DSButton = document.getElementById("start");
start3DSButton.click();
});
cardForm.on("token-error", (response) => {
// Show error to the consumer
});
cardForm.on("error", (response) => {
// Show error to the consumer
});
const {
checkVersion,
initiateAuthentication,
ChallengeWindowSize,
} = GlobalPayments.ThreeDSecure;
// Add submit click event handler
document.addEventListener(“DOMContentLoaded”, () => {
const checkVersionButton = document.getElementById(“start”);
if (!checkVersionButton) {
return;
}
checkVersionButton.addEventListener(“click”, start3DS);
});
// Handle 3DS 2.0 workflow
const start3DS = async (e) => {
e.preventDefault();
try {
const versionCheckData = await checkVersion(“/ThreeDSecure/CheckEnrollment / ”, {
card: {
reference: document.getElementById(“payment - token”).value,
},
order: {
amount: document.getElementById("amount").value,
currency: document.getElementById("currency").value,
}
});
if (versionCheckData.status === "NOT_ENROLLED" && versionCheckData.liabilityShift !== "YES") {
// TODO: avoid authorization and ask the customer to try another card instead
return false;
}
if (versionCheckData.liabilityShift === "YES") {
// TODO: proceed with authorization
paymentForm.submit();
return true;
}
const authenticationData = await initiateAuthentication(“/ThreeDSecure/InitiateAuthentication / ”, {
card: {
reference: document.getElementById(“payment - token”).value,
},
order: {
amount: document.getElementById("amount").value,
currency: document.getElementById("currency").value,
// shipping address
// billing address
},
payer: {
email: "john.smith@example.com",
},
versionCheckData: versionCheckData,
challengeWindow: {
windowSize: ChallengeWindowSize.Windowed600x400,
displayMode: “lightbox”,
}
});
// You have a liability shift and fraud protection
if (authenticationData.liabilityShift === "YES" && (authenticationData.result === "SUCCESS_AUTHENTICATED" || authenticationData.result === "SUCCESS_ATTEMPT_MADE")) {
// TODO: proceed with authorization
paymentForm.submit();
return true;
}
// Add serverTransactionId to form as a hidden input
const serverTransactionId = document.createElement("input");
serverTransactionId.type = "hidden";
serverTransactionId.name = "serverTransactionId";
serverTransactionId.value = authenticationData.serverTransactionId || authenticationData.challenge.response.data.threeDSServerTransID || versionCheckData.serverTransactionId;
// Submit data to the integration's backend for processing
paymentForm.appendChild(serverTransactionId);
paymentForm.submit();
return true;
} catch (error) {
console.log(error);
console.error(“An error occurred”, error.reasons);
return false;
}
return false;
};To get a deeper understanding of 3D Secure, we’ve provided some references for both API and SDK integrations.
Generate an access token.
The full 3D secure resource for your reference.
3DS integration is supported via our JavaScript 3DS library.
Our 3D Secure solution is available for testing in our free Sandbox environment registered users of this developer portal. Use our resources below to start testing different scenarios.