Las credenciales se leen en tiempo de ejecución desde la configuración o la base de datos, no desde el código.
La llave secreta no aparece en el repositorio, ni en el navegador, ni en los logs.
Se manda Idempotency-Key en cada creación, derivada del contenido de la orden, no solo de su id.
El id del pago se guarda localmente al crearlo (no hay búsqueda por external_reference).
La respuesta de la creación se verifica: total, sales_tax_amount y service_fee_amount son los esperados.
El endpoint de webhook verifica la firma HMAC sobre el cuerpo crudo, con comparación de tiempo constante.
El webhook responde 200 rápido y procesa de forma asíncrona e idempotente por el id del evento.
El endpoint de webhook está registrado en el portal, en el entorno que le corresponde a la llave.
Llegar a success_urlno marca la orden como pagada.
Existe un respaldo de consulta directa para las órdenes que quedaron en pending.
Las llamadas HTTP tienen timeouts (~30 s), con reintentos solo en la creación y con la misma llave de idempotencia.
La lógica de reversión intenta primero la anulación y cae al reembolso cuando la transacción ya está liquidada.
Se probó de punta a punta al menos un pago real con el monto mínimo antes de salir a producción.
Solo móvil: la llave secreta no está en el binario de la app, el checkout se abre en el navegador del sistema,
las URLs de retorno son Universal/App Links con https, y la app confirma a través de su propio backend.