From 1a27e282e36bef7b90e89a07d0656ea42fff5147 Mon Sep 17 00:00:00 2001 From: "Kalana B. Thilakarathna" Date: Wed, 6 Aug 2025 16:11:02 +0530 Subject: [PATCH] merchant onboarding process completed --- Singer_Hexdive/ApplicationDbContext.cs | 7 + Singer_Hexdive/Controllers/ApiController.cs | 15 ++ Singer_Hexdive/Enums/ApprovedStatus.cs | 9 + Singer_Hexdive/Enums/BankAccountTypes.cs | 8 + .../Exceptions/NotfoundException.cs | 7 + .../IMerchantRepository.cs | 32 ++++ .../ServiceInterfaces/BaseMerchantService.cs | 25 +++ Singer_Hexdive/Models/IOs/ApiRequest.cs | 14 ++ Singer_Hexdive/Models/IOs/ApiResponse.cs | 12 ++ Singer_Hexdive/Models/IdentificationType.cs | 12 ++ Singer_Hexdive/Models/MerchantBankDetails.cs | 25 +++ .../Models/MerchantBusinessDetail.cs | 26 +++ Singer_Hexdive/Models/MerchantDirectors.cs | 21 +++ .../Models/MerchantPersonalDetail.cs | 27 +++ Singer_Hexdive/Models/MerchantShareHolders.cs | 21 +++ Singer_Hexdive/Program.cs | 22 ++- .../Repositories/MerchantRepository.cs | 146 ++++++++++++++++ .../FunctionHandlers/MerchantManager.cs | 71 ++++++++ Singer_Hexdive/Services/MerchantService.cs | 165 ++++++++++++++++++ Singer_Hexdive/Singer_Hexdive.csproj | 13 +- Singer_Hexdive/Validations/FieldValidators.cs | 92 ++++++++++ Singer_Hexdive/appsettings.json | 17 +- 22 files changed, 768 insertions(+), 19 deletions(-) create mode 100644 Singer_Hexdive/Enums/ApprovedStatus.cs create mode 100644 Singer_Hexdive/Enums/BankAccountTypes.cs create mode 100644 Singer_Hexdive/Exceptions/NotfoundException.cs create mode 100644 Singer_Hexdive/Interfaces/RepositoryInterfaces/IMerchantRepository.cs create mode 100644 Singer_Hexdive/Interfaces/ServiceInterfaces/BaseMerchantService.cs create mode 100644 Singer_Hexdive/Models/IOs/ApiRequest.cs create mode 100644 Singer_Hexdive/Models/IOs/ApiResponse.cs create mode 100644 Singer_Hexdive/Models/IdentificationType.cs create mode 100644 Singer_Hexdive/Models/MerchantBankDetails.cs create mode 100644 Singer_Hexdive/Models/MerchantBusinessDetail.cs create mode 100644 Singer_Hexdive/Models/MerchantDirectors.cs create mode 100644 Singer_Hexdive/Models/MerchantPersonalDetail.cs create mode 100644 Singer_Hexdive/Models/MerchantShareHolders.cs create mode 100644 Singer_Hexdive/Repositories/MerchantRepository.cs create mode 100644 Singer_Hexdive/Services/FunctionHandlers/MerchantManager.cs create mode 100644 Singer_Hexdive/Services/MerchantService.cs create mode 100644 Singer_Hexdive/Validations/FieldValidators.cs diff --git a/Singer_Hexdive/ApplicationDbContext.cs b/Singer_Hexdive/ApplicationDbContext.cs index 4f0e4fb..cdebd1a 100644 --- a/Singer_Hexdive/ApplicationDbContext.cs +++ b/Singer_Hexdive/ApplicationDbContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; +using Singer_Hexdive.Models; using System.IO; namespace Singer_Hexdive @@ -9,6 +10,12 @@ namespace Singer_Hexdive { public ApplicationDbContext(DbContextOptions options) : base(options) { } + public DbSet IdentificationTypes { get; set; } + public DbSet MerchantBankDetails { get; set; } + public DbSet MerchantBusinessDetail { get; set; } + public DbSet MerchantPersonalDetail { get; set; } + public DbSet MerchantShareHolders { get; set; } + public DbSet MerchantDirectors { get; set; } } public class ApplicationDbContextFactory : IDesignTimeDbContextFactory diff --git a/Singer_Hexdive/Controllers/ApiController.cs b/Singer_Hexdive/Controllers/ApiController.cs index 1f78a5c..9d88c01 100644 --- a/Singer_Hexdive/Controllers/ApiController.cs +++ b/Singer_Hexdive/Controllers/ApiController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Singer_Hexdive.Models.IOs; +using Singer_Hexdive.Services.FunctionHandlers; namespace Singer_Hexdive.Controllers { @@ -7,5 +9,18 @@ namespace Singer_Hexdive.Controllers [Route("api")] public class ApiController : ControllerBase { + private readonly MerchantManager _merchantManager; + + public ApiController(MerchantManager merchantManager) + { + _merchantManager = merchantManager; + } + + [HttpPost("merchant")] + public async Task MerchantExecute([FromBody] ApiRequest request) + { + var response = await _merchantManager.Execute(request); + return StatusCode(response.StatusCode, response); + } } } diff --git a/Singer_Hexdive/Enums/ApprovedStatus.cs b/Singer_Hexdive/Enums/ApprovedStatus.cs new file mode 100644 index 0000000..78fe492 --- /dev/null +++ b/Singer_Hexdive/Enums/ApprovedStatus.cs @@ -0,0 +1,9 @@ +namespace Singer_Hexdive.Enums +{ + public enum ApprovedStatus + { + pending = 1, + approved = 2, + rejected = 3 + } +} diff --git a/Singer_Hexdive/Enums/BankAccountTypes.cs b/Singer_Hexdive/Enums/BankAccountTypes.cs new file mode 100644 index 0000000..4287414 --- /dev/null +++ b/Singer_Hexdive/Enums/BankAccountTypes.cs @@ -0,0 +1,8 @@ +namespace Singer_Hexdive.Enums +{ + public enum BankAccountTypes + { + Saving = 1, + Current = 2, + } +} diff --git a/Singer_Hexdive/Exceptions/NotfoundException.cs b/Singer_Hexdive/Exceptions/NotfoundException.cs new file mode 100644 index 0000000..a3ed06e --- /dev/null +++ b/Singer_Hexdive/Exceptions/NotfoundException.cs @@ -0,0 +1,7 @@ +namespace Singer_Hexdive.Exceptions +{ + public class NotfoundException : Exception + { + public NotfoundException(string message) : base(message) { } + } +} diff --git a/Singer_Hexdive/Interfaces/RepositoryInterfaces/IMerchantRepository.cs b/Singer_Hexdive/Interfaces/RepositoryInterfaces/IMerchantRepository.cs new file mode 100644 index 0000000..68a95d1 --- /dev/null +++ b/Singer_Hexdive/Interfaces/RepositoryInterfaces/IMerchantRepository.cs @@ -0,0 +1,32 @@ +using Singer_Hexdive.Models; + +namespace Singer_Hexdive.Interfaces.RepositoryInterfaces +{ + public interface IMerchantRepository + { + //-------------------------------------------Merchant_Onboarding_Start----------------------------------// + Task GetM_PersonalDetailsAsync(int merchantId); + Task PostM_PersonalDetailsAsync(MerchantPersonalDetail merchantPersonalDetail); + Task GetM_BusinessDetailsAsync(int merchantId); + Task PostM_BusinessDetailsAsync(MerchantBusinessDetail merchantBusinessDetail); + Task PostM_ShareHolderDetailsAsync(MerchantShareHolders merchantShareHolders); + Task PostM_DiretorDetailsAsync(MerchantDirectors merchantDirectors); + Task GetM_BankDetailsAsync(int merchantId); + Task PostM_BankDetailsAsync(MerchantBankDetails merchantBankDetails); + //Task PostM_ReviewDetails(object payload); + //Task PostM_CompleteOnboarding(object payload); + //-------------------------------------------Merchant_Onboarding_End----------------------------------// + + + //-------------------------------------------Merchant_Management_Start----------------------------------// + //Task GetMerchantDetails(string merchantId); + //Task UpdateMerchantDetails(string merchantId, object payload); + //Task DeleteMerchant(string merchantId); + //-------------------------------------------Merchant_Management_End----------------------------------// + + + //-------------------------------------------Merchant_SupportFunctions_Start----------------------------// + Task CheckApprovedStatus(int merchantId, string tableName); + //-------------------------------------------Merchant_SupportFunctions_End------------------------------// + } +} diff --git a/Singer_Hexdive/Interfaces/ServiceInterfaces/BaseMerchantService.cs b/Singer_Hexdive/Interfaces/ServiceInterfaces/BaseMerchantService.cs new file mode 100644 index 0000000..26397b2 --- /dev/null +++ b/Singer_Hexdive/Interfaces/ServiceInterfaces/BaseMerchantService.cs @@ -0,0 +1,25 @@ +using System.Text.Json; + +namespace Singer_Hexdive.Interfaces.ServiceInterfaces +{ + public abstract class BaseMerchantService + { + protected Dictionary DeserializePayload(object payload) + { + if (payload is null) + throw new ArgumentNullException(nameof(payload)); + + var json = JsonSerializer.Serialize(payload); + return JsonSerializer.Deserialize>(json) + ?? throw new InvalidOperationException("Failed to deserialize payload."); + } + + public abstract Task GetM_PersonalDetails(object payload); + public abstract Task PostM_PersonalDetails(object payload); + public abstract Task GetM_BusinessDetails(object payload); + public abstract Task PostM_BusinessDetails(object payload); + public abstract Task GetM_BankDetails(object payload); + public abstract Task PostM_BankDetails(object payload); + + } +} diff --git a/Singer_Hexdive/Models/IOs/ApiRequest.cs b/Singer_Hexdive/Models/IOs/ApiRequest.cs new file mode 100644 index 0000000..91f8d87 --- /dev/null +++ b/Singer_Hexdive/Models/IOs/ApiRequest.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Singer_Hexdive.Models.IOs +{ + [NotMapped] + public class ApiRequest + { + public string FunctionName { get; set; } + + public object Payload { get; set; } + + public string Reference { get; set; } + } +} diff --git a/Singer_Hexdive/Models/IOs/ApiResponse.cs b/Singer_Hexdive/Models/IOs/ApiResponse.cs new file mode 100644 index 0000000..a4d6afc --- /dev/null +++ b/Singer_Hexdive/Models/IOs/ApiResponse.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Singer_Hexdive.Models.IOs +{ + [NotMapped] + public class ApiResponse + { + public int StatusCode { get; set; } + public string Message { get; set; } + public T Data { get; set; } + } +} diff --git a/Singer_Hexdive/Models/IdentificationType.cs b/Singer_Hexdive/Models/IdentificationType.cs new file mode 100644 index 0000000..5814cf4 --- /dev/null +++ b/Singer_Hexdive/Models/IdentificationType.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Singer_Hexdive.Models +{ + public class IdentificationType + { + [Key] + public int Id { get; set; } + public string IdType { get; set; } + public DateTime CreateAt { get; set; } + } +} diff --git a/Singer_Hexdive/Models/MerchantBankDetails.cs b/Singer_Hexdive/Models/MerchantBankDetails.cs new file mode 100644 index 0000000..14d03fc --- /dev/null +++ b/Singer_Hexdive/Models/MerchantBankDetails.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using Singer_Hexdive.Enums; + +namespace Singer_Hexdive.Models +{ + public class MerchantBankDetails + { + [Key] + public int Id { get; set; } + public int FK_MId { get; set; } + [ForeignKey("FK_MId")] + public MerchantPersonalDetail MerchantPersonalDetail { get; set; } + public string CurrencyCode { get; set; } + public BankAccountTypes BankAccountTypes { get; set; } + public string M_BankName { get; set; } + public string M_BankBranchName { get; set; } + public int M_BankAccNum { get; set; } + public string M_BankAccHolderName { get; set; } + public string M_BankHolderIdNum { get; set; } + public string M_PhoneNum { get; set; } + public ApprovedStatus ApprovedStatus { get; set; } = ApprovedStatus.pending; + public DateTime CreatedAt { get; set; } + } +} diff --git a/Singer_Hexdive/Models/MerchantBusinessDetail.cs b/Singer_Hexdive/Models/MerchantBusinessDetail.cs new file mode 100644 index 0000000..5226983 --- /dev/null +++ b/Singer_Hexdive/Models/MerchantBusinessDetail.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using Singer_Hexdive.Enums; + +namespace Singer_Hexdive.Models +{ + public class MerchantBusinessDetail + { + [Key] + public int Id { get; set; } + public int FK_MechantId { get; set; } + [ForeignKey("FK_MechantId")] + public MerchantPersonalDetail merchantPersonalDetail { get; set; } + public string BusinessName { get; set; } + public string BusinessDoingName { get; set; } + public string BusinessRegNum { get; set; } + public string BusinessIncomeTaxNum { get; set; } + public DateTime BusinessRegDate { get; set; } + public DateTime BusinessIncomeDate { get; set; } + public string BusinessAddress { get; set; } + public string BusinessCity { get; set; } + public int PostalCode { get; set; } + public ApprovedStatus ApprovedStatus { get; set; } = ApprovedStatus.pending; + public DateTime CreatedAt { get; set; } + } +} diff --git a/Singer_Hexdive/Models/MerchantDirectors.cs b/Singer_Hexdive/Models/MerchantDirectors.cs new file mode 100644 index 0000000..5af108c --- /dev/null +++ b/Singer_Hexdive/Models/MerchantDirectors.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Singer_Hexdive.Models +{ + public class MerchantDirectors + { + [Key] + public int DirectorId { get; set; } + public int FK_MBusinessId { get; set; } + [ForeignKey("FK_MBusinessId")] + public MerchantBusinessDetail MerchantBusinessDetail { get; set; } + public string D_FullName { get; set; } + public string D_IdNumber { get; set; } + public DateTime Birthday { get; set; } + public string D_MobileNumber { get; set; } + public string D_Address { get; set; } + public DateTime createdAt { get; set; } = DateTime.Now; + + } +} diff --git a/Singer_Hexdive/Models/MerchantPersonalDetail.cs b/Singer_Hexdive/Models/MerchantPersonalDetail.cs new file mode 100644 index 0000000..7ddc943 --- /dev/null +++ b/Singer_Hexdive/Models/MerchantPersonalDetail.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using Singer_Hexdive.Enums; + +namespace Singer_Hexdive.Models +{ + public class MerchantPersonalDetail + { + [Key] + public int MechantId { get; set; } + public string MerchantName { get; set; } + public int FK_IdType { get; set; } + [ForeignKey("FK_IdType")] + public IdentificationType IdentificationType { get; set; } + public string M_IdNumber { get; set; } + public string M_Email { get; set; } + public string M_Mobile_Primary { get; set; } + public string? M_Mobile_Secondary { get; set; } + public string M_Address { get; set; } + public string M_City { get; set; } + public int M_PostalCode { get; set; } + public DateTime OnboardDate { get; set; } + public DateTime CreatedAt { get; set; } = DateTime.Now; + public bool IsActive { get; set; } = true; + public ApprovedStatus ApprovedStatus { get; set; } = ApprovedStatus.pending; + } +} diff --git a/Singer_Hexdive/Models/MerchantShareHolders.cs b/Singer_Hexdive/Models/MerchantShareHolders.cs new file mode 100644 index 0000000..59091a9 --- /dev/null +++ b/Singer_Hexdive/Models/MerchantShareHolders.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Singer_Hexdive.Models +{ + public class MerchantShareHolders + { + [Key] + public int ShareHolderId { get; set; } + public int FK_MBusinessId { get; set; } + [ForeignKey("FK_MBusinessId")] + public MerchantBusinessDetail MerchantBusinessDetail { get; set; } + public string S_FullName { get; set; } + public string S_IdNumber { get; set; } + public DateTime Bithday { get; set; } + public string S_MobileNumber { get; set; } + public string S_Address { get; set; } + public decimal S_SharePercentage { get; set; } + public DateTime createdAt { get; set; } = DateTime.Now; + } +} diff --git a/Singer_Hexdive/Program.cs b/Singer_Hexdive/Program.cs index df2434c..da46f89 100644 --- a/Singer_Hexdive/Program.cs +++ b/Singer_Hexdive/Program.cs @@ -1,15 +1,31 @@ +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Singer_Hexdive; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +builder.Services.AddDbContext(option => + option.UseMySql( + builder.Configuration.GetConnectionString("DefaultConnection"), + ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("DefaultConnection")) + )); + +// Add services to the container. builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + }); + var app = builder.Build(); -// Configure the HTTP request pipeline. +// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); diff --git a/Singer_Hexdive/Repositories/MerchantRepository.cs b/Singer_Hexdive/Repositories/MerchantRepository.cs new file mode 100644 index 0000000..ef9510e --- /dev/null +++ b/Singer_Hexdive/Repositories/MerchantRepository.cs @@ -0,0 +1,146 @@ +using Microsoft.EntityFrameworkCore; +using Singer_Hexdive.Exceptions; +using Singer_Hexdive.Interfaces.RepositoryInterfaces; +using Singer_Hexdive.Models; + +namespace Singer_Hexdive.Repositories +{ + public class MerchantRepository : IMerchantRepository + { + private readonly ApplicationDbContext _context; + + public MerchantRepository(ApplicationDbContext applicationDbContext) + { + _context = applicationDbContext; + } + + //-------------------------------------------Merchant_Onboarding_Start----------------------------------// + public async Task GetM_PersonalDetailsAsync(int merchantId) + { + var merchantPersonalDetail = await _context.MerchantPersonalDetail + .Where(m => m.MechantId == merchantId) + .FirstOrDefaultAsync(); + if (merchantPersonalDetail == null) + { + throw new NotfoundException($"Merchant presonal deatils with ID {merchantId} not found."); + } + return merchantPersonalDetail; + } + + public async Task PostM_PersonalDetailsAsync(MerchantPersonalDetail merchantPersonalDetail) + { + await _context.MerchantPersonalDetail.AddAsync(merchantPersonalDetail); + await _context.SaveChangesAsync(); + return merchantPersonalDetail; + } + + public async Task GetM_BusinessDetailsAsync(int merchantId) + { + var merchantBusinessDetail = await _context.MerchantBusinessDetail + .Where(m => m.FK_MechantId == merchantId) + .FirstOrDefaultAsync(); + + if (merchantBusinessDetail == null) + { + throw new NotfoundException($"Merchant business deatils with ID {merchantId} not found."); + } + + var merchantShareHolders = await _context.MerchantShareHolders + .Where(m => m.FK_MBusinessId == merchantBusinessDetail.Id) + .ToListAsync(); + var merchantDirectors = await _context.MerchantDirectors + .Where(m => m.FK_MBusinessId == merchantBusinessDetail.Id) + .ToListAsync(); + + var details = new + { + BusinessDetails = merchantBusinessDetail, + ShareHolders = merchantShareHolders, + Directors = merchantDirectors + }; + return details; + } + + public async Task PostM_BusinessDetailsAsync(MerchantBusinessDetail merchantBusinessDetail) + { + await _context.MerchantBusinessDetail.AddAsync(merchantBusinessDetail); + await _context.SaveChangesAsync(); + return merchantBusinessDetail; + } + + public async Task PostM_ShareHolderDetailsAsync(MerchantShareHolders merchantShareHolders) + { + await _context.MerchantShareHolders.AddAsync(merchantShareHolders); + await _context.SaveChangesAsync(); + return merchantShareHolders; + } + + public async Task PostM_DiretorDetailsAsync(MerchantDirectors merchantDirectors) + { + await _context.MerchantDirectors.AddAsync(merchantDirectors); + await _context.SaveChangesAsync(); + return merchantDirectors; + } + + public async Task GetM_BankDetailsAsync(int merchantId) + { + var merchantBankDetail = await _context.MerchantBankDetails + .Where(m => m.FK_MId == merchantId) + .FirstOrDefaultAsync(); + if (merchantBankDetail == null) + { + throw new NotfoundException($"Merchant Bank deatils with ID {merchantId} not found."); + } + return merchantBankDetail; + } + + public async Task PostM_BankDetailsAsync(MerchantBankDetails merchantBankDetails) + { + await _context.MerchantBankDetails.AddAsync(merchantBankDetails); + await _context.SaveChangesAsync(); + return merchantBankDetails; + } + + //-------------------------------------------Merchant_Onboarding_End------------------------------------// + + + + //-------------------------------------------Merchant_Management_Start----------------------------------// + //-------------------------------------------Merchant_Management_End------------------------------------// + + + + //-------------------------------------------Merchant_SupportFunctions_Start----------------------------// + public async Task CheckApprovedStatus(int merchantId, string tableName) + { + object? approveSatatus = null; + + switch (tableName) + { + case "MerchantPersonalDetail": + approveSatatus = await _context.MerchantPersonalDetail + .Where(m => m.MechantId == merchantId) + .Select(m => new { m.ApprovedStatus }) + .FirstOrDefaultAsync(); + break; + case "MerchantBusinessDetail": + approveSatatus = await _context.MerchantBusinessDetail + .Where(m => m.FK_MechantId == merchantId) + .Select(m => new { m.ApprovedStatus }) + .FirstOrDefaultAsync(); + break; + case "MerchantBankDetail": + approveSatatus = await _context.MerchantBankDetails + .Where(m => m.FK_MId == merchantId) + .Select(m => new { m.ApprovedStatus }) + .FirstOrDefaultAsync(); + break; + default: + throw new ArgumentException("Invalid table name provided."); + } + + return (int)approveSatatus; + } + //-------------------------------------------Merchant_SupportFunctions_End------------------------------// + } +} diff --git a/Singer_Hexdive/Services/FunctionHandlers/MerchantManager.cs b/Singer_Hexdive/Services/FunctionHandlers/MerchantManager.cs new file mode 100644 index 0000000..be396e6 --- /dev/null +++ b/Singer_Hexdive/Services/FunctionHandlers/MerchantManager.cs @@ -0,0 +1,71 @@ +using Singer_Hexdive.Exceptions; +using Singer_Hexdive.Interfaces.ServiceInterfaces; +using Singer_Hexdive.Models.IOs; + +namespace Singer_Hexdive.Services.FunctionHandlers +{ + public class MerchantManager + { + private readonly Dictionary>> _functionHandler; + + public MerchantManager(BaseMerchantService merchantService) + { + _functionHandler = new() + { + {"PostM_PersonalDetails", merchantService.PostM_PersonalDetails } + }; + } + + public async Task> Execute(ApiRequest request) + { + + + if (!_functionHandler.TryGetValue(request.FunctionName, out var handler)) + { + return new ApiResponse + { + StatusCode = 404, + Message = $"Function '{request.FunctionName}' not found.", + Data = null + }; + } + try + { + var results = await handler(request.Payload); + return new ApiResponse + { + StatusCode = 200, + Message = "success", + Data = results + }; + } + catch (NotfoundException ex) + { + return new ApiResponse + { + StatusCode = 404, + Message = ex.Message, + Data = null + }; + } + catch (InvalidOperationException ex) + { + return new ApiResponse + { + StatusCode = 400, + Message = ex.Message, + Data = null + }; + } + catch (ArgumentException ex) + { + return new ApiResponse + { + StatusCode = 500, + Message = ex.Message, + Data = null + }; + } + } + } +} diff --git a/Singer_Hexdive/Services/MerchantService.cs b/Singer_Hexdive/Services/MerchantService.cs new file mode 100644 index 0000000..16830f8 --- /dev/null +++ b/Singer_Hexdive/Services/MerchantService.cs @@ -0,0 +1,165 @@ +using System.Text.Json; +using Singer_Hexdive.Enums; +using Singer_Hexdive.Interfaces.RepositoryInterfaces; +using Singer_Hexdive.Interfaces.ServiceInterfaces; +using Singer_Hexdive.Models; +using Singer_Hexdive.Validations; + +namespace Singer_Hexdive.Services +{ + public class MerchantService : BaseMerchantService + { + private readonly IMerchantRepository _merchantRepository; + + public MerchantService(IMerchantRepository merchantRepository) + { + _merchantRepository = merchantRepository; + } + + //-------------------------------------------Merchant_Onboarding_Start----------------------------------// + public override async Task GetM_PersonalDetails(object payload) + { + var data = DeserializePayload(payload); + int merchantId = FieldValidators.EnsureNotLessZero(data["MerchantId"].GetInt32(), "MerchantId"); + var result = await _merchantRepository.GetM_PersonalDetailsAsync(merchantId); + return result; + } + public override async Task PostM_PersonalDetails(object payload) + { + var data = DeserializePayload(payload); + int approveStatus = await _merchantRepository.CheckApprovedStatus(data["MechantId"].GetInt32(), "MerchantPersonalDetail"); + if (approveStatus == 2) + { + var merchantPersonalDetail = new MerchantPersonalDetail + { + MerchantName = FieldValidators.EnsureNotNullOrEmpty(data["MerchantName"].GetString(), "MerchantName"), + FK_IdType = FieldValidators.EnsureNotLessZero(data["FK_IdType"].GetInt32(), "FK_IdType"), + M_IdNumber = FieldValidators.EnsureNotNullOrEmpty(data["M_IdNumber"].GetString(), "M_IdNumber"), + M_Email = FieldValidators.EnsureValidEmail(data["M_Email"].GetString(), "M_Email"), + M_Mobile_Primary = FieldValidators.IsValidPhoneNumber(data["M_Mobile_Primary"].GetString(), "M_Mobile_Primary"), + M_Mobile_Secondary = FieldValidators.IsValidPhoneNumber(data["M_Mobile_Secondary"].GetString(), "M_Mobile_Secondary"), + M_Address = FieldValidators.EnsureNotNullOrEmpty(data["M_Address"].GetString(), "M_Address"), + M_City = FieldValidators.EnsureNotNullOrEmpty(data["M_City"].GetString(), "M_City"), + M_PostalCode = FieldValidators.EnsureNotLessZero(data["M_PostalCode"].GetInt32(), "M_PostalCode"), + OnboardDate = data["OnboardDate"].GetDateTime(), + }; + var result = await _merchantRepository.PostM_PersonalDetailsAsync(merchantPersonalDetail); + return result; + } + else + { + throw new InvalidOperationException("Merchant is not approved for onboarding."); + } + } + + public override async Task GetM_BusinessDetails(object payload) + { + var data = DeserializePayload(payload); + int merchantId = FieldValidators.EnsureNotLessZero(data["MerchantId"].GetInt32(), "MerchantId"); + var result = await _merchantRepository.GetM_PersonalDetailsAsync(merchantId); + return result; + } + + public override async Task PostM_BusinessDetails(object payload) + { + var data = DeserializePayload(payload); + int approveStatus = await _merchantRepository.CheckApprovedStatus(data["MechantId"].GetInt32(), "MerchantBusinessDetail"); + if (approveStatus == 2) + { + var merchantBusinessDetail = new MerchantBusinessDetail + { + FK_MechantId = FieldValidators.EnsureNotLessZero(data["MechantId"].GetInt32(), "MechantId"), + BusinessName = FieldValidators.EnsureNotNullOrEmpty(data["BusinessName"].GetString(), "BusinessName"), + BusinessDoingName = FieldValidators.EnsureNotNullOrEmpty(data["BusinessDoingName"].GetString(), "BusinessDoingName"), + BusinessRegNum = FieldValidators.EnsureNotNullOrEmpty(data["BusinessRegNum"].GetString(), "BusinessRegNum"), + BusinessIncomeTaxNum = FieldValidators.EnsureNotNullOrEmpty(data["BusinessIncomeTaxNum"].GetString(), "BusinessIncomeTaxNum"), + BusinessRegDate = data["BusinessRegDate"].GetDateTime(), + BusinessIncomeDate = data["BusinessIncomeDate"].GetDateTime(), + BusinessAddress = FieldValidators.EnsureNotNullOrEmpty(data["BusinessAddress"].GetString(), "BusinessAddress"), + BusinessCity = FieldValidators.EnsureNotNullOrEmpty(data["BusinessCity"].GetString(), "BusinessCity"), + PostalCode = FieldValidators.EnsureNotLessZero(data["PostalCode"].GetInt32(), "PostalCode"), + }; + var result = (MerchantBusinessDetail)await _merchantRepository.PostM_BusinessDetailsAsync(merchantBusinessDetail); + + + if (data["ShareHolders"].ValueKind == JsonValueKind.Array) + { + foreach (var shareHolder in data["ShareHolders"].EnumerateArray()) + { + var shareHolderDetail = new MerchantShareHolders + { + FK_MBusinessId = result.Id, + S_FullName = FieldValidators.EnsureNotNullOrEmpty(shareHolder.GetProperty("S_FullName").GetString(), "S_FullName"), + S_IdNumber = FieldValidators.EnsureNotNullOrEmpty(shareHolder.GetProperty("S_IdNumber").GetString(), "S_IdNumber"), + S_MobileNumber = FieldValidators.IsValidPhoneNumber(shareHolder.GetProperty("S_MobileNumber").GetString(), "S_MobileNumber"), + S_Address = FieldValidators.EnsureNotNullOrEmpty(shareHolder.GetProperty("S_Address").GetString(), "S_Address"), + S_SharePercentage = shareHolder.GetProperty("S_SharePercentage").GetDecimal(), + Bithday = shareHolder.GetProperty("Bithday").GetDateTime(), + }; + await _merchantRepository.PostM_ShareHolderDetailsAsync(shareHolderDetail); + } + } + + if (data["Directors"].ValueKind == JsonValueKind.Array) + { + foreach (var directors in data["Directors"].EnumerateArray()) + { + var DirectorsDetail = new MerchantDirectors + { + FK_MBusinessId = result.Id, + D_FullName = FieldValidators.EnsureNotNullOrEmpty(directors.GetProperty("D_FullName").GetString(), "D_FullName"), + D_IdNumber = FieldValidators.EnsureNotNullOrEmpty(directors.GetProperty("D_IdNumber").GetString(), "D_IdNumber"), + D_MobileNumber = FieldValidators.IsValidPhoneNumber(directors.GetProperty("D_MobileNumber").GetString(), "D_MobileNumber"), + D_Address = FieldValidators.EnsureNotNullOrEmpty(directors.GetProperty("D_Address").GetString(), "D_Address"), + Birthday = directors.GetProperty("D_Bithday").GetDateTime(), + }; + await _merchantRepository.PostM_DiretorDetailsAsync(DirectorsDetail); + } + } + return result; + } + else + { + throw new InvalidOperationException("Merchant is not approved for onboarding."); + } + } + + public override async Task GetM_BankDetails(object payload) + { + var data = DeserializePayload(payload); + int merchantId = FieldValidators.EnsureNotLessZero(data["MerchantId"].GetInt32(), "MerchantId"); + var result = await _merchantRepository.GetM_BankDetailsAsync(merchantId); + return result; + } + + public override async Task PostM_BankDetails(object payload) + { + var data = DeserializePayload(payload); + int approveStatus = await _merchantRepository.CheckApprovedStatus(data["MechantId"].GetInt32(), "MerchantBankDetail"); + if (approveStatus == 2) + { + var merchantBankDetails = new MerchantBankDetails + { + FK_MId = FieldValidators.EnsureNotLessZero(data["MechantId"].GetInt32(), "MechantId"), + CurrencyCode = FieldValidators.EnsureNotNullOrEmpty(data["CurrencyCode"].GetString(), "CurrencyCode"), + BankAccountTypes = (BankAccountTypes)FieldValidators.EnsureNotLessZero(data["BankAccountTypes"].GetInt32(), "BankAccountTypes"), + M_BankName = FieldValidators.EnsureNotNullOrEmpty(data["M_BankName"].GetString(), "M_BankName"), + M_BankBranchName = FieldValidators.EnsureNotNullOrEmpty(data["M_BankBranchName"].GetString(), "M_BankBranchName"), + M_BankAccNum = FieldValidators.EnsureNotLessZero(data["M_BankAccNum"].GetInt32(), "M_BankAccNum"), + M_BankAccHolderName = FieldValidators.EnsureNotNullOrEmpty(data["M_BankAccHolderName"].GetString(), "M_BankAccHolderName"), + M_BankHolderIdNum = FieldValidators.EnsureNotNullOrEmpty(data["M_BankHolderIdNum"].GetString(), "M_BankHolderIdNum"), + M_PhoneNum = FieldValidators.IsValidPhoneNumber(data["M_PhoneNum"].GetString(), "M_PhoneNum") + }; + var result = await _merchantRepository.PostM_BankDetailsAsync(merchantBankDetails); + return result; + } + else + { + throw new InvalidOperationException("Merchant is not approved for onboarding."); + } + } + + + //-------------------------------------------Merchant_Onboarding_End----------------------------------// + } +} diff --git a/Singer_Hexdive/Singer_Hexdive.csproj b/Singer_Hexdive/Singer_Hexdive.csproj index f887e76..adb0142 100644 --- a/Singer_Hexdive/Singer_Hexdive.csproj +++ b/Singer_Hexdive/Singer_Hexdive.csproj @@ -7,17 +7,12 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - - - diff --git a/Singer_Hexdive/Validations/FieldValidators.cs b/Singer_Hexdive/Validations/FieldValidators.cs new file mode 100644 index 0000000..151abf8 --- /dev/null +++ b/Singer_Hexdive/Validations/FieldValidators.cs @@ -0,0 +1,92 @@ +namespace Singer_Hexdive.Validations +{ + public class FieldValidators + { + public static string EnsureNotNullOrEmpty(string value, string fieldName) + { + if (string.IsNullOrWhiteSpace(value)) + throw new ArgumentException($"{fieldName} cannot be empty.", fieldName); + else + { + return value; + } + } + + public static int EnsureNotLessZero(int value, string fieldName) + { + if (value < 0) + throw new ArgumentException($"{fieldName} cannot be less than zero.", fieldName); + else + { + return value; + } + } + + public static decimal EnsureIsMin(decimal value, int minValue, string fieldName) + { + if (value > minValue) + throw new ArgumentException($"{fieldName} cannot be less than {minValue}.", fieldName); + else + { + return value; + } + } + + public static decimal EnsureIsMax(decimal value, int maxValue, string fieldName) + { + if (value < maxValue) + throw new ArgumentException($"{fieldName} cannot be less than {maxValue}.", fieldName); + else + { + return value; + } + } + + //public static bool IsValidEmail(string email, string fieldName) + //{ + // if (string.IsNullOrWhiteSpace(email)) + // throw new ArgumentException($"{fieldName} cannot be empty.", fieldName); + // try + // { + // var addr = new System.Net.Mail.MailAddress(email); + // return addr.Address == email; + // } + // catch + // { + // throw new ArgumentException($"{fieldName} is not a valid .", fieldName); + // } + //} + + public static string IsValidPhoneNumber(string phoneNumber, string fieldName) + { + if (string.IsNullOrWhiteSpace(phoneNumber)) + throw new ArgumentException($"{fieldName} cannot be empty.", fieldName); + + + if (!phoneNumber.All(char.IsDigit) || phoneNumber.Length != 10) + throw new ArgumentException($"{fieldName} must be exactly 10 digits.", fieldName); + + return phoneNumber; + } + + + public static string EnsureValidEmail(string email, string fieldName) + { + if (string.IsNullOrWhiteSpace(email)) + throw new ArgumentException($"{fieldName} cannot be empty.", fieldName); + + try + { + var addr = new System.Net.Mail.MailAddress(email); + if (addr.Address != email) + throw new ArgumentException($"{fieldName} is not a valid email address.", fieldName); + } + catch + { + throw new ArgumentException($"{fieldName} is not a valid email address.", fieldName); + } + + return email; + } + } +} diff --git a/Singer_Hexdive/appsettings.json b/Singer_Hexdive/appsettings.json index 10f68b8..5a2d545 100644 --- a/Singer_Hexdive/appsettings.json +++ b/Singer_Hexdive/appsettings.json @@ -1,9 +1,12 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ConnectionStrings": { + "DefaultConnection": "server=localhost;port=3306;database=singer;user=root;password=" + }, + "AllowedHosts": "*" }