Checkout por API


Overview

Este es el segundo paso de la modalidad api: procesar el pago con los datos que capturaste en tu propio checkout.

Antes tienes que haber generado el pago con checkout_type: "api", que te devuelve un payment_id y un token. Ese par autentica esta llamada.

1. POST /payment/generate-payment   →  { payment_id, token }
2. POST /payment/transaction-checkout/{medio}   ←  estás aquí

Hay un endpoint por medio de pago:

Medio Endpoint
Tarjeta POST /api/v1/payment/transaction-checkout/card
PSE POST /api/v1/payment/transaction-checkout/pse
Bre-B POST /api/v1/payment/transaction-checkout/bre-b
Efectivo POST /api/v1/payment/transaction-checkout/cash

{info} POST /api/v1/payment/transaction-checkout (sin sufijo) es un alias de /card y se mantiene por compatibilidad. En integraciones nuevas usa /card, que dice qué hace.

Si necesitas probar el alias tal cual, recibe exactamente lo mismo que /card:

{danger} Esta modalidad recibe el número de tarjeta y el CVV en tu servidor: te aplica PCI DSS. Si no estás certificado, usa checkout_type: redirect, que hace lo mismo sin que el dato sensible pase por tu sistema.

Todos los endpoints comparten el bloque payment, un bloque customer_payer y, cuando tu cobro lo pide, la dirección de envío. Después cada medio agrega el suyo.

{warning} customer_payer no es igual en todos. Solo el pago con tarjeta exige los datos completos (dirección, ciudad, departamento, país, código postal y teléfono). PSE, Bre-B y efectivo solo piden name y email; si envías el resto, se ignora.

Un intento por cobro

Cada payment_id admite un solo intento de transacción. El body de la petición no cambia; lo que cambia es que no puedes volver a cobrar el mismo payment_id después de esa primera transacción.

El payment_id y el token que recibiste en generate-payment sirven para una transacción. Aplica a tarjeta, PSE, efectivo y Bre-B. El estado de esa transacción no abre un segundo intento.

Si el pago es rechazado y quieres reintentar, genera un cobro nuevo con /api/v1/payment/generate-payment y usa el nuevo payment_id + token.

HTTP Cuándo Qué hacer
403 El cobro ya tiene una transacción (cualquier estado) Crear un cobro nuevo. No reuses el mismo payment_id
422 Validación: el body está incompleto o inválido Corregir el body y repetir el mismo cobro
429 Hay otra petición en curso para este cobro Esperar y repetir el mismo cobro

Ejemplo de respuesta cuando el cobro ya se usó:

{
    "message": "Este cobro ya tiene una transacción y no permite reintentos"
}

{warning} El flujo 3DS (/api/v1/payment/3ds/enroll/{transaction_id} y /api/v1/payment/3ds/auth-continue/{transaction_id}) continúa la transacción ya creada. Eso no es un reintento y sigue permitido.

{info} Consultar el estado o recibir el webhook no crea otra transacción. Siguen funcionando igual.

Parámetros del pago

Nombre del campo Descripción Reglas
payment Objeto con las credenciales del pago generado en modalidad api ['required']
payment.id El payment_id que devolvió generar el pago ['required', 'string']
payment.token El token que devolvió generar el pago. Solo se muestra una vez ['required', 'string']

Parámetros del cliente

Nombre del campo Descripción Reglas
customer_payer Datos de quien paga ['required']
customer_payer.name Nombre de quien paga. En efectivo el mínimo baja a 2 caracteres ['required', 'string', 'min:5', 'max:255']
customer_payer.email Correo de quien paga. Solo se aceptan caracteres alfanuméricos ['required', 'email']

Los siguientes solo aplican al pago con tarjeta:

Nombre del campo Descripción Reglas
customer_payer.address_1 Dirección principal ['required', 'string', 'min:5', 'max:100']
customer_payer.address_2 Dirección secundaria ['required', 'string', 'min:1', 'max:100']
customer_payer.city Ciudad ['required', 'string', 'min:1', 'max:100']
customer_payer.state Departamento o estado ['required', 'string', 'min:1', 'max:100']
customer_payer.zip_code Código postal ['required', 'numeric', 'digits_between:1,10']
customer_payer.country País en ISO3. Ver lista de países ['required', 'string', 'in:COL,USA,MEX,...']
customer_payer.identification_type Tipo de documento. Ver enumeraciones ['nullable', 'in:CC,CE,TI,PPT,DNI,NIT,Pasaporte,Otro']
customer_payer.id_number Número de documento. Con NIT debe llevar dígito de verificación (900123456-7). No puede ser un número de tarjeta ['nullable', 'digits_between:5,15']
customer_payer.dialling_code Indicativo telefónico con +. Obligatorio salvo que envíes payment_card.cellphone ['required', 'regex:/^\+\d{1,3}$/i']
customer_payer.cellphone Celular, solo dígitos. Obligatorio salvo que envíes payment_card.cellphone ['required', 'numeric', 'digits_between:5,15']

