Inicio
DocumentaciĂłn
Recursos
Partners
Comunidad

Recursos

Revisa las actualizaciones de nuestras soluciones y operatividad del sistema o pide soporte técnico.

Partners

Conoce nuestro programa para agencias o desarrolladores que ofrecen servicios de integraciĂłn y vendedores que quieren contratarlos.

Comunidad

Recibe las Ășltimas novedades, pide ayuda a otros integradores y comparte tus conocimientos.

CĂłmo integrar 3DS con Checkout Transparente - Checkout Transparente - Mercado Pago Developers

BĂșsqueda inteligente powered by OpenAI 

CĂłmo integrar 3DS con Checkout Transparente

En esta documentaciĂłn encontrarĂĄs toda la informaciĂłn necesaria para realizar la integraciĂłn con 3DS con Checkout Transparente. Para obtener mĂĄs informaciĂłn sobre cĂłmo funciona este tipo de autenticaciĂłn, consulte 3DS 2.0.

Importante
Para integrarse con 3DS, se deben cumplir ciertos requisitos. Antes de continuar con los siguientes pasos, revise la secciĂłn Requisitos previos y asegĂșrese de que se cumplan todos.

Integrar con 3DS

La autenticación 3DS se puede realizar a través de dos flujos distintos: con o sin Challenge, que son pasos adicionales que el comprador debe completar para garantizar su identidad. La decisión de incluir o no el Challenge depende del emisor de la tarjeta y del perfil de riesgo de la transacción que se realiza.

Obtenga también información sobre las integraciones a través de Checkout Bricks, un método de pago modular, seguro y personalizable que automatiza varios de los procesos que se describen a continuación.

Para transacciones de bajo riesgo, la informaciĂłn enviada en el momento del pago es suficiente y los pasos adicionales de Challenge no son necesarios. Sin embargo, para casos donde existe un alto riesgo de fraude, Challenge es requerido para verificar la identidad del comprador, lo que aumenta la conversiĂłn de las transacciones con tarjeta.

A continuaciĂłn se presentan los pasos para realizar una integraciĂłn con 3DS.

  1. Debes usar el SDK JS de Mercado Pago en el checkout para generar el token de la tarjeta de crédito.
  2. Después, postea los datos del checkout junto con el token de la tarjeta para su backend.
  3. AllĂ­, haz una llamada para crear un nuevo pago con los datos recibidos. Es necesario que sea enviado el atributo three_d_secure_mode con uno de los siguientes valores:
    1. not_supported: no se debe usar 3DS (es el valor por default).
    2. optional: se puede requerir 3DS o no, dependiendo del perfil de riesgo de la transacciĂłn.
Importante
Recomendamos usar el valor optional en la implementaciĂłn del 3DS, ya que equilibra la seguridad y la aprobaciĂłn de transacciones.

AdemĂĄs, la captura del pago debe ser automĂĄtica (capture=true) y la transacciĂłn debe crearse con el modo binario desactivado (binary mode=false), ya que podrĂ­a quedar pendiente esperando que el comprador complete el Challenge.
          
<?php
  use MercadoPago\Client\Payment\PaymentClient;
  MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");

  $client = new PaymentClient();
  $request_options = new RequestOptions();
  $request_options->setCustomHeaders(["X-Idempotency-Key: <SOME_UNIQUE_VALUE>"]);

  $payment = $client->create([
    "transaction_amount" => <TRANSACTION_AMOUNT>,
    "token" => "CARD_TOKEN",
    "description" => "<DESCRIPTION>",
    "installments" => <INSTALLMENTS_NUMBER>,
    "payment_method_id" => "<PAYMENT_METHOD_ID>",
    "issuer_id" => "<ISSUER_ID>",
    "payer" => [
      "email" => $_POST['email']
    ],
    "three_d_secure_mode" => "optional"
  ], $request_options);
  echo implode($payment);
?>

        
          
