using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Tiku.Application.Commerce; using Tiku.Application.PlatformBilling; using Tiku.Application.Security; using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.PlatformBilling; internal sealed class PlatformBillingNotificationService( ITenantExecutionScope tenantExecutionScope, IPlatformBillingPaymentGateway paymentGateway) : IPlatformBillingNotificationService { public async Task ProcessAsync(PlatformBillingNotification notification, CancellationToken cancellationToken = default) { var parsed = await paymentGateway.ParseNotificationAsync( notification.Provider, new PaymentNotificationRequest(Guid.Empty, notification.Provider, notification.Headers, notification.RawBody, notification.Body), cancellationToken); if (!parsed.SignatureValid || !parsed.Paid) throw Error("Payment notification was not a valid paid event.", "platform_billing_notification_invalid"); await tenantExecutionScope.ExecuteAsync( new SystemScopeRequest(null, SystemScopeCallerType.Platform, nameof(PlatformBillingNotificationService), "Settle platform billing notification", parsed.EventId, true), async (services, token) => { var db = services.GetRequiredService(); var order = await db.PlatformBillingOrders.AsNoTracking() .SingleOrDefaultAsync(value => value.OrderNo == parsed.OrderNo, token) ?? throw Error("Platform billing order was not found.", "platform_billing_order_not_found"); if (order.TotalAmountCents != parsed.AmountCents) throw Error("Payment notification amount does not match the order.", "platform_billing_payment_amount_mismatch"); var payment = await db.PlatformBillingPayments.AsNoTracking() .Where(value => value.TenantId == order.TenantId && value.OrderId == order.Id && value.Provider == parsed.Provider) .OrderByDescending(value => value.CreatedAt) .FirstOrDefaultAsync(token) ?? throw Error("Platform billing payment was not found.", "platform_billing_payment_not_found"); await services.GetRequiredService().MarkPaidAsync( payment.Id, parsed.EventId, parsed.EventType, parsed.ProviderTradeNo, parsed.PaidAt ?? DateTimeOffset.UtcNow, parsed.RawPayload, null, token); }, cancellationToken); } private static PlatformBillingException Error(string message, string code) { return new PlatformBillingException(message, code); } }