Un sitio en PHP recibe cripto con CryptoLike con tres piezas de código: una función que firma las solicitudes a la API con hash_hmac, una llamada que crea el pago de un pedido y un manejador de webhooks que marca el pedido como pagado. No hace falta ninguna extensión aparte de cURL, y el mismo código corre en PHP 7.4 y 8.x, en Laravel, Symfony o PHP puro.
Esta guía es para un sitio o una aplicación propios. Una tienda en WooCommerce, OpenCart, PrestaShop o Magento 2 no necesita código: instala el plugin de WooCommerce o el módulo de tu plataforma.
Qué necesitas
Una cuenta de CryptoLike, un servidor PHP que pueda hacer solicitudes HTTPS salientes y una URL pública https:// para el webhook. Guarda el secreto de la API y el secreto del webhook en variables de entorno: los ejemplos leen CRYPTOLIKE_API_KEY_ID, CRYPTOLIKE_API_SECRET y CRYPTOLIKE_WEBHOOK_SECRET, además de CRYPTOLIKE_API_URL para la URL de la API de prueba (si no está definida, se usa producción). Prueba todo primero con claves de prueba y redes de prueba, así no se mueve dinero real.
1. Crea una clave API de prueba
En el panel abre Desarrolladores → Claves API → Crear clave y dale a la clave los permisos create_invoice y read. Las claves que empiezan con ck_test_ funcionan con redes de prueba. Las claves de prueba se crean en el entorno de prueba: el soporte de CryptoLike te da acceso a su panel, donde creas la misma clave, y la URL de su API (CRYPTOLIKE_API_URL en los ejemplos). El panel de producción emite solo claves reales, y en la red principal una clave de prueba se rechaza con 401 auth.test_key; cambia a una clave real solo cuando el flujo funcione de punta a punta. El secreto se muestra una sola vez: ponlo directo en el entorno de tu servidor, nunca en un repositorio ni en una página.
2. Firma las solicitudes a la API con hash_hmac
Cada solicitud lleva tres encabezados: el ID de la clave, la hora Unix en segundos y una firma HMAC-SHA256 de cuatro líneas: la marca de tiempo, el método, la ruta con su query y el SHA-256 del cuerpo. Esta función lo hace con cURL:
function cryptolike(string $method, string $path, ?array $payload = null, ?string $idempotencyKey = null): array
{
$body = $payload === null ? '' : json_encode($payload, JSON_UNESCAPED_SLASHES);
$ts = (string) time();
$canonical = implode("\n", [$ts, strtoupper($method), $path, hash('sha256', $body)]);
$headers = [
'X-API-Key: ' . getenv('CRYPTOLIKE_API_KEY_ID'),
'X-Timestamp: ' . $ts,
'X-Signature: ' . hash_hmac('sha256', $canonical, getenv('CRYPTOLIKE_API_SECRET')),
'Content-Type: application/json',
];
if ($idempotencyKey !== null) {
$headers[] = 'Idempotency-Key: ' . $idempotencyKey;
}
$ch = curl_init((getenv('CRYPTOLIKE_API_URL') ?: 'https://api.cryptolike.net') . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body === '' ? null : $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$data = json_decode((string) $response, true) ?? [];
if ($status >= 400) {
throw new RuntimeException(($data['error']['code'] ?? 'http_' . $status) . ': ' . ($data['error']['message'] ?? ''));
}
return $data;
}El cuerpo del que calculas el hash debe ser exactamente la cadena que envías; por eso la función lo codifica una sola vez. Antes de la primera llamada real, compara tu firma con el vector de referencia de autenticación de la API: un 401 auth.invalid_signature casi siempre significa otra cadena de cuerpo o la hora en milisegundos.
3. Crea el pago de un pedido
Crea un pago por pedido desde el servidor. Ponle precio en tu moneda y deja que el cliente elija la moneda cripto, o pasa un coin_id de GET /v1/currencies para fijarla. Sin coin_id, la página de pago muestra las monedas disponibles para facturas, cada una con un monto aproximado marcado con ≈; para ofrecer solo algunas en este pedido, agrega allowed_coin_ids con los id que tienen invoice_enabled: true en GET /v1/currencies firmado. Con el ID del pedido como clave de idempotencia, un doble clic o un reintento devuelve el mismo pago:
$invoice = cryptolike('POST', '/v1/invoices', [
'amount' => '49.90',
'currency' => 'USD',
'order_id' => $order->id,
'idempotency_by_order_id' => true,
'return_url' => 'https://example.com/orders/' . $order->id,
], 'order-' . $order->id);
$order->saveInvoiceId($invoice['id']);Los montos son cadenas decimales, nunca números de punto flotante: '49.90', no 49.9. La respuesta trae id, status (new) y checkout_url.
4. Envía al cliente a la página de pago
Redirige a checkout_url: la página muestra el monto, la red, la dirección con código QR, el tiempo restante y el estado en vivo, y después del pago devuelve al cliente a return_url. Para que el cliente se quede en tu sitio, incrusta la misma página con el widget de pago. Tu página solo recibe el ID de la factura; el secreto de la API se queda en el servidor.
header('Location: ' . $invoice['checkout_url'], true, 303);
exit;5. Verifica el webhook y marca el pedido como pagado
Agrega un endpoint en el panel, en Desarrolladores → Webhooks, con los eventos invoice.* y guarda su secreto. El manejador verifica la firma sobre el cuerpo sin procesar, rechaza marcas de tiempo antiguas, omite los eventos que ya procesó y responde 200 rápido:
$raw = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$secret = (string) getenv('CRYPTOLIKE_WEBHOOK_SECRET');
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $secret);
if ($secret === '' || !ctype_digit($ts) || abs(time() - (int) $ts) > 300 || !hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
$event = json_decode($raw, true);
if (!alreadyProcessed($event['event_id'])) {
if ($event['type'] === 'invoice.paid') {
markOrderPaid($event['data']['invoice']['order_id']);
}
rememberEvent($event['event_id']);
}
http_response_code(200);Lee el cuerpo con php://input, no con $_POST, y no lo decodifiques ni lo vuelvas a codificar antes de la verificación. Cada evento se entrega al menos una vez y puede llegar en desorden: confía en el estado dentro de data, no en el orden de llegada. Más detalles y el vector de prueba: cómo verificar los webhooks de pagos cripto.
6. Prueba en una red de prueba y pasa a producción
Crea un pago con la clave de prueba, págalo con monedas de prueba en la red que indica la página y comprueba que llegan invoice.pending y después invoice.paid. Luego maneja los demás resultados: invoice.underpaid (el cliente puede completar el pago a la misma dirección hasta el vencimiento), invoice.expired (ofrece un pago nuevo) e invoice.reverted (detén la entrega del pedido). Reemplaza la clave de prueba por una real y recorre la lista de verificación para producción de la guía para desarrolladores.
Errores comunes y cómo corregirlos
| Error | Qué pasó | Qué hacer |
|---|---|---|
401 auth.invalid_signature |
La cadena firmada no coincide con la solicitud | Calcula el hash del cuerpo exacto que envías; firma la ruta con su query; usa segundos, no milisegundos |
401 auth.expired_timestamp |
El reloj del servidor tiene un desfase de más de 5 minutos | Sincroniza el reloj con NTP |
403 auth.permission_denied |
A la clave le falta un permiso | Agrega create_invoice o read a la clave en el panel |
409 conflict |
El pedido ya tiene un pago por otro monto o ya recibió dinero | Lee details.invoice_id; no crees un segundo pago |
422 idempotency.mismatch |
El mismo Idempotency-Key llegó con otro cuerpo |
Usa una clave por pedido y por solicitud |
422 no_allowed_coins |
Ninguna moneda de allowed_coin_ids está disponible para facturas en esta divisa |
Envía otros id o quita el campo para ofrecer todas las monedas |
¿Prefieres un SDK?
El SDK oficial de PHP envuelve las mismas llamadas para PHP 7.4 – 8.x sin dependencias en tiempo de ejecución: solicitudes firmadas, reintentos ante 429 y 5xx, una factura abierta por pedido y Webhook::verify(). El código de esta guía es lo que el SDK hace por dentro, así que puedes empezar con uno u otro.
Preguntas frecuentes
¿Necesito Composer o una librería para recibir cripto en PHP?
No. Para firmar una solicitud a CryptoLike bastan hash() y hash_hmac() de la biblioteca estándar de PHP, y las solicitudes pasan por cURL. La función del paso 2 alcanza para toda la integración. El SDK oficial de PHP agrega reintentos, paginación y utilidades para tiendas, y también funciona sin Composer: copias sus archivos en tu proyecto.
¿Puedo mostrar la página de pago en mi propio sitio?
Sí. Crea el pago en tu servidor como en el paso 3 y, en lugar de redirigir, incrusta la misma página de pago con el widget de pago. Antes agrega la dirección de tu sitio a los orígenes permitidos: la tarjeta Orígenes permitidos en la página Configuración del panel. El widget solo lee datos públicos del pago, así que el secreto de la API nunca llega al navegador.
¿Cómo sé a qué pedido corresponde un webhook?
El objeto invoice del webhook trae el order_id que enviaste al crear el pago, y metadata si lo enviaste. Busca el pedido por ese valor, revisa el estado dentro de data y guarda el event_id: así una entrega repetida del mismo evento se confirma sin modificar el pedido dos veces.