MercadoPagoConfig.setAccessToken("<ENV_ACCESS_TOKEN>");
    PaymentClient client = new PaymentClient();
    PaymentCreateRequest createRequest =
        PaymentCreateRequest.builder()
            .transactionAmount(new BigDecimal(<TRANSACTION_AMOUNT>))
            .token("<CARD_TOKEN>")
            .description("<DESCRIPTION>")
            .installments(<INSTALLLMENTS_NUMBER>)
            .paymentMethodId("<PAYMENT_METHOD_ID>")
            .payer(
               PaymentPayerRequest.builder()
                 .email("<BUYER_EMAIL>")
                 .build()
            )
            .threeDSecureMode("optional")
            .build();
    client.create(createRequest);

        
          
using MercadoPago.Config;
using MercadoPago.Client.Payment;
using MercadoPago.Resource.Payment;
MercadoPagoConfig.AccessToken = "<ENV_ACCESS_TOKEN>";
var request = new PaymentCreateRequest
{
    TransactionAmount = <TRANSACTION_AMOUNT>,
    Token = "<CARD_TOKEN>",
    Description = "<DESCRIPTION>",
    Installments = <INSTALLLMENTS_NUMBER>,
    Payer = new PaymentPayerRequest
    {
        Email = "<BUYER_EMAIL>",
    },
    ThreeDSecureMode = "optional",
};
var client = new PaymentClient();
Payment payment = await client.CreateAsync(request);

        
          
import { MercadoPagoConfig, Payment } from 'mercadopago';

const client = new MercadoPagoConfig({ accessToken: '<ENV_ACCESS_TOKEN>' });
const payment = new Payment(client);

const body = {
  transaction_amount: <TRANSACTION_AMOUNT>,
  token: '<CARD_TOKEN>',
  description:  '<DESCRIPTION>',
  installments: <INSTALLMENTS_NUMBER>,
  payment_method_id: '<PAYMENT_METHOD_ID>',
  issuer_id: '<ISSUER_ID>',
  payer: {
    email: '<BUYER_EMAIL>',
  },
  three_d_secure_mode: 'optional'
}
payment.create({ body: body, requestOptions: { idempotencyKey: '<SOME_UNIQUE_VALUE>' } }).then(console.log).catch(console.log);

        
          
require 'mercadopago'
sdk = Mercadopago::SDK.new('<ENV_ACCESS_TOKEN>')
payment_request = {
  token: '<CARD_TOKEN>',
  installments: <INSTALLLMENTS_NUMBER>,
  transaction_amount: <TRANSACTION_AMOUNT>,
  description: '<DESCRIPTION>',
  payer: {
    email: '<BUYER_EMAIL>',
  },
  three_d_secure_mode: 'optional'
}
payment_response = sdk.payment.create(payment_request)
payment = payment_response[:response]

        
          
import mercadopago
sdk = mercadopago.SDK("<ENV_ACCESS_TOKEN>")
payment_data = {
    "transaction_amount": <TRANSACTION_AMOUNT>,
    "token": "<CARD_TOKEN>",
    "description": "<DESCRIPTION>",
    "installments": <INSTALLLMENTS_NUMBER>,
    "payer": {
        "email": "<BUYER_EMAIL>",
    },
    "three_d_secure_mode": "optional"
}
payment_response = sdk.payment().create(payment_data)
payment = payment_response["response"]

        
          
package main

import (
	"context"
	"fmt"

	"github.com/mercadopago/sdk-go/pkg/config"
	"github.com/mercadopago/sdk-go/pkg/payment"
)

func main() {
	accessToken := "<ENV_ACCESS_TOKEN>"

	cfg, err := config.New(accessToken)
	if err != nil {
		fmt.Println(err)
		return
	}

	client := payment.NewClient(cfg)

	request := payment.Request{
		TransactionAmount:<TRANSACTION_AMOUNT>,
		Payer: &payment.PayerRequest{
			Email: "<BUYER_EMAIL>",
		},
		Token:        "<CARD_TOKEN>",
		Installments: <INSTALLLMENTS_NUMBER>,
		Description: "<DESCRIPTION>",
		ThreeDSecureMode: "optional",
	}

	resource, err := client.Create(context.Background(), request)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(resource)
}

        
          
