using System.Text.Json; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Tiku.Application.Commerce; using Tiku.Application.PlatformBilling; using Tiku.Application.Security; using Tiku.Domain.Platform; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Persistence; namespace Tiku.Infrastructure.PlatformBilling; internal sealed class PlatformBillingPaymentGateway( ITenantExecutionScope tenantExecutionScope) : IPlatformBillingPaymentGateway { public Task CreatePaymentAsync( string provider, CreatePaymentProviderRequest request, CancellationToken cancellationToken = default) => ExecuteAsync(provider, request.OrderNo, async (resolvedProvider, account, token) => await resolvedProvider.CreatePaymentAsync(account, request, token), cancellationToken); public Task ParseNotificationAsync( string provider, PaymentNotificationRequest request, CancellationToken cancellationToken = default) => ExecuteAsync(provider, "payment-notification", async (resolvedProvider, account, token) => await resolvedProvider.ParsePaymentNotificationAsync(account, request, token), cancellationToken); private Task ExecuteAsync( string provider, string correlationId, Func> operation, CancellationToken cancellationToken) { return tenantExecutionScope.ExecuteAsync( new SystemScopeRequest( null, SystemScopeCallerType.Platform, nameof(PlatformBillingPaymentGateway), "Resolve the platform-owned SaaS billing payment account", correlationId, IsGlobal: true), async (services, token) => { var dbContext = services.GetRequiredService(); var platformTenantId = await dbContext.Tenants.AsNoTracking() .Where(value => value.Mode == TenantMode.PlatformOwned) .Select(value => value.Id) .SingleOrDefaultAsync(token); if (platformTenantId == Guid.Empty) { throw new PaymentProviderException("Platform-owned tenant is missing.", "platform_payment_owner_missing"); } var normalized = NormalizeProvider(provider); var resolvedProvider = services.GetServices() .SingleOrDefault(value => value.Provider == normalized) ?? throw new PaymentProviderException("Payment provider is not supported.", "payment_provider_not_supported"); PaymentProviderAccount account; if (normalized == PaymentProviders.Manual) { account = new PaymentProviderAccount( platformTenantId, PaymentProviders.Manual, "PlatformCollect", JsonDocument.Parse("{}").RootElement.Clone(), JsonDocument.Parse("{}").RootElement.Clone()); } else { var channel = await dbContext.PlatformPaymentChannels.AsNoTracking() .Where(value => value.Provider == normalized && value.Status == PlatformPaymentChannelStatus.Active) .OrderBy(value => value.Priority) .FirstOrDefaultAsync(token) ?? throw new PaymentProviderException("Platform payment channel is missing.", "platform_payment_channel_missing"); account = new PaymentProviderAccount( platformTenantId, normalized, channel.Mode, channel.ConfigPublic, JsonDocument.Parse("{}").RootElement.Clone()); } return await operation(resolvedProvider, account, token); }, cancellationToken); } private static string NormalizeProvider(string provider) { var normalized = provider.Trim().ToLowerInvariant().Replace("-", "_", StringComparison.Ordinal); return normalized switch { "wechat" or "wechatpay" or "wxpay" or "wx_pay" => PaymentProviders.WechatPay, "ali_pay" => PaymentProviders.Alipay, PaymentProviders.WechatPay or PaymentProviders.Alipay or PaymentProviders.Manual => normalized, _ => throw new PaymentProviderException("Payment provider is not supported.", "payment_provider_not_supported") }; } }