DST - Dossier de spécification technique
DAT - Dossier d'Architecture Technique Arpelin
1. Objectif du document
Ce document décrit l'implémentation technique détaillée du logiciel Arpelin, CMS cloud avec modules de caisse enregistreuse et bornes de commande certifiés NF525. Il constitue le référentiel technique d'implémentation conformément aux exigences du paragraphe 4.3.1 du référentiel NF525.
2. Architecture technique générale
2.1. Vue d'ensemble de l'architecture
┌─────────────────────────────────────────────────────────────┐
│ INTERNET (HTTPS/TLS) │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ NGINX 1.24 (Load Balancer) │
│ Certificats SSL/TLS automatiques │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ PHP-FPM 8.3 (FastCGI) │
│ Gestion sessions + Cache opcache │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ MARIADB 10.11.13 │
│ Bases dédiées par client isolées │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ VPS OVH Ubuntu 24.04.2 LTS │
│ x86_64 GNU/Linux 6.8.0-60 │
└─────────────────────────────────────────────────────────────┘
2.2. Caractéristiques techniques
Plateforme : Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-60-generic x86_64)
Serveur web : Nginx 1.24 avec modules SSL/TLS
Moteur PHP : PHP-FPM 8.3 avec opcache activé
Base de données : MariaDB 10.11.13 (compatible MySQL)
Chiffrement : HTTPS/TLS obligatoire (certificats automatiques)
Versioning : Git local pour gestion du code source
3. Architecture applicative
3.1. Structure des répertoires et modules
/var/www/
├── _main/ ← Code CMS commun (versionné Git)
│ ├── core/ ← Moteur principal
│ │ ├── router.php ← Routeur principal URLs
│ │ ├── session.php ← Gestion sessions sécurisées
│ │ ├── auth.php ← Authentification utilisateurs
│ │ └── database.php ← Couche d'abstraction BDD
│ ├── modules/ ← Modules fonctionnels
│ │ ├── caisse/ ← Module caisse NF525 ⭐
│ │ │ ├── interface.php ← Interface caisse
│ │ │ ├── validation.php ← Validation transactions
│ │ │ ├── tickets.php ← Génération tickets
│ │ │ └── logs.php ← Traçabilité NF525
│ │ ├── borne/ ← Module bornes commande ⭐
│ │ │ ├── interface.php ← Interface client autonome
│ │ │ ├── catalogue.php ← Affichage produits
│ │ │ └── commande.php ← Processus commande
│ │ ├── cloturesv ← Module clôtures ⭐
│ │ │ ├── automatique.php ← Clôtures programmées
│ │ │ ├── manuelle.php ← Clôtures à la demande
│ │ │ └── rapports.php ← Génération rapports
│ │ └── backoffice/ ← Administration
│ │ ├── articles.php ← Gestion catalogue
│ │ ├── users.php ← Gestion utilisateurs
│ │ └── stats.php ← Statistiques/rapports
│ ├── themes/ ← Templates d'affichage
│ │ ├── caisse/ ← Templates caisse
│ │ ├── borne/ ← Templates borne
│ │ └── admin/ ← Templates administration
│ └── api/ ← Interfaces API
│ ├── payments/ ← Intégrations paiement
│ │ ├── sumup.php ← API SumUp
│ │ ├── smilenpay.php ← API Smile&Pay
│ │ └── satispay.php ← API Satispay
│ └── webhooks/ ← Retours paiement
├── _files/ ← Scripts privés (versionné Git)
│ ├── config/ ← Configuration système
│ │ ├── database.conf ← Paramètres BDD
│ │ ├── security.conf ← Paramètres sécurité
│ │ └── nf525.conf ← Configuration NF525
│ ├── security/ ← Fonctions sécurité
│ │ ├── encryption.php ← Chiffrement/déchiffrement
│ │ ├── hash.php ← Fonctions hashage
│ │ └── signature.php ← Signatures électroniques
│ ├── database/ ← Accès base de données
│ │ ├── connect.php ← Connexions BDD
│ │ ├── migrations.php ← Scripts migration
│ │ └── backup.php ← Sauvegardes automatiques
│ └── nf525/ ← Conformité NF525 ⭐
│ ├── jet.php ← Journal évènements techniques
│ ├── archivage.php ← Archivage données
│ ├── integrite.php ← Contrôle intégrité
│ └── export.php ← Export conformité
├── cms_client1/ ← Instance client 1
│ ├── index.php ← Point d'entrée
│ ├── .htaccess ← Règles réécriture
│ └── assets/ ← Ressources client
└── .git/ ← Dépôt Git versioning
3.2. Architecture modulaire
3.2.1. Moteur principal (Core)
Router : Gestion des URLs et routage vers modules appropriés
Session Manager : Gestion sessions sécurisées 8h avec régénération token
Auth Manager : Authentification multi-rôles (Caissier/Manager/Admin)
Database Layer : Couche d'abstraction pour accès aux bases de données
3.2.2. Modules NF525 ⭐
Module Caisse : Interface encaissement, validation, génération tickets
Module Bornes : Interface client autonome, intégration caisse
Module Clôtures : Automatiques (cron) et manuelles (droits)
Module Archivage : Conservation données figées, exports conformité
3.2.3. API et intégrations
Payment APIs : SumUp, Smile&Pay, Satispay
Webhooks : Retours validation paiement temps réel
Export APIs : Formats conformes audit (CSV/PDF)
4. Base de données - Architecture technique
4.1. Modèle de données NF525 ⭐
4.1.1. Tables critiques conformité
Table transactions
(données figées) ⭐
CREATE TABLE transactions (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
panier_id INT(11) NOT NULL, -- Numéro ticket
user_id VARCHAR(255) NOT NULL, -- Caissier responsable
transaction_date DATETIME NOT NULL, -- Horodatage précis
total_ht DECIMAL(10,2) NOT NULL, -- Total HT
total_tva DECIMAL(10,2) NOT NULL, -- Total TVA
total_ttc DECIMAL(10,2) NOT NULL, -- Total TTC
payment_method VARCHAR(50) NOT NULL, -- Moyen paiement
ticket_content TEXT NOT NULL, -- Contenu ticket figé
signature VARCHAR(255) NOT NULL, -- Signature électronique
hash_control VARCHAR(64) NOT NULL, -- Hash contrôle
is_immutable TINYINT(1) DEFAULT 1, -- Indicateur figé
INDEX idx_date (transaction_date),
INDEX idx_user (user_id),
INDEX idx_panier (panier_id)
) ENGINE=InnoDB;
Table logs
(audit JET) ⭐
CREATE TABLE logs (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
user_id VARCHAR(255) NOT NULL, -- Utilisateur action
action TEXT NOT NULL, -- Description action
function VARCHAR(50) NOT NULL, -- Code fonction
log_date DATETIME NOT NULL, -- Horodatage précis
signature TEXT NOT NULL, -- Signature données
current_hash VARCHAR(64) NOT NULL, -- Hash action actuelle
previous_hash VARCHAR(64) NOT NULL, -- Hash précédent (chaîne)
data_before TEXT, -- Données avant
data_after TEXT, -- Données après
ip_address VARCHAR(45), -- Adresse IP
INDEX idx_date (log_date),
INDEX idx_user (user_id),
INDEX idx_function (function),
INDEX idx_hash_chain (current_hash, previous_hash)
) ENGINE=InnoDB;
Table closures
(clôtures) ⭐
CREATE TABLE closures (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
closure_number VARCHAR(50) NOT NULL UNIQUE, -- Numéro clôture
closure_type ENUM('AUTO', 'MANUAL') NOT NULL, -- Type clôture
period_start DATETIME NOT NULL, -- Début période
period_end DATETIME NOT NULL, -- Fin période
closure_date DATETIME NOT NULL, -- Date clôture
user_id VARCHAR(255), -- Utilisateur (si manuelle)
total_ht DECIMAL(12,2) NOT NULL, -- Total HT période
total_tva DECIMAL(12,2) NOT NULL, -- Total TVA période
total_ttc DECIMAL(12,2) NOT NULL, -- Total TTC période
payment_methods_detail TEXT NOT NULL, -- Détail moyens paiement
tva_detail TEXT NOT NULL, -- Détail TVA par taux
transactions_count INT(11) NOT NULL, -- Nombre transactions
report_content LONGTEXT NOT NULL, -- Rapport complet
hash_previous VARCHAR(64), -- Hash clôture précédente
hash_current VARCHAR(64) NOT NULL, -- Hash clôture actuelle
signature VARCHAR(255) NOT NULL, -- Signature cryptographique
is_locked TINYINT(1) DEFAULT 1, -- Verrou modification
INDEX idx_date (closure_date),
INDEX idx_period (period_start, period_end),
INDEX idx_hash_chain (hash_current, hash_previous)
) ENGINE=InnoDB;
4.1.2. Tables fonctionnelles
Table articles
CREATE TABLE articles (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL, -- Nom article
price_ttc DECIMAL(8,2) NOT NULL, -- Prix TTC
tva_rate DECIMAL(4,2) NOT NULL, -- Taux TVA
barcode VARCHAR(255), -- Code-barres
category_id INT(11), -- Catégorie
stock_quantity INT(11) DEFAULT NULL, -- Stock (si géré)
stock_enabled TINYINT(1) DEFAULT 0, -- Gestion stock activée
is_active TINYINT(1) DEFAULT 1, -- Article actif
created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_barcode (barcode),
INDEX idx_category (category_id),
INDEX idx_active (is_active)
) ENGINE=InnoDB;
Table users
CREATE TABLE users (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE, -- Nom utilisateur
email VARCHAR(255) NOT NULL UNIQUE, -- Email
password_hash VARCHAR(255) NOT NULL, -- Mot de passe hashé
role ENUM('CAISSIER', 'MANAGER', 'ADMIN') NOT NULL,
first_name VARCHAR(255), -- Prénom
last_name VARCHAR(255), -- Nom
is_active TINYINT(1) DEFAULT 1, -- Compte actif
last_login DATETIME, -- Dernière connexion
created_date DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_role (role)
) ENGINE=InnoDB;
4.2. Isolation des données par client
4.2.1. Stratégie multi-tenant
Base dédiée : Chaque client a sa propre base MySQL
Convention nommage :
cms_domaine-client_extension
Isolation complète : Aucune donnée partagée entre clients
Résolution automatique : Via nom de domaine d'accès
4.2.2. Gestion des connexions
// Connexion dynamique selon domaine
function getClientDatabase($domain) {
$sanitized = preg_replace('/[^a-zA-Z0-9_-]/', '', $domain);
$dbName = 'cms_' . str_replace(['.', '-'], '_', $sanitized);
return new PDO("mysql:host=localhost;dbname=$dbName",
$username, $password, $options);
}
5. Implémentation de la sécurité
5.1. Authentification et sessions
5.1.1. Gestion des sessions sécurisées
// Configuration session sécurisée
ini_set('session.cookie_secure', 1); // HTTPS uniquement
ini_set('session.cookie_httponly', 1); // Pas d'accès JavaScript
ini_set('session.use_strict_mode', 1); // Mode strict
ini_set('session.gc_maxlifetime', 28800); // 8 heures
session_regenerate_id(true); // Régénération token
5.1.2. Hashage mots de passe
// Hashage sécurisé (PHP 8.3)
$hashedPassword = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 3
]);
5.2. Chiffrement et signatures ⭐
5.2.1. Hashage SHA-256 pour intégrité
function generateHash($data) {
return hash('sha256', json_encode($data) . SALT_NF525);
}
function generateTransactionSignature($transaction) {
$data = [
'id' => $transaction['id'],
'date' => $transaction['transaction_date'],
'total' => $transaction['total_ttc'],
'user' => $transaction['user_id']
];
return generateHash($data);
}
5.2.2. Signature électronique tickets ⭐
function generateTicketSignature($ticketData) {
// Signature complète (255 caractères)
$fullSignature = hash('sha256', serialize($ticketData) . SECRET_KEY);
// Extrait pour ticket (positions 3, 7, 13, 19)
$extractSignature = substr($fullSignature, 3, 1) .
substr($fullSignature, 7, 1) .
substr($fullSignature, 13, 1) .
substr($fullSignature, 19, 1);
return [
'full' => $fullSignature,
'extract' => strtoupper($extractSignature)
];
}
5.3. Protection HTTPS/TLS
5.3.1. Configuration Nginx
server {
listen 443 ssl http2;
server_name *.arpelin.com;
# Certificats SSL automatiques
ssl_certificate /etc/ssl/certs/arpelin.crt;
ssl_certificate_key /etc/ssl/private/arpelin.key;
# Configuration TLS moderne
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
# Headers sécurité
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# Redirection HTTP vers HTTPS
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
6. Implémentation modules NF525 ⭐
6.1. Module caisse enregistreuse
6.1.1. Processus de validation transaction
function validateTransaction($cartData, $paymentData, $userId) {
// 1. Validation des données
if (!validateCartIntegrity($cartData)) {
throw new Exception("Erreur intégrité panier");
}
// 2. Calcul totaux automatiques
$totals = calculateTotals($cartData);
// 3. Génération numéro ticket unique
$ticketNumber = generateTicketNumber();
// 4. Création transaction immutable
$transaction = [
'panier_id' => $ticketNumber,
'user_id' => $userId,
'transaction_date' => date('Y-m-d H:i:s'),
'total_ht' => $totals['ht'],
'total_tva' => $totals['tva'],
'total_ttc' => $totals['ttc'],
'payment_method' => $paymentData['method'],
'ticket_content' => generateTicketContent($cartData, $totals),
'signature' => generateTransactionSignature($transaction),
'hash_control' => generateHash($transaction)
];
// 5. Insertion BDD (données figées)
insertTransaction($transaction);
// 6. Log audit JET
logAction('TRANSACTION_VALIDATE', $userId, $transaction);
// 7. Génération ticket client
return generatePrintableTicket($transaction);
}
6.1.2. Génération tickets conformes
function generateTicketContent($cartData, $totals) {
$ticket = [];
// En-tête obligatoire
$ticket[] = getCompanyHeader();
$ticket[] = str_repeat('-', 40);
// Détail articles
foreach ($cartData['items'] as $item) {
$ticket[] = sprintf("%-20s %2dx%6.2f %8.2f",
substr($item['name'], 0, 20),
$item['quantity'],
$item['price'],
$item['total']
);
}
$ticket[] = str_repeat('-', 40);
// Totaux TVA par taux
foreach ($totals['tva_detail'] as $rate => $amount) {
$ticket[] = sprintf("TVA %4.1f%% %26.2f", $rate, $amount);
}
$ticket[] = str_repeat('-', 40);
$ticket[] = sprintf("TOTAL TTC %28.2f", $totals['ttc']);
$ticket[] = sprintf("Payé par %s %20.2f",
$paymentData['method'], $paymentData['amount']);
// Footer obligatoire NF525
$ticket[] = str_repeat('-', 40);
$ticket[] = sprintf("Ticket N° %s", $ticketNumber);
$ticket[] = date('d/m/Y H:i:s');
$ticket[] = sprintf("Signature: %s", $extractSignature);
return implode("\n", $ticket);
}
6.2. Journal d'évènements techniques (JET) ⭐
6.2.1. Enregistrement automatique
function logAction($actionCode, $userId, $data = null, $before = null) {
// Récupération hash précédent
$previousHash = getLastLogHash();
// Préparation log
$logData = [
'user_id' => $userId,
'action' => getActionDescription($actionCode),
'function' => $actionCode,
'log_date' => date('Y-m-d H:i:s'),
'data_before' => $before ? json_encode($before) : null,
'data_after' => $data ? json_encode($data) : null,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
'previous_hash' => $previousHash
];
// Génération signature et hash
$logData['signature'] = generateLogSignature($logData);
$logData['current_hash'] = generateHash($logData);
// Insertion base (ajout uniquement)
insertLog($logData);
}
6.2.2. Codes d'action standardisés
const JET_ACTIONS = [
'ORDER_PICKUP' => 'Commande retirée',
'ORDER_DELETE' => 'Commande supprimée',
'UNPAID_ORDER_DELETE' => 'Commande non payée supprimée',
'CANCEL_ENTRY' => 'Annulation d\'entrée',
'PRICE_MODIFY' => 'Modification de prix',
'DISCOUNT_ITEM' => 'Remise sur article',
'DISCOUNT_GLOBAL' => 'Remise globale',
'CART_VIEW' => 'Consultation panier',
'CART_UPDATE' => 'Mise à jour panier',
'CART_GLOBAL' => 'Action globale panier',
'TRANSACTION_VALIDATE' => 'Validation transaction',
'CLOSURE_AUTO' => 'Clôture automatique',
'CLOSURE_MANUAL' => 'Clôture manuelle'
];
6.3. Système de clôtures ⭐
6.3.1. Clôtures automatiques (cron)
// Script cron quotidien (00:00)
function executeAutomaticClosure($type = 'DAILY') {
$periods = [
'DAILY' => [
'start' => date('Y-m-d 00:00:00', strtotime('-1 day')),
'end' => date('Y-m-d 23:59:59', strtotime('-1 day'))
],
'MONTHLY' => [
'start' => date('Y-m-01 00:00:00', strtotime('-1 month')),
'end' => date('Y-m-t 23:59:59', strtotime('-1 month'))
],
'YEARLY' => [
'start' => date('Y-01-01 00:00:00', strtotime('-1 year')),
'end' => date('Y-12-31 23:59:59', strtotime('-1 year'))
]
];
$period = $periods[$type];
// Récupération transactions période
$transactions = getTransactionsByPeriod($period['start'], $period['end']);
// Calculs automatiques
$totals = calculatePeriodTotals($transactions);
$paymentMethods = calculatePaymentMethods($transactions);
$tvaDetails = calculateTvaDetails($transactions);
// Génération rapport
$report = generateClosureReport($type, $period, $totals,
$paymentMethods, $tvaDetails);
// Hash et signature
$previousHash = getLastClosureHash();
$currentHash = generateHash($report);
$signature = generateClosureSignature($report);
// Sauvegarde clôture
saveClosureData([
'closure_number' => generateClosureNumber(),
'closure_type' => 'AUTO',
'period_start' => $period['start'],
'period_end' => $period['end'],
'closure_date' => date('Y-m-d H:i:s'),
'total_ht' => $totals['ht'],
'total_tva' => $totals['tva'],
'total_ttc' => $totals['ttc'],
'payment_methods_detail' => json_encode($paymentMethods),
'tva_detail' => json_encode($tvaDetails),
'transactions_count' => count($transactions),
'report_content' => $report,
'hash_previous' => $previousHash,
'hash_current' => $currentHash,
'signature' => $signature
]);
// Log action
logAction('CLOSURE_AUTO', 'SYSTEM', $closureData);
}
7. Intégrations APIs de paiement
7.1. Architecture modulaire paiements
7.1.1. Interface commune
interface PaymentProcessorInterface {
public function initializePayment($amount, $currency, $orderId);
public function processPayment($paymentData);
public function handleWebhook($webhookData);
public function validatePayment($transactionId);
}
7.1.2. Implémentation SumUp
class SumUpProcessor implements PaymentProcessorInterface {
private $apiKey;
private $merchantCode;
public function initializePayment($amount, $currency, $orderId) {
$endpoint = 'https://api.sumup.com/v0.1/checkouts';
$data = [
'checkout_reference' => $orderId,
'amount' => $amount,
'currency' => $currency,
'merchant_code' => $this->merchantCode,
'description' => 'Commande Arpelin #' . $orderId
];
return $this->makeApiCall($endpoint, $data);
}
public function handleWebhook($webhookData) {
// Validation signature webhook
if (!$this->validateWebhookSignature($webhookData)) {
throw new Exception('Webhook signature invalide');
}
// Traitement retour paiement
$orderId = $webhookData['checkout_reference'];
$status = $webhookData['status'];
if ($status === 'PAID') {
return $this->confirmPayment($orderId, $webhookData);
}
return false;
}
}
7.2. Gestion des webhooks
7.2.1. Endpoint unifié webhooks
// /api/webhooks/payment.php
function handlePaymentWebhook($provider, $data) {
// Log réception webhook
logAction('WEBHOOK_RECEIVED', 'SYSTEM', [
'provider' => $provider,
'data' => $data
]);
try {
// Routage selon provider
$processor = PaymentProcessorFactory::create($provider);
$result = $processor->handleWebhook($data);
if ($result) {
// Mise à jour commande
updateOrderStatus($result['order_id'], 'PAID');
// Log succès
logAction('PAYMENT_CONFIRMED', 'SYSTEM', $result);
return ['status' => 'success'];
}
} catch (Exception $e) {
// Log erreur
logAction('WEBHOOK_ERROR', 'SYSTEM', [
'error' => $e->getMessage(),
'provider' => $provider
]);
return ['status' => 'error', 'message' => $e->getMessage()];
}
}
8. Système de sauvegarde et archivage
8.1. Sauvegardes automatiques
8.1.1. Script de sauvegarde quotidienne
#!/bin/bash
# /etc/cron.daily/arpelin-backup
BACKUP_DIR="/var/backups/arpelin"
DATE=$(date +%Y%m%d)
RETENTION_DAYS=7
# Sauvegarde de toutes les bases clients
for db in $(mysql -e "SHOW DATABASES LIKE 'cms_%';" | grep cms_); do
echo "Sauvegarde base $db..."
mysqldump --single-transaction --routines --triggers $db > \
"$BACKUP_DIR/${db}_${DATE}.sql"
gzip "$BACKUP_DIR/${db}_${DATE}.sql"
done
# Sauvegarde fichiers clients
tar -czf "$BACKUP_DIR/files_${DATE}.tar.gz" /var/www/cms_*/assets/
# Nettoyage anciennes sauvegardes
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Sauvegarde terminée: $(date)"
8.1.2. Vérification intégrité sauvegardes
function verifyBackupIntegrity($backupFile) {
// Vérification taille minimale
if (filesize($backupFile) < 1024) {
return false;
}
// Test décompression
if (substr($backupFile, -3) === '.gz') {
$testCommand = "gunzip -t " . escapeshellarg($backupFile);
exec($testCommand, $output, $returnCode);
return $returnCode === 0;
}
return true;
}
8.2. Archivage données NF525 ⭐
8.2.1. Processus d'archivage automatique
function archiveNF525Data($closureId) {
$closure = getClosureById($closureId);
// Génération archive période
$archiveData = [
'closure_id' => $closureId,
'period_start' => $closure['period_start'],
'period_end' => $closure['period_end'],
'transactions' => getTransactionsByPeriod(
$closure['period_start'],
$closure['period_end']
),
'logs' => getLogsByPeriod(
$closure['period_start'],
$closure['period_end']
),
'closure_data' => $closure
];
// Génération fichier archive
$archiveFile = generateArchiveFile($archiveData);
// Calcul empreinte
$archiveHash = hash_file('sha256', $archiveFile);
// Enregistrement référence archive
saveArchiveReference([
'closure_id' => $closureId,
'archive_file' => basename($archiveFile),
'archive_path' => $archiveFile,
'archive_hash' => $archiveHash,
'archive_date' => date('Y-m-d H:i:s'),
'file_size' => filesize($archiveFile)
]);
return $archiveFile;
}
9. Monitoring et surveillance
9.1. Surveillance système
9.1.1. Monitoring performances
function checkSystemHealth() {
$health = [
'timestamp' => date('Y-m-d H:i:s'),
'disk_usage' => disk_free_space('/') / disk_total_space('/') * 100,
'memory_usage' => memory_get_usage(true) / (1024 * 1024),
'load_average' => sys_getloadavg()[0],
'database_status' => checkDatabaseConnections(),
'ssl_certificate' => checkSSLCertificate(),
'backup_status' => checkLastBackup()
];
// Alertes si seuils dépassés
if ($health['disk_usage'] > 90) {
sendAlert('Espace disque critique: ' . $health['disk_usage'] . '%');
}
return $health;
}
9.1.2. Logs d'accès et erreurs
# Configuration Nginx logging
access_log /var/log/nginx/arpelin_access.log combined;
error_log /var/log/nginx/arpelin_error.log warn;
# Format personnalisé pour audit
log_format audit_format '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
9.2. Contrôle intégrité NF525 ⭐
9.2.1. Vérification chaîne de logs
function verifyLogChainIntegrity($startDate = null, $endDate = null) {
$logs = getLogs($startDate, $endDate, 'ORDER BY id ASC');
$errors = [];
for ($i = 1; $i < count($logs); $i++) {
$current = $logs[$i];
$previous = $logs[$i - 1];
// Vérification chaînage
if ($current['previous_hash'] !== $previous['current_hash']) {
$errors[] = [
'type' => 'CHAIN_BREAK',
'log_id' => $current['id'],
'message' => 'Rupture chaîne entre logs ' .
$previous['id'] . ' et ' . $current['id']
];
}
// Vérification hash actuel
$calculatedHash = generateHash($current);
if ($calculatedHash !== $current['current_hash']) {
$errors[] = [
'type' => 'HASH_INVALID',
'log_id' => $current['id'],
'message' => 'Hash invalide pour log ' . $current['id']
];
}
}
return [
'valid' => empty($errors),
'errors' => $errors,
'total_logs' => count($logs),
'verification_date' => date('Y-m-d H:i:s')
];
}
9.2.2. Export conformité audit
function exportAuditData($startDate, $endDate, $format = 'CSV') {
// Récupération données période
$transactions = getTransactionsByPeriod($startDate, $endDate);
$logs = getLogsByPeriod($startDate, $endDate);
$closures = getClosuresByPeriod($startDate, $endDate);
// Vérification intégrité avant export
$integrityCheck = verifyLogChainIntegrity($startDate, $endDate);
// Génération export
$exportData = [
'metadata' => [
'export_date' => date('Y-m-d H:i:s'),
'period_start' => $startDate,
'period_end' => $endDate,
'format' => $format,
'integrity_verified' => $integrityCheck['valid']
],
'transactions' => $transactions,
'logs' => $logs,
'closures' => $closures,
'integrity_report' => $integrityCheck
];
// Calcul hash export
$exportHash = generateHash($exportData);
$exportData['export_hash'] = $exportHash;
// Génération fichier selon format
switch ($format) {
case 'CSV':
return generateCSVExport($exportData);
case 'JSON':
return json_encode($exportData, JSON_PRETTY_PRINT);
case 'PDF':
return generatePDFReport($exportData);
default:
throw new Exception('Format non supporté');
}
}
10. Déploiement et gestion des versions
10.1. Système de versioning Git
10.1.1. Structure branches
main
├── production ← Version stable déployée
├── staging ← Tests pré-production
├── development ← Développement actif
└── hotfix/* ← Corrections urgentes
10.1.2. Script de déploiement
#!/bin/bash
# deploy.sh
VERSION=$1
BACKUP_DIR="/var/backups/deployments"
if [ -z "$VERSION" ]; then
echo "Usage: ./deploy.sh <version>"
exit 1
fi
echo "Déploiement version $VERSION..."
# Sauvegarde version actuelle
cd /var/www
tar -czf "$BACKUP_DIR/pre-deploy-$(date +%Y%m%d-%H%M%S).tar.gz" _main/ _files/
# Mise à jour code
git fetch origin
git checkout $VERSION
# Vérification intégrité
if ! git verify-tag $VERSION; then
echo "ERREUR: Tag non signé ou invalide"
exit 1
fi
# Tests automatiques
php /var/www/_files/tests/integrity_check.php
if [ $? -ne 0 ]; then
echo "ERREUR: Tests d'intégrité échoués"
exit 1
fi
# Rechargement configuration
systemctl reload nginx
systemctl reload php8.3-fpm
echo "Déploiement $VERSION terminé avec succès"
10.2. Tests automatiques
10.2.1. Tests d'intégrité NF525
// /var/www/_files/tests/integrity_check.php
class NF525IntegrityTests {
public function testDatabaseSchema() {
$requiredTables = [
'transactions', 'logs', 'closures',
'articles', 'users', 'archives'
];
foreach ($requiredTables as $table) {
if (!$this->tableExists($table)) {
throw new Exception("Table manquante: $table");
}
}
return true;
}
public function testSignatureFunctions() {
$testData = ['test' => 'data', 'timestamp' => time()];
// Test génération signature
$signature = generateHash($testData);
if (strlen($signature) !== 64) {
throw new Exception("Signature invalide");
}
// Test reproductibilité
$signature2 = generateHash($testData);
if ($signature !== $signature2) {
throw new Exception("Signatures non reproductibles");
}
return true;
}
public function testLogChaining() {
// Test sur les 100 derniers logs
$logs = getLastLogs(100);
$integrity = verifyLogChainIntegrity();
if (!$integrity['valid']) {
throw new Exception("Chaîne de logs compromise");
}
return true;
}
public function runAllTests() {
$tests = [
'Database Schema' => 'testDatabaseSchema',
'Signature Functions' => 'testSignatureFunctions',
'Log Chaining' => 'testLogChaining'
];
$results = [];
foreach ($tests as $name => $method) {
try {
$this->$method();
$results[$name] = 'PASS';
} catch (Exception $e) {
$results[$name] = 'FAIL: ' . $e->getMessage();
}
}
return $results;
}
}
11. Performance et optimisation
11.1. Optimisation base de données
11.1.1. Configuration MariaDB
# /etc/mysql/mariadb.conf.d/99-arpelin.cnf
[mysqld]
# Optimisations mémoire
innodb_buffer_pool_size = 1G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2
# Optimisations requêtes
query_cache_type = 1
query_cache_size = 128M
tmp_table_size = 64M
max_heap_table_size = 64M
# Index et performances
innodb_file_per_table = 1
innodb_flush_method = O_DIRECT
11.1.2. Index critiques NF525
-- Index pour performances requêtes audit
CREATE INDEX idx_logs_date_user ON logs(log_date, user_id);
CREATE INDEX idx_transactions_date ON transactions(transaction_date);
CREATE INDEX idx_closures_period ON closures(period_start, period_end);
-- Index pour chaînage
CREATE INDEX idx_logs_hash_chain ON logs(current_hash, previous_hash);
CREATE INDEX idx_closures_hash_chain ON closures(hash_current, hash_previous);
-- Index fonctionnels
CREATE INDEX idx_articles_active_barcode ON articles(is_active, barcode);
CREATE INDEX idx_users_active_role ON users(is_active, role);
11.2. Cache et optimisation PHP
11.2.1. Configuration OPcache
# /etc/php/8.3/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.save_comments=1
11.2.2. Cache applicatif
class CacheManager {
private static $cache = [];
public static function get($key) {
return self::$cache[$key] ?? null;
}
public static function set($key, $value, $ttl = 3600) {
self::$cache[$key] = [
'value' => $value,
'expires' => time() + $ttl
];
}
public static function getCachedArticles() {
$cacheKey = 'articles_active';
$cached = self::get($cacheKey);
if ($cached && $cached['expires'] > time()) {
return $cached['value'];
}
$articles = getActiveArticles();
self::set($cacheKey, $articles, 1800); // 30 minutes
return $articles;
}
}
12. Documentation technique et maintenance
12.1. Procédures de maintenance
12.1.1. Maintenance préventive mensuelle
// Script maintenance mensuelle
function monthlyMaintenance() {
$maintenanceLog = [];
// 1. Optimisation bases de données
$databases = getAllClientDatabases();
foreach ($databases as $db) {
optimizeDatabase($db);
$maintenanceLog[] = "Base $db optimisée";
}
// 2. Nettoyage logs anciens (si configuré)
$retentionDays = getLogRetentionDays();
if ($retentionDays > 0) {
cleanOldLogs($retentionDays);
$maintenanceLog[] = "Logs > $retentionDays jours nettoyés";
}
// 3. Vérification intégrité globale
$integrityCheck = verifyGlobalIntegrity();
$maintenanceLog[] = "Intégrité: " .
($integrityCheck['valid'] ? 'OK' : 'ERREURS DÉTECTÉES');
// 4. Rapport maintenance
$report = generateMaintenanceReport($maintenanceLog);
sendMaintenanceReport($report);
return $maintenanceLog;
}
12.1.2. Surveillance continue
// Script surveillance (cron toutes les 5 minutes)
function continuousMonitoring() {
$alerts = [];
// Vérification espace disque
$diskUsage = getDiskUsage();
if ($diskUsage > 85) {
$alerts[] = "Espace disque critique: {$diskUsage}%";
}
// Vérification connexions BDD
$dbStatus = checkDatabaseConnections();
if (!$dbStatus['all_connected']) {
$alerts[] = "Connexions BDD en erreur: " .
implode(', ', $dbStatus['errors']);
}
// Vérification certificats SSL
$sslCheck = checkSSLCertificates();
if ($sslCheck['expires_soon']) {
$alerts[] = "Certificat SSL expire dans " .
$sslCheck['days_remaining'] . " jours";
}
// Envoi alertes si nécessaire
if (!empty($alerts)) {
sendAlerts($alerts);
}
}
12.2. Documentation API interne
12.2.1. API fonctions NF525
/**
* API NF525 - Fonctions principales conformité
*/
class NF525API {
/**
* Valide une transaction et génère ticket
* @param array $cartData Données panier
* @param array $paymentData Données paiement
* @param string $userId Identifiant caissier
* @return array Données transaction et ticket
*/
public function validateTransaction($cartData, $paymentData, $userId) {
// Implémentation voir section 6.1.1
}
/**
* Génère clôture manuelle
* @param string $startDate Date début période
* @param string $endDate Date fin période
* @param string $userId Utilisateur demandeur
* @return array Données clôture générée
*/
public function generateManualClosure($startDate, $endDate, $userId) {
// Implémentation voir section 6.3.1
}
/**
* Exporte données pour audit
* @param string $startDate Date début
* @param string $endDate Date fin
* @param string $format Format export (CSV/JSON/PDF)
* @return string Chemin fichier généré
*/
public function exportAuditData($startDate, $endDate, $format = 'CSV') {
// Implémentation voir section 9.2.2
}
/**
* Vérifie intégrité chaîne de logs
* @param string|null $startDate Date début (optionnelle)
* @param string|null $endDate Date fin (optionnelle)
* @return array Résultat vérification
*/
public function verifyLogIntegrity($startDate = null, $endDate = null) {
// Implémentation voir section 9.2.1
}
}
13. Conformité et audit
13.1. Points de contrôle NF525 ⭐
13.1.1. Checklist conformité technique
✅ Traçabilité : Table
logs
avec chaînage cryptographique✅ Inaltérabilité : Données figées en table
transactions
✅ Horodatage : Précision à la seconde pour toutes opérations
✅ Signature électronique : SHA-256 + extrait 4 caractères ticket
✅ Clôtures automatiques : Cron quotidien/mensuel/annuel
✅ Clôtures manuelles : Interface dédiée avec droits
✅ Archivage : Conservation données période selon réglementation
✅ Numérotation : Tickets séquentiels uniques par client
✅ TVA : Calcul automatique par taux article
✅ Moyens paiement : Traçabilité complète tous moyens
13.1.2. Procédure audit technique
function performTechnicalAudit($auditPeriod) {
$auditResults = [
'audit_date' => date('Y-m-d H:i:s'),
'period' => $auditPeriod,
'checks' => []
];
// 1. Vérification intégrité données
$integrityCheck = verifyLogChainIntegrity(
$auditPeriod['start'],
$auditPeriod['end']
);
$auditResults['checks']['data_integrity'] = $integrityCheck;
// 2. Cohérence transactions/clôtures
$coherenceCheck = verifyTransactionClosureCoherence($auditPeriod);
$auditResults['checks']['coherence'] = $coherenceCheck;
// 3. Complétude numérotation
$numberingCheck = verifyTicketNumbering($auditPeriod);
$auditResults['checks']['numbering'] = $numberingCheck;
// 4. Validation signatures
$signatureCheck = verifyAllSignatures($auditPeriod);
$auditResults['checks']['signatures'] = $signatureCheck;
// 5. Conformité tickets
$ticketCheck = verifyTicketCompliance($auditPeriod);
$auditResults['checks']['tickets'] = $ticketCheck;
// Génération rapport audit
return generateAuditReport($auditResults);
}
13.2. Mise à disposition administration
13.2.1. Interface audit dédiée
// Interface pour contrôles administratifs
class AdminAuditInterface {
public function getAuditAccess($adminCredentials) {
// Validation credentials administrateur
if (!$this->validateAdminAccess($adminCredentials)) {
throw new Exception("Accès non autorisé");
}
return [
'data_access' => $this->getDataAccessMethods(),
'export_tools' => $this->getExportTools(),
'verification_tools' => $this->getVerificationTools()
];
}
public function getDataAccessMethods() {
return [
'transactions' => '/admin/audit/transactions',
'logs' => '/admin/audit/logs',
'closures' => '/admin/audit/closures',
'archives' => '/admin/audit/archives'
];
}
public function generateComplianceReport($clientId, $period) {
// Génération rapport conformité complet
$client = getClientInfo($clientId);
$transactions = getTransactionsByPeriod(
$period['start'], $period['end']
);
$logs = getLogsByPeriod(
$period['start'], $period['end']
);
return [
'client_info' => $client,
'period' => $period,
'transactions_count' => count($transactions),
'total_amount' => array_sum(array_column($transactions, 'total_ttc')),
'integrity_status' => verifyLogChainIntegrity(),
'export_file' => exportAuditData(
$period['start'], $period['end'], 'PDF'
)
];
}
}
14. Conclusion
14.1. Synthèse technique
Le Dossier d'Architecture Technique (DAT) d'Arpelin présente une implémentation robuste et conforme aux exigences NF525, structurée autour de :
Architecture sécurisée : HTTPS/TLS, isolation données par client
Modules NF525 certifiés : Caisse, bornes, clôtures, archivage
Traçabilité complète : Chaînage cryptographique, JET, immutabilité
Performance optimisée : Cache, index, configuration adaptée
Maintenance facilitée : Scripts automatisés, monitoring continu
14.2. Points techniques clés ⭐
Sécurité cryptographique : SHA-256 pour toutes signatures et hash
Isolation données : Base MySQL dédiée par client
Traçabilité inviolable : Chaînage blockchain dans table logs
Archivage conformé : Conservation automatique avec empreintes
API modulaires : Intégrations paiement extensibles
14.3. Évolutivité
L'architecture technique permet :
Montée en charge horizontale (ajout VPS)
Intégration nouvelles APIs paiement
Extension modules fonctionnels
Maintien conformité lors évolutions
Document établi dans le cadre de la certification NF525 Version : 1.0 Date : Janvier 2024 Responsable : Emre KOSE
Last updated