Deno
Stack: Deno ≥2.0 + Hono. Sem SDK npm — usa fetch direto (mais robusto pra edge).
main.ts
import { Hono } from 'jsr:@hono/hono';
const ZF_API = Deno.env.get('ZFISCOO_API_URL')!;
const ZF_KEY = Deno.env.get('ZFISCOO_API_KEY')!;
const ZF_SECRET = Deno.env.get('ZFISCOO_WEBHOOK_SECRET')!;
const app = new Hono();
app.post('/emitir', async (c) => {
const pedido = await c.req.json();
const res = await fetch(`${ZF_API}/v1/nfce`, {
method: 'POST',
headers: {
Authorization: `Bearer ${ZF_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': `nfce:${pedido.id}`,
},
body: JSON.stringify({
issuer_id: Deno.env.get('ZFISCOO_ISSUER_ID')!,
external_ref: pedido.id,
items: pedido.items,
payment: pedido.payment,
}),
});
return c.json(await res.json(), res.status as 200);
});
app.post('/zfiscoo/webhook', async (c) => {
const raw = await c.req.text();
const signature = c.req.header('x-signature') ?? '';
const keyData = new TextEncoder().encode(ZF_SECRET);
const key = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(raw));
const expected = Array.from(new Uint8Array(sig))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
const received = signature.replace(/^sha256=/, '');
if (expected !== received) {
return c.json({ error: 'invalid signature' }, 401);
}
const event = JSON.parse(raw);
console.log(`[zfiscoo] ${event.type} → ${event.data.id}`);
return c.json({ ok: true });
});
Deno.serve({ port: 8000 }, app.fetch);deno run --allow-net --allow-env main.ts