3D Secure Authentication
Integrate 3D Secure into your payment flow to reduce fraud and comply with regulatory requirements.
What is 3D Secure?
3D Secure (3DS) is an authentication protocol that adds an extra layer of security to card transactions. When active, the issuing bank can ask the cardholder to authenticate themselves during the payment process.
Benefits:
- ✅ Fraud reduction
- ✅ Regulatory compliance
- ✅ Customer trust
Authentication Flow
The system automatically evaluates each transaction. You will only receive the AUTHENTICATION_REQUIRED status when necessary.
Implementation
Step 1: First Request
Always send authenticationId: null in your first payment or subscription request.
- Payments
- Subscriptions
{
"customer": {
"id": "customer112",
"name": "Carlos Pérez",
"email": "[email protected]",
"phoneNumber": "+50370000000"
},
"order": {
"id": "EPAY001",
"amount": 15.9,
"description": "insurance",
"name": "auto"
},
"cardId": "69167196381d34f25eb22e9d",
"authenticationId": null,
"locationCode": "N1C0CD001"
}
{
"planId": 1544,
"customer": {
"id": "Customer001",
"name": "Carlos Pérez",
"email": "[email protected]",
"phoneNumber": "+50370000000"
},
"paymentMethod": {
"id": "689f93049b36afba505e7bc8"
},
"backupPaymentMethod": {
"id": "689f69319b36afba505e7bc0"
},
"authenticationId": null,
"locationCode": "N1C0CD001"
}
Step 2: Handle Response
If you receive the AUTHENTICATION_REQUIRED status, you must display the authentication iframe:
- Payments
- Subscriptions
{
"status": "AUTHENTICATION_REQUIRED",
"message": "Payment requires 3DS authentication",
"error": null,
"authentication": {
"url": "https://front-3ds-sandbox.n1co.com/authentication/b574cee2-1ce7-4aa7-b176-8a6390e91081",
"id": "b574cee2-1ce7-4aa7-b176-8a6390e91081"
},
"order": null,
"createdAt": "2023-09-28T18:53:51.1070240Z"
}
{
"status": "AUTHENTICATION_REQUIRED",
"message": "Payment requires 3DS authentication",
"error": null,
"authentication": {
"url": "https://front-3ds-sandbox.n1co.com/authentication/9b77e558-7d5d-4fdd-b84c-f295225ba2e1",
"id": "9b77e558-7d5d-4fdd-b84c-f295225ba2e1"
},
"subscription": null,
"payment": {
"chargeId": null,
"authorizationCode": null
}
}
Step 3: Render Iframe
Create an iframe to display the authentication interface:
const iframe = document.createElement('iframe');
iframe.src = authentication.url;
iframe.width = 600;
iframe.height = 400;
document.getElementById('authentication-container').appendChild(iframe);
Implementation Examples:
- ⚛️ Official ePay subscriptions demo with 3DS (React) → — see
src/components/IFrameWithMessageListener.js
Step 4: Listen for PostMessage
The iframe will send a message when authentication finishes. Keep two details in mind:
- The message arrives as JSON text: call
JSON.parse(event.data)before reading its fields. - The origin depends on the environment. Take it from the
authentication.urlreturned by the API instead of hard-coding it:
| Environment | 3DS iframe origin |
|---|---|
| Sandbox | https://front-3ds-sandbox.n1co.com |
| Production | https://front-3ds.n1co.com |
// authentication.url is the value received in the AUTHENTICATION_REQUIRED response
const expectedOrigin = new URL(authentication.url).origin;
window.addEventListener('message', function(event) {
// Validate message origin
if (event.origin !== expectedOrigin) {
return;
}
let data;
try {
// The iframe sends the message as JSON text
data = JSON.parse(event.data);
} catch {
return;
}
// Handle different statuses
handleAuthenticationResult(data);
});
function handleAuthenticationResult(data) {
const { MessageType, Status, AuthenticationId, ErrorDetail } = data;
if (MessageType === 'authentication.complete' && Status === 'SUCCESS') {
// Proceed with the second request
completePayment(AuthenticationId);
} else if (Status === 'EXPIRED') {
// Authentication expired
showError('Authentication has expired. Please try again.');
} else {
// FAILED or ERROR: show error to the user
showError(ErrorDetail || 'Authentication failed. Please try another payment method.');
}
}
PostMessage Structure
| Field | Type | Description |
|---|---|---|
MessageType | String | authentication.complete | authentication.failed |
Status | String | PENDING | SUCCESS | FAILED | ERROR | EXPIRED |
AuthenticationId | String | Unique authentication ID for the second request |
OrderId | String | Order ID |
OrderAmount | Number | Order amount |
ErrorTitle | String | Error title, when authentication was not successful |
ErrorDetail | String | Error detail, when authentication was not successful |
Possible Statuses:
| Status | Description | Action |
|---|---|---|
SUCCESS | ✅ Successful authentication | Proceed with the second request |
FAILED | ❌ Failed to authenticate with the bank | Request another payment method |
ERROR | ❌ Internal processor error | Retry or contact support |
EXPIRED | ⏱️ Authentication session expired | Restart the process |
PENDING | ⏳ Still processing | Continue waiting |
Step 5: Second Request
Once the message with MessageType = "authentication.complete" and Status = "SUCCESS" is received, make a new request including the authenticationId:
The response to this second request will have the status SUCCEEDED if the authentication was completed correctly.
Best Practices
✅ Do
- Always send
authenticationId: nullin the first request - Validate the origin of PostMessage messages against the origin of
authentication.url(it differs between Sandbox and Production) - Handle all possible statuses (SUCCESS, FAILED, ERROR, EXPIRED, PENDING)
- Provide clear messages to the user during the process
- Log events: For debugging and analysis
- Design a responsive UI: The iframe should adapt to different devices
❌ Don't
- ❌ Do not reuse
authenticationIdfrom previous transactions - ❌ Do not send
authenticationIdif you did not receiveAUTHENTICATION_REQUIRED