Ir al contenido

Checkout por API

Ver .md

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

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

POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout

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.

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.

Decide con error.code, no con el texto ni solo con el HTTP. Todos los errores traen el objeto error:

HTTP error.code Cuándo Qué hacer
403 payment_already_used El cobro ya tiene una transacción (cualquier estado, también si ya se pagó) Crear un cobro nuevo. No reuses el mismo payment_id
403 payment_already_paid El cobro ya fue pagado No cobrar de nuevo
403 payment_in_progress Tiene transacciones en progreso que agotan su límite Esperar el resultado
403 payment_expired Pasó la fecha límite del cobro Crear un cobro nuevo
403 payment_inactive El cobro fue desactivado Crear un cobro nuevo
403 invalid_payment_credentials El payment.id o el payment.token no son válidos. Mismo código en ambos casos, para no revelar qué ids existen Revisar el par guardado
422 validation_failed Validación: el body está incompleto o inválido Corregir el body y repetir el mismo cobro
429 payment_in_progress Hay otra petición en curso para este cobro Esperar y repetir el mismo cobro

Ejemplo de respuesta cuando el cobro ya se usó:

403Forbidden
{
"message": "Este cobro ya tiene una transacción y no permite reintentos",
"error": {
"type": "invalid_request_error",
"code": "payment_already_used",
"message": "Este cobro ya tiene una transacción y no permite reintentos",
"param": "payment.id"
}
}
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']
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']

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']

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']

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 de solicitud sin token:

POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout/card
Ventana de terminal
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:

POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout/card
Ventana de terminal
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"
}
}'
200OK
{
"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.efipay.co/Checkout/Transaction/9af329f1-e96a-40ab-b466-94a412f12c4a/Response",
"approved_at": "2026-07-31T14:32:10.000000Z",
"description": "Aprobada",
"save": true,
"payment_id": "9af329f1-e96a-40ab-b466-94a412f12c4a",
"transaction": { "…": "mismos campos de la raíz" }
}
200OK
{
"transaction_id": 20482,
"status": "Rechazada",
"status_key": "rejected",
"response_code": "51",
"error": {
"code": "51",
"message": "Transacción declinada. Fondos insuficientes",
"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",
"save": true,
"payment_id": "9af329f1-e96a-40ab-b466-94a412f12c4a",
"transaction": { "…": "mismos campos de la raíz" }
}
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.

422Unprocessable Entity
{
"message": "El campo payment_card.cvv es obligatorio.",
"errors": {
"payment_card.cvv": ["El campo payment_card.cvv es obligatorio."]
},
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "El campo payment_card.cvv es obligatorio.",
"param": "payment_card.cvv"
}
}
403Forbidden
{
"message": "El id o el token del cobro no son válidos.",
"error": {
"type": "authorization_error",
"code": "invalid_payment_credentials",
"message": "El id o el token del cobro no son válidos.",
"param": "payment.token"
}
}

Si el par es válido pero el cobro ya no admite pagos, el error.code dice por qué (payment_already_used, payment_expired…). Ver Un intento por cobro.

Si activaste 3D Secure, la respuesta no es ninguna de estas: es la instrucción para continuar la autenticación. Sigue con 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.

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

Ventana de terminal
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
}'

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.

{
"transaction_id": 1,
"amount": 100000,
"currency_type": "COP",
"status": "Pendiente",
"status_key": "pending",
"response_code": null,
"error": null,
"description": "Transacción en proceso: Recolectando Data",
"…": "resto de campos de la transacción, iguales a los de `transaction`",
"save": true,
"payment_id": "9e84d84b-e1e6-4a6a-a0eb-53becc71c359",
"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.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);
}

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']
POSThttps://sag.efipay.co/api/v1/payment/3ds/enroll/20481

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();
}
}
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

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.

{
"transaction_id": 1,
"amount": 100000,
"currency_type": "COP",
"status": "Pendiente",
"status_key": "pending",
"response_code": null,
"error": null,
"description": "Transacción en proceso: Recolectando Data",
"…": "resto de campos de la transacción, iguales a los de `transaction`",
"save": true,
"payment_id": "9e84d84b-e1e6-4a6a-a0eb-53becc71c359",
"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.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);
}

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']
POSThttps://sag.efipay.co/api/v1/payment/3ds/auth-continue/20481

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

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.

POSThttps://sag.efipay.co/api/v1/payment/3ds/reject/20481
Ventana de terminal
curl -X POST \
'/api/v1/payment/3ds/reject/20481' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H "Content-type: application/json"
200OK
{
"save": true,
"transaction": {
"transaction_id": 20481,
"status": "Rechazada",
"description": "Pago rechazado por el usuario en el proceso 3DS"
}
}
403Forbidden

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']
POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout/pse
Ventana de terminal
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/"
}
}'

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']
POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout/bre-b
Ventana de terminal
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

{
"transaction_id": 20481,
"status": "Pendiente",
"status_key": "pending",
"…": "resto de campos de la transacción",
"save": true,
"payment_id": "9a6f8166-644e-4680-bc37-66535e591ea5",
"transaction": { "…": "mismos campos de la raíz" },
"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

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']
POSThttps://sag.efipay.co/api/v1/payment/transaction-checkout/cash
Ventana de terminal
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"
}
}'

Última actualización: