Facturas
Creá, listá, consultá y anulá facturas; todos los campos; cotizaciones; y cómo el hosted checkout maneja la elección de red, el estado en vivo y la vuelta a tu sitio.
Última actualización 27 oct 2026
Una factura le pide a un comprador un monto en USD, pagado en USDT o USDC en una red. Cada factura tiene su propia dirección de depósito, así que un pago se asocia a su factura por la dirección a la que se mandó.
Crear una factura
POST /api/v1/crypto/invoices — permiso crypto-invoices:create, header Idempotency-Key obligatorio (ver Idempotencia).
Hay dos formas de crearla:
- Red y token fijos. Mandá
networkytoken. La factura quedaPENDINGde inmediato y la respuesta traedeposit_address,totalypayment_uri. - Elige el comprador. Omití los dos. La factura toma el entorno de tu API key; con una key legacy, mandá
modepara elegirlo: Sandbox (mode: "test"en la API) o Live ("live"). La factura quedaAWAITING_SELECTION; el comprador elige una red y un token en el hosted checkout, que recién ahí asigna la dirección.
Los requests válidos más chicos:
{ "amount": "49.00", "title": "Pro plan, 1 month", "network": "POLYGON_AMOY", "token": "USDC" }{ "amount": "49.00", "title": "Pro plan, 1 month", "mode": "test" }Un request completo, con un vendedor, la parte de un socio y un markup:
curl -X POST https://api.aliquot.cc/api/v1/crypto/invoices \
-H "Authorization: Bearer $ALIQUOT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-77310" \
-d '{
"amount": "100.00",
"network": "POLYGON_AMOY",
"token": "USDC",
"fee_payer": "CUSTOMER",
"markup_percent": 2,
"merchant": "halden_type",
"splits": [{ "account": "kerning_weekly", "percent": 10 }],
"title": "Halden Grotesk — Desktop license",
"merchant_order_id": "order-77310",
"buyer_email": "[email protected]",
"redirect_url": "https://shop.example.com/orders/77310",
"metadata": { "cart_id": "c_5521" }
}'Campos del request
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
amount | string | Sí | Precio en USD, con como máximo dos decimales. Mínimo "1.00". |
title | string | Sí | Lo que se está pagando, 1–200 caracteres. Se muestra en el checkout. |
currency | string | No | Solo "USD", el valor por defecto. |
network | string | Con token | Uno de POLYGON_MAINNET, POLYGON_AMOY, BSC_MAINNET, BSC_TESTNET, BASE_MAINNET, BASE_SEPOLIA. Mandalo junto con token, u omití los dos. |
token | string | Con network | USDT o USDC, disponible en esa red (ver Redes y tokens). |
mode | string | Con una key legacy, si no hay network | "test" (Sandbox) o "live". Si también mandás network, tienen que coincidir. Una key de Sandbox o de Live completa su propio modo y responde 403 API_KEY_ENVIRONMENT_MISMATCH para el otro. |
fee_payer | string | No | CUSTOMER (por defecto): el comprador paga la comisión de procesamiento y el markup encima de amount. MERCHANT: el comprador paga amount y ambos salen de la parte del vendedor. |
markup_percent | number | No | La parte de tu plataforma, 0–20, con como máximo dos decimales. Por defecto 0. |
merchant | string | No | Reference de la cuenta MERCHANT que vende. Si se omite, tu cuenta platform es el vendedor. Una cuenta PARTNER no puede ser el vendedor. |
splits | array | No | Hasta 20 entradas { "account": "<reference>", "percent": <number> }. Cada porcentaje es mayor que 0 con como máximo dos decimales; en total, como máximo 100. Ninguna cuenta dos veces. |
description | string | No | Hasta 2000 caracteres. |
merchant_order_id | string | No | El ID de tu pedido, 1–200 caracteres. Se puede buscar con q y viene en los webhooks invoice.created y refund.claimable. |
redirect_url | string | No | A dónde manda el checkout al comprador después de pagar. Ver Volver a tu sitio. |
metadata | object | No | Cualquier objeto JSON. Se guarda y se devuelve tal cual. |
buyer_email | string | No | Si está, el comprador recibe por email los links de reclamo de reembolsos. Nunca se muestra en el checkout. |
expires_in_minutes | integer | No | Ventana de pago, 1–1440. Por defecto 30. Se cuenta desde la creación si mandás network y token; si no, desde que el comprador elige un par. Ver Ventana de pago. |
Cómo fee_payer, markup_percent y splits se convierten en montos se explica, con un ejemplo resuelto, en Ledger y splits. Las cuentas nombradas en merchant y splits tienen que existir; una reference desconocida da 422 en ese campo.
El objeto factura
201 Created devuelve la factura. Un replay del mismo Idempotency-Key la devuelve con 200.
| Campo | Descripción |
|---|---|
id | inv_…. |
status | Ver Estados. |
livemode | false en Sandbox, true en Live. |
currency | "USD". |
fee_payer | CUSTOMER o MERCHANT. |
network, token, decimals | El par de pago y los decimales del token. null mientras está AWAITING_SELECTION. |
base | El precio en unidades del token una vez elegido el par; antes, el precio en USD. |
platform_fee, markup, merchant_net, total | El desglose de comisiones en unidades del token. total es lo que el comprador tiene que mandar. null mientras está AWAITING_SELECTION. |
received | Suma de las transferencias en el token de la factura vistas hasta ahora. |
deposit_address | La dirección a la que paga el comprador. null mientras está AWAITING_SELECTION. |
payment_uri | Una URI EIP-681 para wallets y códigos QR, que pide exactamente total del token. |
required_confirmations | El umbral de confirmaciones que aplica a esta factura. Ver Confirmaciones. |
expires_at | Fin de la ventana de pago. |
merchant | { "reference", "name" } del vendedor. |
splits | [{ "account", "name", "percent", "amount" }]; amount es null hasta que se elige un par. |
title, description, merchant_order_id, redirect_url, metadata, buyer_email | Tal como se mandaron. |
payment_link_id | El payment link que creó la factura (plink_…), o null si la creaste con este endpoint. |
pay_url | El hosted checkout: https://aliquot.cc/pay/{id}. |
created_at, detected_at, paid_at, cancelled_at | Timestamps; null hasta que ocurren. |
Estados
| Estado | Significado | Webhook al entrar |
|---|---|---|
AWAITING_SELECTION | Espera que el comprador elija una red y un token. | invoice.created (al crearse) |
PENDING | Dirección asignada, esperando una transferencia. | invoice.created (al crearse) |
DETECTED | Se vio una transferencia y se está confirmando. No vence. | invoice.detected |
PAID | Confirmada, y el monto cubre el total. | invoice.paid |
OVERPAID | Confirmada con más que el total; el excedente es reembolsable. | invoice.paid e invoice.overpaid |
UNDERPAID | Confirmada pero incompleta. El comprador puede mandar el resto antes de expires_at. | invoice.underpaid |
EXPIRED | La ventana terminó sin que llegara nada. | invoice.expired |
EXPIRED_UNDERPAID | La ventana terminó con un pago parcial, retenido para el comprador como reembolso. | invoice.expired (y refund.claimable) |
REFUNDED | El pago parcial se le devolvió al comprador. | refund.completed |
CANCELLED | La anulaste. | Ninguno |
El diagrama completo del ciclo de vida está en la Introducción.
Consultar una factura
GET /api/v1/crypto/invoices/{id} — permiso crypto-invoices:read.
Devuelve el objeto factura y además:
| Campo | Descripción |
|---|---|
idempotency_key | El Idempotency-Key con el que se creó (null para las facturas iniciadas desde un payment link). |
transfers | [{ "tx_hash", "token", "amount", "confirmations", "required", "status", "block_number" }]. status es SEEN (confirmándose), CONFIRMED u ORPHANED (su bloque fue reemplazado por una reorg). token difiere del de la factura en un pago con el token equivocado. |
ledger | Una vez PAID u OVERPAID: el asiento de liquidación, { "id", "entries": [{ "side", "account", "amount" }] }. Ver Ledger y splits. |
refunds | Los reembolsos retenidos para el comprador de esta factura, con claim_url mientras se pueden reclamar. Ver Reembolsos. |
Listar facturas
GET /api/v1/crypto/invoices — permiso crypto-invoices:read.
| Parámetro de query | Descripción |
|---|---|
mode | test o live. Por defecto, el entorno de tu key (live con una key legacy). |
status | Un estado de la tabla de arriba. |
q | Busca por ID de factura, dirección de depósito, hash de transacción o merchant_order_id. |
cursor, limit | Paginación; limit 1–100, por defecto 25. |
curl "https://api.aliquot.cc/api/v1/crypto/invoices?mode=test&q=order-77310" \
-H "Authorization: Bearer $ALIQUOT_API_KEY"La respuesta es { "items": [...], "next_cursor": "..." }, de la más nueva a la más vieja.
Anular una factura
POST /api/v1/crypto/invoices/{id}/void — permiso crypto-invoices:update. Sin body.
Anular pasa una factura AWAITING_SELECTION o PENDING a CANCELLED y la devuelve. Cualquier otro estado responde 409 CONFLICT: una vez detectado un pago, la factura ya no se puede anular. Anular no manda ningún webhook.
Si el comprador paga igual una factura cancelada, el dinero no se le acredita al vendedor; queda retenido para el comprador como un reembolso LATE_PAYMENT.
Cotización
POST /api/v1/crypto/quote — permiso crypto-invoices:read. Calcula el mismo desglose que tendría una factura, sin crear nada.
{
"amount": "100.00",
"network": "POLYGON_AMOY",
"token": "USDC",
"fee_payer": "CUSTOMER",
"markup_percent": 2,
"merchant": "halden_type",
"splits": [{ "account": "kerning_weekly", "percent": 10 }]
}network y token son obligatorios; fee_payer, markup_percent, merchant y splits son opcionales y toman los mismos valores por defecto que en una factura. La respuesta:
{
"network": "POLYGON_AMOY",
"token": "USDC",
"decimals": 6,
"fee_payer": "CUSTOMER",
"base": "100.00",
"platform_fee": "0.50",
"markup": "2.00",
"merchant_net": "90.00",
"total": "102.50",
"splits": [{ "account": "kerning_weekly", "percent": 10, "amount": "10.00" }]
}El hosted checkout
Mandá al comprador a pay_url (https://aliquot.cc/pay/{id}). La página no necesita nada más de tu parte.
- Elección de red. En una factura
AWAITING_SELECTION, el comprador primero elige uno de los pares de red y token disponibles en el entorno de la factura. Al elegir, se asigna la dirección de depósito, se calculatotalpara ese token y la ventana de pago vuelve a arrancar: el comprador tiene todo elexpires_in_minutesdesde ese momento. Una vez elegido, el par no se puede cambiar. - Pago. El comprador ve el monto exacto, la dirección y un código QR (la
payment_uriEIP-681), y puede pagar desde una wallet del navegador con un botón, que primero cambia la wallet a la red correcta. - Estado en vivo. La página sigue a la factura mientras pasa por
DETECTED, con un contador de confirmaciones, hastaPAID, y después muestra un comprobante, con la entrega del payment link cuando la hay. Si el comprador paga de menos, la página le pide el resto; si la factura vence o hay un reembolso retenido, la página lo indica.
El checkout está construido sobre endpoints públicos que también podés llamar para armar tu propia página de pago:
| Endpoint | Descripción |
|---|---|
GET /api/v1/public/pay/{id} | La vista de la factura apta para el comprador: montos, dirección, confirmations, allowed_pairs, estado del reembolso y fulfillment (null hasta que la factura está pagada; ver Payment links). Sin datos del ledger ni IDs internos. |
POST /api/v1/public/pay/{id}/select | Body { "network", "token" }. Elige el par de una factura AWAITING_SELECTION. 409 si ya se eligió un par distinto o si la factura venció. |
GET /api/v1/public/pay/{id}/stream | Server-sent events. Un evento invoice trae la misma vista que GET /api/v1/public/pay/{id} en cada cambio. El stream se cierra cuando la factura llega a PAID, OVERPAID, EXPIRED, EXPIRED_UNDERPAID, REFUNDED o CANCELLED, y si no, a los 30 minutos; EventSource se reconecta solo. |
const source = new EventSource('https://api.aliquot.cc/api/v1/public/pay/inv_01JB7Q8M3X4K9TPZ2R6WVN5HDC/stream');
source.addEventListener('invoice', (e) => {
const view = JSON.parse(e.data);
console.log(view.status, `${view.confirmations}/${view.required_confirmations}`);
});Cualquiera que tenga el ID de la factura puede abrir su checkout, así que no trates el ID como un secreto, y nunca entregues un pedido a partir de lo que informa un navegador. Entregalo a partir del webhook invoice.paid o de GET /api/v1/crypto/invoices/{id}.
Volver a tu sitio
Cuando el checkout ve que la factura llega a PAID mientras el comprador la tiene abierta, cuenta cinco segundos y navega a redirect_url; el comprador puede elegir quedarse. Un comprador que abre la página después recibe, en cambio, un link para volver, y lo mismo pasa cuando el comprobante muestra la entrega de un payment link, para que no se la pierda. Aliquot no le agrega nada a la URL, así que poné lo que necesites para encontrar el pedido en la propia redirect_url, por ejemplo https://shop.example.com/orders/77310.
Llegar a redirect_url no prueba el pago. Verificá el estado de la factura en tu servidor.
Ventana de pago
expires_in_minutes (30 por defecto) es la ventana de pago: cuánto tiempo tiene el comprador para mandar la plata una vez que existe una dirección de depósito.
- Red y token enviados al crear. La ventana arranca al crear la factura:
expires_ates el momento de creación másexpires_in_minutes. - El comprador elige la red. Primero
expires_atlimita la elección: el comprador tiene que elegir un par dentro de losexpires_in_minutesdesde la creación, o la factura pasa aEXPIRED. Al elegir,expires_atpasa a ser el momento de la elección más el mismoexpires_in_minutes, así que el comprador siempre tiene la ventana completa para pagar, y todo el flujo puede durar hasta el doble deexpires_in_minutes.
Las facturas creadas desde un payment link usan de la misma forma la ventana por defecto de la organización (30 minutos). Una factura que sigue PENDING o AWAITING_SELECTION en expires_at pasa a EXPIRED; una que está UNDERPAID pasa a EXPIRED_UNDERPAID. Una factura DETECTED nunca vence, porque el comprador pagó a tiempo.