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.

📘

Important

By 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

NeedUse
Create a payment and pay by cardPOST /v3/payments
Create a payment and pay by card with a reduced responsePOST /v3/lean/payments
Create an installment plan and pay by cardPOST /v3/installments
Pay an already created payment by cardPOST /v3/payments/{id}/payWithCreditCard
Pay an already created installment plan by cardPOST /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 typeWhat happens
DATA_ONLYSilent authentication, with no payer interaction. The transaction is evaluated based on the device data
FRICTIONLESSThe issuing bank approves automatically, with no challenge visible to the payer
ISSUER_CHALLENGEThe 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.

⚠️

Attention

The timeZoneOffset field must be sent in hours (e.g., 3 for UTC-3). JavaScript's getTimezoneOffset() method returns the value in minutes (180 for UTC-3), so divide the result by 60 before sending.

2. Send the request with threeDSecure and deviceInfo

Include 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}/payWithCreditCard

See 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 plan

Use POST /v3/payments/{id}/payWithCreditCard providing the id of the first open installment. Installment IDs can be retrieved from GET /v3/installments/{id}/payments.

See the endpoint: List payments of an installment

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_ONLY or FRICTIONLESS) 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.

EventHandling
PAYMENT_CONFIRMEDThe challenge was completed and the card capture was approved. Consider the payment confirmed
PAYMENT_CREDIT_CARD_CAPTURE_REFUSEDThe challenge was completed, but the acquirer refused the capture. Do not consider the payment confirmed
PAYMENT_CREDIT_CARD_THREE_D_SECURE_CHALLENGE_FAILEDThe payer failed or abandoned the 3DS challenge. The payment is not captured

See: Events for payments.

Important fields

FieldPurpose
threeDSecure.callbackUrlRequired to trigger 3DS. URL the payer returns to after the challenge. Must be HTTPS in production, with up to 2000 characters
deviceInfo.timeZoneOffsetDevice time zone relative to UTC, in hours (e.g., 3 for UTC-3)
deviceInfo.remoteIpIP of the payer's device. Obtain it through your backend
deviceInfo.userAgentUser-Agent of the payer's browser
threeDSecureChallengeUrlReturned 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

  • timeZoneOffset in minutes: sending the direct return of getTimezoneOffset() (e.g., 180) is incorrect. Divide by 60 and send it in hours.
  • Nonexistent installment route: POST /v3/installments/{id}/payWithCreditCard does not exist and returns 404. Use POST /v3/payments/{id}/payWithCreditCard with the installment's id.
  • 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 threeDSecureChallengeUrl was filled in;
  • the payer returned to the callbackUrl after the challenge;
  • your application received one of the 3DS flow Webhook events;
  • the payment status matches the event received.

Best practices

  • Collect the deviceInfo data on the payer's device at checkout.
  • Send timeZoneOffset in 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 callbackUrl page, inform the payer that the payment is being processed until you receive the result.

API reference

Next steps


Did this page help you?