Operare în producție
Webhooks durabile, fără dublarea operațiilor
Semnătură pe corpul brut, inbox persistent și procesare asincronă în afara requestului HTTP.
1. Configurează un endpoint pe mediu
În consola dezvoltatorilor selectează numai evenimentele necesare, setează URL-ul HTTPS și păstrează secretul de semnare exclusiv pe server. Cheia API și secretul webhook sunt credențiale diferite. Separă test de live și nu folosi același inbox fără un namespace care identifică aplicația, mediul și endpointul. Evenimentul este o notificare verificată, nu o instrucțiune de a executa orice operație din payload.
2. Verifică și persistă înainte de 2xx
constructWebhookEvent din SDK-ul Commerce verifică formatul comun al semnăturii Monedo, inclusiv timestampul. Exemplul este adaptor-agnostic: tu furnizezi corpul brut, headerul și inboxul persistent. insertOnce nu este o funcție SDK; este interfața către baza ta de date. Ea trebuie să scrie evenimentul și outboxul de procesare atomic. Nu folosi un Set, o coadă în memorie sau un promise lăsat să ruleze după închiderea unei funcții serverless.
import { constructWebhookEvent } from '@monedo/commerce';
// Pass the unmodified raw body, the Monedo-Signature header, and the endpoint's
// signing secret. Mount this before JSON body middleware in your web server.
export async function acceptWebhook({ rawBody, signature, secret, inbox }) {
let event;
try {
event = constructWebhookEvent(rawBody, signature, secret);
} catch {
return { status: 400 };
}
if (!event || typeof event.id !== 'string' || typeof event.type !== 'string') {
return { status: 400 };
}
// Implement insertOnce with a database UNIQUE constraint on endpoint + event.id.
// Store and queue in the same transaction (transactional outbox). It must resolve
// for duplicates and throw for storage outages. Never use an in-memory Set.
try {
await inbox.insertOnce(event);
} catch {
return { status: 503 };
}
return { status: 204 };
}
// A separate durable worker processes the inbox and retries failures.
// Do not call payment capture, transfer, email or any external service here.
// Re-read current resources before state changes: events may arrive out of order.
3. Procesează în worker
- Răspunde rapid, în bugetul de livrare, fără capturi, transferuri ori email în handlerul webhook.
- Deduplică prin constrângere unică pe endpoint + event.id, nu doar printr-un SELECT urmat de INSERT.
- Revendică jobul cu lease și retry persistent; folosește dead-letter pentru eșecuri care necesită intervenție.
- Recitește resursa financiară înainte de schimbări locale: evenimentele pot veni în ordine inversă.
- Salvează efectul local și outboxul de fulfillment atomic. Același eveniment poate fi primit de mai multe ori.
- Un eșec de stocare trebuie să întoarcă non-2xx. Nu confirma un eveniment pe care nu îl vei putea procesa.
4. Debug și replay sigur
Înregistrează event.id, tipul, request ID-ul și rezultatul procesării, fără secrete sau date personale inutile. La replay păstrează deduplicarea activă. Un replay repară transportul sau reia o procesare permisă; nu trebuie să recaptureze o plată ori să dubleze un transfer deja executat. La rotația secretului verifică politica de compatibilitate a endpointului și păstrează fereastra de tranziție explicit, nu dezactiva verificarea semnăturii.