feat: add base package payment initiation and verification
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 3m39s
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 3m39s
This commit is contained in:
+12
-2
@@ -57,11 +57,21 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
|
|||||||
if (request.ParentReferralCode == null)
|
if (request.ParentReferralCode == null)
|
||||||
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." };
|
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." };
|
||||||
|
|
||||||
var parent = await _context.Users.FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode,
|
var parent = await _context.Users
|
||||||
cancellationToken: cancellationToken);
|
.Include(u => u.ClubMembership)
|
||||||
|
.FirstOrDefaultAsync(u => u.ReferralCode == request.ParentReferralCode,
|
||||||
|
cancellationToken: cancellationToken);
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
return new VerifyOtpTokenResponseDto() { Success = false, Message = "معرف وجود ندارد." };
|
return new VerifyOtpTokenResponseDto() { Success = false, Message = "معرف وجود ندارد." };
|
||||||
|
|
||||||
|
// بررسی فعالیت باشگاه معرف
|
||||||
|
if (parent.ClubMembership == null || !parent.ClubMembership.IsActive)
|
||||||
|
return new VerifyOtpTokenResponseDto()
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "لینک دعوت معرف فعال نیست. لطفاً از کد دعوت معتبر دیگری استفاده کنید."
|
||||||
|
};
|
||||||
|
|
||||||
if (await _context.Users.CountAsync(x => x.NetworkParentId == parent.Id, cancellationToken: cancellationToken) > 1)
|
if (await _context.Users.CountAsync(x => x.NetworkParentId == parent.Id, cancellationToken: cancellationToken) > 1)
|
||||||
return new VerifyOtpTokenResponseDto() { Success = false, Message = "ظرفیت معرف تکمیل است!!" };
|
return new VerifyOtpTokenResponseDto() { Success = false, Message = "ظرفیت معرف تکمیل است!!" };
|
||||||
|
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ثبت اولیه تراکنش و سفارش پکیج پایه (56 میلیون تومان)
|
||||||
|
/// این Command توسط BFF فراخوانی میشود قبل از ارسال کاربر به درگاه
|
||||||
|
/// </summary>
|
||||||
|
public record InitiateBasePackagePaymentCommand : IRequest<InitiateBasePackagePaymentResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر
|
||||||
|
/// </summary>
|
||||||
|
public long UserId { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InitiateBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// موفقیت عملیات
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// پیام
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه سفارش
|
||||||
|
/// </summary>
|
||||||
|
public long OrderId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه تراکنش CMS
|
||||||
|
/// </summary>
|
||||||
|
public long TransactionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ پکیج
|
||||||
|
/// </summary>
|
||||||
|
public long Amount { get; set; }
|
||||||
|
}
|
||||||
+173
@@ -0,0 +1,173 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using ValidationException = FluentValidation.ValidationException;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler برای ثبت اولیه تراکنش و سفارش پکیج پایه
|
||||||
|
/// این Handler فقط Transaction و Order را با وضعیت Pending ایجاد میکند
|
||||||
|
/// BFF سپس کاربر را به درگاه PYMS هدایت میکند
|
||||||
|
/// </summary>
|
||||||
|
public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<InitiateBasePackagePaymentCommand, InitiateBasePackagePaymentResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ پکیج پایه (56 میلیون تومان)
|
||||||
|
/// </summary>
|
||||||
|
private const long BasePackageAmount = 56_000_000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج پایه در دیتابیس
|
||||||
|
/// </summary>
|
||||||
|
private const long BasePackageId = 4;
|
||||||
|
|
||||||
|
public InitiateBasePackagePaymentCommandHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
ILogger<InitiateBasePackagePaymentCommandHandler> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<InitiateBasePackagePaymentResponseDto> Handle(
|
||||||
|
InitiateBasePackagePaymentCommand request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Initiating base package payment for UserId: {UserId}",
|
||||||
|
request.UserId);
|
||||||
|
|
||||||
|
// 1. پیدا کردن کاربر
|
||||||
|
var user = await _context.Users
|
||||||
|
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("User not found. UserId: {UserId}", request.UserId);
|
||||||
|
throw new NotFoundException(nameof(User), request.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. بررسی عدم خرید قبلی پکیج پایه
|
||||||
|
if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"User {UserId} has already purchased base package via {Method}",
|
||||||
|
request.UserId,
|
||||||
|
user.PackagePurchaseMethod);
|
||||||
|
|
||||||
|
return new InitiateBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "شما قبلاً پکیج پایه را خریداری کردهاید."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. بررسی عدم وجود سفارش Pending
|
||||||
|
var pendingOrder = await _context.UserOrders
|
||||||
|
.FirstOrDefaultAsync(o =>
|
||||||
|
o.UserId == request.UserId &&
|
||||||
|
o.PaymentStatus == PaymentStatus.Pending &&
|
||||||
|
o.PackageId == BasePackageId,
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
if (pendingOrder != null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"User {UserId} has pending order {OrderId}",
|
||||||
|
request.UserId,
|
||||||
|
pendingOrder.Id);
|
||||||
|
|
||||||
|
// برگرداندن سفارش موجود برای ادامه پرداخت
|
||||||
|
var existingTransaction = await _context.Transactions
|
||||||
|
.FirstOrDefaultAsync(t => t.Id == pendingOrder.TransactionId, cancellationToken);
|
||||||
|
|
||||||
|
return new InitiateBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "سفارش قبلی در انتظار پرداخت یافت شد.",
|
||||||
|
OrderId = pendingOrder.Id,
|
||||||
|
TransactionId = existingTransaction?.Id ?? 0,
|
||||||
|
Amount = BasePackageAmount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. پیدا کردن آدرس پیشفرض کاربر
|
||||||
|
var defaultAddress = await _context.UserAddresses
|
||||||
|
.Where(a => a.UserId == request.UserId)
|
||||||
|
.OrderByDescending(a => a.Created)
|
||||||
|
.FirstOrDefaultAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (defaultAddress == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No address found for user {UserId}", request.UserId);
|
||||||
|
throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. ایجاد Transaction با وضعیت Pending
|
||||||
|
var transaction = new Transaction
|
||||||
|
{
|
||||||
|
Amount = BasePackageAmount,
|
||||||
|
Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}",
|
||||||
|
PaymentStatus = PaymentStatus.Pending,
|
||||||
|
Type = TransactionType.DepositIpg
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Transactions.Add(transaction);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Created Transaction {TransactionId} with Pending status for UserId {UserId}",
|
||||||
|
transaction.Id,
|
||||||
|
request.UserId);
|
||||||
|
|
||||||
|
// 6. ایجاد UserOrder با وضعیت Pending
|
||||||
|
var order = new UserOrder
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
PackageId = BasePackageId,
|
||||||
|
Amount = BasePackageAmount,
|
||||||
|
PaymentStatus = PaymentStatus.Pending,
|
||||||
|
DeliveryStatus = DeliveryStatus.None,
|
||||||
|
UserAddressId = defaultAddress.Id,
|
||||||
|
PaymentMethod = PaymentMethod.IPG,
|
||||||
|
TransactionId = transaction.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.UserOrders.Add(order);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Created UserOrder {OrderId} for UserId {UserId}, TransactionId: {TransactionId}",
|
||||||
|
order.Id,
|
||||||
|
request.UserId,
|
||||||
|
transaction.Id);
|
||||||
|
|
||||||
|
return new InitiateBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "تراکنش و سفارش با موفقیت ثبت شد.",
|
||||||
|
OrderId = order.Id,
|
||||||
|
TransactionId = transaction.Id,
|
||||||
|
Amount = BasePackageAmount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
ex,
|
||||||
|
"Error in InitiateBasePackagePaymentCommand for UserId: {UserId}",
|
||||||
|
request.UserId);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
||||||
|
|
||||||
|
public class InitiateBasePackagePaymentCommandValidator : AbstractValidator<InitiateBasePackagePaymentCommand>
|
||||||
|
{
|
||||||
|
public InitiateBasePackagePaymentCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.UserId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه کاربر معتبر نیست.");
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تکمیل یا رد پرداخت پکیج پایه (فراخوانی توسط BFF بعد از Callback)
|
||||||
|
/// </summary>
|
||||||
|
public record VerifyBasePackagePaymentCommand : IRequest<VerifyBasePackagePaymentResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه سفارش CMS
|
||||||
|
/// </summary>
|
||||||
|
public long OrderId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه تراکنش CMS
|
||||||
|
/// </summary>
|
||||||
|
public long TransactionId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// آیا پرداخت موفق بوده؟ (نتیجه Verify از BFF)
|
||||||
|
/// </summary>
|
||||||
|
public bool PaymentSuccess { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// کد پیگیری بانکی (در صورت موفقیت)
|
||||||
|
/// </summary>
|
||||||
|
public string? RefId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// پیام از درگاه
|
||||||
|
/// </summary>
|
||||||
|
public string? Message { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class VerifyBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// موفقیت عملیات
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// پیام
|
||||||
|
/// </summary>
|
||||||
|
public string Message { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه سفارش
|
||||||
|
/// </summary>
|
||||||
|
public long OrderId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه تراکنش
|
||||||
|
/// </summary>
|
||||||
|
public long TransactionId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// کد پیگیری بانکی
|
||||||
|
/// </summary>
|
||||||
|
public string? ReferenceCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// موجودی کیف پول بعد از شارژ
|
||||||
|
/// </summary>
|
||||||
|
public long WalletBalance { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// موجودی تخفیف بعد از شارژ
|
||||||
|
/// </summary>
|
||||||
|
public long DiscountBalance { get; set; }
|
||||||
|
}
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
|
using CMSMicroservice.Domain.Enums;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handler برای تکمیل یا رد پرداخت پکیج پایه
|
||||||
|
/// فراخوانی توسط BFF بعد از اینکه نتیجه Verify از PYMS مشخص شد
|
||||||
|
///
|
||||||
|
/// اگر PaymentSuccess = true:
|
||||||
|
/// - شارژ کیف پول (Balance + DiscountBalance)
|
||||||
|
/// - ثبت لاگ تغییر کیف پول
|
||||||
|
/// - بهروزرسانی سفارش و کاربر
|
||||||
|
///
|
||||||
|
/// اگر PaymentSuccess = false:
|
||||||
|
/// - فقط آپدیت وضعیت تراکنش و سفارش به Reject
|
||||||
|
/// </summary>
|
||||||
|
public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBasePackagePaymentCommand, VerifyBasePackagePaymentResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
private readonly ILogger<VerifyBasePackagePaymentCommandHandler> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// مبلغ پکیج پایه (56 میلیون تومان)
|
||||||
|
/// </summary>
|
||||||
|
private const long BasePackageAmount = 56_000_000;
|
||||||
|
|
||||||
|
public VerifyBasePackagePaymentCommandHandler(
|
||||||
|
IApplicationDbContext context,
|
||||||
|
ILogger<VerifyBasePackagePaymentCommandHandler> logger)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<VerifyBasePackagePaymentResponseDto> Handle(
|
||||||
|
VerifyBasePackagePaymentCommand request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Processing base package payment result. OrderId: {OrderId}, TransactionId: {TransactionId}, PaymentSuccess: {PaymentSuccess}",
|
||||||
|
request.OrderId,
|
||||||
|
request.TransactionId,
|
||||||
|
request.PaymentSuccess);
|
||||||
|
|
||||||
|
// 1. پیدا کردن سفارش با کاربر
|
||||||
|
var order = await _context.UserOrders
|
||||||
|
.Include(o => o.User)
|
||||||
|
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||||
|
|
||||||
|
if (order == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Order not found. OrderId: {OrderId}", request.OrderId);
|
||||||
|
throw new NotFoundException(nameof(UserOrder), request.OrderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. پیدا کردن تراکنش
|
||||||
|
var transaction = await _context.Transactions
|
||||||
|
.FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken);
|
||||||
|
|
||||||
|
if (transaction == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Transaction not found. TransactionId: {TransactionId}", request.TransactionId);
|
||||||
|
throw new NotFoundException(nameof(Transaction), request.TransactionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. بررسی Idempotency - اگر قبلاً تایید شده باشد
|
||||||
|
if (order.PaymentStatus == PaymentStatus.Success)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Order {OrderId} already verified successfully",
|
||||||
|
request.OrderId);
|
||||||
|
|
||||||
|
var existingWallet = await _context.UserWallets
|
||||||
|
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||||
|
|
||||||
|
return new VerifyBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "پرداخت قبلاً با موفقیت تایید شده است.",
|
||||||
|
OrderId = order.Id,
|
||||||
|
TransactionId = transaction.Id,
|
||||||
|
ReferenceCode = transaction.RefId,
|
||||||
|
WalletBalance = existingWallet?.Balance ?? 0,
|
||||||
|
DiscountBalance = existingWallet?.DiscountBalance ?? 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. اگر پرداخت ناموفق بود - فقط وضعیت را Reject میکنیم
|
||||||
|
if (!request.PaymentSuccess)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(
|
||||||
|
"Payment failed. OrderId: {OrderId}, Message: {Message}",
|
||||||
|
request.OrderId,
|
||||||
|
request.Message);
|
||||||
|
|
||||||
|
transaction.PaymentStatus = PaymentStatus.Reject;
|
||||||
|
order.PaymentStatus = PaymentStatus.Reject;
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
return new VerifyBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = request.Message ?? "پرداخت ناموفق بود.",
|
||||||
|
OrderId = order.Id,
|
||||||
|
TransactionId = transaction.Id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. پرداخت موفق - پیدا کردن یا ایجاد کیف پول
|
||||||
|
var userWallet = await _context.UserWallets
|
||||||
|
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||||
|
|
||||||
|
if (userWallet == null)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Creating new wallet for UserId: {UserId}", order.UserId);
|
||||||
|
userWallet = new UserWallet
|
||||||
|
{
|
||||||
|
UserId = order.UserId,
|
||||||
|
Balance = 0,
|
||||||
|
DiscountBalance = 0,
|
||||||
|
NetworkBalance = 0
|
||||||
|
};
|
||||||
|
_context.UserWallets.Add(userWallet);
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. شارژ کیف پول (هم Balance و هم DiscountBalance)
|
||||||
|
var oldBalance = userWallet.Balance;
|
||||||
|
var oldDiscountBalance = userWallet.DiscountBalance;
|
||||||
|
|
||||||
|
userWallet.Balance += BasePackageAmount;
|
||||||
|
userWallet.DiscountBalance += BasePackageAmount;
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
|
||||||
|
order.UserId,
|
||||||
|
oldBalance,
|
||||||
|
userWallet.Balance,
|
||||||
|
oldDiscountBalance,
|
||||||
|
userWallet.DiscountBalance);
|
||||||
|
|
||||||
|
// 7. بهروزرسانی Transaction
|
||||||
|
transaction.PaymentStatus = PaymentStatus.Success;
|
||||||
|
transaction.PaymentDate = DateTime.Now;
|
||||||
|
transaction.RefId = request.RefId;
|
||||||
|
|
||||||
|
// 8. ثبت لاگ تغییر کیف پول
|
||||||
|
var changeLog = new UserWalletChangeLog
|
||||||
|
{
|
||||||
|
WalletId = userWallet.Id,
|
||||||
|
CurrentBalance = userWallet.Balance,
|
||||||
|
ChangeValue = BasePackageAmount,
|
||||||
|
CurrentNetworkBalance = userWallet.NetworkBalance,
|
||||||
|
ChangeNerworkValue = 0,
|
||||||
|
CurrentDiscountBalance = userWallet.DiscountBalance,
|
||||||
|
ChangeDiscountValue = BasePackageAmount,
|
||||||
|
IsIncrease = true,
|
||||||
|
RefrenceId = transaction.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
await _context.UserWalletChangeLogs.AddAsync(changeLog, cancellationToken);
|
||||||
|
|
||||||
|
// 9. بهروزرسانی Order
|
||||||
|
order.TransactionId = transaction.Id;
|
||||||
|
order.PaymentStatus = PaymentStatus.Success;
|
||||||
|
order.PaymentDate = DateTime.Now;
|
||||||
|
order.PaymentMethod = PaymentMethod.IPG;
|
||||||
|
|
||||||
|
// 10. بهروزرسانی User
|
||||||
|
order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Base package payment completed successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}",
|
||||||
|
order.Id,
|
||||||
|
order.UserId,
|
||||||
|
transaction.Id,
|
||||||
|
request.RefId);
|
||||||
|
|
||||||
|
return new VerifyBasePackagePaymentResponseDto
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.",
|
||||||
|
OrderId = order.Id,
|
||||||
|
TransactionId = transaction.Id,
|
||||||
|
ReferenceCode = request.RefId,
|
||||||
|
WalletBalance = userWallet.Balance,
|
||||||
|
DiscountBalance = userWallet.DiscountBalance
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
ex,
|
||||||
|
"Error in VerifyBasePackagePaymentCommand. OrderId: {OrderId}",
|
||||||
|
request.OrderId);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
||||||
|
|
||||||
|
public class VerifyBasePackagePaymentCommandValidator : AbstractValidator<VerifyBasePackagePaymentCommand>
|
||||||
|
{
|
||||||
|
public VerifyBasePackagePaymentCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.OrderId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه سفارش معتبر نیست.");
|
||||||
|
|
||||||
|
RuleFor(x => x.TransactionId)
|
||||||
|
.GreaterThan(0)
|
||||||
|
.WithMessage("شناسه تراکنش معتبر نیست.");
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -22,7 +22,7 @@ public class AdminGetJwtTokenQueryHandler : IRequestHandler<AdminGetJwtTokenQuer
|
|||||||
.ThenInclude(u => u.Contract)
|
.ThenInclude(u => u.Contract)
|
||||||
.Include(u => u.UserRoles)
|
.Include(u => u.UserRoles)
|
||||||
.ThenInclude(ur => ur.Role)
|
.ThenInclude(ur => ur.Role)
|
||||||
|
.Include(u => u.ClubMembership)
|
||||||
.FirstOrDefaultAsync(f => f.Mobile == request.Username, cancellationToken);
|
.FirstOrDefaultAsync(f => f.Mobile == request.Username, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new Exception("Invalid username or password.");
|
throw new Exception("Invalid username or password.");
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class GetJwtTokenQueryHandler : IRequestHandler<GetJwtTokenQuery, GetJwtT
|
|||||||
.ThenInclude(u => u.Contract)
|
.ThenInclude(u => u.Contract)
|
||||||
.Include(u => u.UserRoles)
|
.Include(u => u.UserRoles)
|
||||||
.ThenInclude(ur => ur.Role)
|
.ThenInclude(ur => ur.Role)
|
||||||
|
.Include(u => u.ClubMembership)
|
||||||
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(User), request.Id);
|
.FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(User), request.Id);
|
||||||
return new GetJwtTokenResponseDto()
|
return new GetJwtTokenResponseDto()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,9 +101,13 @@ public class DayaLoanApiService : IDayaLoanApiService
|
|||||||
|
|
||||||
// تنظیم merchant-permission-key از Configuration
|
// تنظیم merchant-permission-key از Configuration
|
||||||
var permissionKey = _configuration["DayaApi:MerchantPermissionKey"];
|
var permissionKey = _configuration["DayaApi:MerchantPermissionKey"];
|
||||||
if (!string.IsNullOrEmpty(permissionKey))
|
if (!string.IsNullOrEmpty(permissionKey) && _httpClient.DefaultRequestHeaders.TryGetValues("merchant-permission-key", out var values))
|
||||||
{
|
{
|
||||||
|
if (values== null || !values.Any())
|
||||||
|
{
|
||||||
|
|
||||||
_httpClient.DefaultRequestHeaders.Add("merchant-permission-key", permissionKey);
|
_httpClient.DefaultRequestHeaders.Add("merchant-permission-key", permissionKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -125,7 +129,6 @@ public class DayaLoanApiService : IDayaLoanApiService
|
|||||||
NationalCodes = nationalCodes
|
NationalCodes = nationalCodes
|
||||||
};
|
};
|
||||||
|
|
||||||
// فراخوانی API
|
|
||||||
var response = await _httpClient.PostAsJsonAsync(
|
var response = await _httpClient.PostAsJsonAsync(
|
||||||
"/api/merchant/contracts",
|
"/api/merchant/contracts",
|
||||||
requestBody,
|
requestBody,
|
||||||
|
|||||||
@@ -42,6 +42,22 @@ public class GenerateJwtTokenService : IGenerateJwtToken
|
|||||||
else
|
else
|
||||||
claims.Add(new Claim("IsSignMainContract", false.ToString()));
|
claims.Add(new Claim("IsSignMainContract", false.ToString()));
|
||||||
|
|
||||||
|
// ClubMembership Claims
|
||||||
|
if (user.ClubMembership != null)
|
||||||
|
{
|
||||||
|
claims.Add(new Claim("IsClubMemberActive", user.ClubMembership.IsActive.ToString()));
|
||||||
|
if (user.ClubMembership.LastModified.HasValue && user.ClubMembership.LastModified.Value > lastModified)
|
||||||
|
lastModified = user.ClubMembership.LastModified.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
claims.Add(new Claim("IsClubMemberActive", "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package Purchase Status
|
||||||
|
claims.Add(new Claim("HasPurchasedPackage",
|
||||||
|
(user.PackagePurchaseMethod != PackagePurchaseMethod.None).ToString()));
|
||||||
|
|
||||||
if (user.UserRoles != null && user.UserRoles.Any())
|
if (user.UserRoles != null && user.UserRoles.Any())
|
||||||
{
|
{
|
||||||
foreach (var userRole in user.UserRoles)
|
foreach (var userRole in user.UserRoles)
|
||||||
|
|||||||
@@ -62,6 +62,20 @@ service PackageContract
|
|||||||
get: "/GetUserPackageStatus"
|
get: "/GetUserPackageStatus"
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Base Package Payment (56M)
|
||||||
|
rpc InitiateBasePackagePayment(InitiateBasePackagePaymentRequest) returns (InitiateBasePackagePaymentResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/InitiateBasePackagePayment"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
rpc VerifyBasePackagePayment(VerifyBasePackagePaymentRequest) returns (VerifyBasePackagePaymentResponse){
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/VerifyBasePackagePayment"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
message CreateNewPackageRequest
|
message CreateNewPackageRequest
|
||||||
{
|
{
|
||||||
@@ -177,3 +191,38 @@ message GetUserPackageStatusResponse
|
|||||||
google.protobuf.StringValue last_order_number = 8;
|
google.protobuf.StringValue last_order_number = 8;
|
||||||
google.protobuf.Timestamp last_purchase_date = 9;
|
google.protobuf.Timestamp last_purchase_date = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base Package Payment Messages
|
||||||
|
message InitiateBasePackagePaymentRequest
|
||||||
|
{
|
||||||
|
int64 user_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InitiateBasePackagePaymentResponse
|
||||||
|
{
|
||||||
|
bool success = 1;
|
||||||
|
string message = 2;
|
||||||
|
int64 order_id = 3;
|
||||||
|
int64 transaction_id = 4;
|
||||||
|
int64 amount = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VerifyBasePackagePaymentRequest
|
||||||
|
{
|
||||||
|
int64 order_id = 1;
|
||||||
|
int64 transaction_id = 2;
|
||||||
|
bool payment_success = 3;
|
||||||
|
google.protobuf.StringValue ref_id = 4;
|
||||||
|
google.protobuf.StringValue message = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VerifyBasePackagePaymentResponse
|
||||||
|
{
|
||||||
|
bool success = 1;
|
||||||
|
string message = 2;
|
||||||
|
int64 order_id = 3;
|
||||||
|
int64 transaction_id = 4;
|
||||||
|
google.protobuf.StringValue reference_code = 5;
|
||||||
|
int64 wallet_balance = 6;
|
||||||
|
int64 discount_balance = 7;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
||||||
|
using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
||||||
|
using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
||||||
using CMSMicroservice.Protobuf.Protos.Package;
|
using CMSMicroservice.Protobuf.Protos.Package;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
@@ -53,6 +55,34 @@ public class PackageProfile : IRegister
|
|||||||
.Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue
|
.Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue
|
||||||
? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime())
|
? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime())
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
|
// InitiateBasePackagePayment (56M پکیج پایه)
|
||||||
|
config.NewConfig<InitiateBasePackagePaymentRequest, InitiateBasePackagePaymentCommand>()
|
||||||
|
.Map(dest => dest.UserId, src => src.UserId);
|
||||||
|
|
||||||
|
config.NewConfig<InitiateBasePackagePaymentResponseDto, InitiateBasePackagePaymentResponse>()
|
||||||
|
.Map(dest => dest.Success, src => src.Success)
|
||||||
|
.Map(dest => dest.Message, src => src.Message)
|
||||||
|
.Map(dest => dest.OrderId, src => src.OrderId)
|
||||||
|
.Map(dest => dest.TransactionId, src => src.TransactionId)
|
||||||
|
.Map(dest => dest.Amount, src => src.Amount);
|
||||||
|
|
||||||
|
// VerifyBasePackagePayment (تأیید پرداخت پکیج پایه)
|
||||||
|
config.NewConfig<VerifyBasePackagePaymentRequest, VerifyBasePackagePaymentCommand>()
|
||||||
|
.Map(dest => dest.OrderId, src => src.OrderId)
|
||||||
|
.Map(dest => dest.TransactionId, src => src.TransactionId)
|
||||||
|
.Map(dest => dest.PaymentSuccess, src => src.PaymentSuccess)
|
||||||
|
.Map(dest => dest.RefId, src => src.RefId)
|
||||||
|
.Map(dest => dest.Message, src => src.Message);
|
||||||
|
|
||||||
|
config.NewConfig<VerifyBasePackagePaymentResponseDto, VerifyBasePackagePaymentResponse>()
|
||||||
|
.Map(dest => dest.Success, src => src.Success)
|
||||||
|
.Map(dest => dest.Message, src => src.Message)
|
||||||
|
.Map(dest => dest.OrderId, src => src.OrderId)
|
||||||
|
.Map(dest => dest.TransactionId, src => src.TransactionId)
|
||||||
|
.Map(dest => dest.ReferenceCode, src => src.ReferenceCode)
|
||||||
|
.Map(dest => dest.WalletBalance, src => src.WalletBalance)
|
||||||
|
.Map(dest => dest.DiscountBalance, src => src.DiscountBalance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage;
|
|||||||
using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage;
|
using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
||||||
|
using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
||||||
|
using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetPackage;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetPackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
||||||
@@ -52,4 +54,15 @@ public class PackageService : PackageContract.PackageContractBase
|
|||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<GetUserPackageStatusRequest, GetUserPackageStatusQuery, GetUserPackageStatusResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetUserPackageStatusRequest, GetUserPackageStatusQuery, GetUserPackageStatusResponse>(request, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Base Package Payment (56M)
|
||||||
|
public override async Task<InitiateBasePackagePaymentResponse> InitiateBasePackagePayment(InitiateBasePackagePaymentRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<InitiateBasePackagePaymentRequest, InitiateBasePackagePaymentCommand, InitiateBasePackagePaymentResponse>(request, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<VerifyBasePackagePaymentResponse> VerifyBasePackagePayment(VerifyBasePackagePaymentRequest request, ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<VerifyBasePackagePaymentRequest, VerifyBasePackagePaymentCommand, VerifyBasePackagePaymentResponse>(request, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user