Un événement compté, une ligne écrite : c’est le principe de base retenu par une jeune équipe produit qui construit un SaaS sur WordPress et doit facturer ses clients à l’usage (appels d’API, exports de fichiers, envois de messages), sans disposer du temps ni du budget pour développer un ERP maison capable de gérer devis, factures et relances.
La bonne échelle de complexité ici n’est pas un système de gestion complet, mais une architecture en trois couches bien séparées : la capture des événements d’usage, leur agrégation périodique, et l’export vers un outil de facturation qui reste, lui, externe et spécialisé.
Vue d’ensemble de l’architecture

L’arborescence logique du dispositif tient en quelques composants, sans dépendance croisée excessive :
extension-usage-facturation/
├── includes/
│ ├── class-event-logger.php (écrit un événement d’usage)
│ ├── class-usage-table.php (crée et interroge la table dédiée)
│ ├── class-aggregateur.php (cron quotidien, calcule les totaux)
│ └── class-export-facturation.php (génère le fichier consommé en aval)
├── uninstall.php
└── extension-usage-facturation.php
Aucun de ces fichiers ne cherche à reproduire les fonctions d’un ERP : la génération de facture elle-même, la TVA, les relances de paiement, restent hors périmètre et confiées à un outil de facturation existant, alimenté par les données agrégées.
Couche 1 : une table d’événements en écriture seule
Chaque appel facturable écrit une ligne dans une table dédiée, créée avec dbDelta() au moment de l’activation, jamais modifiée après son insertion :
function usage_creer_table() {
global $wpdb;
$table = $wpdb->prefix . 'usage_evenements';
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$table} (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
client_id BIGINT UNSIGNED NOT NULL,
type_evenement VARCHAR(50) NOT NULL,
quantite INT NOT NULL DEFAULT 1,
date_evenement DATETIME NOT NULL,
PRIMARY KEY (id),
KEY client_periode (client_id, date_evenement)
) {$charset};";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
register_activation_hook( __FILE__, 'usage_creer_table' );
L’index composite sur client_id et date_evenement est ce qui rend l’agrégation quotidienne rapide même avec plusieurs millions de lignes accumulées sur l’année.
Couche 2 : agrégation quotidienne plutôt que calcul en temps réel
Calculer le total facturable à chaque affichage de tableau de bord solliciterait la table d’événements en continu. Une tâche WP-Cron quotidienne agrège la veille dans une table de synthèse, bien plus légère à interroger ensuite :
add_action( 'usage_agreger_quotidien', function() {
global $wpdb;
$table = $wpdb->prefix . 'usage_evenements';
$hier = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
$totaux = $wpdb->get_results( $wpdb->prepare(
"SELECT client_id, type_evenement, SUM(quantite) AS total
FROM {$table}
WHERE DATE(date_evenement) = %s
GROUP BY client_id, type_evenement",
$hier
) );
foreach ( $totaux as $ligne ) {
$wpdb->replace( $wpdb->prefix . 'usage_synthese_quotidienne', array(
'client_id' => $ligne->client_id,
'type_evenement' => $ligne->type_evenement,
'jour' => $hier,
'total' => $ligne->total,
) );
}
} );
La méthode $wpdb->replace() rend cette agrégation idempotente : si la tâche est relancée manuellement après un incident, elle écrase la ligne du jour concerné plutôt que de la dupliquer.
Couche 3 : un export, pas une intégration complète
En fin de mois, un export génère un fichier structuré (CSV ou JSON) par client, à partir des tables de synthèse quotidienne, transmis ensuite à l’outil de facturation choisi par l’équipe. Cette limite volontaire évite à l’extension de devoir gérer elle-même les devis, les relances impayées ou la comptabilité, des sujets qui relèvent d’un outil spécialisé et non d’une extension WordPress.
Pourquoi ne pas tout centraliser dans WordPress
La tentation, une fois la table d’événements en place, est d’ajouter progressivement la génération de factures, puis le suivi des paiements, puis les relances. Chacune de ces briques ajoute une responsabilité éloignée du cœur de métier du produit SaaS lui-même. Garder la frontière au niveau de l’export évite de transformer, par petites additions successives, une extension légère en ERP non maintenu, sans les garanties de conformité qu’un outil de facturation dédié offre nativement.
En résumé
Facturer à l’usage ne nécessite qu’une table d’événements en écriture seule, une agrégation quotidienne par tâche planifiée, et un export régulier vers un outil de facturation existant. Cette architecture reste largement suffisante pour une jeune équipe produit, et elle a l’avantage de rester lisible et maintenable sans expertise comptable interne.