using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using ManagerService.EmailTemplates;
using ManagerService.Helpers;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ManagerService.Services
{
///
/// Sends transactional emails via the Resend API (https://resend.com), using the shared
/// branded HTML shell for every template.
///
public class ResendEmailService : IEmailService
{
private readonly ResendSettings _settings;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
public ResendEmailService(IOptions settings, IHttpClientFactory httpClientFactory, ILogger logger)
{
_settings = settings.Value;
_httpClientFactory = httpClientFactory;
_logger = logger;
}
private async Task SendAsync(string toEmail, string subject, string html)
{
try
{
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
var payload = JsonSerializer.Serialize(new
{
from = _settings.FromEmail,
to = new[] { toEmail },
subject,
html,
});
var content = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://api.resend.com/emails", content);
if (!response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
_logger.LogError("Resend API error {StatusCode} sending to {ToEmail}: {Body}", response.StatusCode, toEmail, body);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send email to {ToEmail} via Resend", toEmail);
}
}
public Task SendWelcomeEmailAsync(string toEmail, string firstName, string loginUrl) => SendAsync(
toEmail,
"Bienvenue sur MyInfoMate !",
EmailLayout.Render(
$"Bienvenue{(string.IsNullOrEmpty(firstName) ? "" : $", {firstName}")} !",
"Votre espace MyInfoMate est prêt. Vous pouvez dès maintenant vous connecter et configurer votre lieu culturel.",
"Accéder à mon espace", loginUrl));
public Task SendTrialStartedEmailAsync(string toEmail, string firstName, DateTime trialEndsAt) => SendAsync(
toEmail,
"Votre essai gratuit a démarré",
EmailLayout.Render(
"Votre essai gratuit de 14 jours a démarré",
$"Vous avez accès à toutes les fonctionnalités du plan Essentiel jusqu'au {trialEndsAt:dd/MM/yyyy}, sans carte bancaire. " +
"Vos pages visiteur afficheront un léger filigrane « Aperçu » tant que l'essai n'est pas converti en abonnement."));
public Task SendTrialCheckInEmailAsync(string toEmail, string firstName) => SendAsync(
toEmail,
"Comment se passe votre essai MyInfoMate ?",
EmailLayout.Render(
"Comment ça se passe ?",
"Cela fait une semaine que vous testez MyInfoMate. Une question, besoin d'aide pour configurer votre contenu ? Répondez simplement à cet e-mail."));
public Task SendTrialEndingReminderEmailAsync(string toEmail, string firstName, DateTime trialEndsAt, string checkoutUrl) => SendAsync(
toEmail,
"Votre essai se termine bientôt",
EmailLayout.Render(
"Votre essai se termine dans quelques jours",
$"Votre essai gratuit se termine le {trialEndsAt:dd/MM/yyyy}. Passez à l'abonnement Essentiel (39€/mois HTVA) pour garder votre espace actif et retirer le filigrane « Aperçu ».",
"Activer mon abonnement", checkoutUrl));
public Task SendTrialLastDayEmailAsync(string toEmail, string firstName, string checkoutUrl) => SendAsync(
toEmail,
"Dernier jour de votre essai gratuit",
EmailLayout.Render(
"C'est le dernier jour de votre essai",
"Sans abonnement actif, votre espace sera désactivé demain. Activez votre abonnement dès maintenant pour ne pas perdre votre configuration.",
"Activer mon abonnement", checkoutUrl));
public Task SendPaymentConfirmedEmailAsync(string toEmail, string firstName) => SendAsync(
toEmail,
"Paiement confirmé — abonnement actif",
EmailLayout.Render(
"Votre abonnement est actif !",
"Merci ! Votre paiement a été confirmé et votre abonnement Essentiel est maintenant actif. Le filigrane « Aperçu » a été retiré de vos pages visiteur. Votre facture vous sera envoyée sous peu."));
public Task SendPaymentFailedEmailAsync(string toEmail, string firstName, string customerPortalUrl) => SendAsync(
toEmail,
"Échec du paiement de votre abonnement",
EmailLayout.Render(
"Le paiement de votre abonnement a échoué",
"Nous n'avons pas pu traiter le paiement de votre abonnement MyInfoMate. Merci de mettre à jour votre moyen de paiement pour éviter une interruption de service.",
"Mettre à jour mon moyen de paiement", customerPortalUrl));
public Task SendUserInvitationEmailAsync(string toEmail, string setPasswordUrl) => SendAsync(
toEmail,
"Vous êtes invité(e) sur MyInfoMate",
EmailLayout.Render(
"Vous avez été invité(e) sur MyInfoMate",
"Un administrateur vous a donné accès à son espace MyInfoMate. Définissez votre mot de passe pour commencer.",
"Définir mon mot de passe", setPasswordUrl));
public Task SendPasswordResetEmailAsync(string toEmail, string firstName, string resetUrl) => SendAsync(
toEmail,
"Réinitialisation de votre mot de passe",
EmailLayout.Render(
"Réinitialisation de mot de passe",
"Vous avez demandé à réinitialiser votre mot de passe MyInfoMate. Si vous n'êtes pas à l'origine de cette demande, ignorez cet e-mail.",
"Définir un nouveau mot de passe", resetUrl));
public Task NotifyAdminAsync(string subject, string message) => SendAsync(
_settings.AdminEmail,
$"[MyInfoMate] {subject}",
EmailLayout.Render(subject, message.Replace("\n", "
")));
}
}