{info} Tu plantilla de checkout manda. Si configuraste una plantilla que oculta la dirección, la ciudad, el departamento, el país, el indicativo o el celular, esos campos dejan de ser obligatorios. Sin plantilla, todos los marcados como required lo son.

Dirección de envío

Si el pago generado se configuró con "advance_options" y existe request_address_delivery se requerirá la siguiente información, de lo contrario no necesita enviarse.

Nombre del campo Descripción Reglas
delivery_address Objeto con la dirección de envío ['required']
delivery_address.address Dirección de entrega ['required_with:delivery_address', 'string', 'min:5', 'max:100']
delivery_address.department_id Id de la lista de departamentos ['required_with:delivery_address', 'exists:departments,id']
delivery_address.city_id Id de la lista de ciudades ['required_with:delivery_address', 'exists:cities,id']
delivery_address.observations Indicaciones para la entrega ['nullable']

Pago con tarjeta

Parámetros de la tarjeta

Adicional a los parámetros anteriores se deben agregar los siguientes:

Puedes pagar de dos formas, y son excluyentes: o envías los datos de la tarjeta, o envías un token de una tarjeta ya guardada. Si mandas token junto con number, name, expiration_date o cvv, la petición se rechaza.

Nombre del campo Descripción Reglas
payment_card Objeto con los datos de la tarjeta ['required']
payment_card.token Token de una tarjeta guardada con el tokenizador. Si lo envías, no envíes ningún otro dato de la tarjeta ['sometimes', 'missing_with:payment_card.number,payment_card.name,payment_card.expiration_date,payment_card.cvv']
payment_card.number Número de la tarjeta, sin espacios. La franquicia debe estar habilitada en tu comercio ['required', 'missing_with:payment_card.token', 'numeric', 'digits_between:14,16']
payment_card.name Nombre impreso en la tarjeta. Solo letras y espacios ['required', 'missing_with:payment_card.token', 'string']
payment_card.expiration_date Vencimiento en formato YYYY-MM, con mes entre 01 y 12. No puede estar vencida ['required', 'missing_with:payment_card.token', 'date_format:Y-m', 'after_or_equal:<mes actual>']
payment_card.cvv Código de seguridad de 3 o 4 dígitos ['required', 'missing_with:payment_card.token', 'regex:/^\d{3,4}$/i']
payment_card.installments Número de cuotas ['required', 'integer', 'between:1,60']
payment_card.identification_type Tipo de documento del tarjetahabiente. Ver enumeraciones ['nullable', 'in:CC,CE,TI,PPT,DNI,NIT,Pasaporte,Otro']
payment_card.id_number Número de documento del tarjetahabiente. No puede ser un número de tarjeta ['nullable', 'numeric', 'digits_between:5,15']
payment_card.dialling_code Indicativo telefónico con +. Obligatorio salvo que envíes customer_payer.cellphone ['required', 'regex:/^\+\d{1,3}$/i']
payment_card.cellphone Celular, solo dígitos. Obligatorio salvo que envíes customer_payer.cellphone ['required', 'numeric', 'digits_between:5,15']
payment_card.redirect_url A dónde vuelve el cliente tras el iframe de 3DS. Debe ser una URL que responda ['nullable', 'url', 'active_url', 'max:500']

{info} Los campos marcados required dejan de serlo si tu plantilla de checkout los oculta, y también si envías payment_card.token: en ese caso la tarjeta ya está guardada.

Información del navegador

Puedes enviar datos del navegador de tu cliente. Normalmente son opcionales y sirven para el antifraude, pero si activas 3D Secure con enable_3ds: true siete de ellos pasan a ser obligatorios, porque la red los exige para autenticar.

Nombre del campo Descripción Reglas
enable_3ds Activa la autenticación 3D Secure para este pago ['sometimes', 'nullable', 'boolean']
browser_information Objeto con la información del navegador del cliente ['required_if:enable_3ds,true']
browser_information.colorDepth Profundidad de color de la pantalla, por ejemplo "24" ['required_if:enable_3ds,true', 'string', 'max:5']
browser_information.language Idioma del navegador, por ejemplo "es-CO" ['required_if:enable_3ds,true', 'string', 'max:10']
browser_information.screenHeight Alto de la pantalla en píxeles ['required_if:enable_3ds,true', 'numeric']
browser_information.screenWidth Ancho de la pantalla en píxeles ['required_if:enable_3ds,true', 'numeric']
browser_information.timeDifference Diferencia horaria con UTC en minutos, la que devuelve getTimezoneOffset() ['required_if:enable_3ds,true', 'numeric']
browser_information.javaScriptEnabled Si el navegador tiene JavaScript activo ['required_if:enable_3ds,true', 'boolean']
browser_information.javaEnabled Si el navegador tiene Java activo ['required_if:enable_3ds,true', 'boolean']
browser_information.acceptLanguage Preferencias de idioma del navegador, por ejemplo "en-US" ['nullable', 'string', 'max:10']
browser_information.ipAddress IP del cliente que hace la solicitud ['nullable', 'ip']
browser_information.sessionId Identificador único de la sesión del usuario ['nullable', 'string', 'max:255']
browser_information.userAgent Navegador y sistema operativo del usuario ['nullable', 'string', 'max:255']

