Ir al contenido

Docs

Host-to-host (H2H): muestra el pago cripto en tu propia interfaz

Muestra un pago de CryptoLike en tu pantalla: dirección, monto exacto, red, QR y plazo de la factura, elección de moneda por API y pedido confirmado por webhook.

Actualizado

Host-to-host (H2H) significa que tu servidor crea el pago con la API de CryptoLike y tu propia interfaz lo muestra: el pagador nunca abre una página de CryptoLike. Cada respuesta con la factura (invoice) trae lo que necesita la pantalla — la dirección, el monto exacto, la red, los datos del QR y el plazo de pago — y un webhook firmado le avisa a tu servidor cuando el pago está confirmado. Aquí verás cuándo elegir H2H, cómo es el flujo, qué debe mostrar la pantalla y qué hacer cuando el pagador envía de menos, de más o tarde.

Cuándo elegir H2H, la página de pago o el widget

Las tres opciones muestran la misma factura, con los mismos estados y webhooks; cambia quién dibuja la pantalla de pago.

Opción Quién dibuja la pantalla Elígela cuando Qué construyes
Página de pago CryptoLike, en checkout_url Quieres el camino más corto: una redirección y un webhook Una redirección desde tu backend
Widget de pago CryptoLike, en un marco dentro de tu página El cliente debe quedarse en tu flujo de compra, y la página de CryptoLike puede aparecer dentro Tu origen en la lista permitida, un script y una regla de CSP
H2H Tú Una app para celular, un quiosco, un bot de mensajería o un flujo de compra que debe verse por completo como tu producto La pantalla de pago con cada elemento de la lista de abajo

La página de pago y el widget ya muestran la advertencia de red, el plazo, el estado en vivo y los textos para un pago insuficiente o vencido en inglés, ruso y español. Con H2H todo eso queda a tu cargo, así que empieza por la página de pago o el widget, salvo que tu interfaz tenga que ser del todo tuya. El widget se describe en la guía del widget de pago.

Cómo es el flujo

  1. Tu servidor crea la factura — POST /v1/invoices, firmada con tu clave API y con el encabezado Idempotency-Key. Envía coin_id para fijar la moneda ahora; omítelo para que el pagador la elija en tu interfaz.
  2. Si la factura no tiene moneda, el pagador la elige. Tu servidor lee GET /v1/invoices/{id}/coin-options, tu interfaz muestra las monedas y tu servidor fija la elección con POST /v1/invoices/{id}/coin.
  3. Tu interfaz muestra el pago a partir de la respuesta con la factura: red, dirección, payer_amount, QR, cuenta regresiva hasta ttl_expires_at y estado.
  4. Tu servidor sigue el estado con los webhooks invoice.* y, para el contador de confirmaciones, con GET /v1/invoices/{id}, no más de una vez cada pocos segundos. Tu página le pregunta a tu servidor, nunca a la API de CryptoLike.
  5. Tu servidor confirma el pedido solo con un webhook invoice.paid verificado; después tu interfaz muestra la confirmación.

El secreto de la API se queda en tu servidor. El navegador o la app reciben solo lo que tu servidor decide mostrar y nunca firman solicitudes. La firma se explica en autenticación.

Crea la factura

La solicitud de abajo fija la moneda desde el inicio (USDT en TRON). Sin coin_id, la respuesta trae los campos de la moneda, address y el bloque de pago en null hasta la elección.

http
POST /v1/invoices HTTP/1.1
Host: api.cryptolike.net
Content-Type: application/json
Idempotency-Key: order-10042
X-API-Key: <key_id>
X-Timestamp: <unix seconds>
X-Signature: <HMAC-SHA256 of the request>

{"amount": "49.90", "currency": "USD", "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f", "order_id": "10042", "description": "Order #10042 — annual plan", "ttl_seconds": 1800, "fee_paid_by": "merchant"}

La respuesta es la factura (valores de ejemplo de la referencia de la API):

json
{
  "id": "4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "status": "new",
  "currency": "USD",
  "amount": "49.90",
  "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f",
  "ticker": "USDT",
  "network": "tron",
  "address": "TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g",
  "amount_crypto": "49.90",
  "payer_amount": "49.90",
  "paid_amount": "0",
  "remaining_amount": "49.90",
  "overpaid_amount": "0",
  "amount_fiat": "49.90",
  "fiat_currency": "USD",
  "rate_locked": "1.0004",
  "rate_locked_at": "2026-09-16T12:00:00Z",
  "late_payment": false,
  "fee_paid_by": "merchant",
  "order_id": "10042",
  "description": "Order #10042 — annual plan",
  "return_url": null,
  "webhook_url": null,
  "metadata": null,
  "allowed_coin_ids": null,
  "checkout_url": "https://cryptolike.net/pay/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "ttl_expires_at": "2026-09-16T12:30:00Z",
  "expired_at": null,
  "created_at": "2026-09-16T12:00:00Z",
  "updated_at": "2026-09-16T12:00:00Z",
  "payment_uri": "tron:TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g?amount=49.9&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "qr_payload": "TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g",
  "confirmations": null,
  "required_confirmations": 20
}