curl --location --request POST 'https://api.mercadopago.com/v1/payments' \
--header 'Authorization: <ENV_ACCESS_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "payer": {
        "email": "<BUYER_EMAIL>"
    },
    "additional_info": {
        "items": [
            {
                "quantity": <ITEM_QUANTITY>,
                "category_id": <CATEGORY_ID>,
                "title": <ITEM_TITLE>,
                "unit_price": <TRANSACTION_AMOUNT>
            }
        ]
    },
    "payment_method_id": <PAYMENT_METHOD_ID>,
    "marketplace": "NONE",
    "installments": <INSTALLLMENTS_NUMBER>,
    "transaction_amount": <TRANSACTION_AMOUNT>,
    "description": "<DESCRIPTION>",
    "token": "CARD_TOKEN",
    "three_d_secure_mode": "optional",
    "capture": true,
    "binary_mode": false
}'

        

En caso de que no sea necesario utilizar el flujo de Challenge, el campo del status del pago tendrĂĄ valor approved y no serĂĄ necesario mostrarlo, por lo que puedes seguir con el flujo de tu aplicaciĂłn.

Para casos en que el Challenge es necesario, el status mostrarĂĄ el valor pending, y el status_detail serĂĄ pending_challenge.

Importante
En este Ășltimo caso, la respuesta mostrarĂĄ un atributo del pago llamado three_ds_info con los campos external_resource_url, que contiene la URL del Challenge, y creq, un identificador del Challenge request. SerĂĄ necesario mostrar el Challenge y tratar su resultado con los pasos siguientes.

Overview del response (se omitiĂł informaciĂłn)

Cuando se inicia el Challenge, el usuario tiene aproximadamente 5 minutos para completarlo. Si no se completa, el banco rechazarĂĄ la transacciĂłn y Mercado Pago considerarĂĄ el pago cancelado. Mientras el usuario no complete el Challenge, el estado del pago permanecerĂĄ como pending_challenge.

          
{
    "id": 52044997115,
    ...
    "status": "pending",
    "status_detail": "pending_challenge",
    ...
    "three_ds_info":
    {
        "external_resource_url": "https://acs-public.tp.mastercard.com/api/v1/browser_Challenges",
        "creq": "eyJ0aHJlZURTU2VydmVyVHJhbnNJRCI6ImJmYTVhZjI0LTliMzAtNGY1Yi05MzQwLWJkZTc1ZjExMGM1MCIsImFjc1RyYW5zSUQiOiI3MDAwYTI2YS1jYWQ1LTQ2NjQtOTM0OC01YmRlZjUwM2JlOWYiLCJjaGFsbGVuZ2VXaW5kb3dTaXplIjoiMDQiLCJtZXNzYWdlVHlwZSI6IkNSZXEiLCJtZXNzYWdlVmVyc2lvbiI6IjIuMS4wIn0"
    },
    "owner": null
}


        
  1. Para una mejor visualizaciĂłn del Challenge del 3DS de forma responsiva, debes agregar el CSS que se muestra a continuaciĂłn.

css

  #myframe{
    width: 500px;
    height: 600px;
    border: none;
  }
  @media only screen and (width <= 980px) {
    #myframe{
      width: 100%;
      height: 440px;
    }
  }
  1. Para mostrar el Challenge, es necesario que generes un iframe que contenga un formulario con method post, action que contenga la URL obtenida en el campo external_resource_url, y un input oculto con el valor obtenido en creq. Después, debes hacer el post del form a continuación para empezar el challenge.
          
function doChallenge(payment) {
  try {
    const {
      status,
      status_detail,
      _three_ds_info: { creq, external_resource_url },
    } = payment;
    if (status === "pending" && status_detail === "pending_challenge") {
      var iframe = document.createElement("iframe");
      iframe.name = "myframe";
      iframe.id = "myframe";
      document.body.appendChild(iframe);

      var idocument = iframe.contentWindow.document;

      var myform = idocument.createElement("form");
      myform.name = "myform";
      myform.setAttribute("target", "myframe");
      myform.setAttribute("method", "post");
      myform.setAttribute("action", external_resource_url);

      var hiddenField = idocument.createElement("input");
      hiddenField.setAttribute("type", "hidden");
      hiddenField.setAttribute("name", "creq");
      hiddenField.setAttribute("value", creq);
      myform.appendChild(hiddenField);
      iframe.appendChild(myform);

      myform.submit();
    }
  } catch (error) {
    console.log(error);
    alert("Error doing challenge, try again later.");
  }
}


        

