Autenticación Webhooks

Es importante verificar la integridad de todas las notificaciones que Conekta te envía por webhook para asegurar que son legítimos y que no han sido modificados. El mecanismo para hacer esta verificación involucra un juego de llaves RSA público/privado para tu compañía para firmar un sha256 del cuerpo de la petición.

Inicialización - Solo Una Vez

El primer paso es crear un juego de llaves RSA en Conekta, vas a recibir la llave pública para verificar mensajes y Conekta se va a quedar con la llave privada para firmar mensajes. Solo es necesario realizar este proceso una vez. Puedes iniciar llaves de webhook con un curl así:

curl --request POST \
  --url https://api.conekta.io/webhook_keys \
  --header 'accept: application/vnd.conekta-v2.0.0+json' \
  -u key_eYvWV7gSDkNYXsmr: \
  --header 'content-type: application/json' \
  --data-raw '{
    "active": true
  }'

Lo cuál va retornar el siguiente mensaje

{
  "active": true,
  "livemode": false,
  "created_at": 1651706790,
  "id": "62730ba6fb7dfd6a712f118e",
  "object": "webhook_key",
  "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0iz57mpVAvxQtuxOyWsW\nhM1Jai7WB5cNZFs8xK53A9X9LQiXz30pzoFIhVo9Zm5K5GBpb9frCH8w6yr+/xrX\n0VUjbp1VTgZ2iGOm83ykLN7YYQJk5pCt/B69eFPYbMCKFzvauwTtN9tf2KcLQQ2y\nSohxd3H51uUIGcxnSR5oVPoCdY4geSWfK0/FE4SAyVsTB/b3mS0KUor7R2tZupKm\nrS26O6QFQrk0ELuGIIriJimjxaQG9V7E/TumKkbDPAcJsiZBF8oep02sXbdNpaxl\nj5PNkVIQ2F09BfDJl71DrcAIKYXG7HSgDEoiRkZ3jIzudUNA+qkpYwHJ5Qx9qmgy\nuQIDAQAB\n-----END PUBLIC KEY-----\n"
}

Puedes ver más detalle en la sección de en la referencia de llaves para webhooks.

Verificación de Mensajes - En Cada Mensaje

Puedes validar la integridad del mensaje consumiendo el header de 'DIGEST' y comprobando que representa el el sha256 del mensaje cifrado por la llave privada. Como nota, es esperado que la codificación del payload está en UTF-8.

{"data":{"object":{"id":"61fdc53b0211a6764e57ec4f","livemode":true,"created_at":1644021051,"currency":"MXN","channel":{"segment":"Checkout","checkout_request_id":"4ed49de7-60e7-4e87-a0de-48b433f3ea2f","checkout_request_type":"PaymentLink","id":"channel_2rFeDewRPQpn3NFoe"},"payment_method":{"name":"OscarNava","exp_month":"12","exp_year":"22","object":"card_payment","type":"credit","last4":"0002","brand":"visa","issuer":"banamex","account_type":"BANAMEX","country":"MX","fraud_indicators":[]},"object":"charge","description":"Payment from order","status":"declined","amount":10000,"fee":708,"customer_id":"cus_2rFeDEaPCq2GkhLS8","order_id":"ord_2rFeDeCbsDG8R6bK4"},"previous_attributes":{}},"livemode":true,"webhook_status":"failing","webhook_logs":[{"id":"webhl_2rFeDewRPQpn3NFoj","url":"https://www.example.com/conekta/webhooks","failed_attempts":5,"last_http_response_status":598,"object":"webhook_log","last_attempted_at":0}],"id":"61fdc53b0211a6764e57ec53","object":"event","type":"charge.created","created_at":1644021051}

Digest ejemplo:

