Monta una tienda de eSIM funcional sobre la API de eSIMfly con el SDK oficial @esimfly/sdk: autentícate, replica el catálogo de paquetes a precio mayorista, crea pedidos con cargo al saldo de tu cuenta y entrega a tu usuario un código QR listo para escanear, sin escribir ni una línea de firma de peticiones.
npm install @esimfly/sdk (sin dependencias, con tipos TypeScript incluidos)Cada petición necesita cuatro cabeceras: tu código de acceso, un ID de petición único, una marca de tiempo y una firma HMAC-SHA256 calculada con tu clave secreta. El SDK las genera en cada llamada, así que la autenticación se reduce a un constructor. Mantén el cliente en tu servidor: la clave secreta nunca debe llegar a un navegador ni a una app móvil.
// $ npm install @esimfly/sdk
// esimfly.js - one client for your whole backend (server-side only!)
import { ESIMfly } from '@esimfly/sdk';
export const esimfly = new ESIMfly({
accessCode: process.env.ESIMFLY_ACCESS_CODE, // esf_...
secretKey: process.env.ESIMFLY_SECRET_KEY, // sk_...
});
// Every call is signed for you: a fresh request ID (UUID v4), a millisecond
// timestamp and an HMAC-SHA256 signature of
// timestamp + requestId + accessCode + requestBody
// computed with your secret key. Requests older than 5 minutes are rejected.
const { balance, currency } = await esimfly.balance.get();
console.log(balance, currency); // 250.00 'USD'El catálogo devuelve todos los paquetes que puedes vender con tu precio mayorista; la cobertura abarca 200 países y más de 450 redes. Consúltalo de forma puntual mientras desarrollas, pero en producción replícalo en tu propia base de datos de forma programada y sirve tu tienda desde ahí: los precios cambian cuando los operadores modifican sus tarifas y el endpoint de pedidos siempre cobra el precio vigente.
// Ad-hoc lookup: packages for one destination, with YOUR wholesale price
const { packages } = await esimfly.packages.list({ search: 'Turkey', type: 'local', limit: 50 });
console.log(packages[0]);
// { package_code: '1648812', name: 'Turkey 1 GB 7 Days', type: 'local',
// data_amount_gb: 1, validity_days: 7, cost: 2.72, currency: 'USD', ... }
// Production: mirror the whole catalog into your database on a schedule
// (every 6-12 h) and serve your storefront from it. listAll()/sync() page
// with limit=100 and pause 1 s between pages, so a full sync stays inside
// the rate limit.
await esimfly.packages.sync(async (batch) => {
await db.packages.upsertMany(batch.map((p) => ({
packageCode: p.package_code, // opaque - store it verbatim
name: p.name,
cost: p.cost, // your buy price
sellPrice: p.cost * 1.3, // your margin, your rules
dataGb: p.data_amount_gb,
validityDays: p.validity_days,
})));
});Una sola llamada con el código del paquete compra la eSIM con cargo al saldo de tu cuenta y devuelve todo lo necesario para instalarla: cadena LPA, imagen del código QR y enlaces de instalación con un toque. Pasa siempre una idempotencyKey (tu propio ID de pedido): si la petición expira, reintentar con la misma clave devuelve el pedido original en lugar de cobrarte dos veces. La mayoría de los pedidos se completan al instante; unos pocos paquetes devuelven status: "pending_details" y waitForEsim() sondea hasta que están listos.
const order = await esimfly.orders.create({
packageCode: '1648812',
quantity: 1,
idempotencyKey: cart.id, // your own id: a retry can never charge you twice
});
console.log(order.orderReference); // order_1692123456_ab7cd
console.log(order.newBalance); // 47.28 - charged from your balance
const esim = order.esims[0];
console.log(esim.iccid); // 8932042000010078801
console.log(esim.lpaString); // LPA:1$rsp-3104.idemia.io$DOAZJ-...
// A few packages are provisioned asynchronously (status: 'pending_details').
// waitForEsim() polls the order every 20 s for up to 10 minutes:
if (order.status === 'pending_details') {
const ready = await esimfly.orders.waitForEsim(order.orderReference);
console.log(ready.esim.iccid);
}Cada pedido incluye tres mecanismos de entrega, y conviene mostrarlos todos: el código QR para instalar desde otro dispositivo, el enlace universal de Apple para instalar en iOS con un toque y la cadena LPA en bruto como alternativa manual.
// Everything your user needs is in order.esims[i]:
//
// esim.lpaString -> render your own QR code from it (any QR library)
// and show it for manual entry
// (Settings > Cellular > Add eSIM > Enter manually)
// esim.directAppleInstallUrl -> one-tap install on iOS 17.4+ (universal link)
// esim.directAndroidInstallUrl -> one-tap install on supported Android devices
// esim.qrCodeUrl -> ready-made base64 PNG if you prefer not to render
await db.esims.create({
orderReference: order.orderReference,
iccid: esim.iccid,
lpaString: esim.lpaString,
appleUrl: esim.directAppleInstallUrl,
androidUrl: esim.directAndroidInstallUrl,
expiresAt: esim.expired_time,
});
app.get('/my-esim/:orderRef', async (req, res) => {
const esim = await db.esims.findByOrder(req.params.orderRef);
res.render('esim', { lpa: esim.lpaString, appleUrl: esim.appleUrl, androidUrl: esim.androidUrl });
});Todo fallo es un ESIMflyError con el code de la API: decide según el código, nunca según el mensaje:
import { ESIMflyError } from '@esimfly/sdk';
try {
await esimfly.orders.create({ packageCode, idempotencyKey: cart.id });
} catch (err) {
if (err instanceof ESIMflyError) {
switch (err.code) {
case 'INSUFFICIENT_BALANCE': // err.response.needToLoad tells you how much
return alertOps(err.response);
case 'INVALID_PACKAGE': // catalog changed - resync
return resyncCatalog();
case 'RATE_LIMIT_EXCEEDED':
return retryLater();
}
}
throw err;
}orderReference, el ICCID y los datos de instalación para que los usuarios puedan volver a abrir su eSIM más tarde.newBalance y avísate antes de que se agote.La referencia completa de endpoints, ejemplos en Python y PHP y un entorno de pruebas interactivo están en la documentación.