Skip to main content
La cuenta comerciante debe tener identidad aprobada antes de crear un link checkout autenticado con POST /v1/payins. Las cuentas persona requieren KYC aprobado y las cuentas empresa KYB aprobado; de lo contrario la API responde HTTP 403 verification_required.
Esto no bloquea la página pública /pay/{token} ni el pago de un link creado antes del gate. El pagador no necesita iniciar sesión en CBPay.
Crea un link de cobro universal: un solo POST /v1/payins con method: "checkout" devuelve una URL pública brandeada donde el pagador elige cómo pagar. El cobro se denomina en el saldo virtual que tú elijas (settlement_asset: USD, USDT, USDC, BTC, GOLD, SILVER o PLATINUM; si se omite usa tu payin_settlement_asset, USD es el saldo principal) y todo pago se convierte automáticamente a ese saldo al acreditarse — salvo que te paguen en el mismo asset, ahí no hay conversión. El fiat local (BOB, MXN, ARS) se rechaza aquí. La página organiza el pago en cuatro pestañas:
  • CBPay — pago directo con la app: el alias y el QR del comercio; quien escanea con la app paga al instante por transferencia interna, en cualquiera de los 10 saldos.
  • Crypto — las monedas disponibles agrupadas por red (hoy USDT en TRON y Ethereum, USDC en Ethereum y BTC; redes nuevas aparecen solas al habilitarse), cada una con una dirección de depósito exclusiva de ese cobro y QR escaneable por wallets externas (Trust Wallet, MetaMask, Binance y similares).
  • Fiat — el pagador elige su país entre todos los que tienen corredor de payin vivo y ve los métodos disponibles (QR, transferencia bancaria, pago hosted) con el monto local cotizado al momento.
  • Tarjeta — pago con tarjeta de crédito o débito en una página segura, listado por moneda de cargo (hoy BOB y USD; monedas de adquirentes futuros aparecen solas). Cada moneda es una opción de pago independiente con su propio monto cotizado.
  • amount se denomina en el settlement_asset: "50" con USDT son 50 USDT; "0.001" con BTC son 0.001 BTC; "2" con GOLD son 2 gramos de oro. No envíes currency — es el contrato viejo y responde 400 (el cobro ya no se ata a una moneda local).
  • country es opcional y solo preselecciona el país en la página; el pagador puede cambiarlo.
  • GOLD no tiene riel de pago propio: el cobro se alcanza siempre por conversión automática desde lo que pague el cliente.
Respuesta 201:
Comparte la checkout_url (link, correo, WhatsApp, QR impreso). La página no exige login, lleva el branding de tu organización y se actualiza sola: cuando el pago se confirma por cualquiera de los métodos, muestra “pagado” y redirige a tu success_url si la configuraste.

Cómo paga cada rail

  • Fiat multi-país: el pagador elige país y método; el monto local se cotiza al momento (meta → USD → moneda local con tu payin_rate del corredor, redondeado hacia arriba) y queda congelado al elegir el método. El abono acredita con la conversión y comisiones normales de payin y después se convierte al settlement_asset. Un pago anunciado MENOR a la cotización congelada se acredita igual (la plata es real) pero no marca el link como pagado. Si un país ofrece el mismo método en varias monedas (ej. Bolivia con QR en BOB y en USD), la página lista cada moneda como opción independiente. En transferencias bancarias en México el link genera una CLABE dedicada y exclusiva de ese cobro: el pagador transfiere el monto exacto sin poner referencia — el abono se detecta y liquida automático porque la cuenta identifica al link. Si la cuenta dedicada no puede emitirse en ese momento, la página degrada al camino clásico (cuenta general del comercio + referencia obligatoria en la glosa).
  • Tarjeta (multi-moneda): la pestaña lista cada moneda de cargo disponible con su monto cotizado; al elegir una se abre la página de pago hosted en esa moneda. Cada moneda es una materialización independiente (puedes cotizar en BOB y en USD sobre el mismo link; paga la primera que complete). En la página de pago el pagador puede usar una tarjeta guardada: escribe su correo, lo verifica con un código y elige — con “Recordar este dispositivo” no repite el código por 30 días (ver tarjetas guardadas).
  • Vigencia del link para tarjeta: para materializar una tarjeta, el link de checkout debe conservar al menos 5 minutos de vigencia. Si queda menos, el endpoint público responde 422 checkout_expiring_soon; pide al comercio un link nuevo antes de pagar con tarjeta.
  • Crypto (wallet por cobro): al elegir una moneda se genera una dirección exclusiva con su qr_payload y qr_png_base64 — el QR lleva SIEMPRE la dirección cruda (BTC bech32, TRON base58, ETH hex) para máxima compatibilidad con wallets y exchanges (Binance y similares rechazan URIs BIP-21/EIP-681); el monto exacto se muestra al lado con botón copiar. Si el asset pagado difiere del settlement_asset, el due cotizado ya incluye la conversión (la cubre el pagador; tú recibes tu meta exacta). Los pagos parciales se acumulan y la página muestra cuánto falta. Cotizaciones con BTC/GOLD/SILVER/PLATINUM se refrescan cada 15 minutos.
  • App CBPay: el QR del comercio embebe el link (cbpay:pay?to=…&checkout=…). La app paga por transferencia interna en cualquiera de los 10 saldos: mismo asset ⇒ meta exacta; distinto ⇒ due con la conversión incluida. El monto se valida server-side contra una cotización fresca — si no cubre el cobro responde 422 checkout_amount_mismatch con el monto vigente. Integradores: POST /v1/transfers acepta el campo opcional checkout_token (o el QR extendido en to_qr_token); el destino se fuerza a la cuenta del link.

