3-D Secure is a protocol designed to be an additional security layer for online credit and debit cards transactions.
This document provides an integration guide for partners who want to integrate 3D Secure 2.0 (3DS 2.0) payment flow into your standard integration with Dvip Payment App (with using RocketGate gateway).
Dvip Payment App supports 3ds in the first version for some time. Below diagram represents the way how it's implemented:
{F253653}
As you can see, to make a transaction only one "transaction.create" call is required (like for any other processors). In case we discover that 3ds is required, API will return code 615 with proper parameters required for redirecting a customer. When a transaction is done, Rocketgate sends all needed data to the Dvip Payment App for the proper listener method and the transaction can be finished on our side.
3D Secure 2 (3DS2) is the updated authentication protocol for online card payments. 3DS2 is designed to improve upon 3D Secure 1 (3DS1). Information about the transaction at hand, the cardholder's browser or mobile device, as well as the cardholder's account history with the given merchant are all collected and eventually passed on to the issuing bank. Because of that, 3DS2 has a different data flow.
The new data flow consists of three transactions:
- the first transaction which does the device fingerprinting
- the second transaction which cause the 3ds lookup
- the third transaction which performs the authentication and purchase request
Below diagram presents a new way of integration with Dvip Payment App
{F254386}
==={anchor #step1}Step 1===
First call of “transaction.create” method looks very similar to 3ds 1.0. The only difference is that a new flag “force_3ds” in the pp_config array is required
```
...
'pp_config' => [
'force_3ds' => '1',
],
```
NOTE: This flag is just for tests, and currently it’s the only way to enable 3ds on Rocketgate side. Possible that we will be able to omit that flag in future.
NOTE: This param will be required in all 3 steps right now.
That’s a temporary thing, just to be able to test 3ds 2 explicitly on both production and staging envs.
When everything will be ready we will replace that flag with correct case automatically based on rules from this ticket: https://phab.dvipdev.com/T22380
In case, we got 3ds 2.0 response from RocketGate new parameters will be returned in the Dvip Payment App response:
- _3DSECURE_DEVICE_COLLECTION_URL
- _3DSECURE_DEVICE_COLLECTION_JWT
==={anchor #step2}Step 2===
To prepare a second call to Dvip Payment App a new parameter “fingerprint” is required. To get that “magic” param we need to use those two new parameters from above response. Here’s the simple example how it can be done.
- fingerprint.php file which contains a html form (with proper js code):
```
lang=html
<form action="<?= $_GET[‘_3ds_url’] ?>" method="POST" target="myFrame" id="postToIframe">
<input type="hidden" name="JWT" value="<?= $_GET[‘_3ds_jwt’] ?>" />
</form>
```
```
lang=javascript
<script>
window.onload = function() {
// Auto submit form on page load console.info("about to submit form");
document.getElementById('postToIframe').submit
};
console.log("adding event listener")
</script>
```
- payment page - uses above file for obtain a proper fingerprint of the device
```
lang=html
<iframe NAME="myFrame" style="display:none” src="fingerprint.php?_3ds_url=<?= $_3DSECURE_DEVICE_COLLECTION_URL; ?>&_3ds_jwt=<?= $_3DSECURE_DEVICE_COLLECTION_JWT ?>"></iframe>
<input type="text" value="" name="fingerprint" id="deviceFingerprintingId">
```
```
lang=javascript
<script type="text/javascript">
window.addEventListener("message", function(event) {
if (event.origin === "https://centinelapistag.cardinalcommerce.com") {
let data = JSON.parse(event.data);
console.info('Merchant received a message:', data);
if (data !== undefined && data.Status) {
console.info('Cardinal DF status successful. Trying to obtain DF reference ID');
//Use in cmpi_lookup if DFReferenceId not provided in JWT let
deviceFingerprintingId = data.SessionId;
console.info('Cardinal DF ID: ' + deviceFingerprintingId);
document.getElementById("deviceFingerprintingId").value = deviceFingerprintingId;
} else {
console.error("Cardinal data element is undefined or the status is false");
}
} else {
console.error("event.origin is not what we expected for Cardinal: " + event.origin);
}
}, false);
</script>
```
WARNING: the event.origin depends on environment of RocketGate merchant and according to documentation might be:
for production: https://centinelapi.cardinalcommerce.com, for development: https://centinelapistag.cardinalcommerce.com
For above example the fingerprint will be set in the field: input#deviceFingerprintingId
When we get the proper fingerprint of the customer device we can call the transaction.create method again with the given value passed in the field named “fingerprint” along with another 2 required parameters “user_agent” and “user_accept” (browser HTTP Accept Headers) which you can obtain from $_SERVER and forward to us.
More details here: https://phab.dvipdev.com/w/public/payment/partners/apidocs/transaction.create/
As a result, Dvip Payment API will return code 615 (similar for 3ds 1.0) which means that transaction was declined and 3d authentication is required. Additionally, in that case, the method will return new parameters:
- ACSredirectURL
- ACSJWT
- ACStermURL
- ACSmd
==={anchor #step3}Step 3===
All parameters from step 2 must be used to prepare a redirect request to the page where the customer can be authenticated. To do that we need to sent to ACSredirectURL below parameters (with POST method):
- MD
- JWT
NOTE: that MD param is ACSmd from response.
JWT = ACSJWT - that should be equal to your payment page URL, where user is supposed to comeback after applying form on above url.
WARNING: The above request MUST be a POST request.
The RG recommends redirect implementation similar like for fingerprint. So, as before, to use an iframe with a form and with auto submit mechanism (example in javascript above).
==={anchor #step4}Step 4===
After successful authorization, the customer will be sent back to the payment page with additional parameters. Within the passed parameters the most important is “MD” which is required for the 3rd “transaction.create” call [note: you can use transaction_id refering to payment_tran_id from 1st or 2nd transaction.create request, instead of providing CC data]. Value of the given parameter must be passed in the request as the “md” field. In result Dvip Payment App can prepare a final request to RocketGate gateway and will return a final transaction result like 700 for success (or some error code if transaction will be rejected on rocketgate side).
**Testing implemetation**
Entire process has been prepared and implemented on the staging server and it’s just for demo and testing purposes.
https://payment.dvipdev.com/emulate/rg3ds
**Testing values**
Test values can be found here:
https://help.rocketgate.com/support/solutions/articles/28000020933-3ds-2-0-tests-reference-guide-v1-0
WARNING: This version is the first version of 3DS 2.0 implementation. We are still in contact with RockatGate support and possible that some details might be changed in the near future.
====Transition from 3ds v2.0 to 3ds v1.0====
There is also another issue which is depends on merchant/bank configuration (possible it will be change in the future) however possible case is that payment process is starting as v2.0 and at some point has been changed into v1.0.
In general to that place where payment API returns 615 everything looks how describe above. Durirng transition from 2.0 to 1.0 user is redirected from cardinal challange website into payment RG listener (as usuall in 3ds 1.0 where request contain parameters like MD, PaRes, etc) and in that place the real purchase transaction is processing. Afterwards, user is redirecting from listener into success or fail url of the partner website (dvip, dbe etc). In that case result of transaction is appended to the given url.
```
RETURN_URL?data={"success":true,"rg_avs_response":"","rg_cvv2_code":"","responseCode":"0","reasonCode":"0","guidNo":"123","cardHash":"krEo3AEWeFxBkFXjLGLNgFYW1tczOtAE3ThURajRAo=","remote_tran_id":"123","payment_tran_id":1219954,"rg_merchant_id":"1358984622","rg_customer_id":"A1_5489895","rg_reason_code":"0","rg_guid_no":"123","rg_cc_hash":"krEo3AEWeFxBkFXjLGLNgFYW1tczOtAE3ThURajRAo=","partial_billing":0,"partial_billing_factor":1,"items_data":[{"item_id":"10707","tracking_item":"171"}],"sub_ids":[5881],"ppac_id":"2","hash":"6dda5e3a43fae9ac31c6fe809b634c72"}
```
In result, there is a need of recognition a type of request on partner side and, in case of 3ds v1.0, there's no need to call additional transaction.create request (like for 3ds 2.0) because transaction is done already. The result of repeated transaction will ended with code 685 (Transaction declined because of last successful transaction made few minutes ago by the same user )
**Note**: all 3DS v1 transactions are automatically stopped on our end just after success (and partners got notifications about that)