Los campos que usa tu pantalla:

  • payer_amount y ticker — exactamente lo que el pagador debe enviar. Con fee_paid_by: payer la comisión de la plataforma ya está incluida, así que payer_amount es mayor que amount_crypto; muestra payer_amount, nunca amount ni amount_crypto. Las comisiones están en la página de precios.
  • network — btc, ltc, eth o tron. Un mismo ticker puede estar en dos redes (USDT en TRON y en Ethereum), y la red decide a dónde envía el pagador.
  • address — la dirección de depósito de esta factura.
  • payment_uri — un enlace que abre la billetera con la dirección y el monto que falta pagar: BIP-21 en Bitcoin y Litecoin, EIP-681 en Ethereum y un enlace tron: en TRON.
  • qr_payload — lo que va en el código QR: solo la dirección en todas las redes, sin el monto. Muchos pagadores envían desde un exchange, y el formulario de retiro de un exchange acepta solo una dirección. Si quieres un «QR con monto» para billeteras, ofrécelo como interruptor en Bitcoin y Litecoin y codifica ahí payment_uri.
  • ttl_expires_at — el plazo de pago. Lo define ttl_seconds, de 60 segundos a 30 días; sin él se aplica la configuración de tu cuenta (30 minutos por defecto). Para un precio en USD u otra moneda fiat, el tipo de cambio de rate_locked queda fijo hasta ese momento.
  • confirmations y required_confirmations — el contador de confirmaciones; confirmations es null mientras no llega nada.

Guarda cada monto como la cadena que devuelve la API: no lo redondees ni lo conviertas a un número de punto flotante.

Deja que el pagador elija la moneda en tu interfaz

Para una factura creada sin coin_id, la lista de monedas y la elección pasan por dos llamadas: la misma lista y la misma operación que en la página de pago.

http
GET /v1/invoices/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9/coin-options
json
{
  "coin_options": [
    { "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f", "ticker": "USDT", "name": "Tether USD", "network": "tron", "network_name": "TRON",
      "decimals": 6, "confirmations": 20, "estimated_amount": "49.90", "rate_as_of": "2026-09-16T12:00:03Z",
      "estimate_reason": null, "estimate_message": null },
    { "coin_id": "a01d6e2b-3c4f-4a5b-8c7d-9e0f1a2b3c4d", "ticker": "BTC", "name": "Bitcoin", "network": "btc", "network_name": "Bitcoin",
      "decimals": 8, "confirmations": 2, "estimated_amount": null, "rate_as_of": null,
      "estimate_reason": "rate_stale",
      "estimate_message": { "key": "checkout.estimate.rate_stale", "default": "The exchange rate is updating. Choose this currency to see the exact amount." } }
  ]
}
  • Muestra network_name junto a cada ticker y estimated_amount con el signo ≈: es una estimación. Cuando es null, muestra en su lugar el texto de estimate_message; la moneda se puede elegir igual.
  • La lista incluye solo las monedas que ofreces para facturas y que corresponden a la moneda de la factura, reducidas a allowed_coin_ids cuando la factura los tiene; las monedas en mantenimiento quedan fuera.
  • Si la moneda ya está elegida, la llamada responde 409 con details.coin_id, y la factura ya tiene la dirección.

Fija la elección desde tu servidor, con un Idempotency-Key:

http
POST /v1/invoices/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9/coin HTTP/1.1
Content-Type: application/json
Idempotency-Key: order-10042-coin

{"coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f"}

La respuesta es la factura con address, payer_amount, payment_uri, qr_payload y ttl_expires_at, con la misma forma que arriba. La elección vuelve a calcular el monto y fija el tipo de cambio, así que muestra el payer_amount de esta respuesta y no la estimación. Rechazos: 400 con details.field: coin_id para una moneda fuera de las opciones; 409 cuando ya hay una moneda elegida (aunque sea la misma) o la factura ya no está en new; 503 cuando el tipo de cambio o una dirección nueva no están disponibles en ese momento: espera unos segundos y envía la misma solicitud. Una repetición con el mismo Idempotency-Key devuelve la primera respuesta. La moneda se elige una sola vez; si el pagador también tiene abierta la página de pago, esta cambia al pago de inmediato.

Qué debe mostrar tu pantalla de pago

Estos elementos están en la página de pago de CryptoLike; tu pantalla necesita cada uno.

