Courriels
Les six messages envoyés par le module, leur déclencheur, leur contenu et la garantie de non-répétition.
Le module envoie six messages. Tous partent par core/email, donc par le fournisseur configuré pour le projet, et tous sont bâtis sur le BaseLayout du core : ce sont les mêmes expéditeur, en-tête, largeur et pied que les courriels de compte que le client a déjà reçus.
Les six messages
| Message | Déclencheur | Objet | Pièce jointe |
|---|---|---|---|
| Nouvelle facture | Émission, manuelle ou par récurrence, et bouton « Renvoyer ». | Facture FAC-2026-0001 | La facture en PDF. |
| Rappel de paiement | Tâche de rappels, avant l'échéance. | Rappel : facture FAC-2026-0001, échéance dans 7 jours | — |
| Paiement en retard | Tâche de rappels, après l'échéance. | Facture FAC-2026-0001 en retard de 12 jours | — |
| Alerte de retard | Premier constat de retard, à l'adresse de ..._ADMIN_EMAIL. | Retard : facture FAC-2026-0001 (Client inc.) | — |
| Paiement confirmé | Webhook Stripe, paiement encaissé. | Paiement reçu : facture FAC-2026-0001 | Le reçu en PDF, seulement si la facture est soldée. |
| Remboursement | Webhook Stripe, remboursement effectué. | Remboursement : facture FAC-2026-0001 | — |
La cadence des rappels et des avis de retard est décrite dans Récurrences et rappels.
Ce que porte un message
Toujours la même ossature, celle du BaseLayout : logo de l'émetteur, titre, une phrase d'ouverture, un seul encadré, un seul bouton, puis les mentions et le lien de paiement en clair.
L'encadré est ce qui porte l'état, par sa couleur : neutre pour une facture ou un rappel, vert pour un paiement reçu, rouge pour un retard. Ces couleurs, comme celle du bouton, viennent du thème du projet, dans ses valeurs claires : le vert est son success, le rouge son danger, le bouton sa couleur primary. Il ne contient que ce dont le lecteur a besoin pour décider : le numéro, le montant à payer, l'échéance, et l'acompte déjà versé s'il y en a un. Les montants, les dates et les identifiants y sont en chasse fixe.
La ventilation des taxes n'est pas dans les courriels. Sous-total, TPS, TVQ et détail des lignes vivent sur le PDF joint et sur la page publique. Un courriel qui recopie une facture complète est un courriel que personne ne lit, et la seule version qui fait foi reste le document.
Deux exceptions à cette ossature :
- L'avis de retard ajoute une ligne d'intérêts courus et un total dû séparé par un filet, parce que le montant réclamé n'est plus celui de la facture.
- L'alerte interne n'a ni salutation, ni lien de support dans le pied, et l'encadré y porte le courriel du client en
mailto:: le destinataire est chez vous, et ce qu'il veut, c'est écrire au client.
Le lien de paiement est répété en toutes lettres sous le bouton, sauf sur les deux messages qui ne demandent aucune action. Un client dont le logiciel supprime les boutons, ou qui transfère le message en texte brut, garde un lien utilisable.
L'habillage appartient au site
Un module fournit le contenu d'un message, jamais l'identité de celui qui l'envoie. Tout ce qui entoure ce contenu vient donc de la configuration du site, sans qu'aucune variable du module n'intervienne :
| Élément | Source |
|---|---|
| Nom et adresse d'expédition | LRVO_EMAIL_FROM_NAME, LRVO_EMAIL_FROM_ADDRESS |
| Logo de l'en-tête | LRVO_EMAIL_LOGO, cliquable vers LRVO_EMAIL_LOGO_URL |
| Nom affiché sans logo, et dans le pied | LRVO_NAME |
| Adresse de support du pied | LRVO_SUPPORT_EMAIL |
C'est la même enveloppe que pour une réinitialisation de mot de passe : un destinataire qui verrait deux identités différentes pour un même site aurait de bonnes raisons de se méfier de l'une des deux.
Une seule exception, et elle est fonctionnelle plutôt que visuelle : l'adresse de réponse. Reply-To porte LRVO_MODULE_BILLING_COMPANY_EMAIL, pour qu'une réponse à une facture arrive à la comptabilité plutôt qu'à l'adresse d'envoi technique.
Cette même adresse est écrite en toutes lettres dans le message, sur la facture, l'avis de retard et l'avis de remboursement : « Si vous avez des questions sur cette facture, écrivez-nous à [email protected]. » Le bouton de réponse y mène déjà, mais un courriel transféré, imprimé, ou lu dans un client qui masque les en-têtes perd cette information. Une adresse écrite dans le corps ne dépend pas de la façon dont le message est ouvert. Sans ..._COMPANY_EMAIL configurée, la phrase invite simplement à répondre au courriel.
L'émetteur, lui, signe le document : c'est sur la facture et sur le reçu que figurent sa raison sociale, son adresse, son logo et ses numéros d'inscription. Voir Configuration.
L'alerte interne de retard est le seul message sans lien de support dans son pied : son destinataire travaille chez vous.
Chaque message part une fois
Un cron qui repasse toutes les heures, un webhook que Stripe rejoue : sans garde, le client reçoit trois fois le même rappel.
Avant chaque envoi, le module réserve un créneau dans lrvo_billing_reminder, identifié par la facture, le genre de message et son rang dans l'échelle. Un index unique partiel rend la seconde réservation impossible. Si l'envoi échoue, le créneau est relâché et le passage suivant réessaie ; s'il réussit, il est clos pour de bon.
Les envois du cron sont espacés de 900 ms, ce qui tient sous la limite de débit de Resend et ménage tout autant un relais SMTP.
En cas d'échec
sendEmail ne lève jamais : il retourne { success, error }. Un échec est journalisé, compté dans le résumé de la tâche, et le créneau relâché. Une facture s'émet même si son courriel ne part pas : le document existe, le lien de paiement existe, et le bouton « Renvoyer » de l'admin permet de retenter.
Le PDF suit la même règle. Si son rendu échoue, le courriel part sans pièce jointe plutôt que pas du tout, et le client accède au document par le lien.
Notes
- Les liens sont absolus, construits sur
NEXT_PUBLIC_URL. Sans cette variable, les courriels partent avec des liens qui ne mènent nulle part. - L'alternative en texte brut est tirée du même arbre que la version HTML. Il n'y a pas deux versions à maintenir, donc pas de version qui vieillit.
- Chaque message porte les étiquettes
module=billingetkind=…, ce qui permet de filtrer les statistiques de livraison par type de message. Ces étiquettes sont propres à Resend et sont ignorées sous SMTP, qui n'a pas d'équivalent. - Sans fournisseur de courriel configuré, rien n'est envoyé et rien ne casse : les échecs sont journalisés, la facturation continue de fonctionner.