Cuando el Challenge es finalizado, el status del pago serĂĄ actualizado. SerĂĄ approved si la autenticaciĂłn fue exitosa, rejected si no lo fue y, en caso de que la autenticaciĂłn no fuera hecha, el pago permanecerĂĄ pending. Esta actualizaciĂłn no es inmediata, puede tardar unos instantes.

Mira la secciĂłn a continuaciĂłn para mĂĄs detalles sobre cĂłmo consultar el status de cada transacciĂłn.

Consultar status de la transacciĂłn

Para saber cuĂĄl es el resultado de la transacciĂłn, hay tres opciones:

  • Notificaciones: RecibirĂĄs la notificaciĂłn del cambio del status del pago usando Webhooks y deberĂĄs redireccionar el buyer para una pantalla que indica que la transacciĂłn fue exitosa. Consulta la secciĂłn de Webhooks y aprende cĂłmo configurarlos.
  • API de Payments: DeberĂĄs hacer un pooling en Payments y, si el status cambia, redireccionar el buyer para una pantalla de confirmaciĂłn.
  • Tratar el evento del iframe (recomendado): debes recordar que el evento solo indica que finalizĂł el Challenge y no que el pago pasĂł a un status final, dado que la actualizaciĂłn no es inmediata y puede tardar unos instantes. DeberĂĄs hacer una consulta en Payments y, si el status cambia, redireccionar al buyer para una pantalla que indica que la transacciĂłn fue exitosa.

Para tratar el evento del iframe, sigue los pasos a continuaciĂłn.

Realizar implementaciĂłn

Utilice el cĂłdigo JavaScript a continuaciĂłn para implementar y escuchar el evento que indica que el Challenge ha finalizado, de esta manera es posible redirigir al cliente a la pantalla de confirmaciĂłn.

          
window.addEventListener("message", (e) => {
     if (e.data.status === "COMPLETE") {
         window.open("congrats.html");
     }
});


        

Buscar status del pago

El siguiente Javascript indica cĂłmo se puede realizar la bĂșsqueda del status de pago actualizado y mostrarlo en la pantalla de confirmaciĂłn.

          
document.addEventListener("DOMContentLoaded", async function (e) {
 init();
});

async function init() {
 const id = localStorage.getItem("paymentId");

 try {
   const response = await fetch("/get_payment/" + id, {
     method: "GET",
   });
   const result = await response.json();
   if (result.status != 200) throw new Error("error getting payment");
   document.getElementById("congrats-div").innerHTML =
     "Pagamento " + result.data.id + " -> Status: " + result.data.status;
 } catch (error) {
   alert("Unexpected error\n" + JSON.stringify(error));
 }
}


        
Importante
Si el pago continĂșa pending despuĂ©s del timeout del Challenge, entonces deberĂĄs redireccionar al buyer para una pantalla que informe que el pago expirĂł y que es necesario crear uno nuevo (la actualizaciĂłn no es inmediata, puede tardar unos instantes).

Después de seguir estos pasos, tu integración estå lista para autenticar transacciones con 3DS.

Posibles status del pago

Una transacciĂłn con 3DS puede devolver diferentes status segĂșn el tipo de autenticaciĂłn realizada (con o sin Challenge). En un pago sin Challenge, el status de la transacciĂłn serĂĄ directamente approved o rejected.

En un pago con Challenge, la transacción estarå en status pending y se iniciarå el proceso de autenticación con el banco. Solo después de esta etapa se mostrarå el status final.

A continuaciĂłn se muestra una tabla con los posibles status y sus descripciones correspondientes.

StatusStatus_detailDescripciĂłn
"approved""accredited"TransacciĂłn aprobada sin autenticaciĂłn.
"rejected"-TransacciĂłn denegada sin autenticaciĂłn. Para verificar los motivos, consulta la lista estĂĄndar de status detail.
"pending""pending_challenge"TransacciĂłn pendiente de autenticaciĂłn o timeout del Challenge.
"rejected""cc_rejected_3ds_challenge"TransacciĂłn denegada debido a falla en el Challenge.
"cancelled""expired"Transacción con Challenge cancelada después de 24 horas en estado pendiente.

Prueba de integraciĂłn

Para facilitar la validaciĂłn de pagos con 3DS, hemos creado un entorno de pruebas tipo sandbox. Este entorno devuelve resultados ficticios que sĂłlo se utilizan para simular y validar la implementaciĂłn.

Importante
Para probar la integraciĂłn, es necesario utilizar sus credenciales de prueba. AsegĂșrese tambiĂ©n de incluir el atributo three_d_secure_mode, definiĂ©ndolo como optional para garantizar la correcta implementaciĂłn del pago 3DS.

Para probar pagos en un entorno sandbox, se deben usar tarjetas especĂ­ficas que permitan probar la implementaciĂłn del desafĂ­o con flujos de Ă©xito y fallo, segĂșn la tabla a continuaciĂłn:

TarjetaFlujoNĂșmeroCĂłdigo de SeguridadFecha de Vencimiento
MastercardChallenge exitoso5483 9281 6457 462312311/25
MastercardChallenge no autorizado5361 9568 0611 755712311/25

Los pasos para crear el pago son los mismos. En caso de duda sobre cĂłmo crear pagos con tarjeta, consulta la documentaciĂłn sobre Tarjetas.

          
<?php
  use MercadoPago\Client\Payment\PaymentClient;
  use MercadoPago\MercadoPagoConfig;
  MercadoPagoConfig::setAccessToken("YOUR_ACCESS_TOKEN");

  $client = new PaymentClient();
  $request_options = new RequestOptions();
  $request_options->setCustomHeaders(["X-Idempotency-Key: <SOME_UNIQUE_VALUE>"]);

  $payment = $client->create([
    "transaction_amount" => (float) $_POST['transactionAmount'],
    "token" => $_POST['token'],
    "description" => $_POST['description'],
    "installments" => $_POST['installments'],
    "payment_method_id" => $_POST['paymentMethodId'],
    "issuer_id" => $_POST['issuer'],
    "payer" => [
      "email" => $_POST['email'],
      "identification" => [
        "type" => $_POST['identificationType'],
        "number" => $_POST['number']
      ]
    ],
    "three_d_secure_mode" => "optional"
  ], $request_options);
  echo implode($payment);
?>

        
          
import { MercadoPagoConfig, Payment } from 'mercadopago';

const client = new MercadoPagoConfig({ accessToken: 'YOUR_ACCESS_TOKEN' });
const payment = new Payment(client);

const body = {
transaction_amount: req.transaction_amount,
  token: req.token,
  description: req.description,
  installments: req.installments,
  payment_method_id: req.paymentMethodId,
  issuer_id: req.issuer,
  payer: {
    email: req.email,
    identification: {
      type: req.identificationType,
      number: req.number
    }
  },
  three_d_secure_mode: 'optional' 
};

payment.create({ body: body, requestOptions: { idempotencyKey: '<SOME_UNIQUE_VALUE>' } }).then(console.log).catch(console.log);

        
          
PaymentClient client = new PaymentClient();

PaymentCreateRequest paymentCreateRequest =
   PaymentCreateRequest.builder()
       .transactionAmount(request.getTransactionAmount())
       .token(request.getToken())
       .description(request.getDescription())
       .installments(request.getInstallments())
       .paymentMethodId(request.getPaymentMethodId())
       .payer(
           PaymentPayerRequest.builder()
               .email(request.getPayer().getEmail())
               .firstName(request.getPayer().getFirstName())
               .identification(
                   IdentificationRequest.builder()
                       .type(request.getPayer().getIdentification().getType())
                       .number(request.getPayer().getIdentification().getNumber())
                       .build())
               .build())
       .threeDSecureMode("optional")
       .build();

client.create(paymentCreateRequest);

        
          
require 'mercadopago'
sdk = Mercadopago::SDK.new('YOUR_ACCESS_TOKEN')
 
