Create a charge with 3DS
Authenticate credit card payments with the issuing bank.
3D Secure (3DS) is an additional authentication protocol for credit card transactions. It verifies the cardholder's identity with the issuing bank before approving the transaction, adding a layer of security against fraud.
To trigger 3DS, send the threeDSecure and deviceInfo objects along with the card details when creating or paying a payment with a credit card.
ImportantBy the end of this guide, you will have sent a credit card payment authenticated via 3DS, redirected the payer to the issuing bank's challenge when necessary, and tracked the result via Webhook.
When to use
Use 3DS when your integration needs to:
- authenticate the cardholder with the issuing bank before approval;
- create a one-off or installment payment with a credit card;
- pay an already created payment or installment plan with a card.
Routes that support 3DS
| Need | Use |
|---|---|
| Create a payment and pay by card | POST /v3/payments |
| Create a payment and pay by card with a reduced response | POST /v3/lean/payments |
| Create an installment plan and pay by card | POST /v3/installments |
| Pay an already created payment by card | POST /v3/payments/{id}/payWithCreditCard |
| Pay an already created installment plan by card | POST /v3/payments/{id}/payWithCreditCard, providing the id of the first open installment |
There is no payment route at the installment plan level. To pay an already created installment plan, use the payments route with the id of an installment.
Before you start
Make sure that:
- 3DS is enabled in your production account. To enable it, contact our support team;
- the customer is registered and their ID is stored. See: Customer registration;
- your application receives payment Webhooks. See: Events for payments;
- your application has an HTTPS URL to receive the payer after the challenge (
callbackUrl).
Define:
- whether the payment will be created and paid in the same request or an already created payment will be paid later;
- whether the sale will be a one-off or installment payment;
- which page the payer will see when returning from the challenge;
- how your checkout will collect the payer's device data.
How it works
%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7}}}%%
flowchart TD
A["Collect device data at checkout"] --> B["Send the request with threeDSecure and deviceInfo"]
B --> C{"Did the response return threeDSecureChallengeUrl?"}
C --> CSim(("Yes"))
C --> CNao(("No"))
CSim --> D["Redirect the payer to the challenge"]
D --> E["Payer authenticates with the issuing bank"]
E --> F["Asaas redirects the payer to the callbackUrl"]
F --> G["Receive the Webhook with the result"]
CNao --> G
classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
class A inicio
class B validacao
class C decisao
class D,E,F correcao
class G sucesso
class CSim respostaSim
class CNao respostaNao
linkStyle default stroke:#94A3B8,stroke-width:2px
linkStyle 2 stroke:#22C55E,stroke-width:4px
linkStyle 3 stroke:#EF4444,stroke-width:4px
The issuing bank decides which type of authentication will be applied:
| Authentication type | What happens |
|---|---|
DATA_ONLY | Silent authentication, with no payer interaction. The transaction is evaluated based on the device data |
FRICTIONLESS | The issuing bank approves automatically, with no challenge visible to the payer |
ISSUER_CHALLENGE | The issuing bank requires an action from the payer, such as entering an OTP code or authenticating in the bank's app |
The request's response is not the final confirmation when there is a challenge. The authentication and capture result arrives via Webhook.
1. Collect device data
At checkout, collect from the payer's browser or app the information the issuing bank uses to assess the transaction risk.
Browser collection example
const deviceInfo = {
colorDepth: window.screen.colorDepth,
screenHeight: window.screen.height,
screenWidth: window.screen.width,
timeZoneOffset: new Date().getTimezoneOffset() / 60, // in hours: 180 minutes becomes 3
language: navigator.language,
userAgent: navigator.userAgent
};The remoteIp must be obtained by your backend, from the source IP of the payer's request.
AttentionThe
timeZoneOffsetfield must be sent in hours (e.g.,3for UTC-3). JavaScript'sgetTimezoneOffset()method returns the value in minutes (180for UTC-3), so divide the result by 60 before sending.
2. Send the request with threeDSecure and deviceInfo
threeDSecure and deviceInfoInclude the threeDSecure and deviceInfo objects in the request body, in addition to the standard payment or installment fields.
The example below pays an already created payment:
POST /v3/payments/{id}/payWithCreditCardSee the endpoint: Pay a charge with a credit card.
Request example
{
"creditCard": {
"holderName": "João da Silva",
"number": "4111111111111111",
"expiryMonth": "05",
"expiryYear": "2028",
"ccv": "123"
},
"creditCardHolderInfo": {
"name": "João da Silva",
"email": "[email protected]",
"cpfCnpj": "12345678901",
"postalCode": "01310-100",
"address": "Av. Paulista",
"addressNumber": "100",
"province": "Centro",
"phone": "11987654321"
},
"threeDSecure": {
"callbackUrl": "https://seusite.com.br/callback/3ds"
},
"deviceInfo": {
"colorDepth": 24,
"screenHeight": 900,
"screenWidth": 1440,
"timeZoneOffset": 3,
"language": "pt-BR",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"remoteIp": "200.100.50.25"
}
}The same objects are accepted in POST /v3/payments, POST /v3/lean/payments, and POST /v3/installments, along with the payment or installment creation fields.
Paying an already created installment planUse
See the endpoint: List payments of an installmentPOST /v3/payments/{id}/payWithCreditCardproviding theidof the first open installment. Installment IDs can be retrieved fromGET /v3/installments/{id}/payments.
3. Redirect the payer when there is a challenge
Check the threeDSecureChallengeUrl field in the response:
- filled in: the issuing bank requires a challenge. Redirect the payer to this URL;
null: authentication happened silently (DATA_ONLYorFRICTIONLESS) and no redirection is needed.
Response example with a pending challenge
{
"id": "pay_xxxxxxxxxxxxx",
"status": "PENDING",
"billingType": "CREDIT_CARD",
"value": 100.00,
"threeDSecureChallengeUrl": "https://banco-emissor.com.br/3ds/challenge?token=abc123"
}Response example without a challenge (frictionless or data-only)
{
"id": "pay_xxxxxxxxxxxxx",
"status": "CONFIRMED",
"billingType": "CREDIT_CARD",
"value": 100.00,
"threeDSecureChallengeUrl": null
}After the payer completes the challenge in the issuing bank's environment, Asaas redirects the payer to the provided callbackUrl.
4. Track the result via Webhook
At the end of the 3DS flow, Asaas sends a Webhook to the URL configured in your account with one of the events below. You can also query the payment status to confirm the result.
| Event | Handling |
|---|---|
PAYMENT_CONFIRMED | The challenge was completed and the card capture was approved. Consider the payment confirmed |
PAYMENT_CREDIT_CARD_CAPTURE_REFUSED | The challenge was completed, but the acquirer refused the capture. Do not consider the payment confirmed |
PAYMENT_CREDIT_CARD_THREE_D_SECURE_CHALLENGE_FAILED | The payer failed or abandoned the 3DS challenge. The payment is not captured |
Important fields
| Field | Purpose |
|---|---|
threeDSecure.callbackUrl | Required to trigger 3DS. URL the payer returns to after the challenge. Must be HTTPS in production, with up to 2000 characters |
deviceInfo.timeZoneOffset | Device time zone relative to UTC, in hours (e.g., 3 for UTC-3) |
deviceInfo.remoteIp | IP of the payer's device. Obtain it through your backend |
deviceInfo.userAgent | User-Agent of the payer's browser |
threeDSecureChallengeUrl | Returned in the response. Challenge URL the payer must be redirected to, or null when there is no challenge |
The deviceInfo fields are not required, but they are used by the issuing bank to assess the transaction risk. Send all the fields your integration is able to collect.
Common errors
Attention
timeZoneOffsetin minutes: sending the direct return ofgetTimezoneOffset()(e.g.,180) is incorrect. Divide by 60 and send it in hours.- Nonexistent installment route:
POST /v3/installments/{id}/payWithCreditCarddoes not exist and returns404. UsePOST /v3/payments/{id}/payWithCreditCardwith the installment'sid.- Sandbox testing: in sandbox, all transactions are automatically approved, even with the 3DS parameters. It is not yet possible to test the challenge flow in this environment.
- 3DS not enabled: for production use, contact our support team to enable 3DS.
Confirm the result
After the request, confirm that:
- the response returned the payment
id; - the payer was redirected when
threeDSecureChallengeUrlwas filled in; - the payer returned to the
callbackUrlafter the challenge; - your application received one of the 3DS flow Webhook events;
- the payment status matches the event received.
Best practices
- Collect the
deviceInfodata on the payer's device at checkout. - Send
timeZoneOffsetin hours. - Redirect the payer as soon as you receive
threeDSecureChallengeUrl. - Use the Webhook or a payment query as the final confirmation, not just the payer's return to the
callbackUrl. - Handle the three Webhook events of the 3DS flow.
- On the
callbackUrlpage, inform the payer that the payment is being processed until you receive the result.
API reference
ImportantSee the full reference for the endpoints that support 3DS to learn about all available fields, formats, and responses.
Next steps
Updated 2 days ago
