3D Secure
3D Secure is an XML-based protocol designed to be an additional security layer for online credit and debit card transactions.
It adds an authentication step for online payments, directly integrated to the card brands and bank servers. That means, whenever the card brands or a bank approve a sale from happening through 3DSecure, the liability and risk is shifted from the merchant to the bank. Given these circumstances, it prevents chargebacks from happening since the card brands and banks will actually be responsible for the transaction approval.
While most of the US based cards are enrolled to the 3DS, there are cards are not enrolled to it, and consequently, this service is only possible when the credit card and issuing bank participate on the program.
Frictionless mode 3DS
Frictionless mode is a trade-off: you opt for better user experience while having a lower success ratio on 3DS. If compared to Strict mode, Frictionless mode is less secure, since for all the cases when the issuing bank and credit card bank ask for additional information, the process will fail. From a technical perspective, you improve user’s experience by not sending them to a different website, since all the process happens within a hidden iframe on your website.
From our data, using this method reduces churn. In terms of success ratio, what we’ve seen is that frictionless 3DS works 30-70% of the times based off your customer base and business variables.
Authorization
Authorization process for our 3DS API consists in generating a hex-digested SHA256 signature, using four main variables:
- Your API Key;
- The fully qualified URL you’re trying to reach on the API;
- JSON dumped data you’re posting for that request, with keys ordered alphabetically;
- Your API Secret.
Let’s say you’re trying to reach the endpoint /hello on a http://example.com base url and you want to send the following data:
{
"name": "John Doe",
"card": "4111111111111111",
"state": "California",
"city": "San Francisco"
}
The first thing that you would have to do is sorting the json keys alphabetically:
{
"card": "4111111111111111",
"city": "San Francisco",
"name": "John Doe",
"state": "California"
}
Then, strip all the empty spaces from that string:
{"card":"4111111111111111","city":"San Francisco","name":"John Doe","state":"California"}
And then, signing it like this:
API_KEY='my-api-key'
URL='http://example.com/hello'
DATA='{"card":"4111111111111111","city":"San Francisco","name":"John Doe","state":"California"}'
SECRET='no-one-knows'
# It would look like this:
# my-api-keyhttp://example.com/hello{"card":"4111111111111111","city":"San Francisco","name":"John Doe","state":"California"}no-one-knows
SIGNATURE=$API_KEY$URL$DATA$SECRET
echo $SIGNATURE
# Digest/sign it!
# Should look like this: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
echo -n $SIGNATURE | openssl dgst -sha256
That signature and your API Key should be passed on all requests in the following request headers:
Header | Required | Value |
---|---|---|
x-mpi-api-key |
Yes | my-api-key |
x-mpi-signature |
Yes | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
Check card enrollment
At the moment, we only support 3DS for VISA and MasterCard cards. Still, not all of the cards and issuing banks participate to the 3DS program. So, in order to know if a card is participating or not, you should make the first request which is to get enrollment status for a specific credit card.
HTTP Request
POST https://mpi.3dsintegrator.com/index.php/enrolled-status
Request Fields
Parameter | Required? | Description | Type |
---|---|---|---|
pan |
Yes | The credit card number | string |
Sample request
curl -v -X POST -H 'Content-Type: application/json' \
-H 'x-mpi-api-key: YOUR_API_KEY' \
-H 'x-mpi-signature: 1b742fab984f86b141991917c71c99b81109078e4874ce1c9fb925a038af0386' \
-d '{"pan":"4111111111111111"}' \
"https://mpi.3dsintegrator.com/index.php/enrolled-status"
The above command returns JSON structured like this:
{"enrollment_status":"N"}
Responses
Response | Description | Liability |
---|---|---|
enrollment_status = Y |
Participating bank and card issuer | Card Issuer |
enrollment_status = N |
Not participating bank or card issuer | Merchant |
enrollment_status = U |
Unknown, usually a server side error on card brand / issuing bank APIs | Merchant |
enrollment_status = A |
Attempted, but something went wrong | Merchant |
Based off your business nature and your own preferences you can block a transaction from happening or just let it proceed without 3DS protection. If you want to proceed without 3DS protection, just send over the transaction data to your gateway of choice; otherwise, if you want to block a transaction from happening, you can just send the customer back to the credit card details screen and ask him to try another card. At this point, our implementation is very permissive. It’s up to the merchants to choose what makes sense to them, on their business context. We usually recommend the permissive approach as getting too strict would result in loss of sales.
Payment Authentication Request
Whenever you get a enrollment_status = Y
from the previous step, you’re ready to start the Payment Authentication Request. This step basically consists in exchanging the credit card data, and unique ids to your application with the API to get URLs and a token which will run the 3DSecure redirects under your mode of choice. A very important fragment of data that you will be passing at this request is also the return_url
. This is the endpoint that you will receive on the next steps, a POST containing the final response for the 3DS process.
HTTP Request
POST https://mpi.3dsintegrator.com/index.php/auth-request
Request Fields
Parameter | Required? | Description | Type |
---|---|---|---|
pan |
Yes | The credit card number | string |
card_exp_month |
Yes | Card expiration month in two digits. | string (2 digits) |
card_exp_year |
Yes | Card expiration year in two digits. | string (2 digits) |
amount |
Yes | Amount of the transaction. Maximum allowed precision is 2 decimal cases | float (precision: 2) |
transaction_id |
Yes | An unique identifier for the transaction in course. | string |
message_id |
Yes | An unique identifier for the order. Can be the same than transaction_id |
string |
return_url |
Yes | Callback URL for processing the transaction. Protocol must be https . |
string |
Sample request
curl -v -X POST -H 'Content-Type: application/json' \
-H 'x-mpi-api-key: YOUR_API_KEY' \
-H 'x-mpi-signature: 16851d38cdc413e3481fedc17b03fb080eaf45a1ab20a4e6b9c7c2bb93a72799' \
-d '{"amount":2,"card_exp_month":"12","card_exp_year":"20","message_id":"0001","pan":"4111111111111111","return_url":"https://localhost/3ds/callback","transaction_id":"0001"}' \
"https://mpi.3dsintegrator.com/index.php/auth-request"
The above command returns JSON structured like this:
{
"AcsUrl": "https://mpi.3dsintegrator.com/demoacs/",
"PaReq": "eJx1kduOgkAMhl/FmL2mHJWQ2oRFE7nQGOV+M4FGSJaDA4i==",
"TermUrl": "https://localhost/3ds/callback",
"MD": "0001"
}
Responses
Response | Description | Type |
---|---|---|
AcsUrl |
Participating bank / card issuer page to run the redirects | string |
PaReq |
The payment authentication request token | string |
TermUrl |
The url you will be receiving callbacks, passed as return_url on the request. |
string |
MD |
Attempted, but something went wrong | string |
After having the PaReq token, you have to make a choice in regards to which 3DS flavor you will be using: strict or frictionless. For strict, your user will be forwarded to VISA or MasterCard confirmation page. For frictionless, the process will happen inside your own system, inside an iframe, with a supporting endpoint, but you will have a lower approval rate.
Payment Authentication Response
Once you have the PaRes
token, you are then ready to finish the 3D Secure authentication and make a decision of wether you will let this transaction pass or not. At this point, you will be doing a request to one last endpoint, with your PaRes data and the card authentication details provided on the Payment Authorization Request step, so the card brands and issuing banks can gather the information they need to provide a verdict and prevent that transaction from charging back.
HTTP Request
POST https://mpi.3dsintegrator.com/index.php/auth-response
Request Fields
Parameter | Required? | Description | Type |
---|---|---|---|
pares |
Yes | The Payment Authentication Response token. | string |
pan |
Yes | The credit card number | string |
card_exp_month |
Yes | Card expiration month in two digits. | string (2 digits) |
card_exp_year |
Yes | Card expiration year in two digits. | string (2 digits) |
amount |
Yes | Amount of the transaction. Maximum allowed precision is 2 decimal cases | float (precision: 2) |
transaction_id |
Yes | An unique identifier for the transaction in course. | string |
message_id |
Yes | An unique identifier for the order. Can be the same than transaction_id |
string |
return_url |
Yes | Callback URL for processing the transaction. Protocol must be https . |
string |
Sample request
curl -v -X POST -H 'Content-Type: application/json' \
-H 'x-mpi-api-key: YOUR_API_KEY' \
-H 'x-mpi-signature: c2cc490e0d1c34f4498f14159230b69af396ff56d6509bdcbba26cbf43179f5e' \
-d '{"amount":2,"card_exp_month":"12","card_exp_year":"2020","message_id":"0001","pan":"4111111111111111","pares":"PARES","return_url":"https://localhost/3ds/callback","transaction_id":"0001"}' \
"https://mpi.3dsintegrator.com/index.php/auth-response"
The above command returns JSON structured like this:
{
"pares":"eJzNWdnO4siSf...[REDACTED for readability purposes]...HlhAAAAAAA=",
"success":true,
"id":" 0001",
"status":"Y",
"time":"20170929 09:35:08",
"cavv":"AAACACZ5YQAAABlwJHlhAAAAAAA=",
"xid":"ICAgICAgICAgICAgICAgIDAwMDE=",
"cavv_algorithm":"2",
"eci":"05",
"error":""
}
Responses
Response | Description | Type |
---|---|---|
pares |
The Payment Authentication Response token. | string |
id |
The transaction_id provided. Please notice that sometimes it has carriage return / leading and trailing spaces. | string |
success |
If the request to 3DS API succeeded or not. Not related to liability shift. | boolean |
status |
Request status and liability indicator. See table “Statuses” below. | string |
time |
Time that the response / verdict was given. YYYYMMDD HH:MM:SS formatted. |
string |
cavv |
Cardholder Authentication Verification Value. Should be forwarded to the gateway for liability shift. | string |
xid |
3DS Internal Transaction ID. Should be forwarded to the gateway for liability shift. | string |
cavv_algorithm |
The algorithm used to create the cryptogram cointained in cavv field. Sometimes should be forwarded to the gateway |
string |
eci |
Electronic Commerce Indicator. Indicates the the authentication results of this specific transaction. See “Liability” table below. | string |
Based off the response you get above, you know if the liability of this transaction has been shifted to the issuing card or bank. In order to understand if that happened, you need to take under consideration the fields status
and eci
even though our API responds with a success
field. the success
field just means that the request was successful, not that the liability was shifted.
Statuses
Status | Definition |
---|---|
Y |
Liability has been shifted - see table Liability below section to see detailed coverage. |
N |
Liability has not been shifted. Although, there are no reasons explicitly provided. |
U |
Liability has not been shifted. There was an error with the card brand or issuing bank API. |
A |
Liability may have been shifted or not - see table Liability for a verdict |
Liability
Verdict | MasterCard (ECI) | Visa (ECI) |
---|---|---|
3DS authentication succeeded and liability shifted | 2 or 02 |
5 or 05 |
3DS authentication attempted, but was not or could not be completed | 1 or 01 |
6 or 06 |
3DS authentication failed or could not be attempted; Card and/or issuing bank are not secured by 3DS, technical errors, or improper configuration. |
0 or 00 |
7 or 07 |
As stated before, the business decisions on processing a transaction that had a failed 3DS attempt or liability not shifted or not is totally yours. Our 3DS process was built in a way that you, the merchant, can choose what makes more sense to your business. 3DS protection is only one of the products we offer to fight chargebacks, so if you want full protection, we recommend taking a look also on our Fraud Portal, Alerts API, Chargeback Representment and our Kount checks in order to have more data to drive you on a decision of blocking a transaction or not. In these cases, the more data you have, the more power you have to make an assertive decision.
Javascript SDK
This SDK is the simplest way to integrate risk based authentication to your online shop cart.
Installation
This is the front-end implementation of the SDK. Below is the instructions for leveling up your form to use 3D Secure.
1. Add the javascript
Include the following code on the billing page (where you get the credit card information) before closing the tag </body>
.
<script src="https://js.paycertify.com/3ds.js"></script>
2. Initialize the ThreeDS library
The following code will start the library with the checkout form including the credit card data (first parameter) and the server side integration url (second parameter). You can also add some options to customize the integration.
<script>
var threeDS = new PayCertify.ThreeDS('payment-form', 'http://example.com/3ds/check_enrollment', { timeout: 5000 })
threeDS.init();
</script>
You can use the following options to make 3DS integration fits better your checkout form:
Field | Description | Default |
---|---|---|
interval |
The interval in miliseconds which the javascript will wait to check 3DS form for an answer. | 500 |
timeout |
Timeout in seconds that the javscript SDK will wait for an 3DS answer before executing the callback. | 10000 |
fallback |
Change it to true if you want to fallback to strict mode when frictionless method fails. | false |
onSuccess |
Callback executed when 3DS is executed and successful. | function |
onError |
Callback executed when 3DS fails. | function |
3. Describe your inputs
To work properly, you should describe your inputs from checkout form to be sent to the server. You should add to some inputs the attribute data-threeds
with the type of information the input contains. As you see on the following table:
Value | Description |
---|---|
id |
Unique identifier for the transaction. |
transaction-amount |
Transaction amount. |
cc-num |
Credit card number. |
cc-month |
Credit card expiration month. (MM) |
cc-year |
Credit card expiration year. (YYYY) |
You can use as the following example:
<form class="" action="index.html" method="post" id="payment-form">
<!-- your checkout fields ... -->
<input type="text" name="name" />
<input type="text" name="address" />
<!-- 3DS fields ... -->
<input type="hidden" name="transaction_id" data-threeds="id" />
<input type="text" name="card_number" data-threeds="cc-num" />
<input type="text" name="card_expiration_month" data-threeds="cc-month" />
<input type="text" name="card_expiration_year" data-threeds="cc-year" />
<input type="text" name="transaction_amount" data-threeds="transaction-amount" />
</form>
4. Execute 3DS integration on server-side
On your server side, you should have an endpoint waiting for the 3DS information to check card enrollment. The Javascript SDK will be waiting for a JSON in the following format:
{
"AcsUrl": "https://mpi.3dsintegrator.com/demoacs/",
"PaReq": "eJx1kduOgkAMhl/FmL2mHJWQ2oRFE7nQGOV+M4FGSJaDA4i==",
"TermUrl": "https://example.com/3ds/callback",
"MD": "0001"
}
Then, you should receive the callback with the 3DS response (TermUrl from JSON response) in order to get the 3DS Authorization. The Javascript will be waiting for an JSON response as following:
{
"pares":"eJzNWdnO4siSf...[REDACTED for readability purposes]...HlhAAAAAAA=",
"cavv":"AAACACZ5YQAAABlwJHlhAAAAAAA=",
"xid":"ICAgICAgICAgICAgICAgIDAwMDE=",
"cavv_algorithm":"2",
"eci":"05",
"error":""
}
5. Sending the form
After all these steps, 3DS data was now added to your form and it will continue the natural form submitting flow to your checkout route. The following fields were added to your form and can be used to forward 3DS Data to your gateway.
Field | Description |
---|---|
cavv |
Cardholder Authentication Verification Value. Should be forwarded to the gateway for liability shift. |
xid |
3DS Internal Transaction ID. Should be forwarded to the gateway for liability shift. |
cavv_algorithm |
The algorithm used to create the cryptogram cointained in cavv field. Sometimes should be forwarded to the gateway. |
eci |
Electronic Commerce Indicator. Indicates the the authentication results of this specific transaction. |
Forwarding 3DS data
After you get the Payment Authentication Response, you should forward specific parameters to our gateway so the transaction can finally be protected.