eSimfly Logo
TUTORIAL NODE.JS · PARTE 2

Recargas de eSIM, control de consumo y webhooks

Vender la eSIM es solo la mitad del producto; la otra mitad es lo que ocurre después: mostrar a los clientes los datos que les quedan, vender recargas en el momento oportuno y reaccionar al instante cuando una eSIM se instala, se activa o se queda sin datos, sin sondear. Esta guía cubre las tres cosas.

¿Acabas de llegar? Empieza por la primera parte: Cómo vender eSIM en tu app con Node.js; allí se configura el cliente esimfly que se usa a continuación.

1. Controla el consumo de datos en tiempo real

Consulta cualquier eSIM por su ICCID para conocer su consumo y validez. Úsalo cuando el cliente abra su eSIM en tu app; para los avisos de "te quedan pocos datos" deja que el webhook esim.usage.threshold (paso 3) te lo diga en lugar de sondear todas las eSIM.

// esimfly = the client from part one
const { data, validity } = await esimfly.esims.usage({ iccid: '8910300001234567890' });
console.log(data);
// { total_mb: 1024, used_mb: 256, remaining_mb: 768,
//   usage_percentage: 25, is_unlimited: false }
console.log(validity.expires_at); // 2026-02-05T10:30:00Z

// Call it when the customer opens their eSIM and cache it for 5-15 minutes.
// For "running low" alerts you don't need to poll at all - see webhooks below.

2. Vende recargas en eSIM existentes

Las recargas son la venta recurrente con mayor margen del negocio de las eSIM: el cliente ya tiene el perfil instalado, así que no hay ninguna fricción de alta. Consulta siempre primero los paquetes compatibles por ICCID: no todos los paquetes pueden recargar todas las eSIM.

// 1. Which packages can THIS eSIM be topped up with? (they depend on the eSIM)
const { packages } = await esimfly.topups.packages({ iccid, limit: 100 });

// 2. Apply a top-up - charged from your balance, active immediately
const topup = await esimfly.topups.create({ iccid, packageCode: packages[0].package_code });
console.log(topup.esimData);  // { newTotalVolumeGB: 8, newRemainingVolumeGB: 8, expiredTime: ... }
console.log(topup.newBalance);

// Top-ups are not idempotent: on a network timeout, check
// esimfly.esims.usage({ iccid }) before trying again.

3. Recibe webhooks (sin sondeo)

eSIMfly envía un POST HTTP firmado a tu servidor cuando algo cambia en una eSIM: el perfil se instala en un teléfono (esim.installed, en segundos), el plan se activa o caduca (esim.status.changed) o los datos restantes bajan de 500 / 200 / 100 / 50 MB (esim.usage.threshold). Registra tu URL una sola vez y elige los eventos que quieres recibir.

// One-time setup: register your endpoint and pick the events you want
const { webhook } = await esimfly.webhooks.set({
  webhookUrl: 'https://your-server.com/api/esimfly-webhook',
  events: ['esim.installed', 'esim.status.changed', 'esim.usage.threshold'],
});
// SAVE webhook.secret (whsec_...) - shown once, needed to verify deliveries.

// Events you can subscribe to:
//   esim.installed        profile enabled on a phone for the first time   (seconds)
//   esim.status.changed   NEW -> ACTIVE -> DEPLETED / EXPIRED               (<= 30 min)
//   esim.usage.threshold  500 / 200 / 100 / 50 MB remaining                (seconds)
//   esim.profile.updated  every SM-DP+ state change (chatty - usually skip)
//   esim.provisioned      an asynchronously provisioned order is ready

Verifica siempre la firma

Cada entrega va firmada con HMAC-SHA256 (cabecera X-Webhook-Signature). Verifícala contra el cuerpo en bruto de la petición con una comparación de tiempo constante: nunca proceses una carga sin firmar ni verifiques contra un JSON reserializado.

import { constructWebhookEvent, ESIMflyError } from '@esimfly/sdk';

// Express: keep the RAW body - the signature covers the exact bytes
app.post('/api/esimfly-webhook',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    let event;
    try {
      event = constructWebhookEvent(
        req.body,                            // raw Buffer
        req.header('X-Webhook-Signature'),   // sha256=<hex>
        process.env.ESIMFLY_WEBHOOK_SECRET,
      );
    } catch (err) {
      const badSignature = err instanceof ESIMflyError && err.code === 'INVALID_SIGNATURE';
      return res.status(badSignature ? 401 : 400).end();
    }

    // Deduplicate: retries reuse the same X-Webhook-Id
    const deliveryId = req.header('X-Webhook-Id');
    if (await db.webhookDeliveries.exists(deliveryId)) return res.json({ received: true });
    await db.webhookDeliveries.insert({ id: deliveryId, event: event.event, data: event.data });
    res.json({ received: true }); // ack within 10 s, then do the work

    switch (event.event) {
      case 'esim.installed':
        await db.esims.update({ iccid: event.data.iccid }, { installedAt: event.data.installed_at });
        break;
      case 'esim.status.changed':
        await db.esims.update({ iccid: event.data.iccid }, { status: event.data.new_status });
        break;
      case 'esim.usage.threshold':
        if (event.data.threshold_remaining_mb <= 200) await sendLowDataPush(event.data.iccid);
        break;
    }
  });

Lista de comprobación para producción

  • Confirma los webhooks rápido: devuelve 200 de inmediato y procesa la carga de forma asíncrona; los endpoints lentos se reintentan y verás duplicados.
  • Deduplica por ID de entrega: usa la cabecera X-Webhook-Id para que los reintentos nunca entreguen una eSIM dos veces.
  • Deja que los webhooks vigilen por ti: suscríbete a los eventos de instalación, estado y datos bajos y consulta el consumo solo cuando un cliente abra su eSIM (guárdalo en caché unos minutos). Los límites son por clave de API, normalmente 100 peticiones por minuto y 1000 por hora.

Construye el ciclo de vida completo

Suspender, cancelar, entrega por SMS, eventos de red y mucho más: la referencia completa de endpoints está en la documentación.