Le contenu de cette page est encore disponible en français seul. La navigation, les formulaires et le pied de page sont déjà traduits.
GUIDE TECHNIQUE & STATUTAIRE • VERSION 6.0

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 :

Extrait XML TEIF v1.8.8 généré par le moteur :
<?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/89757

Toutes 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.

Autofattura TD17

Intégration / Autoliquidation pour achat de services auprès de prestataires étrangers non résidents (Art. 17 comma 2 DPR 633/72).

Bollo Virtuale 2.00 €

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-MPE

La 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.

Autoliquidation BTP (Art. 283-2 nonies du CGI) :

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.1

La 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.

SIGNATURE ASYMÉTRIQUE ECDSA secp256k1
CODE QR OBLIGATOIRE Encodage TLV Base64
CHAÎNAGE SHA-256 APPEND-ONLY Previous Invoice Hash
🇲🇦

5. Maroc : Contrôle ICE 15 Chiffres & Conformité DGI

CGI Maroc Art. 145 & Art. 211 • Télé-déclarations SIMPL-TVA

Au 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).

🇦🇪

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.

🇪🇬

7. Égypte : Facturation Électronique ETA & Retenues WHT

Loi 206/2020 sur les Procédures Fiscales Unifiées • Schéma JSON/XML ETA v1.0

La 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).

🔌

8. Guide d'Intégration ERP & Connecteurs Statutaires

SAP S/4HANA • Dynamics 365 BC • Odoo • Sage 100 • Universal REST • Webhooks HMAC

L'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é.

SAP S/4HANA

8.1 SAP S/4HANA — Connecteur IDoc • Proxy HTTPS / SAP CPI

IDoc INVOIC02 • eDocument Framework • CPI Groovy

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).

Mapping SAP Integration Suite (Groovy Script dans SAP CPI) :
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
}
Appel direct ABAP (Programme d'interface ou BAdI EDOC_ADAPTOR) :
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.
Dynamics 365 BC

8.2 Microsoft Dynamics 365 Business Central — Extension AL • OData / Web Services

Codeunit 80 Event Subscriber • REST Client • Telemetry

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 AL Business Central (Extension VATMesh Bridge) :
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;
}
Odoo 16 / 17 / 18

8.3 Odoo — Co-processeur Python XML-RPC • Automatisation des Scellements

account.move • XML-RPC External Bridge • ir.attachment

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.

Script Co-processeur Python autonome (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()
Sage 100 / Cloud

8.4 Sage 100 • Sage Cloud — Passerelle d'Export PNM & FEC

Format PNM ASCII • FEC Art. L.47 A-I • Automate de Transfert

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.

Exemple de structure PNM Sage 100 (Export Ventes Journal VT) :
#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
Passerelle de validation et conformité Sage PNM (Node.js) :
/**
 * 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 };
REST & Webhooks

8.5 Universal REST API & Webhooks Sécurisés HMAC-SHA256

cURL • Python • Node.js • Signature X-VATMesh-Signature

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.

1. Requête d'arbitrage transfrontalier et calcul de retenue (cURL) :
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"
  }'
2. Soumission d'une facture complète pour scellement souverain (Python Requests) :
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'
3. Réception de Webhook avec vérification de signature HMAC-SHA256 (Node.js Express) :
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'));
Besoin d'un accompagnement d'intégration ERP sur-mesure ?

Nos Solution Architects et ingénieurs fiscaux accompagnent les DAFs, ESN et cabinets dans le déploiement de vos connecteurs SAP, Dynamics 365, Odoo et Sage.