Conversión automática al saldo elegido

Todo abono en un asset distinto al settlement_asset se convierte con el motor de conversiones de tu cuenta (mismos spreads y límites que POST /v1/swaps). El estado agregado viaja en conversion_status:
  • GET {checkout_url}/state — estado del link: status, paid_method, settlement_asset, asset_amount, materializaciones fiat congeladas (fiat_methods), progreso crypto (crypto con due/received) y conversion_status.
  • GET {checkout_url}/quote — cotizaciones ANTES de elegir: countries (catálogo por país; cada país lista sus corredores en options[] — una fila por método+moneda), cards (opciones de tarjeta por país y moneda con su local_amount), crypto (due indicativo por par) y cbpay (alias + dues por asset). Con ?country=XX agrega country_quote con el monto local por opción de ese país.
  • POST {checkout_url}/methods/{method} — materializa la opción elegida. Métodos fiat exigen ?country=XX; si el país ofrece el método en más de una moneda (tarjetas, QR BOB/USD en Bolivia) exige además &currency=YYY; crypto usa crypto:<chain>:<asset> (ej. crypto:tron:usdt) sin país. Re-POST de la misma combinación devuelve la MISMA materialización.
  • POST {checkout_url}/collect/otp / POST {checkout_url}/collect — los endpoints de cobro pull. La opción debe materializarse primero; el monto siempre es el de esa materialización y nunca lo envía el pagador.
Para una opción C2P materializada de VE/VES, el pagador entrega la claveDinamica generada por su banco en el campo otp de POST {checkout_url}/collect. No llames a /collect/otp para C2P; ese endpoint solo aplica a un método que emita un OTP antes del cobro. Útil si prefieres renderizar tu propia página de pago sobre el mismo link.

Holds de collect y retries seguros

La plataforma screenea al pagador antes de llamar al core/proveedor. Un hold PEP o de firewall responde 202 con status: "in_review" y review_id; el pagador no ha sido cobrado. El approve admin solo libera la review. Reintenta el mismo request de collect con la misma idempotency_key para ejecutar el cargo. Checkout hosted deriva una clave interna estable del link y de los mismos datos del pagador/cobro, así que el retry seguro es el request idéntico, no un link nuevo. Si el core/proveedor agota el tiempo o responde 5xx, el payin queda pending y se reconcilia por el webhook del payin. La plataforma jamás reenvía el cargo automáticamente. Si otro intento ya decidió el payin, responde 409 checkout_not_pending; lee el estado del link en vez de crear otro. Un pagador que solo entrega documento se screenea como identidad separada: document_value viaja como idNumber y name queda vacío. El documento nunca se copia al nombre, y el screening solo documental no califica para la vía rápida PEP basada solo en nombre.
  • Un link = un cobro: el primer método que completa el pago gana; un pago posterior por otro rail no se acredita (elegir un método NO bloquea los demás mientras nadie pague).
  • expires_in acepta de 600 a 604800 segundos (10 minutos a 7 días; default 24 horas). Al vencer sin pago el payin pasa a expired y recibes el webhook payin_expired.
  • El retry con la misma idempotency_key devuelve el mismo link (la URL no cambia); jamás se abre un segundo cobro.
  • El settlement_asset debe estar habilitado para tu organización; si está apagado la creación responde 422 settlement_asset_disabled.