Ejemplo y respuesta

Ejemplo de solicitud sin token:

curl -X POST \
"/api/v1/payment/transaction-checkout" \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json" \
-d '{
    "payment": {
        "id": "9a6f8166-644e-4680-bc37-66535e591ea5",
        "token": "1rV9zc9DApoOw3a"
    },
    "customer_payer": {
        "name": "Efipay",
        "email": "efipay@gmail.com"
    },
    "payment_card": {
        "number": "5249314023340339",
        "name": "efipay",
        "expiration_date": "2025-05",
        "cvv": "478",
        "identification_type": "CC",
        "id_number": "342343243",
        "installments": "1",
        "dialling_code": "57",
        "cellphone": "3004564884"
    },
    "browser_information": {
        "acceptLanguage": "es-ES",
        "ipAddress": "190.150.0.1",
        "sessionId": "dfds54fds534sd534dsds",
        "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
    }
}'

Ejemplo de solicitud con token:

curl -X POST\
'/api/v1/payment/transaction-checkout/card'\
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json" \
-d '{
    "payment": {
        "id": "9abca90d-978d-4722-891f-b41b6ebbae1e",
        "token": "quSOdCbSljI31wT"
    },
    "customer_payer": {
        "name": "Efipay",
        "email": "email@email.com"
    },
    "payment_card": {
        "token": "eyJpdiI6IkdzaG44RFV5dE5GNlE2MWRSM2lBTGc9PSIsInZhbHVlIjoiZWxyRzkyYTE4THNtY2VseCs5VzlKbW5pa0NicCtiRWhGNWQ5ZTBwTGM1VXF0UXlEemtVWEJyY21ueVIwZ2U5cHNGS2FvdFg1SHVTcmtiQ0phQWNia1JsTUZtQjU3OFpCR0p3bVVlTy9OVVMwM3hJNnJOWGZTbitwL3dlVmtmRStRN2xBZ3paWHI4bDFS
                        N0lBVHhRa2hBPT0iLCJtYWMiOiJlNDY0NmM4YThhNWMzMDYyOGMxNDAxMjA5MmVlMjNjYjNiYWEyMjczNjA5ZGNkMzM0NmMxMzg0YjZhYjgyY2ZhIiwidGFnIjoiIn0=",
        "identification_type": "CC",
        "id_number": "342343243",
        "installments": "1",
        "dialling_code": "57",
        "cellphone": "3004564884"
    }
}'

Respuestas

{success} Pago aprobado code: 200

{
"transaction_id": 20481,
"status": "Aprobada",
"status_key": "approved",
"response_code": "00",
"error": null,
"amount": 50000,
"currency_type": "COP",
"value_cop": 50000,
"tax": 7983,
"payment_method": "credit",
"payment_method_source": "Visa",
"card": { "franchise": "Visa", "bin": "453210", "last_four": "7890" },
"authorization_code": "005077",
"trazability_id": "320000303129",
"url_response": "https://sag-qa.efipay.co/Checkout/Transaction/9af329f1-e96a-40ab-b466-94a412f12c4a/Response",
"approved_at": "2026-07-31T14:32:10.000000Z",
"description": "Aprobada"
}

{danger} Pago rechazado code: 200

{
"transaction_id": 20482,
"status": "Rechazada",
"status_key": "rejected",
"response_code": "51",
"error": {
"code": "51",
"message": "Fondos insuficientes en la cuenta del tarjetahabiente.",
"retryable": false,
"action": "contact_issuer"
},
"amount": 50000,
"payment_method": "credit",
"payment_method_source": "Visa",
"card": { "franchise": "Visa", "bin": "453210", "last_four": "7890" },
"authorization_code": null,
"description": "Transacción declinada. Fondos insuficientes"
}

{warning} Un rechazo llega con 200. La petición fue correcta; lo que no se aprobó es el pago. Decide con status_key, no con el código HTTP. Ver códigos de error.

Cómo decidir sobre la respuesta

Campo Tipo Para qué
status string Estado en español (Aprobada, Rechazada, Pendiente…). Para mostrar
status_key string Clave estable en inglés. Para decidir en tu código
response_code string | null Código crudo de la red (00, 05, 51, M12…). Es el que hay que citarnos en un soporte
error objeto | null null si se aprobó. Si no, {code, message, retryable, action}
error.retryable bool Si tiene sentido volver a intentar. false en fondos insuficientes, tarjeta vencida y fraude
tax número | null Impuesto de la transacción, para cuadrar tu factura
card objeto | null {franchise, bin, last_four}, ya separados
payment_id uuid El cobro al que pertenece. Sirve de correlación si falta transaction_id

Valores de status_key: approved, rejected, failed, pending, started, for_pay, cancelled, reversed, escalated_reversal, authorized.

