Integración con webhooks de pagos
Esta integración te permite recibir notificaciones de transacciones (pagos, devoluciones o anulaciones) en un formato específico mediante una solicitud POST.
Requisitos
- Servidor con soporte de peticiones POST aptos para recibir mensajes
Resultado
- Suscripción configurada y validación de mensajes en su sistema
Para suscribirte a este evento, sigue estos pasos
- Configura tu aplicación para recibir notificaciones de webhooks en una URL pública y segura con el formato esperado que se muestra a continuación.
- Crea una suscripción a webhooks para comenzar a recibir las notificaciones.
- Asegurate de validar los webhooks entrantes utilizando el token correspondiente para verificar su origen Menta y garantizar que no han sido manipulados.
En caso de no poder enviar las notificaciones correctamente (código de respuesta 200
), haremos nuevos reintentos por algunas horas. Luego de este período, se dejará de intentar enviar. Por lo cual, recuerde que es una buena práctica conciliar con cierta periodicidad y de esta manera se asegurará de contar con toda la información actualizada.
Para conciliar recomendamos utilizar el servicio de obtención de transacciones.
Ejemplo de notificación:
OPERATION_CREATED
{
"notification_type": "OPERATION_CREATED",
"merchant_id": "db3ae307-4a5c-4cbb-b48c-80bf1a3fde1d",
"user": "[email protected]",
"datetime": "2022-12-31T23:23:23Z",
"detail": {
"terminal_id": "72171704-7806-4347-b08b-bc2d2d96e68e",
"operation_id": "2fce7d49-f3e2-4b1f-a7bb-7f16d3ea64a2",
"ticket_id": "111111111",
"operation_type": "PAYMENT",
"qr_id": null,
"operation_status": "APPROVED",
"currency": "ARS",
"operation_amount": "200000",
"operation_additional_info": "ABC1234",
"payment_method_type": "DEBIT",
"payment_method_detail": "VISA"
}
}
TAXED_OPERATION_CREATED
{
"notification_type": "TAXED_OPERATION_CREATED",
"merchant_id": "c2192cf8-e221-4ccc-83d4-1c2105d46b9a",
"user": "[email protected]",
"datetime": "2023-09-19T20:07:32Z",
"detail": {
"customer_id": "a9733025-9b74-4cf9-b4fb-f71eeb037054",
"merchant_id": "45b1d469-e3e5-4ac6-8fe2-2e620d00f9a8",
"terminal_id": "c08d4aa6-d560-41c1-acbf-b183eb3794dd",
"merchant_additional_info": "35701",
"transaction_id": "1b35e569-dc28-47be-897f-1c2a9e5a5305",
"operation_id": "6debca65-4faf-48fd-a065-faf32735a52a",
"operation_number": 183863925,
"operation_additional_info": "",
"serial_number": "98282329166214",
"operation_type": "PAYMENT",
"payment_method": "CREDIT",
"gross_amount": 21,
"currency": "ARS",
"datetime": "2024-03-05T20:21:03-03:00",
"status": "APPROVED",
"installments": 1,
"financing": "ESTANDAR",
"user": "[email protected]",
"operation_detail": {
"card": {
"card_bin": "47617390",
"card_mask": "XXXXXXXXXXXX0119",
"card_brand": "VISA",
"is_international_card": false
},
"holder_name": "Tarjetahabiente",
"holder_document": "",
"description": "APROBADO",
"input_mode": "CONTACTLESS",
"reference_operation_number": 394221095,
"reference_operation_id": "50c7571f-750c-43b5-9baf-f5d1d9a4d022"
},
"tax_info": {
"term": 2,
"payment_date": "2024-02-23T09:26:54-03:00",
"net_amount": 18.02,
"customer_term": 8,
"customer_payment_date": "2024-03-04T09:26:54-03:00",
"customer_net_amount": 20.54,
"tax_breakdown": [
{
"tax_code": "MENTA_TO_CUSTOMER_COMMISSION",
"reference": "Menta to customer commission",
"amount": 0.07,
"rate": 0.35
},
{
"tax_code": "MENTA_TO_CUSTOMER_COMMISSION_VAT_TAX",
"reference": "Menta to customer commission VAT Tax",
"amount": 0.02,
"rate": 0.21
},
{
"tax_code": "ACQUIRER_TO_CUSTOMER_COMMISSION",
"reference": "Acquirer to customer commission",
"amount": 0.38,
"rate": 1.8
},
{
"tax_code": "ACQUIRER_TO_CUSTOMER_COMMISSION_VAT_TAX",
"reference": "Acquirer to customer commission VAT Tax",
"amount": 0.08,
"rate": 0.21
},
{
"tax_code": "CUSTOMER_TO_MERCHANT_COMMISSION",
"reference": "Customer to merchant commission",
"amount": 1.26,
"rate": 5.99
},
{
"tax_code": "CUSTOMER_TO_MERCHANT_COMMISSION_VAT_TAX",
"reference": "Customer to merchant commission VAT Tax",
"amount": 0.26,
"rate": 0.21
},
{
"tax_code": "MERCHANT_VAT_TAX",
"reference": "Merchant VAT Tax",
"amount": 0.58,
"rate": 0.21
},
{
"tax_code": "MERCHANT_INCOME_TAX",
"reference": "Merchant Income Tax",
"amount": 0.19
},
{
"tax_code": "MERCHANT_IIBB_TAX",
"reference": "Merchant IIBB Tax",
"amount": 0.68
}
]
}
}
}
Validación de los webhooks
Para garantizar que su servidor solo procese entregas de webhooks enviadas por Menta y para asegurarse de que la entrega no haya sido manipulada, es fundamental validar la firma del webhook antes de continuar con el procesamiento.
Menta utiliza la secret key proporcionada durante la suscripción para generar un hash de firma distinto en cada solicitud, la cual se envía como valor del encabezado X-Menta-Signature-V1 en cada webhook. Para verificar que la firma es auténtica, el cliente debe aplicar la misma función de hash (HMAC con SHA-256) junto con la secret key, y asegurarse de que coincida con la firma enviada.
Pasos para generar y validar firma
- Concatenar los siguientes elementos:
Timestamp | El punto "." | El cuerpo (payload) de la solicitud. |
---|---|---|
enviado en el encabezado X-Menta-Signature-Timestamp | . | en formato JSON as String |
- Obtener la secret key guardada en su servidor.
- Aplicar la función de hash HMAC con algoritmo SHA-256 junto con la secret key.
- Comparar la firma generada con la firma enviada en el encabezado X-Menta-Signature-V1.
Ejemplo de implementación en Kotlin
Puede utilizar el lenguaje de programación que prefiera para implementar la verificación HMAC en su código.
import java.util.Formatter
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
class HashProvider {
fun generateHMAC(timestamp: String, body: String, secretKey: String): String {
val msg = "$timestamp.$body"
val signingKey = SecretKeySpec(secretKey.toByteArray(), "HmacSHA256")
val mac = Mac.getInstance("HmacSHA256")
mac.init(signingKey)
val bytes = mac.doFinal(msg.toByteArray())
return format(bytes)
}
private fun format(bytes: ByteArray): String {
val formatter = Formatter()
bytes.forEach { formatter.format("%02x", it) }
return formatter.toString()
}
}
Probando validación
Para probar la validación de webhooks, puede utilizar los siguientes valores de secret key, timestamp (expresado como Unix timestamp) y JSON payload:
secret key | timestamp |
---|---|
secretKey! | 1697657734 |
Payload:
{
"notification_type": "OPERATION_CREATED",
"merchant_id": "c2192cf8-e221-4ccc-83d4-1c2105d46b9a",
"user": "[email protected]",
"datetime": "2023-07-23T21:10Z",
"detail": {
"terminal_id": "39b4500d-8e14-45ae-89e2-63135534132b",
"operation_id": "8e02915b-9387-412c-946a-bf9c046f62ff",
"ticket_id": "50691299",
"operation_type": "PAYMENT",
"qr_id": null,
"operation_status": "APPROVED",
"currency": "ARS",
"operation_amount": "100",
"operation_additional_info": "",
"payment_method_type": "DEBIT",
"payment_method_detail": "MASTERCARD"
}
}
Si su implementación es correcta, las firmas que genere deben coincidir con los siguientes valores de firma:
el resultado de la firma y el header X-Menta-Signature-V1:
58f8e39497b01f53d13c5144fcd74ddc3bb33aee35d99cd4989b5e04bdf216f7
Recomendaciones
-
Valide el timestamp incluido en la solicitud para evitar ataques de repetición (replay-attack). Un ataque de repetición ocurre cuando un atacante intercepta una solicitud válida junto con su firma y la retransmite. El timestamp se incluye en el proceso de hash y es verificado por la firma, por lo que un atacante no puede cambiar el timestamp sin invalidar la firma. Si la firma es válida pero el timestamp es demasiado antiguo, se recomienda rechazar la solicitud.
-
Asegúrese de que el payload y los encabezados no se modifiquen antes de la verificación. Si utiliza un proxy o un equilibrador de carga, asegúrese de que estos componentes no modifiquen el payload ni los encabezados.
-
Si su lenguaje de programación y la implementación del servidor especifican una codificación de caracteres, asegúrese de manejar el payload como UTF-8. Y ademas, se encuentren en Formato JSON antes de generar el hash.
-
Tenga en consideración la lógica de reintentos mencionada y tenga un control posterior de conciliación para evitar perder información relevante.