payment_data = {
 transaction_amount: params[:transactionAmount].to_f,
 token: params[:token],
 description: params[:description],
 installments: params[:installments].to_i,
 payment_method_id: params[:paymentMethodId],
 payer: {
   email: params[:email],
   identification: {
     type: params[:identificationType],
     number: params[:identificationNumber]
   }
 three_d_secure_mode: "optional",
 }
}
 
payment_response = sdk.payment.create(payment_data)
payment = payment_response[:response]
 
puts payment

        
          
using System;
using MercadoPago.Client.Common;
using MercadoPago.Client.Payment;
using MercadoPago.Config;
using MercadoPago.Resource.Payment;
 
MercadoPagoConfig.AccessToken = "YOUR_ACCESS_TOKEN";
 
var paymentRequest = new PaymentCreateRequest
{
   TransactionAmount = decimal.Parse(Request["transactionAmount"]),
   Token = Request["token"],
   Description = Request["description"],
   Installments = int.Parse(Request["installments"]),
   PaymentMethodId = Request["paymentMethodId"],
   Payer = new PaymentPayerRequest
   {
       Email = Request["email"],
       Identification = new IdentificationRequest
       {
           Type = Request["identificationType"],
           Number = Request["identificationNumber"],
       },
   },
ThreeDSecureMode = "optional",
};
 
var client = new PaymentClient();
Payment payment = await client.CreateAsync(paymentRequest);
 
Console.WriteLine(payment.Status);

        
          
import mercadopago
sdk = mercadopago.SDK("ACCESS_TOKEN")
 
payment_data = {
   "transaction_amount": float(request.POST.get("transaction_amount")),
   "token": request.POST.get("token"),
   "description": request.POST.get("description"),
   "installments": int(request.POST.get("installments")),
   "payment_method_id": request.POST.get("payment_method_id"),
   "payer": {
       "email": request.POST.get("email"),
       "identification": {
           "type": request.POST.get("type"), 
           "number": request.POST.get("number")
       }
   }
   "three_d_secure_mode": "optional"
}
 
payment_response = sdk.payment().create(payment_data)
payment = payment_response["response"]
 
print(payment)

        
          
package main

import (
	"context"
	"fmt"

	"github.com/mercadopago/sdk-go/pkg/config"
	"github.com/mercadopago/sdk-go/pkg/payment"
)

func processPayment(r *http.Request) {
	accessToken := "{{ACCESS_TOKEN}}"

	cfg, err := config.New(accessToken)
	if err != nil {
		fmt.Println(err)
		return
	}

	client := payment.NewClient(cfg)

	request := payment.Request{
		TransactionAmount: r.FormValue("transactionAmount"),
		Token: r.FormValue("token"),
            Description: r.FormValue("description"),
		PaymentMethodID:   r.FormValue("paymentMethodId"),
		Payer: &payment.PayerRequest{
			Email: r.FormValue("email"),
			Identification: &payment.IdentificationRequest{
				Type: r.FormValue("type"),
				Number: r.FormValue("number"),
			},
		},
	}

	resource, err := client.Create(context.Background(), request)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(resource)
}

        
          
curl -X POST \
   -H 'accept: application/json' \
   -H 'content-type: application/json' \
   -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
   'https://api.mercadopago.com/v1/payments' \
   -d '{
         "transaction_amount": 100,
         "token": "CARD_TOKEN",
         "description": "Blue shirt",
         "installments": 1,
         "payment_method_id": "master",
         "issuer_id": 310,
         "payer": {
           "email": "PAYER_EMAIL"
         },
         "three_d_secure_mode": "optional"
   }'

        

Challenge

En ambos flujos (Ă©xito y falla), el Challenge, que es una pantalla similar a la presentada a continuaciĂłn, se mostrarĂĄ dentro del iframe:

Challenge

El cĂłdigo de verificaciĂłn proporcionado es meramente ilustrativo. Para completar el flujo de prueba, simplemente haz clic en el botĂłn Confirmar. Una vez que hayas completado esta acciĂłn, sigue las instrucciones detalladas en la secciĂłn Consultar status de la transacciĂłn para determinar cuĂĄndo se ha finalizado el Challenge y cĂłmo verificar la actualizaciĂłn del pago.