Cuando el cobro se paga recibes el payin_credited con settled_via (ej. crypto:tron:usdt, qr, cbpay), settlement_asset y asset_amount; los pagos crypto agregan crypto_amount y los pagos con la app CBPay agregan transfer_id, asset y amount.
Un cobro pagado con tarjeta sigue la demora de settlement de tarjeta de tu organización (el settlement_hours del fee payin_card): el payin se confirma credited y el link cierra como pagado al instante, pero el saldo cae al llegar settle_at — el payin lleva settlement_pending: true hasta entonces. Los cobros pagados por cualquier otro método (QR, crypto, app CBPay, fintoc) se acreditan de inmediato, como siempre. Detalle en comisiones — settlement de payins con tarjeta.
En GET /v1/payins y GET /v1/payins/{payin_id} los payins de checkout llevan siempre su denominación — settlement_asset + asset_amount — en todo estado (pendiente, vencido y abonado); currency/local_amount quedan vacíos hasta que se usa un método de pago local. Un cobro liquidado en crypto o vía la app CBPay expone su usdt_credited sin fx_rate (no aplica cotización FX). Errores propios del link (los ve quien abre la página):

Idioma

La página hospedada de checkout sigue la cadena pública: ?lang= / ?locale= (un valor inválido se ignora), luego la cookie del pagador cbpay_pay_locale, luego la cuenta del comercio, luego el default_locale de la org, luego Accept-Language, luego inglés. El JSON de la API sigue en inglés. Detalle: Idioma y locale.

FAQ

Los pagos crypto parciales se acumulan: la página muestra cuánto falta hasta cubrir el monto cotizado. Los pagos tardíos que llegan con el link expirado igual acreditan tu cuenta.
La plata queda segura en USDT y el payin reporta conversion_status: pending_retry; la plataforma reintenta automáticamente hasta que el swap resulte — nunca pierdes dinero ni se convierte dos veces.
Sí. Cada método se materializa de forma independiente; volver a pedir el mismo método devuelve la misma materialización. El primer riel que paga liquida el link.

Plazo de liquidación de payins con tarjeta

En los payins con tarjeta, settlement_hours controla cuándo queda disponible el saldo después de confirmar el pago. Acepta 0 o un múltiplo de 24: 0 deja el saldo disponible de inmediato, mientras 24 equivale a un día hábil de EE. UU. y 48 a dos. Los días hábiles son de lunes a viernes, excluyendo feriados federales observados de EE. UU., según la zona horaria de tu organización. Por ejemplo, viernes a las 15:00 más 48 horas liquida el martes a las 15:00 si no interviene un feriado; sábado más 48 horas también liquida el martes. Un valor como 27 se rechaza con HTTP 400 invalid_settlement_hours. El pago se confirma de inmediato como credited; solo el saldo espera hasta settle_at. Los valores settle_at existentes y las configuraciones anteriores no múltiplo mantienen la semántica de horas calendario.

USD como activo principal del ledger

Las cuentas usan USD como valor predeterminado de settlement_asset y payin_settlement_asset; cámbialo cuando quieras con PUT /v1/settlement. Las operaciones en vuelo nunca se vuelven a cotizar. Los montos del ledger en USD usan centavos (dos decimales). Los créditos y d débitos directos, las comisiones y los flujos de payout, checkout, POS y tarjetas usan USD solo cuando el campo de activo de ese flujo lo acepta. Los swaps entre USD y USDT usan paridad 1:1 y no consultan un oráculo FX. BOB, MXN y ARS siguen siendo activos fiat separados del ledger; en v1 el pricing de money-out para esos activos sigue no disponible. Un payin acreditado directamente a una cuenta USD-principal expone credit_asset: USD y el monto fiat acreditado en centavos; usdt_credited sigue siendo el equivalente USD para reportes. El hold de una controversia sigue el activo acreditado: los créditos USD usan hold_asset: USD. disputed y held usan las unidades del activo retenido; disputed_usdt y held_usdt son equivalentes normalizados.
Última modificación el 4 de octubre de 2026