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.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.
amountse denomina en elsettlement_asset:"50"conUSDTson 50 USDT;"0.001"conBTCson 0.001 BTC;"2"conGOLDson 2 gramos de oro. No envíescurrency— es el contrato viejo y responde400(el cobro ya no se ata a una moneda local).countryes opcional y solo preselecciona el país en la página; el pagador puede cambiarlo.GOLDno tiene riel de pago propio: el cobro se alcanza siempre por conversión automática desde lo que pague el cliente.
201:
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_ratedel 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 alsettlement_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_payloadyqr_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 delsettlement_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 responde422 checkout_amount_mismatchcon el monto vigente. Integradores:POST /v1/transfersacepta el campo opcionalcheckout_token(o el QR extendido ento_qr_token); el destino se fuerza a la cuenta del link.
Conversión automática al saldo elegido
Todo abono en un asset distinto alsettlement_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:
Endpoints públicos del link (sin auth, rate-limited)
GET {checkout_url}/state— estado del link:status,paid_method,settlement_asset,asset_amount, materializaciones fiat congeladas (fiat_methods), progreso crypto (cryptocondue/received) yconversion_status.GET {checkout_url}/quote— cotizaciones ANTES de elegir:countries(catálogo por país; cada país lista sus corredores enoptions[]— una fila por método+moneda),cards(opciones de tarjeta por país y moneda con sulocal_amount),crypto(due indicativo por par) ycbpay(alias + dues por asset). Con?country=XXagregacountry_quotecon 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¤cy=YYY; crypto usacrypto:<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.
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 responde202 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.
Reglas del link
- 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_inacepta de 600 a 604800 segundos (10 minutos a 7 días; default 24 horas). Al vencer sin pago el payin pasa aexpiredy recibes el webhookpayin_expired.- El retry con la misma
idempotency_keydevuelve el mismo link (la URL no cambia); jamás se abre un segundo cobro. - El
settlement_assetdebe estar habilitado para tu organización; si está apagado la creación responde422 settlement_asset_disabled.
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.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
¿El mismo link se puede pagar dos veces?
¿El mismo link se puede pagar dos veces?
No. Un link = un cobro: el primer riel que paga gana (
already_paid, 409).
Un depósito crypto que llega después de que el link se liquidó por otro riel
no se acredita — queda retenido para conciliación.¿Qué pasa si el pagador envía menos (o más) crypto que lo cotizado?
¿Qué pasa si el pagador envía menos (o más) crypto que lo cotizado?
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.
¿Cuánto vive un link?
¿Cuánto vive un link?
expires_in entre 600 s y 7 días (default 24 h). Al expirar recibes el
webhook payin_expired y la página pública responde checkout_expired
(410).¿Qué pasa si falla la conversión a mi saldo de liquidación?
¿Qué pasa si falla la conversión a mi saldo de liquidación?
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.¿El pagador puede cambiar de método después de elegir uno?
¿El pagador puede cambiar de método después de elegir uno?
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.
¿Puedo reintentar la creación del link sin riesgo?
¿Puedo reintentar la creación del link sin riesgo?
Sí — reintenta
POST /v1/payins con la misma idempotency_key y
recibes el mismo link. Una clave nueva crea un link nuevo e independiente.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 usanUSD 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.