using Meezi.API.Models.Billing; using Meezi.Core.Enums; using Meezi.Core.Interfaces; namespace Meezi.API.Services; public record PaymentInitResult(bool Success, string? Authority, string? PaymentUrl, string? ErrorMessage); public record PaymentVerifyResult(bool Success, string? RefId, string? ErrorMessage); public interface IBillingPaymentOrchestrator { Task> GetEnabledMethodsAsync(CancellationToken cancellationToken = default); Task InitiateAsync( PaymentProvider provider, long amountRials, string paymentId, string description, string callbackUrl, CancellationToken cancellationToken = default); Task VerifyAsync( PaymentProvider provider, string externalId, long amountRials, CancellationToken cancellationToken = default); } public class BillingPaymentOrchestrator : IBillingPaymentOrchestrator { private readonly IZarinPalGateway _zarinPal; private readonly ITaraPaymentGateway _tara; private readonly ISnappPayGateway _snappPay; private readonly IPlatformRuntimeConfig _platform; private static readonly (string Id, string NameFa)[] AllMethods = [ (PaymentProviderIds.ZarinPal, "زرین‌پال"), (PaymentProviderIds.Tara, "تارا (اعتباری)"), (PaymentProviderIds.SnappPay, "اسنپ‌پی (اقساطی)") ]; public BillingPaymentOrchestrator( IZarinPalGateway zarinPal, ITaraPaymentGateway tara, ISnappPayGateway snappPay, IPlatformRuntimeConfig platform) { _zarinPal = zarinPal; _tara = tara; _snappPay = snappPay; _platform = platform; } public async Task> GetEnabledMethodsAsync( CancellationToken cancellationToken = default) { var defaultId = await _platform.GetAsync("payment.activeGateway", cancellationToken) ?? PaymentProviderIds.ZarinPal; var list = new List(); if (await _zarinPal.IsEnabledAsync(cancellationToken)) list.Add(new PaymentMethodDto(PaymentProviderIds.ZarinPal, "زرین‌پال", defaultId == PaymentProviderIds.ZarinPal)); if (await _tara.IsEnabledAsync(cancellationToken)) list.Add(new PaymentMethodDto(PaymentProviderIds.Tara, "تارا (اعتباری)", defaultId == PaymentProviderIds.Tara)); if (await _snappPay.IsEnabledAsync(cancellationToken)) list.Add(new PaymentMethodDto(PaymentProviderIds.SnappPay, "اسنپ‌پی (اقساطی)", defaultId == PaymentProviderIds.SnappPay)); if (list.Count == 0) list.Add(new PaymentMethodDto(PaymentProviderIds.ZarinPal, "زرین‌پال", true)); if (list.All(m => !m.IsDefault)) list[0] = list[0] with { IsDefault = true }; return list; } public async Task InitiateAsync( PaymentProvider provider, long amountRials, string paymentId, string description, string callbackUrl, CancellationToken cancellationToken = default) { return provider switch { PaymentProvider.Tara => await InitiateTaraAsync(amountRials, paymentId, callbackUrl, cancellationToken), PaymentProvider.SnappPay => await InitiateSnappPayAsync(amountRials, paymentId, callbackUrl, cancellationToken), _ => await InitiateZarinPalAsync(amountRials, description, callbackUrl, cancellationToken) }; } public async Task VerifyAsync( PaymentProvider provider, string externalId, long amountRials, CancellationToken cancellationToken = default) { return provider switch { PaymentProvider.Tara => Map(await _tara.VerifyPaymentAsync(externalId, cancellationToken)), PaymentProvider.SnappPay => Map(await _snappPay.VerifyAndSettleAsync(externalId, cancellationToken)), _ => Map(await _zarinPal.VerifyPaymentAsync(externalId, amountRials, cancellationToken)) }; } private async Task InitiateZarinPalAsync( long amountRials, string description, string callbackUrl, CancellationToken cancellationToken) { if (!await _zarinPal.IsEnabledAsync(cancellationToken)) return new PaymentInitResult(false, null, null, "ZarinPal is disabled."); var r = await _zarinPal.RequestPaymentAsync(amountRials, description, callbackUrl, cancellationToken); return new PaymentInitResult(r.Success, r.Authority, r.PaymentUrl, r.ErrorMessage); } private async Task InitiateTaraAsync( long amountRials, string paymentId, string callbackUrl, CancellationToken cancellationToken) { if (!await _tara.IsEnabledAsync(cancellationToken)) return new PaymentInitResult(false, null, null, "Tara is disabled."); var r = await _tara.RequestPaymentAsync(amountRials, paymentId, callbackUrl, cancellationToken); return new PaymentInitResult(r.Success, r.TraceNumber, r.PaymentUrl, r.ErrorMessage); } private async Task InitiateSnappPayAsync( long amountRials, string paymentId, string callbackUrl, CancellationToken cancellationToken) { if (!await _snappPay.IsEnabledAsync(cancellationToken)) return new PaymentInitResult(false, null, null, "Snapp Pay is disabled."); var r = await _snappPay.RequestPaymentAsync(amountRials, paymentId, callbackUrl, cancellationToken); return new PaymentInitResult(r.Success, r.PaymentToken, r.PaymentUrl, r.ErrorMessage); } private static PaymentVerifyResult Map(ZarinPalVerifyResult r) => new(r.Success, r.RefId, r.ErrorMessage); private static PaymentVerifyResult Map(TaraVerifyResult r) => new(r.Success, r.RefId, r.ErrorMessage); private static PaymentVerifyResult Map(SnappPayVerifyResult r) => new(r.Success, r.RefId, r.ErrorMessage); }