3D Secure (3DS)
Summary: This guide shows how to enable and operate 3D Secure (3DS) in Host‑to‑Host (H2H) mode, including Challenge vs. Frictionless, the
threeds_authentication
toggle, DataOnly notes, Device Data Collection (DDC) with the A55Pay SDK, example payloads, responses, and a secure redirect pattern.
🔎 Overview
Why 3DS? | When it triggers? | What you implement? |
---|---|---|
Liability shift & higher approval with issuers when authenticated. | Based on issuer/provider rules and risk; can be Challenge or Frictionless. | Send a charge request with the correct 3DS flags & device data, render url_3ds when returned, handle webhook & postMessage events. |
Security: Use HTTPS end‑to‑end, never log sensitive data, and validate cross‑window message origins.
🧭 High‑Level Flow (H2H)
sequenceDiagram autonumber participant P as Payer (Merchant Checkout) participant SDK as A55Pay SDK (DDC) participant M as Merchant Backend participant A as A55 API participant I as Issuer/ACS %% 1) Checkout start + DDC P->>SDK: A55Pay.authentication() SDK-->>P: Returns sessionId P->>M: Sends sessionId %% 2) Charge with device_info M->>A: POST /bank/wallet/charge (device_info.session_id=sessionId) %% 3) 3DS branching alt 3DS Challenge required A-->>M: status=pending + url_3ds M-->>P: Open modal/iframe or redirect to url_3ds P->>I: Completes challenge at url_3ds I-->>A: Challenge result A-->>M: Webhook with final status (confirmed/error) M-->>P: Show final result else No challenge A-->>M: Immediate status (confirmed/error) M-->>P: Show result end
⚙️ Enabling & Forcing 3DS
threeds_authentication: true
→ forces 3DS; transaction only proceeds if it is successfully authenticated.threeds_authentication: false
(or omitted) → do not force; the provider may still apply 3DS or DataOnly depending on risk.
Mandatory payload for 3DS: rich customer, address and device_info. Missing details reduce approval and may block 3DS.
🧾 Field Reference (3DS‑Relevant)
Field | Scope | Required for 3DS | Notes |
---|---|---|---|
payer_cell_phone | Customer | ✅ | Used for step‑ups/issuer contact. |
items[] | Order | ✅ | Name/description/quantity/amount required. |
payer_address{...} | Address | ✅ | Street/city/state/postal_code/country. |
device_info.ip_address | Device | ✅ | Public IP. |
device_info.user_agent | Device | ✅ | Browser UA string. |
device_info.http_* | Device | ✅ | Accept headers, language, etc. |
device_info.http_browser_* | Device | ✅ | Java, JS enabled, screen size, color depth, time diff. |
redirect_url | Flow | — | Where to send the customer after 3DS. |
webhook_url | Flow | ✅ | Receives all status updates. |
Tip: Collect device data with A55Pay SDK (DDC) to improve approval and reduce friction (see below).
📡 Example Requests
1) Do not force 3DS (provider may decide)
{
"wallet_uuid": "00000000-0000-0000-0000-000000000000",
"merchant_id": "11111111-1111-1111-1111-111111111111",
"payer_name": "John Doe",
"payer_email": "[email protected]",
"payer_cell_phone": "+5511999999999",
"items": [
{ "name": "Sample Product", "description": "Description", "quantity": 1, "total_amount": 100, "unit_amount": 100, "sku": "SAMPLE-SKU-123", "code": "SAMPLE-CODE-456" }
],
"payer_address": {
"street": "Sample Street", "address_number": "123", "complement": "Apt 101",
"neighborhood": "Sample Neighborhood", "city": "Sample City", "state": "SP",
"postal_code": "00000-000", "country": "BR"
},
"currency": "BRL",
"installment_value": 100,
"installment_count": 1,
"due_date": "2025-12-31",
"description": "Sample transaction",
"type_charge": "credit_card",
"card_name": "John Doe",
"card_number": "4111111111111111",
"card_expiry_month": "12",
"card_expiry_year": "2030",
"card_cvv": "123",
"threeds_authentication": false,
"device_info": {
"ip_address": "192.168.0.1",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)...",
"http_accept_content": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"http_browser_language": "en-US",
"http_browser_java_enabled": true,
"http_browser_javascript_enabled": true,
"http_browser_color_depth": "24",
"http_browser_screen_height": "1080",
"http_browser_screen_width": "1920",
"http_browser_time_difference": "-180",
"http_accept_browser_value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
},
"webhook_url": "https://webhook.example.com/status",
"redirect_url": "https://merchant.example.com/thank-you"
}
2) Force 3DS
{
"wallet_uuid": "00000000-0000-0000-0000-000000000000",
"merchant_id": "11111111-1111-1111-1111-111111111111",
"payer_name": "John Doe",
"payer_email": "[email protected]",
"payer_cell_phone": "+5511999999999",
"items": [
{ "name": "Sample Product", "description": "Description", "quantity": 1, "total_amount": 100, "unit_amount": 100, "sku": "SAMPLE-SKU-123", "code": "SAMPLE-CODE-456" }
],
"payer_address": { "street": "Sample Street", "address_number": "123", "complement": "Apt 101", "neighborhood": "Sample Neighborhood", "city": "Sample City", "state": "SP", "postal_code": "00000-000", "country": "BR" },
"currency": "BRL",
"installment_value": 100,
"installment_count": 1,
"due_date": "2025-12-31",
"description": "Sample transaction",
"type_charge": "credit_card",
"card_name": "John Doe",
"card_number": "4111111111111111",
"card_expiry_month": "12",
"card_expiry_year": "2030",
"card_cvv": "123",
"threeds_authentication": true,
"device_info": { /* same as above */ },
"webhook_url": "https://webhook.example.com/status",
"redirect_url": "https://merchant.example.com/thank-you"
}
3) External 3DS result (already authenticated upstream)
{
"wallet_uuid": "00000000-0000-0000-0000-000000000000",
"merchant_id": "11111111-1111-1111-1111-111111111111",
"payer_name": "John Doe",
"payer_email": "[email protected]",
"payer_cell_phone": "+155599999999",
"items": [ { "name": "Test Product", "description": "Sample", "quantity": 1, "total_amount": 100.00, "unit_amount": 100.00, "sku": "TEST-SKU-001", "code": "TEST-CODE-001" } ],
"payer_address": { "street": "Test Avenue", "address_number": "123", "complement": "Suite 456", "neighborhood": "Downtown", "city": "Testville", "state": "CA", "postal_code": "12345-678", "country": "US" },
"currency": "BRL",
"installment_value": 100.00,
"installment_count": 1,
"due_date": "2025-12-31",
"description": "External 3DS",
"type_charge": "credit_card",
"card_name": "John Doe",
"card_number": "4111111111111111",
"card_expiry_month": "12",
"card_expiry_year": "2030",
"card_cvv": "123",
"threeds_external": {
"eci": "05",
"request_id": "3ds-ext-req-987654321",
"xid": "dXNlci10ZXN0LXhpbGwtYWJjMTIz",
"cavv": "AAABBIIFmAAAAAAAAAAAAAAAAA=",
"version": "2.2.0"
},
"webhook_url": "https://webhook.example.com/payment-status",
"redirect_url": "https://merchant.example.com/thank-you"
}
📬 Responses
Challenge required
{
"status": "pending",
"url_3ds": "https://confirmation.a55.tech/charge/791b390a-f3ce-4c50-90a1-3b813b59b455"
}
No challenge
{ "status": "confirmed" }
Final status will be delivered to your
webhook_url
.
🧰 Post‑Challenge Redirect (Parent‑Window Pattern)
Listen in the parent window and redirect there (avoid window.top.location
from iFrames in cross‑origin contexts):
<script>
window.addEventListener('message', function (event) {
if (event.data && event.data.event === '3ds-auth-complete') {
const chargeUuid = event.data.chargeUuid;
// custom actions
}
// if send redirect_url
if (event.data && event.data.event === '3ds-redirect-request') {
window.location.replace(event.data.redirectUrl);
}
});
</script>
Render the
url_3ds
in a modal/iframe or perform a full‑page redirect. Prefer validatingevent.origin
and an allow‑list of hosts prior to navigation.
📲 Device Data Collection (DDC) — A55Pay SDK
Improve issuer risk signals by collecting device data.
Install
<script src="https://cdn.jsdelivr.net/npm/a55pay-sdk"></script>
How it works
sequenceDiagram participant C as Client/Checkout participant SDK as A55Pay SDK participant B as A55 Backend participant CS as CyberSource C->>SDK: A55Pay.authentication() SDK->>B: POST /setup-authentication B->>SDK: access_token + collection_url SDK->>CS: Device Data Collection CS-->>SDK: profile.completed SDK-->>C: sessionId C->>B: Send charge with device_info.session_id
Usage
A55Pay.authentication({
transactionReference: "transaction-uuid",
cardBrand: "Visa",
cardExpiryMonth: "08",
cardExpiryYear: "2029",
cardNumber: "4111111111111111",
onSuccess: ({ sessionId }) => {
// Include sessionId in your charge payload
},
onError: (err) => console.error('Authentication error:', err)
});
Charge payload snippet
const chargePayload = {
// ...
device_info: {
session_id: sessionId
}
// ...
};
✅ Best Practices
- Implement Device Data Collection (DDC) with the A55Pay SDK and include the returned
session_id
indevice_info
(improves issuer risk signals and reduces friction). - Prefer modal/iframe for 3DS and handle navigation in the parent via
postMessage
. - Provide complete device_info, address, items, and contact data.
- Monitor the webhook for final status transitions.
❓ FAQ
Is 3DS always a challenge? No; issuers may run frictionless based on risk.
What is DataOnly? A frictionless data pass‑through (Visa/Mastercard). It improves approval but does not provide liability shift; only one of 3DS or DataOnly can be enabled at a time per account.
Where do I get the final result? From your webhook; the url_3ds
page is only for authentication.
Updated 19 days ago