Skip to Content
ReceitasBun (Elysia / nativo)

Bun

Stack: Bun ≥1.1 + zfiscoo-sdk (ESM nativo).

bun add zfiscoo-sdk

Servidor com webhook + emissão

server.ts
import { ZfiscooClient } from 'zfiscoo-sdk'; import crypto from 'node:crypto'; const zfiscoo = new ZfiscooClient({ apiUrl: Bun.env.ZFISCOO_API_URL!, apiKey: Bun.env.ZFISCOO_API_KEY!, }); const seen = new Set<string>(); Bun.serve({ port: 3000, async fetch(req) { const url = new URL(req.url); if (url.pathname === '/emitir' && req.method === 'POST') { const pedido = await req.json(); const result = await zfiscoo.nfce.create({ body: { issuer_id: Bun.env.ZFISCOO_ISSUER_ID!, external_ref: pedido.id, items: pedido.items, payment: pedido.payment, }, idempotencyKey: `nfce:${pedido.id}`, }); return Response.json(result); } if (url.pathname === '/zfiscoo/webhook' && req.method === 'POST') { const raw = await req.text(); const signature = req.headers.get('x-signature') ?? ''; const expected = crypto .createHmac('sha256', Bun.env.ZFISCOO_WEBHOOK_SECRET!) .update(raw, 'utf8') .digest('hex'); const received = signature.replace(/^sha256=/, ''); if ( expected.length !== received.length || !crypto.timingSafeEqual( Buffer.from(expected, 'hex'), Buffer.from(received, 'hex'), ) ) { return new Response('invalid signature', { status: 401 }); } const event = JSON.parse(raw); if (seen.has(event.id)) return Response.json({ ok: true, deduped: true }); seen.add(event.id); // Process event console.log(`[zfiscoo] ${event.type} → ${event.data.id}`); return Response.json({ ok: true }); } return new Response('Not Found', { status: 404 }); }, }); console.log('listening on 3000');

Bun usa Bun.env em vez de process.env, mas o crypto é compatível Node. Em prod, troca o Set por Redis ou KV (de-dupe persiste reboot).