using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Application.Common.Authorization; using CMSMicroservice.Application.DayaLoanCQ.Services; using CMSMicroservice.Infrastructure.Persistence; using CMSMicroservice.Infrastructure.Persistence.Interceptors; using CMSMicroservice.Infrastructure.BackgroundJobs; using CMSMicroservice.Infrastructure.BackgroundServices; using CMSMicroservice.Infrastructure.Services.Monitoring; using CMSMicroservice.Infrastructure.Services.Authorization; using CMSMicroservice.Infrastructure.Configuration; using CMSMicroservice.Infrastructure.Services.Payment; using CMSMicroservice.Infrastructure.Services.Commission; using CMSMicroservice.Infrastructure.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Http; using System.Diagnostics; using Microsoft.IdentityModel.Tokens; using System.Text; using CMSMicroservice.Infrastructure.Services; namespace Microsoft.Extensions.DependencyInjection; public static class ConfigureServices { public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) { // Configuration Settings services.Configure(configuration.GetSection(EmailSettings.SectionName)); services.Configure(configuration.GetSection(SmsSettings.SectionName)); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); // Local file manager — files are saved to wwwroot/uploads/ on CMS disk services.AddSingleton(); services.AddScoped(); // Daya Loan API Service - قابل تغییر بین Mock و Real var useMockDayaApi = configuration.GetValue("DayaApi:UseMock", false); if (useMockDayaApi) { // Mock برای Development/Testing services.AddScoped(); } else { // Real Implementation با HttpClient services.AddHttpClient() .SetHandlerLifetime(TimeSpan.FromMinutes(5)) .ConfigureHttpClient((sp, client) => { var config = sp.GetRequiredService(); // Base Address var baseAddress = config["DayaApi:BaseAddress"] ?? "https://testdaya.tadbirandishan.com"; client.BaseAddress = new Uri(baseAddress); // Merchant Permission Key var permissionKey = config["DayaApi:MerchantPermissionKey"]; if (!string.IsNullOrEmpty(permissionKey)) { client.DefaultRequestHeaders.Add("merchant-permission-key", permissionKey); } // Timeout client.Timeout = TimeSpan.FromSeconds(30); }); } // Payment Gateway Service - Multi-Provider Architecture // پشتیبانی از درگاه‌های مختلف: ZarinPal, Daya, Mock var paymentProvider = configuration.GetValue("PaymentProvider", "Mock")?.ToLowerInvariant(); switch (paymentProvider) { case "zarinpal": services.AddHttpClient() .SetHandlerLifetime(TimeSpan.FromMinutes(5)); break; case "daya": services.AddHttpClient() .SetHandlerLifetime(TimeSpan.FromMinutes(5)); break; case "mock": default: services.AddScoped(); break; } services.AddScoped(p => p.GetRequiredService()); // Week Definition Repository - کش در حافظه برای هفته‌ها (Singleton برای کش، با IServiceScopeFactory برای دسترسی به DbContext) services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); // Commission Calculation Strategy Factory - برای سوییچ بین ORM و SP services.AddScoped(); // Chatika API Service - سرویس ایجاد حساب چتیکا services.AddHttpClient() .SetHandlerLifetime(TimeSpan.FromMinutes(5)) .ConfigureHttpClient((sp, client) => { var config = sp.GetRequiredService(); client.Timeout = TimeSpan.FromSeconds(30); }); // Background Workers - Deprecated: Using Hangfire instead // services.AddHostedService(); services.AddScoped(); // Hangfire Job (Scoped for DI) services.AddScoped(); // Hangfire Job for Chatika activation // Expire pending discount orders after 30 minutes services.AddHostedService(); // One-time: Initialize inventory records for existing products if (configuration.GetValue("SeedWorkers:InventoryInitializer:Enabled")) services.AddHostedService(); // One-time: Seed ClubMembershipCycle for existing active memberships if (configuration.GetValue("SeedWorkers:MagicWalletCycleSeed:Enabled")) services.AddHostedService(); // Q26: Auto-deploy stored procedures on startup (checksum-based) services.AddHostedService(); if (configuration.GetValue("UseInMemoryDatabase")) { services.AddDbContext(options => options.UseInMemoryDatabase("MyMemoryDb")); } else { services.AddDbContext(options => options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"), builder => builder.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName))); } // Inventory Business Service services.AddScoped(); #region AddAuthentication var message = ""; services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(jwtBearerOptions => { //jwtBearerOptions.Authority = configuration["Authentication:Authority"]; //jwtBearerOptions.Audience = configuration["Authentication:Audience"]; //jwtBearerOptions.TokenValidationParameters.ValidateAudience = false; //jwtBearerOptions.TokenValidationParameters.ValidateIssuer = true; //jwtBearerOptions.TokenValidationParameters.ValidateIssuerSigningKey = false; jwtBearerOptions.SaveToken = true; jwtBearerOptions.RequireHttpsMetadata = false; jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = configuration["JwtIssuer"], ValidAudience = configuration["JwtAudience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSecurityKey"])) }; try { jwtBearerOptions.Events = new JwtBearerEvents { OnAuthenticationFailed = ctx => { ctx.Response.StatusCode = StatusCodes.Status401Unauthorized; message += "From OnAuthenticationFailed:\n"; message += ctx.Exception.Message; return Task.CompletedTask; }, OnChallenge = ctx => { message += "From OnChallenge:\n"; ctx.Response.StatusCode = StatusCodes.Status401Unauthorized; ctx.Response.ContentType = "text/plain"; return ctx.Response.WriteAsync(message); }, OnMessageReceived = ctx => { message = "From OnMessageReceived:\n"; ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken); if (BearerToken.Count == 0) BearerToken = "no Bearer token sent\n"; message += "Authorization Header sent: " + BearerToken + "\n"; return Task.CompletedTask; }, OnTokenValidated = ctx => { Debug.WriteLine("token: " + ctx.SecurityToken.ToString()); return Task.CompletedTask; } }; } catch (Exception e) { Console.WriteLine(e); throw; } }); services.AddAuthorization(); #endregion return services; } }