Lire la réponse
Quand vous envoyez une facture valide sur le port COM virtuel, le terminal la certifie, la persiste, et le pont relaie une trame de réponse JSON encadrée en END_JSON. Lisez les octets jusqu’à voir le marqueur END_JSON, retirez-le, puis parsez le JSON qui précède. Cette page décrit uniquement la réponse de succès ; les réponses d’erreur sont couvertes dans Erreurs & santé.
Le pont ne vous transmet que les résultats success / error et gère la liaison avec le terminal pour vous. Lire une certification est donc un échange requête/réponse propre — pas de poignée de main, pas de boucle de heartbeat, pas de démultiplexage.
Forme de la réponse de succès
La charge utile de succès est un objet JSON plat avec status = "success". Sur le fil, elle arrive exactement ainsi — notez le END_JSON final sans saut de ligne :
{"status":"success","invoice_id":"INV-2026-0001","sfec_number":"SFEC-202607-00000001-1234","qr_code":"iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7...<PNG Base64 tronqué>...SuQmCC","certification_date":1782864000000,"message":"Invoice certified successfully","protocol":"TCP"}END_JSONIndentée pour la lisibilité (les champs sont émis dans cet ordre) :
{
"status": "success",
"invoice_id": "INV-2026-0001",
"sfec_number": "SFEC-202607-00000001-1234",
"qr_code": "iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7...<PNG Base64 tronqué>...SuQmCC",
"certification_date": 1782864000000,
"message": "Invoice certified successfully",
"protocol": "TCP"
}Référence champ par champ
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
status | string | Oui | Discriminant. "success" signifie que la facture a été acceptée et persistée. Toute autre valeur (ex. "error") n’est pas un succès. |
invoice_id | string | Oui | Renvoie le invoiceId que vous avez envoyé. Sert à corréler la réponse à votre requête. Lisez toujours l’identifiant depuis la clé invoice_id — jamais invoice_number. |
sfec_number | string | Oui | Le numéro de certification fiscale attribué par le terminal. C’est la valeur à stocker et imprimer sur la facture. Format décrit ci-dessous. |
qr_code | string | Oui | Une image PNG encodée en Base64 du QR code fiscal, prête à afficher/imprimer. Ce n’est PAS le texte du QR. Décodage ci-dessous. |
certification_date | number (int64) | Oui | Temps epoch en millisecondes (UTC) de la certification. |
message | string | Oui | Message fixe lisible. Ne branchez pas dessus ; branchez sur status. |
protocol | string | Oui | Champ interne apposé sur chaque trame, sans signification pour votre intégration — ignorez-le. |
Il n’y a aucun autre champ dans la réponse de succès. En particulier, aucun élément de signature n’est renvoyé sur le fil — la signature est intégrée au QR et conservée par le terminal.
Gestion des valeurs nulles. Le sérialiseur du terminal n’émet pas les clés à valeur nulle — un champ nul est entièrement omis plutôt qu’envoyé comme null. Une trame où status == "success" mais où sfec_number ou qr_code est absent n’est pas utilisable pour l’impression fiscale. Règle défensive : exigez status == "success" et un sfec_number non vide et un qr_code non vide avant de considérer la facture comme certifiée.
sfec_number — le numéro de certification fiscale
sfec_number a le format :
SFEC-<yyyyMM>-<sequence>-<crc>| Segment | Production | Notes |
|---|---|---|
SFEC | préfixe littéral | constant |
<yyyyMM> | année+mois dans le fuseau Africa/Brazzaville | ex. 202607 pour juillet 2026 |
<sequence> | compteur incrémental, complété à 8 chiffres | ex. 00000001 |
<crc> | 4 chiffres de contrôle | ex. 1234 |
Exemple : SFEC-202607-00000001-1234.
Traitez sfec_number comme un identifiant opaque — stockez-le et imprimez-le tel quel. Ne le parsez pas et ne le régénérez pas. Le segment <sequence> est un compteur en mémoire qui se réinitialise à 00000001 à chaque redémarrage du terminal ; il n’est donc ni globalement unique ni monotone dans le temps. Ne fondez jamais de logique métier dessus — appuyez-vous sur le sfec_number complet associé à votre propre invoiceId.
qr_code — un PNG Base64, pas du texte QR
Le qr_code est une image PNG du QR code fiscal, encodée en Base64 sans saut de ligne.
La chaîne reçue est donc base64(png_bytes) sans saut de ligne, alphabet Base64 standard, remplissage (padding) présent, et sans préfixe data: — juste le Base64 brut du PNG. Pour l’utiliser, décodez-le en octets et passez-les à n’importe quel moteur de rendu/imprimante comme un PNG. Ne tentez pas de le lire comme du texte QR — c’est une image, pas le contenu encodé.
Décodage du QR (exemples)
Python (pyserial) :
import base64
import json
import serial # pip install pyserial
DELIM = b"END_JSON"
ser = serial.Serial(
port="COM10", baudrate=115200,
bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE,
timeout=35, # secondes ; doit dépasser le temps de certification du terminal
)
raw = ser.read_until(DELIM) # bloque jusqu'à END_JSON ou timeout
if not raw.endswith(DELIM):
raise TimeoutError("serial read timed out before END_JSON")
resp = json.loads(raw[:-len(DELIM)].decode("utf-8"))
invoice_id = resp["invoice_id"] # renvoie votre invoiceId
sfec_number = resp["sfec_number"] # ex. "SFEC-202607-00000001-1234"
certification_date = resp["certification_date"] # epoch ms, ex. 1782864000000
png_bytes = base64.b64decode(resp["qr_code"]) # sans préfixe "data:", sans saut de ligne
with open(f"{sfec_number}.png", "wb") as f:
f.write(png_bytes)C# (System.IO.Ports + System.Text.Json) :
string json = port.ReadTo("END_JSON"); // renvoie le texte avant le marqueur
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
string invoiceId = root.GetProperty("invoice_id").GetString();
string sfecNumber = root.GetProperty("sfec_number").GetString();
long certificationDate = root.GetProperty("certification_date").GetInt64(); // epoch ms
byte[] pngBytes = Convert.FromBase64String(root.GetProperty("qr_code").GetString());
File.WriteAllBytes(sfecNumber + ".png", pngBytes);Java (jSerialComm + org.json) :
JSONObject resp = new JSONObject(readFrame(port)); // le helper retire END_JSON
String invoiceId = resp.getString("invoice_id");
String sfecNumber = resp.getString("sfec_number");
long certificationDate = resp.getLong("certification_date"); // epoch ms
byte[] pngBytes = Base64.getDecoder().decode(resp.getString("qr_code"));
Files.write(Paths.get(sfecNumber + ".png"), pngBytes);certification_date — epoch en millisecondes
certification_date est un temps Unix epoch en millisecondes (UTC). Divisez par 1000 pour des secondes. Pour l’affichage, les horodatages fiscaux du terminal utilisent le fuseau Africa/Brazzaville (UTC+1) ; présentez-le dans ce fuseau pour rester cohérent avec le terminal.
from datetime import datetime, timezone, timedelta
ms = resp["certification_date"] # ex. 1782864000000
dt = datetime.fromtimestamp(ms / 1000, tz=timezone(timedelta(hours=1))) # Africa/Brazzaville = UTC+1
print(dt.isoformat())Corréler une réponse à sa requête
invoice_id dans la réponse est la même chaîne que le invoiceId envoyé. Utilisez-le pour associer une réponse à sa requête :
requête : {"invoiceId":"INV-2026-0001", ...}END_JSON
réponse : {"status":"success","invoice_id":"INV-2026-0001", ...}END_JSON // même valeurComme le pont ne transmet que les trames success / error, la prochaine trame END_JSON lue après l’envoi d’une facture est son résultat de certification. invoiceId est unique — renvoyer le même invoiceId produit une erreur DUPLICATE_INVOICE, pas un second succès.
Ce que le QR / la signature attestent. La facture est signée de façon sécurisée par le terminal. Vous recevez le QR comme PNG prêt à imprimer et vous stockez/imprimez sfec_number (et certification_date) ; les éléments de signature ne sont pas renvoyés dans la réponse — ils sont intégrés dans le QR et conservés par le terminal.
Étape suivante → Erreurs & santé de connexion