Guide de Conformité Fiscale : Facturation Électronique, Reverse Charge & Scellement Probant
Ce guide détaille les spécifications officielles, les formats XML/JSON obligatoires, les algorithmes de signature qualifiée et les schémas d'autoliquidation applicables sur les 7 juridictions interconnectées par VATMesh (Tunisie, Italie, France, Arabie Saoudite, Maroc, Émirats Arabes Unis, Égypte).
1. Tunisie : Format TEIF v1.8.8 & Signature DigiGo
Loi 2000-83 • Loi de Finances 2026 Art. 52-53 • CDPF Art. 41
En Tunisie, la facturation électronique s'articule autour du standard TEIF XML (Tunisian Electronic Invoicing Format) v1.8.8 administré par Tunisie TradeNet (TTN). Le scellement juridique requiert un certificat qualifié X.509 délivré par l'Agence Nationale de Certification Électronique (TunTrust / DigiGo) avec l'OID réglementaire 2.16.788.1.2.6.1.1.
Spécifications Clés :
- Identifiant fiscal : Format 7 chiffres + clé TVA + catégorie + code établissement (ex:
1847070/C/A/M/000). - Timbre fiscal : 1.000 DT obligatoire par facture soumise à TVA (sauf régimes d'exonération totale sous attestation DGI).
- Archivage probant : conservation selon la réglementation applicable, avec empreinte SHA-256 et horodatage applicatif.
<?xml version="1.0" encoding="UTF-8"?>
<TEIF xmlns="urn:ttn:teif:1.8.8">
<Header>
<InvoiceNumber>FA-2026-0042</InvoiceNumber>
<IssueDate>2026-10-15</IssueDate>
<Currency>TND</Currency>
<Seller>
<TaxID>1847070/C/A/M/000</TaxID>
<Name>Société Exportatrice SARL</Name>
</Seller>
</Header>
<TaxTotals>
<BaseAmount>1000.000</BaseAmount>
<TaxAmount rate="0.19">190.000</TaxAmount>
<FiscalStamp>1.000</FiscalStamp>
<TotalTTC>1191.000</TotalTTC>
</TaxTotals>
</TEIF>
2. Italie : FatturaPA XML v1.2.2 & Autofatture TD17-TD27
DPR 633/72 Art. 17 • Legge 205/2017 • Provvedimento SdI 2018/89757Toutes les transactions B2B et B2G en Italie transitent obligatoirement par le Sistema di Interscambio (SdI) sous format FatturaPA XML v1.2.2. Pour les prestations reçues de l'étranger (notamment du Maghreb ou de pays tiers), l'acheteur italien a l'obligation d'émettre une autofacture électronique d'intégration de TVA avant le 15 du mois suivant.
Intégration / Autoliquidation pour achat de services auprès de prestataires étrangers non résidents (Art. 17 comma 2 DPR 633/72).
Application obligatoire de la marque fiscale dématérialisée de 2.00 € sur les opérations hors champ TVA ou exonérées > 77.47 €.
3. France : Factur-X CII EN16931 & E-Reporting DGFiP
Ordonnance 2021-1190 • CGI Art. 289 • Standard Franco-Allemand FNFE-MPELa France applique le standard hybride Factur-X (CII EN16931) : un fichier PDF/A-3 lisible par l'homme intégrant dans ses pièces jointes un flux XML structuré conforme à la norme sémantique européenne EN 16931. Les opérations B2B domestiques sont transmises via le Portail Public de Facturation (PPF) ou les PDP, tandis que les flux transfrontaliers font l'objet d'un e-reporting périodique.
Dans les marchés de sous-traitance du bâtiment et travaux publics, la TVA est légalement acquittée par le preneur d'ordre. Le moteur VATMesh force la mention légale obligatoire et neutralise la taxe à la source.
4. Arabie Saoudite : ZATCA FATOORA (référentiel public)
Résolution 2108 / 2216 • ECDSA secp256k1 • TLV QR Code • UBL 2.1La phase d'intégration (Phase 2) de la ZATCA impose l'émission de factures au format UBL 2.1 XML enrichi de profils spécifiques. Chaque facture B2B doit faire l'objet d'un clearance direct par l'API de la ZATCA avant livraison, et comporter un scellement cryptographique asymétrique (ECDSA) généré à partir du certificat cryptographique CSID de l'émetteur.
5. Maroc : Contrôle ICE 15 Chiffres & Conformité DGI
CGI Maroc Art. 145 & Art. 211 • Télé-déclarations SIMPL-TVAAu Maroc, toute facture commerciale doit comporter l'Identifiant Commun de l'Entreprise (ICE à 15 positions) ainsi que l'Identifiant Fiscal (IF). Le moteur VATMesh exécute la validation algorithmique de structure ICE, applique les règles de retenue à la source et prépare les journaux conformes au référentiel SIMPL-TVA avec conservation probante sur 10 ans (Art. 211 CGI).
- Validation ICE : Contrôle déterministe des 15 chiffres (9 chiffres entreprise + 4 chiffres établissement + 2 chiffres clé de contrôle Modulo).
- Retenue à la source (RAS) : Arbitrage automatique des prestations de services rendues par des fournisseurs étrangers (10 % standard sous réserve de convention bilatérale).
6. Émirats Arabes Unis : Réseau Peppol PINT AE-1 & FTA
Federal Decree-Law No. 8 of 2017 (VAT) • Corporate Tax 9% vs QFZP 0%Les Émirats Arabes Unis déploient le cadre de facturation électronique décentralisé à 5 coins (5-Corner Model) basé sur la spécification Peppol PINT AE-1 supervisée par la Federal Tax Authority (FTA). VATMesh valide le TRN à 15 chiffres et arbitre les flux Mainland vs Free Zones.
- TVA standard 5 % & Export 0 % : Qualification automatique des prestations transfrontalières hors CCG.
- Qualifying Free Zone Persons (QFZP) : Ségrégation documentaire des revenus éligibles au taux 0 % d'impôt sur les sociétés contre le taux statutaire de 9 %.
7. Égypte : Facturation Électronique ETA & Retenues WHT
Loi 206/2020 sur les Procédures Fiscales Unifiées • Schéma JSON/XML ETA v1.0La plateforme nationale de l'Egyptian Tax Authority (ETA) exige la structuration des factures B2B selon la nomenclature GS1 / EGS, le calcul de la TVA (taux général 14 %) et la pré-évaluation des retenues à la source (Withholding Tax / الخصم والتحصيل تحت حساب الضريبة de 1 % à 5 % selon la nature de l'opération).
- Identifiant fiscal égyptien : Validation du Tax Registration Number (TRN à 9 chiffres au format
XXX-XXX-XXX). - Scellement cryptographique : Préparation du payload canonique SHA-256 pour signature CAdES-BES par token matériel Egypt Trust / MCDR.
8. Guide d'Intégration ERP & Connecteurs Statutaires
SAP S/4HANA • Dynamics 365 BC • Odoo • Sage 100 • Universal REST • Webhooks HMACL'infrastructure VATMesh fonctionne comme un co-processeur statutaire et d'interconnexion fiscale pour vos progiciels de gestion intégrés (ERP). Vos systèmes d'information conservent la maîtrise des processus de facturation et de comptabilité, tandis que VATMesh assure la validation de schéma, l'arbitrage transfrontalier de TVA, le scellement local dans le journal d’audit (empreinte SHA-256) ainsi que la préparation des payloads conformes aux référentiels publics (TEIF, FatturaPA, Factur-X, ZATCA FATOORA, UBL 2.1). La signature qualifiée et la transmission aux portails restent votre responsabilité.
8.1 SAP S/4HANA — Connecteur IDoc • Proxy HTTPS / SAP CPI
Dans SAP S/4HANA (ou SAP ECC 6.0), l'émission d'une facture client (Transaction VF01/VF02) déclenche la génération d'un message IDoc de type INVOIC02 via le mécanisme d'output determination (NACE ou BRFplus). Le middleware d'intégration (SAP Integration Suite / Cloud Platform Integration CPI ou On-Premise SAP PO) intercepte le flux, le convertit en JSON canonique VATMesh, appelle l'API de dédouanement et réinjecte le statut dans le cockpit eDocument (EDOC_COCKPIT).
import com.sap.gateway.ip.core.customdev.util.Message
import groovy.json.JsonOutput
import groovy.xml.XmlSlurper
def Message processData(Message message) {
def body = message.getBody(String)
def idoc = new XmlSlurper().parseText(body)
// Extraction des segments IDoc INVOIC02
def header = idoc.IDOC.E1EDK01
def buyerPartner = idoc.IDOC.E1EDKA1.find { it.PARVW == 'RE' || it.PARVW == 'AG' }
def sellerPartner = idoc.IDOC.E1EDKA1.find { it.PARVW == 'LF' || it.PARVW == 'WE' }
def payload = [
corridor: "TN-IT", // Déterminé dynamiquement selon pays émetteur/destinataire
seller_country: sellerPartner.LAND1.text() ?: "TN",
seller_tax_id: sellerPartner.KNREF.text() ?: "1847070/C/A/M/000",
buyer_country: buyerPartner.LAND1.text() ?: "IT",
buyer_vat_id: buyerPartner.PARTN.text() ?: "IT12345678901",
invoice_number: header.BELNR.text(),
issue_date: header.EDATUV.text() ?: new Date().format('yyyy-MM-dd'),
currency: header.CURCY.text() ?: "EUR",
amount_ht: Double.parseDouble(header.BRTWR.text() ?: "0.0"),
items: []
]
idoc.IDOC.E1EDP01.each { item ->
payload.items << [
line_id: item.POSEX.text(),
description: item.ARKTX.text() ?: "Prestation informatique",
quantity: Double.parseDouble(item.MENGE.text() ?: "1.0"),
unit_price_ht: Double.parseDouble(item.NETWR.text() ?: "0.0"),
tax_rate: 0.0 // Soumis à arbitrage VATMesh (reverse charge automatique)
]
}
message.setHeader("Content-Type", "application/json")
message.setHeader("Authorization", "Bearer ${message.getProperty('VATMESH_API_KEY')}")
message.setBody(JsonOutput.toJson(payload))
return message
}
DATA: lo_http_client TYPE REF TO if_http_client,
lv_json_body TYPE string,
lv_response TYPE string,
lv_status TYPE i.
cl_http_client=>create_by_url(
EXPORTING
url = 'https://vatmesh.com/api/v1/compliance/invoices/submit'
IMPORTING
client = lo_http_client ).
lo_http_client->request->set_method( if_http_request=>co_request_method_post ).
lo_http_client->request->set_header_field( name = 'Content-Type' value = 'application/json' ).
lo_http_client->request->set_header_field( name = 'Authorization' value = 'Bearer SEC_API_KEY_LIVE_...' ).
lo_http_client->request->set_cdata( lv_json_body ).
lo_http_client->send( ).
lo_http_client->receive( ).
lo_http_client->response->get_status( IMPORTING code = lv_status ).
lv_response = lo_http_client->response->get_cdata( ).
" Mise à jour du statut eDocument Cockpit avec le Clearance ID reçu
IF lv_status = 200.
" Parse JSON response & enregistre le clearance_id dans la table EDOCUMENT
ENDIF.
8.2 Microsoft Dynamics 365 Business Central — Extension AL • OData / Web Services
Dans Dynamics 365 Business Central (Cloud SaaS ou On-Premise), une extension AL s'abonne à l'événement OnAfterPostSalesDoc du Codeunit 80 (Sales-Post). Dès validation comptable de la facture, l'extension expédie le payload au guichet VATMesh, reçoit l'identifiant d'homologation légale et rattache le fichier XML scellé dans les pièces jointes (Document Attachment).
codeunit 50120 "VATMesh E-Invoicing Bridge"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales-Post", 'OnAfterPostSalesDoc', '', false, false)]
local procedure HandleOnAfterPostSalesDoc(var SalesHeader: Record "Sales Header"; var GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line"; SalesInvHdrNo: Code[20])
var
SalesInvHeader: Record "Sales Invoice Header";
Client: HttpClient;
RequestHeaders: HttpHeaders;
Content: HttpContent;
ContentHeaders: HttpHeaders;
ResponseMessage: HttpResponseMessage;
PayloadObj: JsonObject;
PayloadText: Text;
ResponseText: Text;
EndpointUrl: Label 'https://vatmesh.com/api/v1/compliance/invoices/submit';
begin
if not SalesInvHeader.Get(SalesInvHdrNo) then
exit;
// Construction du payload statutaire JSON
PayloadObj.Add('invoice_number', SalesInvHeader."No.");
PayloadObj.Add('issue_date', Format(SalesInvHeader."Posting Date", 0, 9));
PayloadObj.Add('seller_country', 'TN');
PayloadObj.Add('seller_tax_id', '1847070/C/A/M/000');
PayloadObj.Add('buyer_country', SalesInvHeader."VAT Country/Region Code");
PayloadObj.Add('buyer_vat_id', SalesInvHeader."VAT Registration No.");
PayloadObj.Add('amount_ht', SalesInvHeader.Amount);
PayloadObj.Add('currency', SalesInvHeader."Currency Code");
PayloadObj.WriteTo(PayloadText);
Content.WriteFrom(PayloadText);
Content.GetHeaders(ContentHeaders);
ContentHeaders.Remove('Content-Type');
ContentHeaders.Add('Content-Type', 'application/json');
Client.DefaultRequestHeaders().Add('Authorization', 'Bearer SEC_API_KEY_LIVE_...');
if Client.Post(EndpointUrl, Content, ResponseMessage) then begin
ResponseMessage.Content().ReadAs(ResponseText);
if ResponseMessage.IsSuccessStatusCode() then begin
// Mise à jour de la facture avec le Clearance ID officiel
SalesInvHeader."VATMesh Clearance ID" := ExtractJsonField(ResponseText, 'clearance_id');
SalesInvHeader."VATMesh Legal Status" := 'CLEARED';
SalesInvHeader.Modify();
end else
Session.LogMessage('VATMESH_ERR', 'Clearance failed: ' + ResponseText, Verbosity::Error, DataClassification::CustomerContent, TelemetryScope::ExtensionPublisher, 'ext', 'vatmesh');
end;
end;
local procedure ExtractJsonField(JsonText: Text; FieldName: Text): Text
var
JToken: JsonToken;
JObject: JsonObject;
begin
if JObject.ReadFrom(JsonText) and JObject.Get(FieldName, JToken) then
exit(JToken.AsValue().AsText());
exit('');
end;
}
8.3 Odoo — Co-processeur Python XML-RPC • Automatisation des Scellements
Pour les déploiements Odoo Community ou Enterprise (v16, v17, v18), VATMesh s'intègre via un co-processeur Python autonome ou une action automatisée (Server Action). Le script ci-dessous se connecte à l'API XML-RPC native d'Odoo, extrait les factures clients comptabilisées (state = 'posted') en attente de dédouanement fiscal, exécute l'arbitrage transfrontalier et téléverse le certificat XML/PDF probant dans les pièces jointes de la facture.
vatmesh_odoo_bridge.py) :
#!/usr/bin/env python3
"""
vatmesh_odoo_bridge.py
Pont bidirectionnel Odoo <-> VATMesh API
Conforme : Tunisie TEIF 1.8.8, Italie FatturaPA, France Factur-X, Arabie ZATCA
"""
import os
import sys
import xmlrpc.client
import base64
import requests
ODOO_URL = os.getenv("ODOO_URL", "https://mon-odoo.entreprise.com")
ODOO_DB = os.getenv("ODOO_DB", "production_db")
ODOO_USER = os.getenv("ODOO_USER", "odoo-user")
ODOO_API_KEY = os.getenv("ODOO_API_KEY", "secret_odoo_api_token")
VATMESH_KEY = os.getenv("VATMESH_API_KEY", "vm_live_abc123456789")
VATMESH_API = "https://vatmesh.com/api/v1/compliance/invoices/submit"
def run_sync():
print(f"[*] Connexion à Odoo {ODOO_URL}...")
common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common")
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_API_KEY, {})
if not uid:
sys.exit("[!] Échec d'authentification Odoo")
models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object")
# Recherche des factures clients validées non encore dédouanées
domain = [
('move_type', '=', 'out_invoice'),
('state', '=', 'posted'),
('payment_state', '!=', 'reversed'),
('x_vatmesh_clearance_id', '=', False)
]
fields = ['name', 'invoice_date', 'partner_id', 'amount_untaxed', 'amount_total', 'currency_id']
invoices = models.execute_kw(ODOO_DB, uid, ODOO_API_KEY, 'account.move', 'search_read', [domain], {'fields': fields, 'limit': 50})
print(f"[*] {len(invoices)} facture(s) en attente de dédouanement fiscal.")
for inv in invoices:
inv_id = inv['id']
inv_num = inv['name']
partner = models.execute_kw(ODOO_DB, uid, ODOO_API_KEY, 'res.partner', 'read', [inv['partner_id'][0]], {'fields': ['name', 'vat', 'country_id']})[0]
country_code = partner['country_id'][1] if partner['country_id'] else "TN"
payload = {
"invoice_number": inv_num,
"issue_date": inv['invoice_date'],
"seller_country": "TN",
"seller_tax_id": "1847070/C/A/M/000",
"buyer_country": country_code[:2].upper(),
"buyer_vat_id": partner['vat'] or "N/A",
"amount_ht": inv['amount_untaxed'],
"currency": inv['currency_id'][1] if inv['currency_id'] else "EUR",
"metadata": {"odoo_move_id": inv_id}
}
resp = requests.post(VATMESH_API, json=payload, headers={"Authorization": f"Bearer {VATMESH_KEY}"}, timeout=30)
if resp.status_code == 200:
result = resp.json()
clearance_id = result.get("clearance_id")
signed_xml = result.get("certified_xml_base64", "")
models.execute_kw(ODOO_DB, uid, ODOO_API_KEY, 'account.move', 'write', [[inv_id], {
'x_vatmesh_clearance_id': clearance_id,
'narration': f"Dédouanement Fiscal Conforme VATMesh ID : {clearance_id}"
}])
if signed_xml:
models.execute_kw(ODOO_DB, uid, ODOO_API_KEY, 'ir.attachment', 'create', [{
'name': f"{inv_num}_scellement_statutaire.xml",
'type': 'binary',
'datas': signed_xml,
'res_model': 'account.move',
'res_id': inv_id,
'mimetype': 'application/xml'
}])
print(f"[+] Facture {inv_num} dédouanée avec succès (ID: {clearance_id})")
else:
print(f"[-] Erreur rejet facture {inv_num} : {resp.status_code} - {resp.text}")
if __name__ == "__main__":
run_sync()
8.4 Sage 100 • Sage Cloud — Passerelle d'Export PNM & FEC
Sage 100 Comptabilité et Gestion Commerciale communique historiquement via le standard d'échange PNM (Paramétrable Normalisé Mégatec) ou via l'export FEC (Fichier des Écritures Comptables). La passerelle VATMesh ingère ces flux comptables plats, applique l'arbitrage fiscal transfrontalier (identification des régimes d'exonération, autoliquidation BTP Art. 283-2, timbres fiscaux de 1.000 DT ou 2.00 €), et réémet le flux PNM enrichi avec les références d'archivage et registre SHA-256 pour réimport automatique via l'Automate de Transfert Sage.
#FORMAT PNM
#VERSION 3.00
ECRITURE,VT,151026,41100000,CLIENT EXPORT SARL,FA-2026-0042,D,1191.000,TND,,C
ECRITURE,VT,151026,70600000,PRESTATION CONSULTING,FA-2026-0042,C,1000.000,TND,,C
ECRITURE,VT,151026,44571190,TVA COLLECTEE 19%,FA-2026-0042,C,190.000,TND,,C
ECRITURE,VT,151026,44580000,TIMBRE FISCAL OBLIGATOIRE,FA-2026-0042,C,1.000,TND,,C
/**
* sage_pnm_bridge.js — Passerelle Sage 100 <-> VATMesh
*/
const fs = require('fs');
const https = require('https');
function processSagePNM(filePath, apiKey) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split(/\r?\n/).filter(l => l.startsWith('ECRITURE,'));
// Agrégation par numéro de pièce (facture)
const invoices = {};
for (const line of lines) {
const parts = line.split(',');
const [tag, journal, dateStr, account, label, pieceNo, sens, amountStr, ccy] = parts;
if (!invoices[pieceNo]) {
invoices[pieceNo] = { pieceNo, journal, dateStr, ccy: ccy || 'TND', debit: 0, credit: 0, lines: [] };
}
const amt = parseFloat(amountStr) || 0;
if (sens === 'D') invoices[pieceNo].debit += amt;
if (sens === 'C') invoices[pieceNo].credit += amt;
invoices[pieceNo].lines.push({ account, label, sens, amount: amt });
}
console.log(`[SageBridge] ${Object.keys(invoices).length} pièces détectées dans le flux PNM.`);
for (const [piece, inv] of Object.entries(invoices)) {
if (Math.abs(inv.debit - inv.credit) > 0.001) {
console.error(`[Alerte Déséquilibre] Facture ${piece} : Débit=${inv.debit} vs Crédit=${inv.credit}`);
} else {
console.log(`[Conforme] Facture ${piece} équilibrée (${inv.credit} ${inv.ccy}). Soumission au scellement...`);
}
}
}
module.exports = { processSagePNM };
8.5 Universal REST API & Webhooks Sécurisés HMAC-SHA256
Toutes les plateformes non pourvues de connecteur natif s'interfacent via notre API REST universelle. Pour les flux asynchrones (réceptions d'autofactures SdI, quittances TTN, réceptions ZATCA), configurez un endpoint de webhook sécurisé par empreinte cryptographique HMAC-SHA256.
curl -X POST https://vatmesh.com/api/v1/tax/arbitrate \
-H "Authorization: Bearer SEC_API_KEY_LIVE_..." \
-H "Content-Type: application/json" \
-d '{
"corridor": "IT-TN",
"seller_country": "TN",
"seller_tax_id": "1847070/C/A/M/000",
"buyer_country": "IT",
"buyer_vat_id": "IT12345678901",
"amount_ht": 5000.00,
"currency": "EUR",
"service_type": "consulting_it"
}'
import requests
url = "https://vatmesh.com/api/v1/compliance/invoices/submit"
headers = {
"Authorization": "Bearer SEC_API_KEY_LIVE_...",
"Content-Type": "application/json"
}
payload = {
"invoice_number": "FA-2026-0089",
"issue_date": "2026-10-15",
"seller_country": "FR",
"seller_tax_id": "FR89123456789",
"buyer_country": "MA",
"buyer_vat_id": "001524367000085",
"currency": "EUR",
"amount_ht": 12500.00,
"lines": [
{
"description": "Prestation d'ingénierie logicielle",
"quantity": 1,
"unit_price_ht": 12500.00,
"tax_code": "REVERSE_CHARGE_EXPORT"
}
]
}
response = requests.post(url, json=payload, headers=headers, timeout=15)
result = response.json()
print("Clearance ID:", result.get("clearance_id"))
print("Statut:", result.get("status")) # 'CLEARED'
print("Format produit:", result.get("standard")) # 'FACTUR_X_EN16931'
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = process.env.VATMESH_WEBHOOK_SECRET || 'whsec_...';
// Le corps brut (rawBody) est obligatoire pour la vérification d'empreinte
app.post('/webhooks/vatmesh', express.raw({ type: 'application/json' }), (req, res) => {
const signatureHeader = req.headers['x-vatmesh-signature'];
if (!signatureHeader) {
return res.status(401).send('Signature manquante');
}
// Header format: t=1728993600,v1=5d41402abc4b2a76b9719d911017c592...
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t='))?.slice(2);
const receivedSig = parts.find(p => p.startsWith('v1='))?.slice(3);
// Protection anti-rejeu (tolérance 5 minutes)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - parseInt(timestamp, 10)) > 300) {
return res.status(400).send('Horodatage expiré (anti-replay)');
}
// Calcul du HMAC-SHA256 sur timestamp.body
const signedPayload = `${timestamp}.${req.body.toString('utf8')}`;
const computedSig = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(signedPayload)
.digest('hex');
// Comparaison en temps constant contre les attaques temporelles
if (!crypto.timingSafeEqual(Buffer.from(receivedSig, 'hex'), Buffer.from(computedSig, 'hex'))) {
return res.status(403).send('Signature invalide');
}
const event = JSON.parse(req.body.toString('utf8'));
console.log(`[Webhook Reçu] Événement : ${event.event_type} pour Facture #${event.data.invoice_number}`);
if (event.event_type === 'invoice.cleared') {
console.log(`Facture validée par l'autorité fiscale : ${event.data.clearance_id}`);
}
res.status(200).json({ received: true });
});
app.listen(4000, () => console.log('Écouteur de webhooks sécurisé démarré sur port 4000'));