PY0lEriF5Tt4T0ZVqwEQtS5+skbqUpCWwwDs5EtBEX5vriK15KuLCuTndttDR4jvlSvDsDVSUOCxXrpxMJ0x0cePCa79xGa23r/hcv4CPcfr2UMe4IKJuJSlJc3XCfOQB1rfk6fQCMr7AbiVtvacr91yYxg7QoJq2/Y0YRo7RreT61X7/dmGhgzFrG0TALQ2R0PbQyAXIvO7l5+00Yncdc0IeyCBTL3/Wa0zN+Dc4UZW3iWFpNOIyljDKxmBp+2D0DxPeLfXw7fnVskQMSCM7rCVXSoP7k8BCyIDRZ62QieMXW5CubixFaPFrI1f6K8gh67lguYF2XSY/bYb7IU4Jg==

Puedes comprobar la integridad del mensaje con el siguiente código

#decode base64
cat DIGEST.sha256.base64 | base64 --decode > signature.sha256

#Verify digest
openssl dgst -sha256 -verify pubkey.pem -signature signature.sha256 notification_payload.utf8.json

#Verified OK
require 'openssl'
require 'base64'
pubkey = OpenSSL::PKey::RSA.new("-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0iz57mpVAvxQtuxOyWsW\nhM1Jai7WB5cNZFs8xK53A9X9LQiXz30pzoFIhVo9Zm5K5GBpb9frCH8w6yr+/xrX\n0VUjbp1VTgZ2iGOm83ykLN7YYQJk5pCt/B69eFPYbMCKFzvauwTtN9tf2KcLQQ2y\nSohxd3H51uUIGcxnSR5oVPoCdY4geSWfK0/FE4SAyVsTB/b3mS0KUor7R2tZupKm\nrS26O6QFQrk0ELuGIIriJimjxaQG9V7E/TumKkbDPAcJsiZBF8oep02sXbdNpaxl\nj5PNkVIQ2F09BfDJl71DrcAIKYXG7HSgDEoiRkZ3jIzudUNA+qkpYwHJ5Qx9qmgy\nuQIDAQAB\n-----END PUBLIC KEY-----\n")

payload = '{"data":{"object":{"id":"61fdc53b0211a6764e57ec4f","livemode":true,"created_at":1644021051,"currency":"MXN","channel":{"segment":"Checkout","checkout_request_id":"4ed49de7-60e7-4e87-a0de-48b433f3ea2f","checkout_request_type":"PaymentLink","id":"channel_2rFeDewRPQpn3NFoe"},"payment_method":{"name":"OscarNava","exp_month":"12","exp_year":"22","object":"card_payment","type":"credit","last4":"0002","brand":"visa","issuer":"banamex","account_type":"BANAMEX","country":"MX","fraud_indicators":[]},"object":"charge","description":"Payment from order","status":"declined","amount":10000,"fee":708,"customer_id":"cus_2rFeDEaPCq2GkhLS8","order_id":"ord_2rFeDeCbsDG8R6bK4"},"previous_attributes":{}},"livemode":true,"webhook_status":"failing","webhook_logs":[{"id":"webhl_2rFeDewRPQpn3NFoj","url":"https://www.example.com/conekta/webhooks","failed_attempts":5,"last_http_response_status":598,"object":"webhook_log","last_attempted_at":0}],"id":"61fdc53b0211a6764e57ec53","object":"event","type":"charge.created","created_at":1644021051}'

signature = 'PY0lEriF5Tt4T0ZVqwEQtS5+skbqUpCWwwDs5EtBEX5vriK15KuLCuTndttDR4jvlSvDsDVSUOCxXrpxMJ0x0cePCa79xGa23r/hcv4CPcfr2UMe4IKJuJSlJc3XCfOQB1rfk6fQCMr7AbiVtvacr91yYxg7QoJq2/Y0YRo7RreT61X7/dmGhgzFrG0TALQ2R0PbQyAXIvO7l5+00Yncdc0IeyCBTL3/Wa0zN+Dc4UZW3iWFpNOIyljDKxmBp+2D0DxPeLfXw7fnVskQMSCM7rCVXSoP7k8BCyIDRZ62QieMXW5CubixFaPFrI1f6K8gh67lguYF2XSY/bYb7IU4Jg=='

if pubkey.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), payload)
  puts "Verification succeeded!"
end