{danger} Nunca reintentes automáticamente con error.retryable: false. Reintentar un rechazo por fondos o por tarjeta bloqueada da el mismo resultado, y algunos emisores penalizan la insistencia. El catálogo completo de códigos está en Códigos de error.

{info} La comisión no viene aquí. En el momento del pago todavía no está liquidada. Consúltala después en GET /api/v1/virtual-account/movements/{transaction_id}, que devuelve commission, fee, gravamen, iva, las retenciones y el liquidated_amount. Ver Movimientos.

{danger} Validación code: 422

{
"message": "El campo payment_card.cvv es obligatorio.",
"errors": {
"payment_card.cvv": ["El campo payment_card.cvv es obligatorio."]
}
}

{danger} El par payment.id + payment.token no coincide, o el cobro ya fue pagado code: 403

Si activaste 3D Secure, la respuesta no es ninguna de estas: es la instrucción para continuar la autenticación. Sigue con 3D Secure.

3D Secure

Para implementar el flujo de 3Ds mediante api se requiere que el comercio realice el desarrollo del flujo para estos casos. Como aclaración se debe tener en cuenta que se tienen dos implementaciones diferentes según la franquicia que de la tarjeta, a continuación se explicaran con detalle ambos casos.

Para habilitar 3Ds en transacciones con Visa y Mastercard adiciona el atributo enable_3ds, y junto a él la información del navegador del cliente en browser_information.

{warning} 3D Secure pasará a ser obligatorio. Cuando tengamos la fecha en firme la publicaremos aquí y la anunciaremos por correo; mientras tanto, habilitarlo ya te deja listo. Ver flujo 3Ds

Los campos exactos y sus reglas están en Información del navegador: con enable_3ds: true, siete campos de browser_information pasan a ser obligatorios.

Par obtener la información del navegador puedes usar esta función para javascript

function getBrowserInformation() {
    // Get color depth
    const colorDepth = window.screen.colorDepth;
    // Check if JavaScript is enabled (if this runs, JavaScript is enabled)
    const jsEnabled = true;
    let javaEnabled = false;
    try {
        javaEnabled = navigator.javaEnabled();
    } catch (error) {
        const javaEnabled = false;
    }
    // Get browser language
    const language = navigator.language || navigator.userLanguage;
    // Get screen height and width
    const screenHeight = window.innerHeight;
    const screenWidth = window.innerWidth;
    // Calculate time difference from UTC (in hours)
    const timeDifference = new Date().getTimezoneOffset();

    return {
        colorDepth,
        language,
        screenHeight,
        screenWidth,
        timeDifference,
        javaScriptEnabled: jsEnabled,
        javaEnabled,
    }
}

Ejemplo compra con 3Ds habilitado

curl -X POST\
'/api/v1/payment/transaction-checkout'\
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json" \
-d '{
    "payment": {
        "id": "9a6f8166-644e-4680-bc37-66535e591ea5",
        "token": "1rV9zc9DApoOw3a"
    },
    "customer_payer": {
        "name": "Efipay",
        "email": "efipay@gmail.com"
    },
    "payment_card": {
        "number": "5249314023340339",
        "name": "efipay",
        "expiration_date": "2025-05",
        "cvv": "478",
        "identification_type": "CC",
        "id_number": "342343243",
        "installments": "1",
        "dialling_code": "57",
        "cellphone": "3004564884"
    },
    "browser_information": {
        "colorDepth": "24",
        "language": "es-ES",
        "screenHeight": 726,
        "screenWidth": 2133,
        "timeDifference": 300,
        "javaScriptEnabled": true,
        "javaEnabled": false
    },
    "enable_3ds": true
}'

Flujo Visa (Credibanco)

1. Inicio de la autenticación 3DS

Al iniciar la trx se responderá con un objeto con información para continuar con el flujo de 3ds, el cual tendrá el nombre de la implementación, un código html para agregar en el navegador del cliente y una url para validar el éxito de la operación, en caso de que el objeto 3Ds no sea devuelto se devolverá el objeto con la transacción con su información del estado de la misma.

Ejemplo respuesta 3Ds para continuar con la autenticación.

