Una API, un flujo alojado
CryptoPayIn cotiza un pedido en la divisa de presentación que usted elija, bloquea instantáneas verificadas de los tipos fiat/USD y cripto/USD, asigna una dirección de depósito dedicada y vigila sus propios nodos de blockchain a la espera del pago. La contabilidad interna, las comisiones y los saldos se mantienen en USD. Su backend recibe de inmediato una URL de checkout y, después, eventos de ciclo de vida firmados.
Esta es una API en vivo. No existe un prefijo de entorno de pruebas (sandbox). Cada creación exitosa asigna una dirección on-chain real. Utilice un importe pequeño en una divisa admitida para las pruebas de extremo a extremo y mantenga las claves secretas en su servidor.
Cómo encaja la integración
Genere un secreto una sola vez en Panel -> Desarrolladores.
Envíe mediante POST la divisa del pedido, el importe y el activo seleccionado.
Envíe al cliente a la URL alojada devuelta.
Verifique el HMAC y actualice su pedido de forma idempotente.
Cree su primer pago
Genere una clave de API en el panel de comerciante, guarde el secreto en una variable de entorno y, después, cree un pago desde su backend. El ejemplo utiliza ETH para poder probarlo sin tener que elegir una red de token.
curl --request POST https://cryptopayin.com/v1/payments \
--header "Authorization: Bearer $CPI_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: order_1042" \
--data '{
"amount": 49.99,
"currency": "USD",
"asset": "ETH",
"order_ref": "order_1042",
"redirect_url": "https://shop.example/orders/1042/paid"
}'
const response = await fetch("https://cryptopayin.com/v1/payments", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.CPI_SECRET_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "order_1042"
},
body: JSON.stringify({
amount: 49.99, currency: "USD", asset: "ETH",
order_ref: "order_1042",
redirect_url: "https://shop.example/orders/1042/paid"
})
});
if (!response.ok) throw new Error(await response.text());
const payment = await response.json();
$payload = json_encode([
'amount' => 49.99, 'currency' => 'USD', 'asset' => 'ETH',
'order_ref' => 'order_1042',
'redirect_url' => 'https://shop.example/orders/1042/paid',
]);
$ch = curl_init('https://cryptopayin.com/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CPI_SECRET_KEY'),
'Content-Type: application/json',
'Idempotency-Key: order_1042',
],
]);
$payment = json_decode(curl_exec($ch), true, flags: JSON_THROW_ON_ERROR);
import os, requests
response = requests.post(
"https://cryptopayin.com/v1/payments",
headers={
"Authorization": f"Bearer {os.environ['CPI_SECRET_KEY']}",
"Idempotency-Key": "order_1042",
},
json={
"amount": 49.99, "currency": "USD", "asset": "ETH",
"order_ref": "order_1042",
"redirect_url": "https://shop.example/orders/1042/paid",
}, timeout=15,
)
response.raise_for_status()
payment = response.json()Utilice la respuesta
Guarde el id del pago junto con su pedido y, después, redirija al cliente a checkout_url. No calcule usted mismo un importe en cripto ni una dirección de depósito.
{
"id": "P-9F27C1E4KD",
"object": "payment",
"status": "created",
"amount": 49.99,
"amount_decimal": "49.99",
"amount_minor": 4999,
"currency": "USD",
"currency_minor_units": 2,
"amount_usd": 49.99,
"amount_usd_cents": 4999,
"fx_rate_usd": "1.000000000000",
"fx_source": "fixed:USD",
"fx_observed_at": "2026-07-17T13:00:00+00:00",
"fx_discrepancy_bps": 0,
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0",
"deposit_address": "0x71b8c3d4700000000000000000000000000084e2",
"exchange_rate": "3600.00000000",
"exchange_rate_currency": "USD",
"exchange_rate_source": "median:cb,cg,cl",
"exchange_rate_source_count": 3,
"exchange_rate_observed_at": "2026-07-17T13:00:00+00:00",
"exchange_rate_discrepancy_bps": 12,
"confirmations": 0,
"confirmations_required": 12,
"order_ref": "order_1042",
"checkout_url": "https://cryptopayin.com/i/P-9F27C1E4KD",
"expires_at": "2026-07-17T13:30:00+00:00",
"created_at": "2026-07-17T13:00:00+00:00",
"completed_at": null
}Autenticación
Toda solicitud a la API utiliza la clave secreta en una cabecera HTTP Bearer. Las claves secretas comienzan con csk_live_. El valor cpk_live_ que la acompaña es un identificador público para su panel y no debe utilizarse como credencial Bearer.
Authorization: Bearer csk_live_...Todos los endpointsEl secreto en texto plano solo se devuelve al crear la clave. CryptoPayIn almacena un hash de contraseña más un índice SHA-256 de búsqueda, nunca el secreto en texto plano.
Cree claves independientes por aplicación o entorno y revóquelas de forma independiente. Una cuenta puede tener hasta 50 claves activas.
Nunca incluya un valor csk_live_ en JavaScript de navegador, un binario móvil, un repositorio público o una página de checkout.
Un endpoint de webhook puede aplicarse a toda la cuenta o vincularse a una única clave de API, lo que mantiene las integraciones aisladas.
Un secreto ausente, mal formado, revocado o desconocido devuelve 401 unauthorized. Una cuenta de comerciante suspendida o cerrada se rechaza de la misma manera. Una clave válida utilizada fuera de los permisos concedidos devuelve 403 insufficient_scope.
Idempotencia
Envíe un Idempotency-Key único en cada creación de pago. Si su conexión se interrumpe tras el envío, reintente con el mismo JSON idéntico y la misma clave: CryptoPayIn devuelve el pago original en lugar de asignar otra dirección.
| Caso | Resultado | HTTP |
|---|---|---|
| Primer uso | Crea y devuelve un nuevo pago. | 201 |
| Misma clave + mismo JSON | Devuelve el pago existente con Idempotent-Replayed: true. | 200 |
| Misma clave + JSON diferente | Rechaza la solicitud como idempotency_conflict. | 409 |
Las claves están vinculadas a la credencial de la API y pueden contener entre 1 y 128 letras, dígitos, puntos, guiones bajos, dos puntos o guiones. Un UUID de pedido duradero es una buena opción. El mismo mecanismo también protege los retiros, de modo que un retiro reintentado nunca puede mover fondos dos veces.
Referencia API
La API abarca pagos, enlaces de pago, tiendas, productos, saldos y retiros. Las respuestas utilizan JSON en UTF-8 sobre HTTPS; toda creación o actualización requiere Content-Type: application/json. Las mutaciones están controladas por los permisos de la clave que realiza la llamada. De forma intencionada no existe un flujo CORS de navegador: las llamadas pertenecen a su backend.
/v1/assetsDescubrir los activos disponibles/v1/currenciesDescubrir las divisas fiat de presentación/v1/paymentsCrear un pago/v1/paymentsListar y filtrar pagos/v1/payments/{id}Recuperar un pagoLa versión 1 puede recibir campos y endpoints compatibles con versiones anteriores. Un cambio de contrato disruptivo utilizará una nueva ruta base en lugar de modificar /v1 de forma silenciosa.
Listar activos
/v1/assetsRequiere autenticación BearerUtilice este endpoint como fuente de verdad para las opciones de checkout. Devuelve las entradas del catálogo habilitadas, los tipos de cambio verificados de forma independiente, los importes mínimos equivalentes en USD y si el nodo y el feed de precios están listos. Una entrada puede permanecer listada con available: false mientras un nodo se está sincronizando o su precio no puede verificarse.
curl https://cryptopayin.com/v1/assets \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"data": [{
"asset": "USDT",
"network": "TRC20",
"type": "token",
"decimals": 6,
"minimum_amount": 1,
"currency": "USD",
"base_confirmations": 19,
"available": true,
"rate_usd": "1.00000000",
"rate_source": "median:cb,cg,cl",
"rate_source_count": 3,
"rate_discrepancy_bps": 4,
"rate_status": "healthy",
"rate_updated_at": "2026-07-17T13:00:00+00:00"
}]
}Catálogo e identificadores de red
| Activo | Valor de red | Abreviatura | Confirmaciones base |
|---|---|---|---|
| BTC | mainnet | BTC | 2 |
| ETH | mainnet | ETH | 6 |
| USDT | TRC20 / ERC20 | USDT.TRC20 | 19 / 6 |
| USDC | ERC20 | USDC.ERC20 | 6 |
| DAI | ERC20 | DAI | 6 |
| SHIB | ERC20 | SHIB | 6 |
| PEPE | ERC20 | PEPE | 6 |
| LTC | mainnet | LTC | 6 |
| TRX | mainnet | TRX | 19 |
| DOGE | mainnet | DOGE | 20 |
| XMR | mainnet | XMR | 10 |
| SOL | mainnet | SOL | 32 |
La disponibilidad es dinámica. No incorpore la tabla anterior como una lista de activos permitidos fija. Para símbolos con varias redes, como USDT, envíe network de forma explícita o utilice la abreviatura ASSET.NETWORK.
Listar divisas fiat
/v1/currenciesRequiere autenticación BearerDevuelve las divisas de presentación habilitadas, su precisión ISO y el estado actual de la conversión a USD. Ofrezca únicamente las filas con available: true. El USD es intrínseco; cualquier otra divisa necesita una cotización en vivo reciente y una comprobación de referencia independiente. minimum_amount convierte a esa divisa el mínimo más bajo configurado entre los activos habilitados; el activo elegido puede requerir un importe mayor, así que consulte también siempre GET /v1/assets.
curl https://cryptopayin.com/v1/currencies \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"accounting_currency": "USD",
"data": [{
"currency": "EUR",
"name": "Euro",
"symbol": "€",
"minor_units": 2,
"available": true,
"minimum_amount": "0.86",
"rate_usd": "1.160000000000",
"rate_source": "coinbase+ecb",
"rate_source_count": 2,
"rate_discrepancy_bps": 18,
"rate_updated_at": "2026-07-17T13:00:00+00:00",
"rate_reference_at": "2026-07-16T00:00:00+00:00"
}]
}Omitir la divisa en POST /v1/payments sigue significando USD por compatibilidad con versiones anteriores. Las divisas sin decimales, como el JPY, rechazan importes fraccionarios. Trate los mínimos y los tipos de cambio del catálogo como datos en vivo, nunca como constantes fijas.
Crear un pago
/v1/payments120 solicitudes / minuto / claveCrea una factura en la divisa de presentación solicitada, bloquea instantáneas recién verificadas de los tipos fiat/USD y cripto/USD, calcula el importe exacto en cripto y vincula una dirección de depósito on-chain dedicada.
Cuerpo de la solicitud
| Campo | Tipo | Requisito | Descripción |
|---|---|---|---|
| amount | number o cadena decimal | obligatorio | Importe en currency, con la precisión ISO de esa divisa. Su equivalente en USD bloqueado no debe superar $1,000,000.00; también se aplican los mínimos del activo. |
| currency | string | opcional | Divisa habilitada de 3 letras de GET /v1/currencies. El valor predeterminado es USD. |
| asset | string | obligatorio | Símbolo como ETH, o abreviatura como USDT.TRC20. |
| network | string | condicional | Obligatorio cuando un símbolo existe en varias redes. Ejemplo: ERC20. |
| order_ref | string | opcional | Su identificador de pedido, máximo 128 caracteres. Se devuelve en las respuestas y los eventos de la API. |
| customer_email | string | opcional | Dirección de correo electrónico válida, máximo 190 caracteres. Se almacena junto con el registro de pago del comerciante. |
| redirect_url | string | opcional | URL HTTPS, máximo 255 caracteres, que se ofrece tras un checkout exitoso. |
Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
| id | string | Identificador estable del pago que comienza con P-. |
| status | string | Estado actual del ciclo de vida. |
| amount / amount_decimal / amount_minor | number / string / integer | Importe de presentación solicitado en sus formas práctica, decimal exacta y de unidad menor ISO. |
| currency / currency_minor_units | string / integer | Divisa de presentación bloqueada y su precisión. |
| amount_usd / amount_usd_cents | number / integer | Valor contable interno en USD, inmutable. |
| fx_rate_usd / fx_source / fx_observed_at | decimal string / string / ISO 8601 | Instantánea bloqueada de USD por unidad de presentación y sus metadatos de auditoría. |
| asset / network | string | Activo on-chain resuelto. |
| crypto_amount | decimal string | Importe exacto que el cliente debe enviar. Nunca interprete los decimales de cripto como números de punto flotante binarios. |
| crypto_received | decimal string | Total observado actualmente en la dirección de depósito. |
| deposit_address | string | Dirección dedicada asignada para este pago. |
| exchange_rate | decimal string | Tipo de cambio cripto/USD bloqueado utilizado para calcular crypto_amount; este campo conserva su significado original de la v1. |
| exchange_rate_source / exchange_rate_observed_at | string / ISO 8601 | Instantánea de auditoría del tipo de cambio en cripto, inmutable. |
| confirmations | integer | Confirmaciones de red actuales. |
| confirmations_required | integer | Umbral para este pago. Los niveles de USD más altos pueden requerir confirmaciones adicionales. |
| checkout_url | URL | Factura alojada que se muestra al cliente. |
| expires_at | ISO 8601 | Plazo límite para una factura sin pagar. |
| completed_at | ISO 8601 / null | Momento de liquidación final una vez completado. |
Ambas conversiones en vivo se validan por antigüedad, número de fuentes y divergencia antes de la creación. Si la verificación falla, la creación devuelve un error en lugar de utilizar un tipo de cambio obsoleto. El importe en cripto se redondea al alza con una precisión útil para el activo, de modo que el redondeo nunca deja al comerciante con menos de lo debido.
Listar pagos
/v1/payments240 solicitudes / minuto / claveDevuelve primero los pagos más recientes de la cuenta de comerciante autenticada. Utilice la paginación por cursor para la conciliación y filtros exactos para localizar un pedido sin recorrer todo el historial.
Parámetros de consulta
| Parámetro | Valor predeterminado | Descripción |
|---|---|---|
| limit | 20 | Tamaño de página de 1 a 100. |
| starting_after | — | ID del pago devuelto como next_cursor de la página anterior. |
| status | — | Estado exacto del ciclo de vida, como pending, completed o expired. |
| order_ref | — | Referencia exacta del pedido del comerciante, máximo 128 caracteres. |
curl "https://cryptopayin.com/v1/payments?status=completed&limit=20" \
--header "Authorization: Bearer $CPI_SECRET_KEY"{
"object": "list",
"data": [{
"id": "P-9F27C1E4KD",
"object": "payment",
"status": "completed",
"amount": 49.99,
"currency": "USD",
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0.01388612",
"order_ref": "order_1042",
"confirmations": 12,
"confirmations_required": 12,
"completed_at": "2026-07-17T13:12:42+00:00"
}],
"has_more": true,
"next_cursor": "P-9F27C1E4KD"
}Cuando has_more es true, pase next_cursor sin modificar como starting_after. Los parámetros de consulta de tipo array desconocidos o repetidos se rechazan en lugar de ignorarse.
Recuperar un pago
/v1/payments/{id}240 solicitudes / minuto / claveDevuelve el mismo objeto de pago que la creación, con el estado actualizado, el importe recibido, el hash de la transacción y las confirmaciones. Una clave solo puede recuperar los pagos que pertenecen a su cuenta de comerciante.
curl https://cryptopayin.com/v1/payments/P-9F27C1E4KD \
--header "Authorization: Bearer $CPI_SECRET_KEY"Los webhooks deben impulsar las actualizaciones normales de los pedidos. Utilice la recuperación para conciliar tras un tiempo de espera agotado, verificar un evento, renderizar una página de estado en el backend o reparar entregas fallidas.
Ciclo de vida del pago
Trate siempre el estado de la API como la fuente autorizada. No infiera que un pago se ha completado a partir de una redirección del navegador ni de que el cliente afirme haber pagado.
| Estado | Significado | Acción del comerciante |
|---|---|---|
| created | Factura y dirección asignadas; aún no se detecta financiación. | Mostrar el checkout alojado. |
| pending | A la espera de un pago on-chain utilizable. | Mantener el pedido abierto. |
| underpaid | Los fondos recibidos están por debajo de la tolerancia del comerciante. | Pedir al pagador que envíe el importe restante mostrado. |
| confirming | Se ha detectado el valor suficiente; a la espera de confirmaciones. | No entregue el pedido todavía. |
| completed | Se ha alcanzado el valor y las confirmaciones requeridos. | Entregue el pedido exactamente una vez. |
| overpaid | Se confirmó más de lo esperado. | Entregue el pedido y revise el excedente. |
| expired | No se detectó ningún pago válido antes de la caducidad. | Cree un nuevo pago. |
| failed | Falló la asignación de la dirección o el procesamiento. | Registre el error y cree un nuevo pago. |
Las transferencias on-chain son irreversibles y CryptoPayIn no dispone de un mecanismo de reembolso: un pago confirmado es definitivo. Cualquier devolución por cortesía se gestiona directamente entre usted y su cliente, al margen de la plataforma.
Checkout alojado
Cada pago de la API incluye un checkout_url adaptable. Muestra el comerciante, el importe de presentación solicitado, el equivalente en USD bloqueado cuando corresponde, el importe exacto en cripto, la dirección de depósito, el código QR, el aviso de red, la cuenta atrás y el progreso de confirmación en vivo.
El cliente ve el mismo crypto_amount que devuelve la API durante la ventana de la factura.
El pagador no crea una cuenta de CryptoPayIn ni comparte credenciales.
La página consulta el pago de forma segura y pasa de la espera a la confirmación y, después, al pago.
Se ofrece una redirect_url HTTPS tras el éxito; no constituye una prueba de pago.
Mantenga la entrega del pedido en su backend. La navegación del navegador se puede abandonar, repetir o falsificar; solo un webhook verificado o una solicitud GET autenticada demuestran el estado del pago.
Permisos & ámbitos
Cada clave de API lleva un conjunto fijo de permisos elegido al crearla en Panel → Desarrolladores. Cada endpoint comprueba los ámbitos de la clave antes de realizar cualquier operación; una llamada fuera del alcance concedido a una clave devuelve 403 insufficient_scope con una cabecera X-Required-Scope que indica el permiso que falta. Los ámbitos se establecen una sola vez al crear la clave y no se pueden ampliar después — emita una nueva clave en su lugar. Las claves creadas antes de que existieran los ámbitos conservan exactamente su capacidad original: payments:read y payments:write.
| Ámbito | Concede | Endpoints |
|---|---|---|
payments:read | Listar y recuperar pagos | GET /v1/payments, GET /v1/payments/{id} |
payments:write | Crear pagos alojados | POST /v1/payments |
links:read | Listar y recuperar enlaces de pago | GET /v1/links, GET /v1/links/{id} |
links:write | Crear, editar, pausar y eliminar enlaces de pago | POST/PATCH/DELETE /v1/links |
shops:read | Listar y recuperar tiendas y sus productos | GET /v1/shops, GET .../products |
shops:write | Crear y editar tiendas, productos y variantes | POST/PATCH/DELETE /v1/shops y productos |
balance:read | Consultar saldos en cripto y estimaciones en USD | GET /v1/balance |
payouts:read | Listar y recuperar retiros | GET /v1/payouts, GET /v1/payouts/{id} |
payouts:write | Solicitar retiros on-chain | POST /v1/payouts |
payouts:write mueve fondos on-chain y es irreversible. Concédalo únicamente a claves en las que confíe plenamente, mantenga esas claves en el servidor y utilice preferiblemente una clave dedicada por cada proceso automatizado. GET /v1/account indica los ámbitos de la clave que realiza la llamada y los límites de su cuenta.
Enlaces de pago
Enlaces alojados reutilizables que un cliente puede pagar cualquier número de veces. Un enlace tiene la misma lógica de precios, activo, entrega y preguntas de checkout que el creador de enlaces del panel — la API simplemente la controla. Una cuenta puede tener hasta 50 enlaces de pago.
/v1/linkslinks:read/v1/linkslinks:write/v1/links/{id}links:read/v1/links/{id}links:write/v1/links/{id}links:writeCuerpo de la solicitud
| Campo | Tipo | Requisito | Descripción |
|---|---|---|---|
| title | string | obligatorio | 3–120 caracteres. |
| description | string | opcional | Hasta 2,000 caracteres, se muestra en el checkout. |
| template | string | opcional | Tema de checkout: signature (predeterminado), midnight, atelier, horizon, compact o ledger. |
| public_label | string | opcional | Nombre público del vendedor mostrado a los compradores (2–80 caracteres). Nunca un ID de cuenta. |
| amount_type | string | opcional | fixed (predeterminado) o open (el cliente elige dentro del mínimo/máximo). |
| currency | string | opcional | Divisa de presentación de GET /v1/currencies. El valor predeterminado es la divisa de su cuenta. |
| amount | number o string | condicional | Obligatorio para fixed. En currency con su precisión ISO. |
| min / max | number o string | condicional | Límites para los enlaces de tipo open. max puede ser 0 o quedar omitido para no tener un límite superior. |
| accepted_assets | array de strings | opcional | Códigos de activo como ["BTC","USDT.TRC20"]. Omita el campo para incluir todos los activos disponibles. |
| max_uses | integer | opcional | Límite de pagos completados. 0 significa ilimitado. |
| expires_at | ISO 8601 | opcional | Al menos 5 minutos en el futuro, como máximo 12 meses. UTC. |
| delivery_type | string | opcional | none, text, url o keys — bienes digitales entregados tras el pago. |
| delivery_text / delivery_url | string | condicional | Contenido (≤50,000 caracteres) o una URL https para el tipo de entrega correspondiente. |
| delivery_keys | array de strings | condicional | Una clave por elemento para la entrega de tipo keys. Hasta 10,000, cada una ≤500 caracteres. |
| checkout_fields | array | opcional | Hasta 5 objetos {label, type, required}; el tipo es text, email, textarea o number. |
| success_message / redirect_url | string | opcional | Mensaje posterior al pago (≤500 caracteres) y una redirección https. |
| status | string | opcional | Solo en PATCH: active o paused. |
curl -X POST https://cryptopayin.com/v1/links \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Pro license",
"amount": 49.99,
"currency": "EUR",
"accepted_assets": ["BTC", "ETH", "USDT.TRC20"],
"delivery_type": "keys",
"delivery_keys": ["ABC-1", "ABC-2", "ABC-3"]
}'{
"id": "PL-N5PKTYB7",
"object": "payment_link",
"url": "https://cryptopaylink.co/pay/PL-N5PKTYB7",
"status": "active",
"title": "Pro license",
"amount_type": "fixed",
"currency": "EUR",
"amount": 49.99,
"amount_decimal": "49.99",
"accepted_assets": [{"asset":"BTC","network":"mainnet"},{"asset":"ETH","network":"mainnet"},{"asset":"USDT","network":"TRC20"}],
"uses": {"started": 0, "completed": 0, "in_flight": 0},
"delivery": {"type": "keys", "keys_available": 3, "keys_total": 3},
"expires_at": null,
"created_at": "2026-07-19T19:00:00+00:00"
}PATCH es una actualización parcial: envíe solo los campos que cambia y el resto se conserva, incluidas las claves de licencia sin vender. Para los enlaces de tipo keys, enviar delivery_keys sustituye el conjunto de claves sin vender; las claves ya entregadas nunca se modifican. Eliminar un enlace que tiene pagos se rechaza de forma implícita, manteniendo intacto su historial.
Pago de agentes
Todo enlace de pago activo es también un checkout legible por máquinas: un agente de IA o cualquier script puede descubrirlo, crear una factura y leer la entrega sin necesidad de un navegador, y sin ninguna clave de API, porque se trata de endpoints públicos para compradores en el dominio del enlace, no de endpoints de comerciante. Contrato completo y ejemplo práctico: cryptopayin.com/agents.
https://cryptopaylink.co/pay/{link}.jsonpúblico · descubrimientoDevuelve el estado, el precio, los activos aceptados y el contrato exacto de entrada para la llamada de facturación (campos obligatorios, esquema de envío, variantes).
https://cryptopaylink.co/pay/{link}/invoicepúblico · admite Idempotency-KeyCrea la factura mediante el mismo núcleo, la misma instantánea de precios y los mismos límites antiabuso que la página alojada, y devuelve la dirección de depósito, el importe exacto en cripto, un URI de cartera y la URL del recibo. El agente paga después on-chain desde cualquier cartera que controle.
https://cryptopaylink.co/pay/{link}/receipt?p={payment}público · consultar cada 5–10 sEstado y confirmaciones en vivo; una vez que el pago se completa, la respuesta incluye la entrega (su contenido de texto, una URL privada o una clave de licencia reservada para ese pago), además de su mensaje de éxito y la URL de redirección.
Las tiendas hablan el mismo protocolo
Los escaparates exponen el mismo flujo en su propio dominio: el catálogo con existencias en vivo y, después, una única llamada que valida el carrito, reserva las existencias y devuelve la factura.
https://shopycrypto.com/s/{shop}.jsonpúblico · catálogohttps://shopycrypto.com/s/{shop}/orderpúblico · carrito → factura, admite Idempotency-Keyhttps://shopycrypto.com/s/{shop}/o/{order}/receipt?p={payment}público · consultar cada 5–10 sControles del vendedor
El pago de agentes está activado de forma predeterminada y cuesta la misma comisión fija del 1 %. Desactívelo para toda la cuenta en Panel → Configuración → General → IA y pago de agentes: los endpoints para máquinas de los enlaces y las tiendas responderán entonces 403 agents_disabled mientras sus páginas de checkout para humanos siguen funcionando. Los pagos creados por agentes no llevan ninguna marca especial: son pagos normales en su panel, sus webhooks y sus exportaciones.
Tiendas
Un escaparate alojado que agrupa productos bajo una única página de marca. Una cuenta puede tener hasta 10 tiendas. Los productos se gestionan mediante los endpoints de producto anidados que se muestran a continuación.
/v1/shopsshops:read/v1/shopsshops:write/v1/shops/{id}shops:read/v1/shops/{id}shops:write/v1/shops/{id}shops:writeCuerpo de la solicitud
| Campo | Tipo | Requisito | Descripción |
|---|---|---|---|
| name | string | obligatorio | 2–80 caracteres. |
| tagline | string | opcional | Hasta 160 caracteres. |
| theme | string | opcional | light (predeterminado) o dark. |
| accent | string | opcional | Color de acento hexadecimal de la paleta de la tienda, devuelto como accent_palette en GET /v1/shops. |
| accepted_assets | array de strings | opcional | Activos predeterminados para los productos de la tienda, p. ej. ["BTC","LTC","XMR"]. Se aplica a todos los productos cuando se modifica. |
| status | string | opcional | Solo en PATCH: active o paused. |
Una tienda deshabilitada por CryptoPayIn por motivos de política no se puede reactivar ni eliminar mediante la API y devuelve admin_disabled (403). Eliminar una tienda elimina sus productos; los pagos pasados permanecen intactos.
Productos & variantes
Los productos residen dentro de una tienda. Cada tienda puede tener hasta 50 productos. Un producto puede ser digital (con entrega instantánea) o físico (con países de envío), y puede exponer hasta 30 combinaciones de variantes formadas a partir de 1–3 grupos de opciones.
/v1/shops/{shop}/productsshops:read/v1/shops/{shop}/productsshops:write/v1/shops/{shop}/products/{id}shops:read/v1/shops/{shop}/products/{id}shops:write/v1/shops/{shop}/products/{id}shops:writeCuerpo de la solicitud
| Campo | Tipo | Requisito | Descripción |
|---|---|---|---|
| title | string | obligatorio | 3–120 caracteres. |
| description / blurb | string | opcional | Descripción completa y una línea de tarjeta de tienda de ≤200 caracteres. |
| emoji | string | opcional | Un único emoji que se muestra en la tarjeta del producto. |
| featured | boolean | opcional | Como máximo un producto destacado por tienda. |
| product_type | string | opcional | digital (predeterminado) o physical. |
| shipping_countries | array de strings | condicional | Solo para físicos: códigos ISO como ["FR","BE"], o ["*"] para todo el mundo. |
| amount_type / currency / amount / min / max | mixto | condicional | Precio base, con las mismas reglas que los enlaces de pago. Los productos físicos deben ser fixed. |
| max_uses | integer | opcional | Límite total de ventas (0 = ilimitado). |
| delivery_type + delivery_text/url/keys | mixto | opcional | Entrega digital para el producto base, con la misma estructura que los enlaces de pago. |
| variant_options | array | opcional | 1–3 grupos {name, values[]}, cada uno con 2–10 valores. Las combinaciones no deben superar 30. |
| variants | array | condicional | Un objeto por combinación (véase más abajo). Obligatorio y exhaustivo cuando está presente variant_options. |
| status | string | opcional | Solo en PATCH: active o paused. |
Objeto de variante
| Campo | Tipo | Descripción |
|---|---|---|
| options | array de strings | Un valor por grupo de opciones, en el orden de los grupos, p. ej. ["Pro","Lifetime"]. |
| price | number o string | Precio de la variante en la divisa del producto. |
| stock | integer o null | Unidades restantes, o null para ilimitado. |
| delivery_type + delivery_text/url/keys | mixto | Anulación opcional de la entrega digital por variante (inherit de forma predeterminada). Las claves deben ser únicas en todo el producto. |
curl -X POST https://cryptopayin.com/v1/shops/SH-JYRK8TFJ/products \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Software license",
"amount": 30,
"currency": "USD",
"variant_options": [
{"name": "Edition", "values": ["Standard", "Pro"]},
{"name": "Term", "values": ["1 year", "Lifetime"]}
],
"variants": [
{"options": ["Standard","1 year"], "price": 30, "delivery_type": "keys", "delivery_keys": ["S1Y-1"]},
{"options": ["Standard","Lifetime"], "price": 79, "stock": 10, "delivery_type": "keys", "delivery_keys": ["SLT-1"]},
{"options": ["Pro","1 year"], "price": 59, "delivery_type": "keys", "delivery_keys": ["P1Y-1"]},
{"options": ["Pro","Lifetime"], "price": 149, "stock": 5, "delivery_type": "keys", "delivery_keys": ["PLT-1"]}
]
}'PATCH conserva los pedidos existentes y las claves ya entregadas. Para ajustar precios o existencias, reenvíe el array variants correspondiente; las combinaciones que omita se pausan si tienen pedidos, o se eliminan en caso contrario. Un producto puede tener como máximo 10,000 claves de licencia activas entre su base y sus variantes, y cada clave debe ser única dentro del producto.
Saldo
/v1/balancebalance:readDevuelve sus saldos liquidados en cripto por activo, con una estimación en USD hecha con la mejor información disponible y la comisión de red que se cobra en un retiro. La contabilidad interna siempre está en USD; los saldos se acumulan a partir de los pagos completados, netos de la comisión del comerciante.
{
"object": "list",
"accounting_currency": "USD",
"minimum_payout_usd": 25,
"data": [{
"asset": "USDT",
"network": "TRC20",
"amount": "99.099",
"usd_estimate": 99.02,
"payout_network_fee": "2.445463"
}]
}usd_estimate es null cuando un tipo de cambio verificado en vivo no está disponible momentáneamente; el saldo subyacente sigue siendo exacto. Utilice estos valores para decidir los retiros, no para la contabilidad final.
Retiros
Mueva cripto liquidada a una cartera externa. Los retiros son irreversibles, por lo que este endpoint aplica todas las salvaguardas que aplica el panel: un destino válido para el activo, un tipo de cambio verificado en vivo, el mínimo de la cuenta, saldo suficiente incluyendo la comisión de red, y confirmación de dos factores cuando su cuenta tiene 2FA activado.
/v1/payoutspayouts:read/v1/payoutspayouts:write · 30 / min / clave/v1/payouts/{id}payouts:readCuerpo de la solicitud
| Campo | Tipo | Requisito | Descripción |
|---|---|---|---|
| asset | string | obligatorio | Símbolo o abreviatura, p. ej. LTC o USDT.TRC20. |
| network | string | condicional | Obligatorio cuando el símbolo existe en varias redes. |
| amount | number o string | obligatorio | Importe a enviar, sin incluir la comisión de red, con la precisión del activo. Su valor en USD debe cumplir el mínimo de la cuenta. |
| address | string | obligatorio | Dirección de destino, validada para la cadena del activo. |
| note | string | opcional | Su propia referencia, hasta 255 caracteres. |
| totp_code | string | condicional | Código actual de 6 dígitos o código de recuperación. Obligatorio cuando el 2FA está activado en la cuenta. |
curl -X POST https://cryptopayin.com/v1/payouts \
-H "Authorization: Bearer $CPI_SECRET_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: withdraw-2026-07-19-01" \
-d '{
"asset": "USDT.TRC20",
"amount": "50",
"address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"note": "weekly settlement"
}'{
"id": "W-EM64ZCJB",
"object": "payout",
"status": "requested",
"asset": "USDT",
"network": "TRC20",
"amount": "50",
"fee": "2.445463",
"total_debited": "52.445463",
"address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"txid": null,
"confirmations": 0,
"created_at": "2026-07-19T19:10:00+00:00"
}Ciclo de vida del estado
| Estado | Significado |
|---|---|
requested | Reservado de su saldo; a la espera de revisión del operador o aprobación automática. |
approved / processing | Aprobado y en cola para su difusión por el ejecutor. |
sent | Difundido on-chain; se rellena txid. |
confirmed | Alcanzó las confirmaciones requeridas. Definitivo. |
failed | No se pudo enviar; failure_message explica el motivo y el saldo se devuelve. |
cancelled | Cancelado antes de la difusión; el saldo reservado se devuelve. |
Envíe un Idempotency-Key para que un reintento de red nunca pueda crear un segundo retiro: la misma clave con el mismo cuerpo devuelve el retiro original (Idempotent-Replayed: true); la misma clave con un cuerpo diferente devuelve 409 idempotency_conflict. El código 2FA se excluye deliberadamente de la huella de idempotencia para que un código que cambia no provoque un conflicto falso. La reserva debita su saldo de inmediato; un retiro fallido o cancelado lo devuelve.
Cuenta
/v1/accountcualquier clave válidaDevuelve el perfil de su cuenta, los ámbitos de la clave que realiza la llamada, la comisión de la plataforma y todos los límites vigentes — útil para una integración autoconfigurable o una comprobación previa.
{
"object": "account",
"id": "MC67T3PHQZ",
"fee_bps": 100,
"fee_percent": 1,
"default_currency": "USD",
"two_factor_enabled": false,
"api_key": {"label": "production", "scopes": ["payments:read","payments:write"]},
"limits": {
"shops": {"used": 1, "max": 10},
"payment_links": {"used": 0, "max": 50},
"products_per_shop_max": 50,
"variant_combinations_per_product_max": 30,
"license_keys_per_product_max": 10000,
"active_api_keys_max": 50,
"checkout_fields_per_link_max": 5
},
"minimum_payout_usd": 25
}Webhooks
Añada hasta 10 endpoints públicos HTTPS en Panel -> Desarrolladores. Cada endpoint recibe su propio secreto de firma whsec_..., que se muestra una sola vez. Puede escuchar en toda la cuenta o vincularse a una clave de API activa concreta; los endpoints vinculados a una clave reciben únicamente los pagos creados con esa clave.
Verifique antes de procesar
CryptoPayIn firma el cuerpo exacto de la solicitud en bruto utilizando el secreto del endpoint. La versión 1 firma timestamp + "." + raw_body. Rechace las marcas de tiempo obsoletas antes de aceptar el evento.
import crypto from "node:crypto";
const timestamp = req.headers["x-cpi-timestamp"];
const signature = req.headers["x-cpi-signature"];
const rawBody = req.rawBody; // Buffer captured before JSON parsing
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
throw new Error("stale webhook");
}
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.CPI_WEBHOOK_SECRET)
.update(Buffer.concat([Buffer.from(`${timestamp}.`), rawBody]))
.digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
if (!valid) throw new Error("invalid webhook signature");
$rawBody = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_CPI_TIMESTAMP'] ?? '';
$received = $_SERVER['HTTP_X_CPI_SIGNATURE'] ?? '';
if (!ctype_digit($timestamp) || abs(time() - (int)$timestamp) > 300) {
http_response_code(400); exit('stale webhook');
}
$expected = 'sha256=' . hash_hmac(
'sha256', $timestamp . '.' . $rawBody, getenv('CPI_WEBHOOK_SECRET')
);
if (!hash_equals($expected, $received)) {
http_response_code(401); exit('invalid signature');
}
$event = json_decode($rawBody, true, flags: JSON_THROW_ON_ERROR);
import hashlib, hmac, os, time
raw_body = request.get_data() # bytes, before JSON decoding
timestamp = request.headers.get("X-CPI-Timestamp", "")
received = request.headers.get("X-CPI-Signature", "")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
raise ValueError("stale webhook")
signed = timestamp.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(
os.environ["CPI_WEBHOOK_SECRET"].encode(), signed, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, received):
raise ValueError("invalid signature")Cabeceras de entrega
| Cabecera | Ejemplo | Propósito |
|---|---|---|
| Content-Type | application/json | Cuerpo JSON en UTF-8. |
| X-CPI-Timestamp | 1784293200 | Segundos Unix incluidos en el mensaje firmado. |
| X-CPI-Signature | sha256=... | HMAC-SHA256 en hexadecimal. |
| X-CPI-Signature-Version | v1 | Versión del esquema de firma. |
| X-CPI-Event-Id | evt_a12b... | ID lógico estable del evento; idéntico en todos los reintentos. |
| X-CPI-Delivery-Id | 1842 | ID estable del registro de entrega del endpoint. |
Carga útil del pago
{
"event_id": "evt_a12b34c56d78e90f12345678",
"event": "payment.completed",
"id": "P-9F27C1E4KD",
"status": "completed",
"order_ref": "order_1042",
"amount": 49.99,
"amount_decimal": "49.99",
"amount_minor": 4999,
"currency_minor_units": 2,
"currency": "USD",
"amount_usd": 49.99,
"amount_usd_cents": 4999,
"fx_rate_usd": "1.000000000000",
"fx_source": "fixed:USD",
"fx_observed_at": "2026-07-17T13:00:00Z",
"fx_discrepancy_bps": 0,
"exchange_rate": "3600.00000000",
"exchange_rate_currency": "USD",
"exchange_rate_source": "median:cb,cg,cl",
"exchange_rate_source_count": 3,
"exchange_rate_observed_at": "2026-07-17T13:00:00Z",
"exchange_rate_discrepancy_bps": 12,
"asset": "ETH",
"network": "mainnet",
"crypto_amount": "0.01388612",
"crypto_received": "0.01388612",
"txid": "0x9d81...75af",
"confirmations": 12,
"confirmations_required": 12,
"deposit_address": "0x71b8c3d4700000000000000000000000000084e2",
"sent_at": "2026-07-17T13:12:42Z"
}Reintentos y seguridad del endpoint
Una entrega se considera exitosa con HTTP 200-299. Realice el trabajo costoso de forma asíncrona y responda rápidamente.
Se conservan el cuerpo exacto y el ID del evento; los fallos se reintentan aproximadamente al cabo de 1 minuto, 5 minutos, 30 minutos, 2 horas y 6 horas.
Las respuestas 3xx no se siguen. Registre directamente la URL HTTPS final.
Las IP privadas, de loopback, link-local y reservadas están bloqueadas; cada respuesta DNS se valida y la conexión queda anclada.
Las entregas son al menos una vez. Haga que su gestor sea idempotente registrando event_id con una restricción de unicidad antes de la entrega del pedido. Recupere el objeto de la API al conciliar un evento inesperado.
Referencia de eventos
payment.completedSe confirmó el valor esperado.payment.overpaidSe confirmó más de lo esperado.payment.underpaidSe detectó financiación por debajo de la tolerancia.payment.expiredSe cerró la ventana de la factura sin pagar.payment.failedFalló la configuración o el procesamiento del pago.payout.sentRetiro difundido on-chain.payout.confirmedEl retiro alcanzó las confirmaciones.payout.failedEl retiro no pudo completarse.webhook.testPrueba manual de conectividad.Estructura del evento de retiro
{
"event_id": "evt_b98c76d54e32a10f87654321",
"event": "payout.sent",
"id": "W-8J2K7M4RQP",
"status": "sent",
"asset": "ETH",
"network": "mainnet",
"amount": "0.25",
"fee": "0.00081768",
"address": "0x84f2...9bc1",
"txid": "0xa1c4...07ee",
"confirmations": 0,
"note": "weekly treasury",
"sent_at": "2026-07-17T14:08:31Z"
}Errores y límites de frecuencia
Los errores siempre utilizan un único envoltorio JSON. Ramifique la lógica según error.type; el mensaje legible puede mejorarse sin que cambie la versión. Cite error.request_id o la cabecera de respuesta X-Request-Id correspondiente al contactar con soporte.
{
"error": {
"type": "ambiguous_asset",
"message": "specify network (e.g. USDT.ERC20 or USDT.TRC20)",
"request_id": "b942e21f8dca4b06b8672eb9"
}
}| HTTP | Tipos habituales | Significado |
|---|---|---|
| 400 | invalid_request, unknown_parameter | JSON, consulta o cabecera de idempotencia mal formados. |
| 401 | unauthorized | Credencial o cuenta ausente, inválida o inactiva. |
| 403 | insufficient_scope, admin_disabled | Clave válida sin el permiso requerido (véase X-Required-Scope), o un recurso bloqueado por un administrador. |
| 404 | not_found | Endpoint desconocido, o un recurso fuera de esta cuenta de comerciante. |
| 405 | method_not_allowed | Utilice el método indicado en la cabecera Allow. |
| 409 | idempotency_conflict | Clave reutilizada con un JSON diferente. |
| 413 | request_too_large | El cuerpo JSON supera los 64 KiB. |
| 415 | unsupported_media_type | El cuerpo del POST no se declara como application/json. |
| 422 | invalid, limit, invalid_amount, below_minimum, bad_address, 2fa_required, unsupported_asset | Solicitud bien formada que no superó la validación determinista o alcanzó un límite de recursos. |
| 429 | rate_limited | Espere Retry-After. |
| 500 | server_error | Fallo inesperado; reintente de forma segura con la misma clave de idempotencia. |
| 503 | maintenance, temporarily_unavailable, asset_unavailable, no_rate, price_stale, fiat_rate_unavailable, fiat_rate_stale, derive_failed | Fallo transitorio de la plataforma, del nodo, del precio o de la asignación de dirección. No se sustituye por una conversión obsoleta. |
Límites actuales
| Ámbito | Límite | Ventana |
|---|---|---|
| Techo de seguridad de Nginx por IP | 10 solicitudes/segundo, ráfaga de 30 | Continuo |
| Techo para IP no autenticada | 300 solicitudes | 60 segundos |
| POST /v1/payments, links, shops, products | 120 solicitudes por clave de API | 60 segundos |
| POST /v1/payouts | 30 solicitudes por clave de API | 60 segundos |
| Endpoints GET | 240 solicitudes por clave de API | 60 segundos |
Límites de la cuenta
| Recurso | Límite |
|---|---|
| Tiendas por cuenta | 10 |
| Enlaces de pago por cuenta | 50 |
| Productos por tienda | 50 |
| Combinaciones de variantes por producto | 30 |
| Claves de licencia por producto o enlace | 10,000 |
| Preguntas de checkout por enlace | 5 |
| Claves de API activas por cuenta | 50 |
Consulte su uso en vivo frente a estos límites desde GET /v1/account.
Las respuestas correctas limitadas a nivel de aplicación exponen X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset. Reintente 429, 500 y 503 con retroceso exponencial y jitter. En las solicitudes POST, reutilice siempre el Idempotency-Key original y un JSON idéntico.
Seguridad de la integración
Cárguelos en tiempo de ejecución; nunca registre el valor completo en logs ni lo incluya en el control de versiones.
Un cliente de navegador o móvil no puede almacenar de forma segura un secreto de comerciante.
Compruebe la vigencia de la marca de tiempo y utilice una comparación de firma en tiempo constante antes de analizar el JSON.
Registre los eventos o pedidos procesados de forma transaccional para que los reintentos nunca generen un envío duplicado.
Recupere el pago cuando un evento sea inesperado o su estado local no coincida.
Cree una clave de sustitución, despliéguela, verifique el tráfico y, después, elimine la clave anterior.
El acceso a la cuenta se controla mediante una clave de comerciante de 16 dígitos no recuperable, protegida opcionalmente con TOTP. Guarde tanto el acceso de comerciante como los secretos de la API con el mismo cuidado que las credenciales de una cartera.
Lista de verificación de lanzamiento
No reutilice la copia personal de un desarrollador entre distintos servicios.
Utilice GET /v1/assets y GET /v1/currencies; muestre únicamente las entradas presentes y available: true.
Guarde el secreto de firma una sola vez; verifique la marca de tiempo y la firma y, después, elimine duplicados usando el ID estable del evento.
Ponga a prueba un reintento duplicado y confirme que solo existe un ID de pago.
El estado de su pedido debe seguir siendo seguro ante tipos fiat obsoletos, activos no disponibles, confirmaciones retrasadas y cualquier otro escenario adverso.
Compare sus pedidos con los estados de pago de la API, los registros de webhooks y el libro contable del comerciante.
¿Listo para integrar?
Cree una cuenta en segundos, genere una clave y mantenga esta referencia junto a su código.