Cómo comprobar un pago mediante la API sin webhook
Comprobación de facturas desde el servidor mediante la API: estados, polling limitado, errores, ejecución única del pedido y recuperación tras reiniciar.
Actualizado:
Puedes comprobar un pago sin webhook desde el servidor de tu aplicación: consultar periódicamente la factura mediante la API de Cryptanio, relacionarla con el pedido y ejecutarlo cuando el estado actual sea paid. Esta consulta periódica se llama polling. Requiere una cola de comprobaciones, gestión de errores y protección frente a entregas duplicadas: una respuesta HTTP correcta no confirma por sí sola el pago.
Esta opción resulta útil si la aplicación puede ejecutar tareas en segundo plano, pero todavía no es práctico recibir notificaciones entrantes. Los cambios se detectan en la siguiente consulta; el polling no proporciona una notificación inmediata.
Qué necesitas
Guarda en tu base de datos la relación entre el pedido y la factura: su id, el proyecto, tu reference, el precio acordado y el modo de funcionamiento. Conserva los identificadores como cadenas. Debes comprobar la factura de ese pedido concreto, no una transacción encontrada con un importe parecido.
Prepara una clave API de servidor con permiso de lectura de invoices, acceso al servicio payments y al proyecto correspondiente. Un pedido real necesita el modo de producción adecuado. No pongas la clave en el navegador, el enlace del comprador ni un registro público. El acceso de solo lectura basta para un proceso de conciliación independiente.
Paso 1. Obtén el estado actual
El ejemplo consulta una sola factura. Define CRYPTANIO_API_KEY y CRYPTANIO_RESOURCE_ID en el servidor; el segundo valor es el id guardado de la factura, no el número de pedido ni el TXID.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_RESOURCE_ID:?Set CRYPTANIO_RESOURCE_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
urlencode() {
local LC_ALL=C text="$1" char index
for ((index=0; index<${#text}; index++)); do
char="${text:index:1}"
case "$char" in [a-zA-Z0-9.~_-]) printf '%s' "$char" ;; *) printf '%%%02X' "'$char" ;; esac
done
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request GET \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
"https://api.cryptanio.com/v1/invoices/$(urlencode "$CRYPTANIO_RESOURCE_ID")" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
Tras HTTP 200, analiza el JSON y coteja id, project_id, reference y sandbox con tus registros. Para un pedido real se espera sandbox=false. Si guardaste un precio en invoice_currency e invoice_amount, compruébalos también. Una factura ajena al pedido o un registro de otro modo no deben modificarlo.
El enlace checkout_url de GET es de consulta. No sustituyas con él el enlace interactivo guardado de la respuesta de creación de la factura: leer el estado no crea un pago nuevo ni proporciona un checkout interactivo nuevo.
Paso 2. Distingue la espera del pago correcto
Decide según el status de la factura:
pending: se espera el pago;seen: se ha detectado una transferencia, pero el pago todavía no se ha completado;partial: se ha confirmado parte del importe y la espera continúa según las reglas del pago;underpaid,overpaid,late: se necesita una revisión; ten en cuentaneeds_decisiony la decisión del vendedor;paid: la factura consta como pagada; después de verificar su relación con el pedido, puedes preparar la entrega;expired,canceled: termina la espera activa del comprador, pero conserva el registro para una conciliación posterior.
Que venza el plazo no descarta una recepción tardía. Una decisión manual y una reversión de la transacción también pueden cambiar el tratamiento posterior. No declares un estado definitivamente final ni borres la relación entre pedido y factura al cerrar la página.
Una transacción confirmada y una factura pagada son cosas distintas. Las confirmaciones no prueban que el activo, el importe y los datos de pago requeridos correspondan a ese pedido concreto. La guía para investigar un pedido impagado explica estas diferencias.
Paso 3. Ten en cuenta el importe y el intento de pago
Al cambiar el método de pago, una factura puede tener varios intentos. Para una factura pagada, ten en cuenta settled_attempt_id y settled_attempt. Al revisar el historial, comprueba attempt_history.has_more y el cursor de continuación; para las transferencias del intento seleccionado, usa payment_history. La primera página no contiene necesariamente todo el historial.
amount es el importe exacto en criptomonedas que debe enviarse. amount_raw y paid son cadenas con enteros en las unidades mínimas del activo; decimals define la escala. No los compares como números de coma flotante ni por orden alfabético como cadenas normales. Tu propia comprobación del importe tampoco debe sustituir el estado de la factura y las reglas del proyecto: un exceso de pago puede aceptarse o enviarse a revisión. Tampoco debes sumar pagos insuficientes sin comprobar las condiciones.
Paso 4. Limita el polling y los reintentos
Pon las comprobaciones en una cola de servidor con concurrencia limitada. Varias pestañas del comprador no deben crear ciclos de consulta independientes. Considera el tráfico conjunto de la cuenta: las distintas claves API y modos comparten la cuota. Los límites actuales se indican en la referencia de la API.
Elige los tiempos de espera según tu proceso y deja margen para otras solicitudes. Ante un error de red o HTTP 500/503, utiliza reintentos limitados con una espera creciente y variación aleatoria. Con HTTP 429, reduce el tráfico conjunto; si recibes Retry-After, respeta la pausa indicada. La ausencia de esa cabecera no debe provocar un reintento inmediato. Cada solicitud necesita un tiempo de espera máximo, y cada serie de reintentos, un límite tras el cual la tarea quede pendiente de revisión posterior.
HTTP 401/403 exigen comprobar la clave y los permisos. Con 404, comprueba el ID, el proyecto y el modo: las restricciones de acceso pueden ocultar una factura. Estos errores no significan que el cliente no haya pagado nada.
Paso 5. Guarda la ejecución y la recuperación
Cuando el estado sea paid, guarda de forma coherente el estado del pedido y la tarea de entrega. Utiliza una clave única de pedido y protección frente al procesamiento simultáneo. Volver a leer paid no debe entregar el acceso o el producto otra vez; un fallo posterior a la entrega no debe generar una segunda ejecución.
Guarda la última comprobación correcta y la siguiente tarea en un almacenamiento persistente. Tras reiniciar, retoma las comprobaciones y tareas pendientes, en lugar de iniciar el proceso solo al abrir la página. Limita la espera activa según los plazos de tu proceso; una conciliación independiente en segundo plano debe contemplar pagos tardíos, decisiones y reversiones, incluidos los pedidos previamente pagados. Antes de una entrega pendiente, vuelve a comprobar que siga autorizada; prevé la revisión de los accesos ya concedidos.
Con respuestas preparadas, comprueba la repetición de paid, un tiempo de espera agotado, un reinicio y una transición tras una reversión. El resultado es correcto cuando el pedido se ejecuta una vez y un pago incierto o disputado queda bajo revisión. Más adelante puedes complementar el polling con un webhook, conservando la misma protección de ejecución.