{
    "save": true,
    "transaction": {
        "transaction_id": 1,
        "amount": 100000,
        "currency_type": "COP",
        "value_cop": 100000,
        "payment_method": "credit",
        "payment_method_source": "Credibanco",
        "trazability_id": null,
        "authorization_code": null,
        "transaction_details": {
            "name": "Efipay",
            "identification_type": "CC",
            "identification_number": "123456789",
            "email": "efipay@efipay.com",
            "country": "+57",
            "phone": "3001234567",
            "number_card": "123456******1234",
            "installments": "1",
            "franchise": "Credibanco",
            "status_message": "Transacción en proceso: Recolectando Data"
        },
        "status": "Pendiente",
        "url_response": "https://sag-qa.efipay.co/Checkout/Transaction/9e84d84b-e1e6-4a6a-a0eb-53becc71c359/Response",
        "approved_at": null,
        "production": true,
        "created_at": "2025-03-25 15:20:17",
        "customer_payer": {
            "name": "Efipay",
            "email": "efipay@efipay.com",
            "country": "COL",
            "zip_code": "0000",
            "state": "Bogota",
            "city": "Bogota",
            "address_2": "Cr 23",
            "address_1": "Apto 1A",
            "created_at": "2024-11-07 18:39:40",
            "updated_at": "2024-11-07 18:39:40"
        },
        "currency_rate_conversion": {
            "id": 1,
            "usd_to_cop": 4288.58,
            "eur_to_cop": 4640.617049,
            "trm_for_cop": 1,
            "active": 1,
            "created_at": "2024-10-21T16:20:23.000000Z",
            "updated_at": "2024-10-21T16:20:23.000000Z",
            "deleted_at": null
        },
        "description": "Transacción en proceso: Recolectando Data"
    },
    "3Ds": {
        "implementation" : "credibanco",
        "browser_response" : "<div>...</div>",
        "centinelapistag" : "https://centinelapistag..."
    }
}

Recibida esta respuesta con la transacción pendiente y el objeto de 3Ds puedes tomar el siguiente ejemplo para implementar en tu navegador.

const setup3DsIframe = (browserResponse, centinelapistag) => {
    const wrappedElement = document.getElementById("hidden3ds");
    wrappedElement.innerHTML = iframe;

    const ddcForm = document.querySelector('#ddc-form');
    if (ddcForm) {
        console.log('ddcForm', ddcForm);
        ddcForm.submit();
    }
    let eventMessage3ds = false;
    const threeDsTimeOut = setTimeout(() => {
        if (!eventMessage3ds) {
            //rechazar transacción
            console.log('Error red demasiado tiempo esperando mensaje 3ds');
        }
    }, 50000);

    window.addEventListener("message", (event) => {
        eventMessage3ds = true;
        clearTimeout(threeDsTimeOut)
        if (event.origin === centinelapistag) {
            let data = JSON.parse(event.data);
            console.log('Merchant received a message:', data);
            if (data !== undefined && data.Status) {
                console.log('Songbird ran DF successfully');
                enrollTransaction(); // Continuar con -> 2. Consumir el enroll 3Ds
            } else {
                //rechazar transacción
                console.log('Error evento de mensaje front status 3ds');
            }
        }
    }, false);
}

2. Consumir el enroll 3DS

Cuando se obtenga la repuesta satisfactoria del evento se puede continuar con el consumo del enroll, aquí se pueden presentar dos casos, el primero en donde 3Ds determine la autenticación exitosa y se pueda procesar la transacción sin mas pasos, la segunda donde 3Ds determine hacer una validación adicional con un challenge para autenticar al tarjetahabiente

Para el primer caso se devolverá la transacción con su estado correspondiente a como es costumbre.

Para el caso donde se solicite el challenge se responderá con un nuevo objeto 3Ds así:

Recuerde que este flujo puede presentarse un challenge del banco que redirige al cliente a una nueva ventana para que el cliente pase el challenge por lo que si desea volver a ser redirigido automáticamente a una pagina de tu integración deberás configurar las opciones avanzadas con result_urls, adicional puede usar el webhook para obtener la respuesta en paralelo a su integración.

Request

POST /api/v1/payment/3ds/enroll/{transaction_id}

Nombre del campo Descripción Reglas
payment_card Datos de la tarjeta a autenticar ['required']
payment_card.number Número de la tarjeta, sin espacios ['required', 'numeric', 'digits_between:12,20']
payment_card.name Nombre impreso en la tarjeta. Solo letras y espacios ['required', 'string']
payment_card.expiration_date Vencimiento en formato YYYY-MM. No puede estar vencida ['required', 'date']
payment_card.cvv Código de seguridad de 3 o 4 dígitos ['required', 'numeric', 'digits_between:3,4']
browser_information Datos del navegador del cliente ['required']
browser_information.colorDepth Profundidad de color de la pantalla ['required', 'integer']
browser_information.javaScriptEnabled Si el navegador tiene JavaScript activo ['required', 'boolean']
browser_information.language Idioma del navegador ['required', 'string']
browser_information.screenHeight Alto de la pantalla en píxeles ['required', 'numeric']
browser_information.screenWidth Ancho de la pantalla en píxeles ['required', 'numeric']
browser_information.timeDifference Diferencia horaria con UTC en minutos ['required', 'numeric']
browser_information.ipAddress IP del cliente ['nullable', 'ip']
browser_information.sessionId Identificador de la sesión ['nullable', 'string', 'max:255']
browser_information.userAgent Navegador y sistema operativo ['nullable', 'string', 'max:255']

{danger} La transacción debe estar en estado Pendiente y tener 3DS ya inicializado (setup_status: COMPLETED). Si no, la API responde 403 sin detalle.

Cuerpo de ejemplo:

