checkout
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Webshop.Application.Services.Public
|
||||
{
|
||||
public class DiscountCalculationResult
|
||||
{
|
||||
public decimal TotalDiscountAmount { get; set; } = 0;
|
||||
public List<Guid> AppliedDiscountIds { get; set; } = new List<Guid>();
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
// FIX: IsValid existierte nicht. Wir definieren es hier:
|
||||
// Ein Resultat ist gültig, wenn es keine Fehlermeldung gibt.
|
||||
// (Oder alternativ: wenn der Betrag > 0 ist, je nach Logik)
|
||||
public bool IsValid => string.IsNullOrEmpty(ErrorMessage);
|
||||
}
|
||||
}
|
||||
133
Webshop.Application/Services/Public/EmailService.cs
Normal file
133
Webshop.Application/Services/Public/EmailService.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Configuration; // Für Absender-Config
|
||||
using Resend;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Webshop.Application.Services.Public.Interfaces;
|
||||
|
||||
namespace Webshop.Application.Services.Public
|
||||
{
|
||||
public class EmailService : IEmailService
|
||||
{
|
||||
private readonly IResend _resend;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly ILogger<EmailService> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public EmailService(
|
||||
IResend resend,
|
||||
IWebHostEnvironment env,
|
||||
ILogger<EmailService> logger,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
_resend = resend;
|
||||
_env = env;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task SendEmailConfirmationAsync(string toEmail, string confirmationLink)
|
||||
{
|
||||
var subject = "Willkommen! Bitte bestätigen Sie Ihre E-Mail-Adresse";
|
||||
var title = "E-Mail bestätigen";
|
||||
var body = "Vielen Dank für Ihre Registrierung! Bitte klicken Sie auf den Button unten, um Ihr Konto zu aktivieren.";
|
||||
var btnText = "Konto aktivieren";
|
||||
|
||||
await SendEmailInternalAsync(toEmail, subject, title, body, btnText, confirmationLink);
|
||||
}
|
||||
|
||||
public async Task SendPasswordResetAsync(string toEmail, string resetLink)
|
||||
{
|
||||
var subject = "Passwort zurücksetzen";
|
||||
var title = "Passwort vergessen?";
|
||||
var body = "Wir haben eine Anfrage zum Zurücksetzen Ihres Passworts erhalten. Klicken Sie hier:";
|
||||
var btnText = "Passwort zurücksetzen";
|
||||
|
||||
await SendEmailInternalAsync(toEmail, subject, title, body, btnText, resetLink);
|
||||
}
|
||||
|
||||
public async Task SendOrderConfirmationAsync(Guid orderId, string toEmail)
|
||||
{
|
||||
// Link zur Bestellübersicht (anpassen an dein Frontend Routing)
|
||||
var orderLink = $"{_configuration["App:FrontendUrl"]}/orders/{orderId}";
|
||||
|
||||
var subject = $"Bestellbestätigung #{orderId.ToString().Substring(0, 8).ToUpper()}";
|
||||
var title = "Vielen Dank für Ihre Bestellung!";
|
||||
var body = $"Wir haben Ihre Bestellung #{orderId} erhalten und bearbeiten sie umgehend.";
|
||||
var btnText = "Bestellung ansehen";
|
||||
|
||||
await SendEmailInternalAsync(toEmail, subject, title, body, btnText, orderLink);
|
||||
}
|
||||
|
||||
public async Task SendEmailChangeConfirmationAsync(string toEmail, string confirmationLink)
|
||||
{
|
||||
var subject = "Änderung Ihrer E-Mail-Adresse bestätigen";
|
||||
var title = "Neue E-Mail bestätigen";
|
||||
var body = "Sie haben eine Änderung Ihrer E-Mail-Adresse angefordert. Bitte bestätigen Sie dies:";
|
||||
var btnText = "E-Mail bestätigen";
|
||||
|
||||
await SendEmailInternalAsync(toEmail, subject, title, body, btnText, confirmationLink);
|
||||
}
|
||||
|
||||
// --- Interne Methode für Resend ---
|
||||
private async Task SendEmailInternalAsync(string toEmail, string subject, string title, string body, string btnText, string btnLink)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. Template laden
|
||||
string htmlContent = await LoadAndFormatTemplate(title, body, btnText, btnLink);
|
||||
|
||||
// 2. Resend Message bauen
|
||||
var message = new EmailMessage();
|
||||
message.To.Add(toEmail);
|
||||
// Hole Absender aus Config oder Fallback
|
||||
message.From = _configuration["Resend:FromEmail"] ?? "noreply@deinwebshop.com";
|
||||
message.Subject = subject;
|
||||
message.HtmlBody = htmlContent;
|
||||
|
||||
// 3. Senden
|
||||
await _resend.EmailSendAsync(message);
|
||||
|
||||
_logger.LogInformation($"E-Mail '{subject}' erfolgreich an {toEmail} gesendet via Resend.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Fehler beim Senden der E-Mail an {toEmail} via Resend.");
|
||||
// Optional: Exception werfen, wenn der Prozess abbrechen soll.
|
||||
// Meistens besser: Loggen und "Silent Fail", damit z.B. der Checkout nicht abbricht.
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> LoadAndFormatTemplate(string title, string body, string btnText, string btnLink)
|
||||
{
|
||||
// Pfad: wwwroot/templates/email-template.html
|
||||
var templatePath = Path.Combine(_env.WebRootPath ?? _env.ContentRootPath, "templates", "email-template.html");
|
||||
|
||||
string template;
|
||||
|
||||
if (File.Exists(templatePath))
|
||||
{
|
||||
template = await File.ReadAllTextAsync(templatePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback HTML, falls Datei fehlt
|
||||
_logger.LogWarning($"Template nicht gefunden unter {templatePath}");
|
||||
template = "<html><body><h1>{{ShopName}}</h1><h2>{{Titel}}</h2><p>{{Haupttext}}</p><p><a href='{{CallToActionLink}}'>{{CallToActionText}}</a></p></body></html>";
|
||||
}
|
||||
|
||||
// Platzhalter ersetzen
|
||||
var shopName = _configuration["ShopInfo:Name"] ?? "Webshop";
|
||||
|
||||
return template
|
||||
.Replace("{{ShopName}}", shopName)
|
||||
.Replace("{{Titel}}", title)
|
||||
.Replace("{{Haupttext}}", body)
|
||||
.Replace("{{CallToActionText}}", btnText)
|
||||
.Replace("{{CallToActionLink}}", btnLink)
|
||||
.Replace("{{Jahr}}", DateTime.UtcNow.Year.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,21 +6,7 @@ using Webshop.Domain.Entities;
|
||||
|
||||
namespace Webshop.Application.Services.Public.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Stellt das Ergebnis der Rabattberechnung dar.
|
||||
/// </summary>
|
||||
public class DiscountCalculationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Der gesamte berechnete Rabattbetrag.
|
||||
/// </summary>
|
||||
public decimal TotalDiscountAmount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Die IDs der Rabatte, die erfolgreich angewendet wurden.
|
||||
/// </summary>
|
||||
public List<Guid> AppliedDiscountIds { get; set; } = new List<Guid>();
|
||||
}
|
||||
|
||||
public interface IDiscountService
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Webshop.Application.Services.Public.Interfaces
|
||||
{
|
||||
public interface IEmailService
|
||||
{
|
||||
/// <summary>
|
||||
/// Sendet den Link zur Bestätigung der Registrierung.
|
||||
/// </summary>
|
||||
Task SendEmailConfirmationAsync(string toEmail, string confirmationLink);
|
||||
|
||||
/// <summary>
|
||||
/// Sendet den Link zum Zurücksetzen des Passworts.
|
||||
/// </summary>
|
||||
Task SendPasswordResetAsync(string toEmail, string resetLink);
|
||||
|
||||
/// <summary>
|
||||
/// Sendet eine Bestellbestätigung.
|
||||
/// </summary>
|
||||
Task SendOrderConfirmationAsync(Guid orderId, string toEmail);
|
||||
|
||||
/// <summary>
|
||||
/// Sendet einen Bestätigungslink bei E-Mail-Änderung.
|
||||
/// </summary>
|
||||
Task SendEmailChangeConfirmationAsync(string toEmail, string confirmationLink);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user