Nequi
En esta página
Empieza con Nequi#
Esta página proporciona una guía completa para integrar exitosamente Nequi con DEUNA en su proceso de pago.
Nequi es una billetera digital administrada por Bancolombia y muy utilizada en Colombia.
Cómo funciona#
La integración de DEUNA con Nequi permite a los usuarios pagar a través de un flujo de notificaciones push, completamente manejado dentro del widget de pago DEUNA, sin necesidad de redirecciones externas.
Proceso de pago
La siguiente lista describe un proceso de pago válido con Nequi:
- El usuario selecciona Nequi dentro del widget de pago.
- El usuario ingresa su número de teléfono real de Nequi.
- Se envía una notificación automática a su aplicación móvil Nequi.
- El usuario aprueba el pago directamente en la aplicación.
- DEUNA recibe el estado de pago.
- DEUNA activa el evento de devolución de llamada apropiado en el widget:
onSuccesspara pagos aprobadosonErrorpara los rechazados o vencidos
manejo de eventos onError
El widget DEUNA activa la onError devolución de llamada en los siguientes escenarios:
- El número de teléfono proporcionado en el formulario de pago es válido pero no está asociado a una cuenta Nequi.
- El usuario rechaza la notificación push de la aplicación Nequi.
En ambos casos, DEUNA devuelve una respuesta que incluye un código de error y un mensaje de error que describe la falla. Puede manejar la respuesta según su lógica empresarial y la experiencia de usuario deseada.
Requisitos#
El siguiente contenido enumera todos los requisitos para una integración exitosa con Nequi.
Tus credenciales de sandbox y producción de Nequi deben solicitarse directamente a tu gerente de cuenta de Bancolombia o Nequi.
Entornos:
- Caja de arena: https://api.sandbox.deuna.io
- Producción: https://api.deuna.io
Completa el proceso de certificación con Nequi y obtén las siguientes credenciales proporcionadas por Nequi:
-
Merchant ID
-
Contraseña del comerciante
-
Clave pública.
-
Código de identificación
-
Tiempo para caducar (máx. 45min)
Configurar webhooks en Nequi#
Configura webhooks en tu sesión de Nequi para cada tienda.
Si no configura los webhooks de Nequi, las actualizaciones de estado en Nequi tardarán entre dos y cinco minutos.
Asegúrese de que su punto final de webhook tenga alta disponibilidad y sea escalable. Opcionalmente, puede configurar la reversión de la transacción si la confirmación del pago falla debido a la falta de disponibilidad del servicio.
Carga útil del webhook
Nequi enviará la siguiente carga útil JSON:
{
"commerceCode": "29603",
"value": "1",
"phoneNumber": "3195414070",
"messageId": "60396545535",
"transactionId": "350-12345-34000201-60396545535",
"region": "C001",
"receivedAt": "2023-02-27T15:50:13.527Z",
"paymentStatus": "SUCCESS"
}Campos de carga útil
| Campo | Descripción |
|---|---|
commerceCode | Tu código interno de comerciante Nequi |
value | Monto del pago |
phoneNumber | Número de teléfono móvil del pagador |
messageId | Identificador de transacción único |
transactionId | Identificador de pago |
region | Región de pago: P001 (Panamá) o C001 (Colombia) |
receivedAt | Marca de tiempo de pago en formato JSON |
paymentStatus | Estado de pago: SUCCESS, CANCELEDo DENIED |
Verificación de solicitud de seguridad
Todas las solicitudes de webhooks de Nequi incluyen encabezados de seguridad que debes verificar:
Encabezados de solicitud
{
"Content-Type": "application/json",
"Digest": "SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI=",
"Signature": "keyId=\"YourClientId\",algorithm=\"hmac-sha384\",headers=\"content-type digest\",signature=\"...\""
}Proceso de verificación
Debes verificar dos cosas para cada solicitud:
1. Verificar el resumen corporal
El Digest El encabezado contiene un hash SHA-256 del cuerpo de la solicitud:
// Convert body to string
const parsedBody = JSON.stringify(request.body);
// Calculate SHA-256 hash
const calculatedDigest = `SHA-256=${createHash('sha256')
.update(parsedBody)
.digest('base64')}`;
// Verify it matches the header
if (calculatedDigest !== request.headers.Digest) {
return 401; // Invalid Digest
}2. Verificar la firma de la solicitud
El Signature El encabezado asegura que la solicitud vino de Nequi:
- Analiza el encabezado de la firma.
const parts = request.headers.Signature.split(',');
const signature = {};
for (const part of parts) {
const [key, value] = part.split('=');
signature[key] = value.slice(1, -1); // Remove quotes
}
// Result:
// {
// keyId: "YourClientId",
// algorithm: "hmac-sha384",
// headers: "content-type digest",
// signature: "..."
// }- Construya el texto de firma.
const headerNames = signature.headers.split(' '); // ["content-type", "digest"]
const linesForSignature = [];
for (const headerName of headerNames) {
const value = request.headers[headerName.toLowerCase()];
linesForSignature.push(`${headerName}: ${value}`);
}
const textForSignature = linesForSignature.join('\n');
// Result:
// "content-type: application/json
// digest: SHA-256=43GpOk5L54gfpAMBE0xNX1bj2hJA9JJ1RR0dErHfZhI="- Calcule y verifique HMAC.
const APP_SECRET = 'YourSharedSecret'; // Store securely, never hardcode
// Calculate HMAC-SHA384
const base64hmac = createHmac('sha384', APP_SECRET)
.update(textForSignature)
.digest('base64');
// Convert to base64url format
const calculatedSignature = base64hmac
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
// Verify signature
if (calculatedSignature !== signature.signature) {
return 401; // Invalid Signature
}Ejemplo de implementación completo (Node.js)
const { createHash, createHmac } = require('node:crypto');
// Store securely - use environment variables
const APP_SECRET = process.env.NEQUI_WEBHOOK_SECRET;
async function handleNequiWebhook(request, response) {
try {
// 1. Verify Body Digest
const parsedBody = JSON.stringify(request.body);
const calculatedDigest = `SHA-256=${createHash('sha256')
.update(parsedBody)
.digest('base64')}`;
if (calculatedDigest !== request.headers.digest) {
return response.status(401).send('Invalid Digest');
}
// 2. Parse Signature Header
const parts = request.headers.signature.split(',');
const signatureData = {};
for (const part of parts) {
const [key, value] = part.split('=');
signatureData[key] = value.slice(1, -1);
}
// 3. Build Signing Text
const headerNames = signatureData.headers.split(' ');
const linesForSignature = headerNames.map(name =>
`${name}: ${request.headers[name.toLowerCase()]}`
);
const textForSignature = linesForSignature.join('\n');
// 4. Verify Signature
const base64hmac = createHmac('sha384', APP_SECRET)
.update(textForSignature)
.digest('base64');
const calculatedSignature = base64hmac
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
if (calculatedSignature !== signatureData.signature) {
return response.status(401).send('Invalid Signature');
}
// 5. Process the payment notification asynchronously
processPaymentAsync(request.body);
// 6. Respond immediately (within 10 seconds)
return response.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error:', error);
return response.status(500).send('Internal Server Error');
}
}
async function processPaymentAsync(paymentData) {
// Process payment in background
// Update your database, trigger notifications, etc.
const { transactionId, paymentStatus, value, phoneNumber } = paymentData;
if (paymentStatus === 'SUCCESS') {
// Handle successful payment
} else if (paymentStatus === 'CANCELED' || paymentStatus === 'REFUSED') {
// Handle failed payment
}
}Integrar Nequi#
Ahora que se establecen los requisitos técnicos, puede iniciar la integración paso a paso.
1. Crea un order_token
Para realizar una compra, debes crear un pedido DEUNA.
Realizar una solicitud al Crear un pedido endpoint.
La API devuelve un order_token, que se utiliza durante todo el flujo.
2. Consigue el order_token
Realizar una solicitud al Obtener pedido endpoint.
Utilice este token para los siguientes pasos.
3. Representa el widget de pago.
Después de recibir el token de pedido, puede renderizar el Widget DEUNA.
await DeunaSDK.initPaymentWidget({
orderToken: "<DEUNA order token>",
callbacks: ...,
paymentMethods: [
{
paymentMethod: "voucher",
processors: ["nequi_push_voucher"],
},
],
});4. Realizar un pago con tarjeta
Realizar una solicitud de pago con tarjeta al Compra V2 punto final y procesar el pago.
Respuesta
{
"order": {
"cash_change": 0,
"currency": "USD",
"discounts": [],
"display_items_total_amount": "",
"display_shipping_amount": "",
"display_sub_total": "",
"display_tax_amount": "",
"display_total_amount": "",
"display_total_discount": "",
"gift_card": [],
"items": [
{
"brand": "",
"category": "",
"color": "",
"description": "Papa Fritas",
"details_url": "",
"discounts": [],
"id": "001",
"image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_316_744x744.png?d=600x600",
"isbn": "",
"manufacturer": "",
"name": "Papa Fritas",
"options": "",
"quantity": 1,
"size": "",
"sku": "",
"tax_amount": {
"amount": 44,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"taxable": false,
"total_amount": {
"amount": 594,
"currency": "USD",
"currency_symbol": "$",
"display_amount": "",
"display_original_amount": "",
"display_total_discount": "",
"original_amount": 0,
"total_discount": 0
},
"type": "",
"unit_price": {
"amount": 550,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"uom": "",
"upc": "",
"weight": {
"unit": "",
"weight": 0
}
},
{
"brand": "",
"category": "",
"color": "",
"description": "Hamburguesa ",
"details_url": "",
"discounts": [],
"id": "002",
"image_url": "https://images-staging.getduna.com/95463fb5-6279-4ec3-8ff9-fe07aacd2142/cd928351d12c6b96_domicilio_51330_744x744_1646338877.png?d=600x600",
"isbn": "",
"manufacturer": "",
"name": "Hamburguesa",
"options": "",
"quantity": 2,
"size": "",
"sku": "",
"tax_amount": {
"amount": 88,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"taxable": false,
"total_amount": {
"amount": 3088,
"currency": "USD",
"currency_symbol": "$",
"display_amount": "",
"display_original_amount": "",
"display_total_discount": "",
"original_amount": 0,
"total_discount": 0
},
"type": "",
"unit_price": {
"amount": 1500,
"currency": "USD",
"currency_symbol": "$",
"display_amount": ""
},
"uom": "",
"upc": "",
"weight": {
"unit": "",
"weight": 0
}
}
],
"items_total_amount": 3682,
"metadata": {
"key1": "NO REQUERIDO",
"key2": "NO REQUERIDO"
},
"order_id": "order116",
"payment": {
"data": {
"amount": {
"amount": 3770,
"currency": "USD"
},
"authorization_3ds": {
"html_content": "<div></div>",
"url_challenge": "",
"version": "1.1.1"
},
"authorization_code": "TEST00",
"created_at": "2022-07-22 20:22:48.408419489 +0000 UTC",
"customer": {
"email": "jhondoe@deuna.com",
"id": "xxxxxx-0eb3-450f-8b26-4ff23208470f"
},
"from_card": {
"card_brand": "Visa",
"first_six": "411111",
"last_four": "1111"
},
"id": "order116",
"installments": {
"installment_amount": 5999,
"installment_rate": 0.12,
"installment_type": "MCI",
"installments": 3,
"plan_id": "7471ed27-094d-44c4-a62d-225644b782f7",
"plan_option_id": "87309ea8-3942-4fdf-95ec-ce29a792aff2"
},
"merchant": {
"id": "9a85e296-cc3d-454b-b591-208d6e538126",
"store_code": "all"
},
"metadata": {
"authorization_code": "TEST00"
},
"method_type": "credit_card",
"processor": "paymentez",
"reason": "",
"status": "processed",
"updated_at": "2022-07-22 20:22:48.408809765 +0000 UTC"
}
},
"redirect_url": "",
"scheduled_at": "",
"shipping": null,
"shipping_address": {
"additional_description": "Piso 9",
"address_type": "home",
"address1": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
"address2": "Av. de los Incas 15-33, Ambato 180202, Ecuador",
"city": "Ambato",
"country_code": "EC",
"created_at": "0001-01-01T00:00:00Z",
"first_name": "Jhon",
"id": 0,
"identity_document": "146565656",
"is_default": true,
"last_name": "Doe",
"lat": -1.2480678792202227,
"lng": -78.62532788804577,
"phone": " 946565665",
"state_name": "Tungurahua",
"updated_at": "0001-01-01T00:00:00Z",
"user_id": "xxxxx4e2-xxxx-xxxx-xxxx-xxxxx5b7b2e",
"zipcode": "180202"
},
"shipping_amount": 0,
"shipping_method": null,
"shipping_methods": [],
"shipping_options": {},
"status": "succeeded",
"store_code": "",
"sub_total": 3550,
"tax_amount": 132,
"timezone": "",
"total_amount": 3770,
"total_discount": 0,
"user_instructions": "Piso 9",
"webhook_urls": null
},
"order_token": "0b98dbe8-d265-49bc-b80d-536cea46509c"
}Prueba Nequi#
Para probar Nequi, debe solicitar los datos de prueba de Nequi. Si es necesario, el pago se puede reembolsar a través del Panel de administración de DEUNA o a través del API de reembolsos.