{
    "payment_card": {
        "number": "4000000000001091",
        "name": "Efipay",
        "expiration_date": "2025-12",
        "cvv": "123"
    },
    "browser_information": {
        "colorDepth": "24",
        "language": "es-ES",
        "screenHeight": 726,
        "screenWidth": 2133,
        "timeDifference": 300,
        "javaScriptEnabled": true
    }

}

Response

{
    "transaction": { "transaction_id": 20481, "status": "Pendiente" },
    "3Ds": {
        "implementation": "credibanco",
        "browser_response": "<div id=\"3ds-form\">...</div>"
    }
}

transaction trae la transacción completa, igual que en un pago normal, pero en estado Pendiente. Lo que tienes que usar es 3Ds.browser_response: es HTML que debes insertar en tu página para que el banco muestre su reto al cliente.

Ejemplo cliente

const setup3dsChallenge = browserResponse => {
    const wrappedElement = document.getElementById("challenge3ds");
    wrappedElement.innerHTML = iframe;

    var stepUpForm = document.querySelector('#step-up-form');
    if (stepUpForm) {
        stepUpForm.submit();
    }
}

Tarjetas de prueba de 3DS

Versión de 3DS Tarjetas Tarjetas Visa
"AUTHENTICATION_SUCCESSFUL" 400000 00 0000 2701
"AUTHENTICATION_FAILED" 400000 00 0000 2925
"PENDING_AUTHENTICATION" 400000 00 0000 2503
PENDING_AUTHENTICATION "AUTHENTICATION_FAILED" 400000 00 0000 2370

Flujo Mastercard (Redeban)

1. Inicio de la autenticación 3DS

Al iniciar la trx se responderá con un objeto con información para continuar con el flujo de 3ds, el cual tendrá el nombre de la implementación, un código html para agregar en el navegador del cliente, en caso de que el objeto 3Ds no sea devuelto se devolverá el objeto con la transacción con su información del estado de la misma.

Ejemplo respuesta 3Ds para continuar con la autenticación.

{
    "save": true,
    "transaction": {
        "transaction_id": 1,
        "amount": 100000,
        "currency_type": "COP",
        "value_cop": 100000,
        "payment_method": "credit",
        "payment_method_source": "Credibanco",
        "trazability_id": null,
        "authorization_code": null,
        "transaction_details": {
            "name": "Efipay",
            "identification_type": "CC",
            "identification_number": "123456789",
            "email": "efipay@efipay.com",
            "country": "+57",
            "phone": "3001234567",
            "number_card": "123456******1234",
            "installments": "1",
            "franchise": "Credibanco",
            "status_message": "Transacción en proceso: Recolectando Data"
        },
        "status": "Pendiente",
        "url_response": "https://sag-qa.efipay.co/Checkout/Transaction/9e84d84b-e1e6-4a6a-a0eb-53becc71c359/Response",
        "approved_at": null,
        "production": true,
        "created_at": "2025-03-25 15:20:17",
        "customer_payer": {
            "name": "Efipay",
            "email": "efipay@efipay.com",
            "country": "COL",
            "zip_code": "0000",
            "state": "Bogota",
            "city": "Bogota",
            "address_2": "Cr 23",
            "address_1": "Apto 1A",
            "created_at": "2024-11-07 18:39:40",
            "updated_at": "2024-11-07 18:39:40"
        },
        "currency_rate_conversion": {
            "id": 1,
            "usd_to_cop": 4288.58,
            "eur_to_cop": 4640.617049,
            "trm_for_cop": 1,
            "active": 1,
            "created_at": "2024-10-21T16:20:23.000000Z",
            "updated_at": "2024-10-21T16:20:23.000000Z",
            "deleted_at": null
        },
        "description": "Transacción en proceso: Recolectando Data"
    },
    "3Ds": {
        "implementation" : "redeban",
        "browser_response" : "<div>...</div>"
    }
}

Recibida esta respuesta con la transacción pendiente y el objeto de 3Ds puedes tomar el siguiente ejemplo para implementar en tu navegador.

const setup3DsIframe = iframe => {
    const wrappedElement = document.getElementById("hidden3ds");
    wrappedElement.innerHTML = iframe;
    Array.from(wrappedElement.querySelectorAll("script"))
        .forEach( oldScriptEl => {
        const newScriptEl = document.createElement("script");

        Array.from(oldScriptEl.attributes).forEach( attr => {
            newScriptEl.setAttribute(attr.name, attr.value)
        });

        const scriptText = document.createTextNode(oldScriptEl.innerHTML);
        newScriptEl.appendChild(scriptText);

        oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
    });
    setTimeout(() => {
            console.log("run timeout 5sec");
            authContinueTransaction();// Continuar con -> 2. Consumir el auth continue 3Ds
    }, 5000);
}

2. Consumir el auth continue 3DS

Cuando se obtenga la repuesta satisfactoria del evento se puede continuar con el consumo del enroll, aquí se pueden presentar dos casos, el primero en donde 3Ds determine la autenticación exitosa y se pueda procesar la transacción sin mas pasos, la segunda donde 3Ds determine hacer una validación adicional con un challenge para autenticar al tarjetahabiente

Para el primer caso se devolverá la transacción con su estado correspondiente a como es costumbre.

Para el caso donde se solicite el challenge se responderá con un nuevo objeto 3Ds así:

Request

POST /api/v1/payment/3ds/auth-continue/{transaction_id}

Nombre del campo Descripción Reglas
payment_card Datos de la tarjeta a autenticar ['required']
payment_card.number Número de la tarjeta, sin espacios ['required', 'numeric', 'digits_between:12,20']
payment_card.name Nombre impreso en la tarjeta. Solo letras y espacios ['required', 'string']
payment_card.expiration_date Vencimiento en formato YYYY-MM, con mes entre 01 y 12. No puede estar vencida ['required', 'date_format:Y-m', 'after_or_equal:<mes actual>']
payment_card.cvv Código de seguridad de 3 o 4 dígitos ['required', 'numeric', 'digits_between:3,4']

{info} A diferencia de enroll, aquí browser_information no se valida: la autenticación ya está en curso y basta con reenviar la tarjeta.

Cuerpo de ejemplo:

{
    "payment_card": {
        "number": "4000000000001091",
        "name": "Efipay",
        "expiration_date": "2025-12",
        "cvv": "123"
    },
    "browser_information": {
        "colorDepth": "24",
        "language": "es-ES",
        "screenHeight": 726,
        "screenWidth": 2133,
        "timeDifference": 300,
        "javaScriptEnabled": true
    }

}

Response

{
    "transaction": { "transaction_id": 20481, "status": "Pendiente" },
    "3Ds": {
        "implementation": "redeban",
        "browser_response": { "challenge_request": "iframe" }
    }
}

En Mastercard browser_response es un objeto y no HTML: te indica cómo montar el reto.

Ejemplo cliente

const setup3dsChallenge = browserResponse => {
    const wrappedElement = document.getElementById("challenge3ds");
    wrappedElement.innerHTML = iframe;

    Array.from(wrappedElement.querySelectorAll("script"))
        .forEach( oldScriptEl => {
        const newScriptEl = document.createElement("script");

        Array.from(oldScriptEl.attributes).forEach( attr => {
            newScriptEl.setAttribute(attr.name, attr.value)
        });

        const scriptText = document.createTextNode(oldScriptEl.innerHTML);
        newScriptEl.appendChild(scriptText);

        oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
    });
}
tarjeta descripción monto
2221008123677736 3DS Challenge 151

Abandonar la autenticación

POST /api/v1/payment/3ds/reject/{transaction}

Tu cliente puede cerrar el reto 3DS sin completarlo. Cuando eso pasa, la transacción se queda en Pendiente y tu pedido queda colgado esperando algo que ya no va a llegar.

Llama a este endpoint para cerrarla como rechazada. El parámetro es el transaction_id (el consecutivo) de la transacción que quedó pendiente.

curl -X POST \
'/api/v1/payment/3ds/reject/20481' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json"

{success} Respuesta satisfactoria code: 200

{
"save": true,
"transaction": {
"transaction_id": 20481,
"status": "Rechazada",
"description": "Pago rechazado por el usuario en el proceso 3DS"
}
}

{danger} La transacción no está en Pendiente: ya se resolvió, y no se puede rechazar code: 403

{info} Es el tercer endpoint del flujo 3DS, junto con enroll y auth-continue. Los tres son necesarios: dos para avanzar y este para cerrar el caso en que el cliente se va.

Pago con PSE

Parámetros de PSE

Cuando realizes la petición recibirás una url a la cual deberás redireccionar al usuario para que pueda realizar el pago, cuando el usuario realize el pago en su banco y regrese al comercio sera redireccionado a uno de las siguientes opciones; a la url si agregaste el custom_redirect_url en este checkout si no al checkout de respuesta de efipay.

La validación del pago lo podrás hacer a través de nuestro webhook o consultando el status al ser redireccionado a una de tus url personalizadas de redirección.

Consulta la lista de bancos disponibles aquí. Consulta la lista de datos del formulario para pse aquí.

Adicional a los parámetros anteriores se deben agregar los siguientes:

Nombre del campo Descripción Reglas
pse Objeto con los datos del pago PSE ['required']
pse.financialInstitutionCode Código del banco, de la lista de bancos. El código 0 no es un banco válido, es el placeholder «Selecciona tu banco» ['required', 'not_in:0', 'in:<códigos de la lista de bancos>']
pse.userType Tipo de usuario: natural o jurídico. Ver opciones disponibles ['required', 'in:<tipos de usuario PSE>']
pse.identificationType Tipo de documento. Las opciones válidas dependen del userType que hayas enviado. Ver opciones disponibles ['required', 'in:<tipos de identificación del userType>']
pse.identificationNumber Número de documento. No puede ser un número de tarjeta ['required', 'numeric', 'digits_between:5,15']
pse.fullName Nombre de quien paga ['required', 'string', 'min:5', 'max:64']
pse.cellphoneNumber Celular, exactamente 10 dígitos ['required', 'numeric', 'digits:10']
pse.address Dirección de quien paga ['required', 'string', 'min:5', 'max:64']
pse.email Correo de quien paga. Solo caracteres alfanuméricos ['required', 'email', 'max:110']
pse.redirect A dónde vuelve el cliente después de pagar en su banco. Debe ser una URL que responda ['nullable', 'url', 'active_url', 'max:191']

