feat: add manual membership payment processing with wallet and order management
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 7m8s
All checks were successful
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 7m8s
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||||
|
|
||||||
|
public record ProcessManualMembershipPaymentCommand : IRequest<ProcessManualMembershipPaymentResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ پرداختی (ریال)
|
||||||
|
/// </summary>
|
||||||
|
public long Amount { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شماره مرجع تراکنش
|
||||||
|
/// </summary>
|
||||||
|
public string ReferenceNumber { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// توضیحات (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public string? Description { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه ادمین ثبت کننده
|
||||||
|
/// </summary>
|
||||||
|
public long AdminUserId { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||||
|
|
||||||
|
public class ProcessManualMembershipPaymentCommandHandler : IRequestHandler<ProcessManualMembershipPaymentCommand, ProcessManualMembershipPaymentResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
private readonly ILogger<ProcessManualMembershipPaymentCommandHandler> _logger;
|
||||||
|
|
||||||
|
public ProcessManualMembershipPaymentCommandHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
ILogger<ProcessManualMembershipPaymentCommandHandler> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ProcessManualMembershipPaymentResponseDto> Handle(
|
||||||
|
ProcessManualMembershipPaymentCommand request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Processing manual membership payment for UserId: {UserId}, Amount: {Amount}",
|
||||||
|
request.UserId, request.Amount);
|
||||||
|
|
||||||
|
// 1. بررسی وجود کاربر
|
||||||
|
var user = await _context.Users
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("User not found: {UserId}", request.UserId);
|
||||||
|
throw new NotFoundException($"کاربر با شناسه {request.UserId} یافت نشد");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. بررسی وجود ادمین
|
||||||
|
var admin = await _context.Users
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == request.AdminUserId, cancellationToken);
|
||||||
|
|
||||||
|
if (admin == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("Admin user not found: {AdminUserId}", request.AdminUserId);
|
||||||
|
throw new NotFoundException($"ادمین با شناسه {request.AdminUserId} یافت نشد");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. پیدا کردن یا ایجاد کیف پول
|
||||||
|
var wallet = await _context.UserWallets
|
||||||
|
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
|
||||||
|
|
||||||
|
if (wallet == null)
|
||||||
|
{
|
||||||
|
wallet = new UserWallet
|
||||||
|
{
|
||||||
|
UserId = request.UserId,
|
||||||
|
Balance = 0,
|
||||||
|
NetworkBalance = 0,
|
||||||
|
DiscountBalance = 0
|
||||||
|
};
|
||||||
|
await _context.UserWallets.AddAsync(wallet, cancellationToken);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. افزودن به Balance و DiscountBalance
|
||||||
|
var oldBalance = wallet.Balance;
|
||||||
|
var oldDiscountBalance = wallet.DiscountBalance;
|
||||||
|
|
||||||
|
wallet.Balance += request.Amount;
|
||||||
|
wallet.DiscountBalance += request.Amount;
|
||||||
|
|
||||||
|
// 5. ثبت تراکنش
|
||||||
|
var transaction = new Transaction
|
||||||
|
{
|
||||||
|
Amount = request.Amount,
|
||||||
|
Description = $"پرداخت دستی عضویت - {request.Description ?? "بدون توضیحات"} - مرجع: {request.ReferenceNumber}",
|
||||||
|
PaymentStatus = PaymentStatus.Success,
|
||||||
|
PaymentDate = DateTime.Now,
|
||||||
|
RefId = request.ReferenceNumber,
|
||||||
|
Type = TransactionType.DepositExternal1
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Transactions.Add(transaction);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
// 6. ثبت لاگ Balance
|
||||||
|
var balanceLog = new UserWalletChangeLog
|
||||||
|
{
|
||||||
|
WalletId = wallet.Id,
|
||||||
|
CurrentBalance = wallet.Balance,
|
||||||
|
ChangeValue = request.Amount,
|
||||||
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
|
ChangeNerworkValue = 0,
|
||||||
|
CurrentDiscountBalance = oldDiscountBalance,
|
||||||
|
ChangeDiscountValue = 0,
|
||||||
|
IsIncrease = true,
|
||||||
|
RefrenceId = transaction.Id
|
||||||
|
};
|
||||||
|
await _context.UserWalletChangeLogs.AddAsync(balanceLog, cancellationToken);
|
||||||
|
|
||||||
|
// 7. ثبت لاگ DiscountBalance
|
||||||
|
// var discountLog = new UserWalletChangeLog
|
||||||
|
// {
|
||||||
|
// WalletId = wallet.Id,
|
||||||
|
// CurrentBalance = wallet.Balance,
|
||||||
|
// ChangeValue = 0,
|
||||||
|
// CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
|
// ChangeNerworkValue = 0,
|
||||||
|
// CurrentDiscountBalance = wallet.DiscountBalance,
|
||||||
|
// ChangeDiscountValue = request.Amount,
|
||||||
|
// IsIncrease = true,
|
||||||
|
// RefrenceId = transaction.Id
|
||||||
|
// };
|
||||||
|
// await _context.UserWalletChangeLogs.AddAsync(discountLog, cancellationToken);
|
||||||
|
|
||||||
|
// 8. پیدا کردن یا ایجاد آدرس پیشفرض کاربر
|
||||||
|
var userAddress = await _context.UserAddresses
|
||||||
|
.Where(a => a.UserId == request.UserId)
|
||||||
|
.OrderByDescending(a => a.IsDefault)
|
||||||
|
.ThenBy(a => a.Id)
|
||||||
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (userAddress == null)
|
||||||
|
{
|
||||||
|
userAddress = new UserAddress
|
||||||
|
{
|
||||||
|
UserId = request.UserId,
|
||||||
|
Title = "آدرس پیشفرض",
|
||||||
|
Address = "پرداخت دستی عضویت - آدرس موقت",
|
||||||
|
PostalCode = "0000000000",
|
||||||
|
IsDefault = true,
|
||||||
|
CityId = 1
|
||||||
|
};
|
||||||
|
await _context.UserAddresses.AddAsync(userAddress, cancellationToken);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. ثبت سفارش
|
||||||
|
var order = new UserOrder
|
||||||
|
{
|
||||||
|
UserId = request.UserId,
|
||||||
|
Amount = request.Amount,
|
||||||
|
TransactionId = transaction.Id,
|
||||||
|
PaymentStatus = PaymentStatus.Success,
|
||||||
|
PaymentDate = DateTime.Now,
|
||||||
|
PaymentMethod = PaymentMethod.IPG,
|
||||||
|
DeliveryStatus = DeliveryStatus.None,
|
||||||
|
PackageId = 3,
|
||||||
|
UserAddressId = userAddress.Id,
|
||||||
|
DeliveryDescription = $"پرداخت دستی عضویت توسط ادمین {admin.FirstName} {admin.LastName} - مرجع: {request.ReferenceNumber}"
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.UserOrders.Add(order);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Manual membership payment processed successfully. UserId: {UserId}, Amount: {Amount}, TransactionId: {TransactionId}, OrderId: {OrderId}, AdminUserId: {AdminUserId}",
|
||||||
|
request.UserId, request.Amount, transaction.Id, order.Id, request.AdminUserId);
|
||||||
|
|
||||||
|
return new ProcessManualMembershipPaymentResponseDto
|
||||||
|
{
|
||||||
|
TransactionId = transaction.Id,
|
||||||
|
OrderId = order.Id,
|
||||||
|
NewWalletBalance = wallet.Balance,
|
||||||
|
Message = "پرداخت دستی با موفقیت ثبت شد"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is not NotFoundException)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex,
|
||||||
|
"Error processing manual membership payment for UserId: {UserId}, Amount: {Amount}",
|
||||||
|
request.UserId, request.Amount);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||||
|
|
||||||
|
public class ProcessManualMembershipPaymentCommandValidator : AbstractValidator<ProcessManualMembershipPaymentCommand>
|
||||||
|
{
|
||||||
|
public ProcessManualMembershipPaymentCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.UserId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه کاربر معتبر نیست");
|
||||||
|
|
||||||
|
RuleFor(x => x.Amount)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("مبلغ باید بزرگتر از صفر باشد");
|
||||||
|
|
||||||
|
RuleFor(x => x.ReferenceNumber)
|
||||||
|
.NotEmpty()
|
||||||
|
.WithMessage("شماره مرجع الزامی است")
|
||||||
|
.MaximumLength(50)
|
||||||
|
.WithMessage("شماره مرجع نباید بیشتر از 50 کاراکتر باشد");
|
||||||
|
|
||||||
|
RuleFor(x => x.AdminUserId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه ادمین معتبر نیست");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||||
|
|
||||||
|
public class ProcessManualMembershipPaymentResponseDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه تراکنش ثبت شده
|
||||||
|
/// </summary>
|
||||||
|
public long TransactionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه سفارش ثبت شده
|
||||||
|
/// </summary>
|
||||||
|
public long OrderId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// موجودی جدید کیف پول
|
||||||
|
/// </summary>
|
||||||
|
public long NewWalletBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// پیام موفقیت
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; } = "پرداخت دستی با موفقیت ثبت شد";
|
||||||
|
}
|
||||||
@@ -4,4 +4,5 @@ public enum PaymentMethod
|
|||||||
{
|
{
|
||||||
IPG = 0,
|
IPG = 0,
|
||||||
Wallet = 1,
|
Wallet = 1,
|
||||||
|
Deposit = 2,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.148</Version>
|
<Version>0.0.149</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ service ManualPaymentContract
|
|||||||
get: "/GetAllManualPayments"
|
get: "/GetAllManualPayments"
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rpc ProcessManualMembershipPayment(ProcessManualMembershipPaymentRequest) returns (ProcessManualMembershipPaymentResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/ProcessManualMembershipPayment"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enums mirroring CMSMicroservice.Domain.Enums.ManualPaymentType
|
// Enums mirroring CMSMicroservice.Domain.Enums.ManualPaymentType
|
||||||
@@ -128,3 +135,20 @@ message ManualPaymentModel
|
|||||||
google.protobuf.Timestamp created = 19;
|
google.protobuf.Timestamp created = 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ProcessManualMembershipPaymentRequest
|
||||||
|
{
|
||||||
|
int64 user_id = 1;
|
||||||
|
int64 amount = 2;
|
||||||
|
string reference_number = 3;
|
||||||
|
google.protobuf.StringValue description = 4;
|
||||||
|
int64 admin_user_id = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ProcessManualMembershipPaymentResponse
|
||||||
|
{
|
||||||
|
int64 transaction_id = 1;
|
||||||
|
int64 order_id = 2;
|
||||||
|
int64 new_wallet_balance = 3;
|
||||||
|
string message = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using CMSMicroservice.WebApi.Common.Services;
|
|||||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
using CMSMicroservice.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
||||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.ApproveManualPayment;
|
using CMSMicroservice.Application.ManualPaymentCQ.Commands.ApproveManualPayment;
|
||||||
using CMSMicroservice.Application.ManualPaymentCQ.Commands.RejectManualPayment;
|
using CMSMicroservice.Application.ManualPaymentCQ.Commands.RejectManualPayment;
|
||||||
|
using CMSMicroservice.Application.ManualPaymentCQ.Commands.ProcessManualMembershipPayment;
|
||||||
using CMSMicroservice.Application.ManualPaymentCQ.Queries.GetAllManualPayments;
|
using CMSMicroservice.Application.ManualPaymentCQ.Queries.GetAllManualPayments;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
@@ -56,4 +57,28 @@ public class ManualPaymentService : ManualPaymentContract.ManualPaymentContractB
|
|||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<GetAllManualPaymentsRequest, GetAllManualPaymentsQuery, GetAllManualPaymentsResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetAllManualPaymentsRequest, GetAllManualPaymentsQuery, GetAllManualPaymentsResponse>(request, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<ProcessManualMembershipPaymentResponse> ProcessManualMembershipPayment(
|
||||||
|
ProcessManualMembershipPaymentRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
var command = new ProcessManualMembershipPaymentCommand
|
||||||
|
{
|
||||||
|
UserId = request.UserId,
|
||||||
|
Amount = request.Amount,
|
||||||
|
ReferenceNumber = request.ReferenceNumber,
|
||||||
|
Description = request.Description,
|
||||||
|
AdminUserId = request.AdminUserId
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = await _sender.Send(command, context.CancellationToken);
|
||||||
|
|
||||||
|
return new ProcessManualMembershipPaymentResponse
|
||||||
|
{
|
||||||
|
TransactionId = result.TransactionId,
|
||||||
|
OrderId = result.OrderId,
|
||||||
|
NewWalletBalance = result.NewWalletBalance,
|
||||||
|
Message = result.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user