Enqre

Publicado el 16/8/2026

Códigos QR dinámicos en PHP: cuándo hace falta una API y cuándo no

«Cómo genero un código QR en PHP» tiene dos respuestas muy distintas, y elegir la equivocada cuesta dinero o una reimpresión.

Si el código es estático, hazlo en local

Si solo necesitas una imagen de una URL — una entrada, una factura, un recibo —, instala una biblioteca y listo. endroid/qr-code dibuja PNG y SVG sin conexión: sin clave, sin cuota, sin depender de la disponibilidad de nadie y sin coste.

Eso cubre la mayoría de estas preguntas, y cualquier artículo que se la salte para venderte algo te está gastando la tarde.

Qué cambia con un código impreso

Un código estático es su destino: la URL está en el patrón. Si cambia, todo lo impreso queda mal para siempre.

La pregunta no es «PHP o servicio», sino: tras imprimirlo, ¿el destino tendrá que moverse? Si la respuesta es sí — y en un envase, una placa o un folleto suele serlo —, el código debe llevar una dirección corta que controles. Eso es lo único que una biblioteca no puede darte, porque requiere que alguien responda a esa dirección durante años.

Crear uno

<?php
$ch = curl_init("https://enqre.com/api/v1/qrcodes");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer " . getenv("ENQRE_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "name" => "Order 10432 – delivery note",
        "url"  => "https://shop.example/orders/10432",
    ]),
]);

$body   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($status !== 201) {
    throw new RuntimeException("enqre: HTTP $status — $body");
}

$code = json_decode($body, true)["data"];
// $code["id"]       — store this against your record
// $code["shortUrl"] — the address the QR image should encode
  • Guarda el id devuelto junto a tu registro.
  • Pon tu identificador en el nombre.

Dibuja shortUrl con la biblioteca local que prefieras: son dos tareas separadas.

Mover el destino

<?php
$id = $code["id"];
$ch = curl_init("https://enqre.com/api/v1/qrcodes/$id");
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => "PATCH",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Authorization: Bearer " . getenv("ENQRE_API_KEY"),
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => "https://shop.example/orders/10432/tracking",
    ]),
]);
curl_exec($ch);
curl_close($ch);
// The printed code is untouched. It now opens the tracking page.

Leer los escaneos

<?php
$ch = curl_init("https://enqre.com/api/v1/qrcodes/$id/scans");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("ENQRE_API_KEY")],
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

echo $res["total"];             // every scan ever counted
foreach ($res["data"] as $scan) {
    // createdAt, device, os, browser — and nothing else
}

Conviene saberlo antes de diseñar un informe: un escaneo guarda hora, tipo de dispositivo, sistema y navegador. Ni identidad ni ubicación.

Los errores reales

  • 401 invalid_api_key
  • 400 invalid_body — casi siempre una URL que no lo es.
  • 402 plan_limit_reached
  • 429 rate_limited — más de 60 peticiones por minuto.

Comprueba CURLINFO_RESPONSE_CODE en vez de fiarte del cuerpo.

Dos cosas desde el primer día

Haz la creación idempotente. Dos llamadas crean dos códigos.

No imprimas lo que no has releído.

Cuándo no escribir la integración

Para unos cientos de códigos una sola vez, la subida de CSV es más rápida que cualquier código que escribas. Integra cuando los códigos aparezcan de forma continua.

Respuestas rápidas

  • ¿Mejor biblioteca PHP? endroid/qr-code para dibujar.
  • ¿Dinámico en PHP puro? Sí, si alojas tú la redirección.
  • ¿Autenticación? Authorization: Bearer enq_….
  • ¿Límite? 60 peticiones por minuto.
  • ¿La API devuelve la imagen? No, devuelve shortUrl.
  • ¿Ubicación en los escaneos? No.

Sigue leyendo