{warning} El banco debe estar habilitado para tu comercio y pertenecer a la lista del ambiente de tu token: las listas de prueba y producción no son iguales. Consulta siempre GET /api/v1/resources/checkout/pse-banks con el mismo token con el que vas a cobrar.

Ejemplo para PSE

curl -X POST\
"/api/v1/payment/transaction-checkout/pse"\
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json" \
-d '{
    "payment": {
        "id": "generated_payment_id", //9a6f8166-644e-4680-bc37-66535e591ea5
        "token": "token_payment" //1rV9zc9DApoOw3a
    },
    "customer_payer": {
        "name": "Pepito perez",
        "email": "pepito@email.com"
    },
    "pse": {
        "financialInstitutionCode": "0000",
        "userType": "person",
        "identificationType": "CedulaDeCiudadania",
        "identificationNumber": "123456789",
        "fullName": "Pepito Perez",
        "cellphoneNumber": "3123456789",
        "address": "calle 93 # 32",
        "email": "pepito@email.com",
        "redirect": "https://efipay.co/"
    }
}'

Pago con Bre-B

Parámetros de Bre-B

Iniciar una transacción con bre-b del pago generado con /generate-payment el cual creará un QR disponible por 30 minutos para que el usuario pueda concluir la trasacción en su aplicación bancaria

Es necesario implementar el webhook para recibir el resultado final de la transacción una vez se concluya, adicionalmete podra hacer uso del api de status para consultar el estado de la transacción.

Adicional a los parámetros anteriores se deben agregar los siguientes:

Nombre del campo Descripción Reglas
breb Objeto con los datos del pago Bre-B ['required']
breb.cellphone_number Celular de quien paga, exactamente 10 dígitos ['required', 'numeric', 'digits:10']

Ejemplo para Bre-B

curl -X POST\
"/api/v1/payment/transaction-checkout/bre-b"\
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json" \
-d '{
    "payment": {
        "id": "generated_payment_id", //9a6f8166-644e-4680-bc37-66535e591ea5
        "token": "token_payment" //1rV9zc9DApoOw3a
    },
    "customer_payer": {
        "name": "Pepito perez",
        "email": "pepito@email.com"
    },
    "breb": {
        "cellphone_number": "3123456789",
    }
}'

Respuesta

{
    "save": true,
    "transaction": { "transaction_id": 20481, "status": "Pendiente" },
    "qr_breb": {
        "qr_code_data": "00020101021226580014CO.COM.BREB...",
        "qr_code_image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
        "expiration_date": "2026-04-23 10:43:42"
    }
}
Campo Qué es
qr_code_data La cadena del QR. Genera tú la imagen, o imprímela
qr_code_image La misma imagen ya generada, en base64, si prefieres mostrarla directo
expiration_date El QR vence a los 30 minutos. Después hay que generar otro

Pago en efectivo

Parámetros de efectivo

Ten presente que al usar este checkout para pagos en efectivos al realizar la petición todas las transacciones darán como respuesta el estado por pagar, y la información del cupón para que tu usuario realize el pago en la sucursal de efectivo correspondiente.

Para poder validar el pago de este tipo de transacciones te ofrecemos dos opciones; la primera y más sencilla usar nuestro webhook para notificarte la nueva información sobre las transacciones y la segunda es que realices una consulta del estado del pago después del tiempo de expiración que también te proporcionamos en la respuesta.

Podrás consultar la lista de efectivos disponibles para ti aquí

Adicional a los parámetros anteriores se deben agregar los siguientes:

Nombre del campo Descripción Reglas
cash.franchise Nombre del punto de recaudo, de la lista de efectivos. Debe estar habilitado en tu comercio ['required', 'string']
cash.cellphone_number Celular de quien paga, entre 6 y 10 dígitos ['required', 'numeric', 'min_digits:6', 'max_digits:10']
cash.identification_number Número de documento de quien paga. No puede ser un número de tarjeta ['required', 'numeric', 'digits_between:5,15']

Ejemplo para efectivo

curl -X POST\
"/api/v1/payment/transaction-checkout/cash" \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-type: application/json' \
-d '{
    'payment': {
        'id': 'generated_payment_id',
        'token': 'token_payment'
    },
    'customer_payer': {
        'name': 'Pepito peres',
        'email': 'pepito@gmail.com'
    },
    "cash" : {
        "franchise": "Efecty",
        "cellphone_number": "3123456789",
        "identification_number": "123456789"
    }
}'