Elemento Campo Por qué
La red con su nombre y, para un token, el estándar (TRON, TRC20) network, ticker El mismo ticker y el mismo formato de dirección existen en varias redes
Advertencia de red equivocada junto a la dirección ticker, network «Envía a esta dirección solo USDT en la red TRON. Los fondos enviados por otra red no se pueden recuperar.»
La dirección, con botón para copiar address El pagador la pega en su billetera; los fondos enviados a una dirección mal escrita no se pueden recuperar
El monto exacto, con botón para copiar payer_amount, ticker Una transferencia menor al monto se vuelve un pago insuficiente
El código QR, con el monto escrito al lado qr_payload El QR lleva solo la dirección en todas las redes
Un enlace «Abrir en la billetera» payment_uri Un toque en un teléfono que tiene billetera instalada
El tiempo restante ttl_expires_at Después la factura vence, y una transferencia posterior es un pago tardío
El estado, con las confirmaciones mientras espera status, confirmations, required_confirmations «Pago detectado, 3 de 20 confirmaciones» le indica al pagador que no envíe dos veces
La pantalla de resultado status, paid_amount, remaining_amount Pagado, pago insuficiente con el monto restante, vencido o cancelado, cada uno con el siguiente paso

Lleva la cuenta regresiva con el reloj de tu servidor, no con el del dispositivo del pagador. Cuando llegue a cero, oculta la dirección y el QR y ofrece crear un pago nuevo. Los estados son new, pending, paid, underpaid, expired y cancelled; el artículo sobre confirmaciones explica por qué una transferencia espera antes de contar.

Pago insuficiente, pago excedente y pago tardío

  • Pago insuficiente. Una transferencia confirmada menor a payer_amount pasa la factura a underpaid y envía invoice.underpaid; una diferencia dentro de la tolerancia de pago insuficiente de tu cuenta igual cuenta como paid. La factura sigue abierta para completar el pago en la misma dirección hasta ttl_expires_at: muestra remaining_amount y el payment_uri actualizado, que ahora pide el resto. Después del plazo el pago insuficiente es definitivo; pídele al pagador que te contacte. Más en pago insuficiente.
  • Pago excedente. El estado es paid con overpaid_amount mayor que cero; lo trae invoice.paid, que vuelve a llegar cuando otra transferencia lo aumenta. Todo el monto se acredita a tu saldo; arregla la diferencia con el cliente.
  • Pago tardío. Una transferencia confirmada después de que la factura se cerró (expired, cancelled o un underpaid definitivo) igual se acredita a tu saldo: la factura recibe late_payment: true, te llega invoice.late_payment y el estado no cambia. Pasa también cuando una transferencia ya se detectó pero aún no se confirmó al vencer el plazo: la factura vence y la transferencia llega como pago tardío. Decide si entregas el pedido o devuelves el dinero. Más en pago vencido.
  • Reverso. Si una reorganización de la red elimina una transferencia confirmada, recibes invoice.reverted con los montos recalculados. Detén la entrega hasta que llegue un nuevo invoice.paid.

Confirma el pedido solo con el webhook

Entrega un pedido cuando tu servidor recibe invoice.paid con una firma de webhook válida, no cuando el pagador presiona «Ya pagué» ni por un estado que vio tu página. La entrega es al menos una vez: guarda event_id, responde 2xx en menos de 10 segundos y omite las repeticiones. Los webhooks son los mismos para H2H, la página de pago y el widget, y no traen payment_uri, qr_payload ni el contador de confirmaciones; cuando los necesites, lee GET /v1/invoices/{id}. Los tipos de evento, los reintentos y un vector de prueba están en la referencia de webhooks.

Antes del lanzamiento, haz en una red de prueba un pago por cada caso: pagado, pago insuficiente y luego completado, vencido y una transferencia tardía. Cómo conseguir una clave de prueba y monedas de prueba: pagos cripto de prueba.

Preguntas frecuentes

¿Necesito el widget para H2H?

No. Con H2H tu interfaz dibuja la pantalla con los campos de la factura, y en el navegador del pagador no se carga nada de CryptoLike. El widget es la otra opción: la página de pago de CryptoLike dentro de un marco en tu flujo de compra.

¿Puedo enviar igual al pagador a la página de pago?

Sí. Cada factura tiene checkout_url, y la página de pago muestra la misma factura. Una moneda elegida por API cambia de inmediato una página abierta al pago, así que puedes ofrecer la página como alternativa, por ejemplo a un pagador que tiene la billetera en otro dispositivo.

¿H2H cambia los webhooks?

No. Los eventos, la firma y los reintentos son los mismos que con la página de pago. El bloque de pago de la factura — payment_uri, qr_payload, confirmations y required_confirmations — no viene en el cuerpo del webhook; lee la factura con GET /v1/invoices/{id}.

¿Qué monto envía el pagador cuando él paga la comisión?

payer_amount. Con fee_paid_by: payer ya incluye la comisión de la plataforma, y amount_crypto es lo que recibes tú. Muestra payer_amount y guárdalo como la cadena que devuelve la API.