feat: Implement withdrawal reports query and service integration
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
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.PurchaseGoldenPackage;
|
||||
|
||||
@@ -22,67 +27,135 @@ public class PurchaseGoldenPackageCommandHandler : IRequestHandler<PurchaseGolde
|
||||
|
||||
public async Task<PurchaseGoldenPackageResponseDto> Handle(PurchaseGoldenPackageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: پیادهسازی خرید پکیج طلایی
|
||||
//
|
||||
// 1. پیدا کردن کاربر و بررسی شرایط:
|
||||
// - var user = await _context.Users
|
||||
// .Include(u => u.UserOrders)
|
||||
// .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken)
|
||||
// - if (user == null) throw new NotFoundException("کاربر یافت نشد")
|
||||
// - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
|
||||
// throw new InvalidOperationException("شما قبلاً پکیج طلایی خریداری کردهاید")
|
||||
//
|
||||
// 2. پیدا کردن پکیج:
|
||||
// - var package = await _context.Packages
|
||||
// .FirstOrDefaultAsync(p => p.Id == request.PackageId && p.IsAvailable, cancellationToken)
|
||||
// - if (package == null) throw new NotFoundException("پکیج یافت نشد")
|
||||
// - if (package.Name != "طلایی")
|
||||
// throw new InvalidOperationException("فقط پکیج طلایی قابل خرید است")
|
||||
//
|
||||
// 3. ایجاد سفارش:
|
||||
// - var order = new UserOrder {
|
||||
// UserId = user.Id,
|
||||
// OrderNumber = GenerateOrderNumber(), // مثلاً "ORD" + DateTime.UtcNow.Ticks
|
||||
// TotalPrice = package.Price, // 56,000,000
|
||||
// Status = OrderStatus.Pending,
|
||||
// PaymentMethod = PaymentMethod.IPG,
|
||||
// OrderType = OrderType.PackagePurchase, // enum جدید
|
||||
// PackageId = package.Id
|
||||
// }
|
||||
// - _context.UserOrders.Add(order)
|
||||
// - await _context.SaveChangesAsync(cancellationToken)
|
||||
//
|
||||
// 4. شروع پرداخت با درگاه:
|
||||
// - var paymentResult = await _paymentGateway.InitiatePaymentAsync(
|
||||
// orderId: order.Id,
|
||||
// amount: order.TotalPrice,
|
||||
// description: $"خرید پکیج {package.Name}",
|
||||
// returnUrl: request.ReturnUrl,
|
||||
// cancellationToken: cancellationToken
|
||||
// )
|
||||
// - if (!paymentResult.Success)
|
||||
// throw new InvalidOperationException($"خطا در اتصال به درگاه: {paymentResult.ErrorMessage}")
|
||||
//
|
||||
// 5. ذخیره اطلاعات پرداخت:
|
||||
// - order.TrackingCode = paymentResult.TrackingCode
|
||||
// - order.PaymentGatewayToken = paymentResult.Token
|
||||
// - await _context.SaveChangesAsync(cancellationToken)
|
||||
//
|
||||
// 6. برگشت نتیجه:
|
||||
// - _logger.LogInformation("Golden package purchase initiated for user {UserId}, order {OrderId}", user.Id, order.Id)
|
||||
// - return new PurchaseGoldenPackageResponseDto {
|
||||
// Success = true,
|
||||
// Message = "لطفاً به درگاه پرداخت منتقل شوید",
|
||||
// OrderId = order.Id,
|
||||
// PaymentGatewayUrl = paymentResult.PaymentUrl,
|
||||
// TrackingCode = paymentResult.TrackingCode
|
||||
// }
|
||||
//
|
||||
// نکته 1: OrderType.PackagePurchase را به OrderType enum اضافه کنید
|
||||
// نکته 2: PackageId nullable است در UserOrder - مطمئن شوید میتوانید set کنید
|
||||
// نکته 3: کاربر به صفحه paymentResult.PaymentUrl redirect میشود
|
||||
// نکته 4: پس از پرداخت موفق، VerifyGoldenPackagePurchase فراخوانی میشود
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Starting golden package purchase for UserId: {UserId}, PackageId: {PackageId}",
|
||||
request.UserId,
|
||||
request.PackageId);
|
||||
|
||||
throw new NotImplementedException("PurchaseGoldenPackage needs implementation");
|
||||
// 1. پیدا کردن کاربر
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("User not found for golden package purchase. UserId: {UserId}", request.UserId);
|
||||
throw new NotFoundException(nameof(User), request.UserId);
|
||||
}
|
||||
|
||||
// 2. جلوگیری از خرید مجدد پکیج طلایی
|
||||
if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"User {UserId} has already purchased golden package via {Method}",
|
||||
request.UserId,
|
||||
user.PackagePurchaseMethod);
|
||||
|
||||
throw new ValidationException("شما قبلاً پکیج طلایی را خریداری کردهاید.");
|
||||
}
|
||||
|
||||
// 3. پیدا کردن پکیج
|
||||
var package = await _context.Packages
|
||||
.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken);
|
||||
|
||||
if (package == null)
|
||||
{
|
||||
_logger.LogWarning("Golden package not found. PackageId: {PackageId}", request.PackageId);
|
||||
throw new NotFoundException(nameof(Package), request.PackageId);
|
||||
}
|
||||
|
||||
// اطمینان از اینکه این همان پکیج طلایی است
|
||||
if (!package.Title.Contains("طلایی", StringComparison.OrdinalIgnoreCase) &&
|
||||
!package.Title.Contains("golden", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"PackageId {PackageId} is not a golden package. Title: {Title}",
|
||||
request.PackageId,
|
||||
package.Title);
|
||||
|
||||
throw new ValidationException("فقط پکیج طلایی قابل خرید است.");
|
||||
}
|
||||
|
||||
// 4. پیدا کردن آدرس پیشفرض کاربر (الزامی برای UserOrder)
|
||||
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} in golden package purchase", request.UserId);
|
||||
throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید.");
|
||||
}
|
||||
|
||||
// 5. ایجاد سفارش
|
||||
var order = new UserOrder
|
||||
{
|
||||
UserId = user.Id,
|
||||
PackageId = package.Id,
|
||||
Amount = package.Price,
|
||||
PaymentStatus = PaymentStatus.Pending,
|
||||
DeliveryStatus = DeliveryStatus.None,
|
||||
UserAddressId = defaultAddress.Id,
|
||||
PaymentMethod = PaymentMethod.IPG
|
||||
};
|
||||
|
||||
_context.UserOrders.Add(order);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Created golden package UserOrder {OrderId} for UserId {UserId}, Amount: {Amount}",
|
||||
order.Id,
|
||||
request.UserId,
|
||||
order.Amount);
|
||||
|
||||
// 6. شروع پرداخت با درگاه
|
||||
var paymentRequest = new PaymentRequest
|
||||
{
|
||||
Amount = order.Amount,
|
||||
UserId = user.Id,
|
||||
Mobile = user.Mobile ?? string.Empty,
|
||||
CallbackUrl = request.ReturnUrl,
|
||||
Description = $"خرید پکیج طلایی - سفارش #{order.Id}"
|
||||
};
|
||||
|
||||
var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest, cancellationToken);
|
||||
|
||||
if (!paymentResult.IsSuccess)
|
||||
{
|
||||
_logger.LogError(
|
||||
"Payment gateway initiation failed for golden package. OrderId {OrderId}: {ErrorMessage}",
|
||||
order.Id,
|
||||
paymentResult.ErrorMessage);
|
||||
|
||||
order.PaymentStatus = PaymentStatus.Reject;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}");
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
"Golden package payment initiated successfully. OrderId: {OrderId}, RefId: {RefId}",
|
||||
order.Id,
|
||||
paymentResult.RefId);
|
||||
|
||||
return new PurchaseGoldenPackageResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "لطفاً به درگاه پرداخت منتقل شوید.",
|
||||
OrderId = order.Id,
|
||||
PaymentGatewayUrl = paymentResult.GatewayUrl ?? string.Empty,
|
||||
TrackingCode = paymentResult.RefId ?? string.Empty
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Error in PurchaseGoldenPackageCommand for UserId: {UserId}",
|
||||
request.UserId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
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.VerifyGoldenPackagePurchase;
|
||||
|
||||
@@ -22,94 +27,161 @@ public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler<VerifyG
|
||||
|
||||
public async Task<VerifyGoldenPackagePurchaseResponseDto> Handle(VerifyGoldenPackagePurchaseCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
// TODO: پیادهسازی تایید پرداخت پکیج طلایی
|
||||
//
|
||||
// 1. بررسی Status از درگاه:
|
||||
// - if (request.Status != "OK")
|
||||
// throw new InvalidOperationException("پرداخت توسط کاربر لغو شد")
|
||||
//
|
||||
// 2. پیدا کردن سفارش:
|
||||
// - var order = await _context.UserOrders
|
||||
// .Include(o => o.User)
|
||||
// .ThenInclude(u => u.UserWallet)
|
||||
// .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken)
|
||||
// - if (order == null) throw new NotFoundException("سفارش یافت نشد")
|
||||
// - if (order.Status != OrderStatus.Pending)
|
||||
// throw new InvalidOperationException("این سفارش قبلاً پردازش شده است")
|
||||
//
|
||||
// 3. Verify با درگاه پرداخت:
|
||||
// - var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
||||
// authority: request.Authority,
|
||||
// amount: order.TotalPrice,
|
||||
// cancellationToken: cancellationToken
|
||||
// )
|
||||
// - if (!verifyResult.Success) {
|
||||
// order.Status = OrderStatus.PaymentFailed
|
||||
// order.PaymentFailureReason = verifyResult.ErrorMessage
|
||||
// await _context.SaveChangesAsync(cancellationToken)
|
||||
// throw new InvalidOperationException($"تایید پرداخت ناموفق: {verifyResult.ErrorMessage}")
|
||||
// }
|
||||
//
|
||||
// 4. شارژ کیف پول:
|
||||
// - var wallet = order.User.UserWallet
|
||||
// - if (wallet == null) {
|
||||
// wallet = new UserWallet { UserId = order.UserId, Balance = 0, DiscountBalance = 0 }
|
||||
// _context.UserWallets.Add(wallet)
|
||||
// }
|
||||
// - wallet.Balance += order.TotalPrice // اضافه شدن 56,000,000
|
||||
//
|
||||
// 5. ثبت Transaction:
|
||||
// - var transaction = new Transaction {
|
||||
// UserId = order.UserId,
|
||||
// Amount = order.TotalPrice,
|
||||
// Type = TransactionType.DepositIpg,
|
||||
// Status = TransactionStatus.Completed,
|
||||
// Description = $"شارژ کیف پول از خرید پکیج طلایی - سفارش {order.OrderNumber}",
|
||||
// ReferenceCode = verifyResult.ReferenceCode,
|
||||
// OrderId = order.Id
|
||||
// }
|
||||
// - _context.Transactions.Add(transaction)
|
||||
//
|
||||
// 6. ثبت UserWalletChangeLog:
|
||||
// - var changeLog = new UserWalletChangeLog {
|
||||
// UserId = order.UserId,
|
||||
// ChangeValue = order.TotalPrice,
|
||||
// ChangeType = ChangeType.Deposit,
|
||||
// CurrentBalance = wallet.Balance,
|
||||
// Description = $"شارژ از خرید پکیج طلایی",
|
||||
// TransactionId = transaction.Id
|
||||
// }
|
||||
// - _context.UserWalletChangeLogs.Add(changeLog)
|
||||
//
|
||||
// 7. Set PackagePurchaseMethod:
|
||||
// - order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase
|
||||
//
|
||||
// 8. بهروزرسانی سفارش:
|
||||
// - order.Status = OrderStatus.Processing // یا Completed
|
||||
// - order.PaymentStatus = PaymentStatus.Paid
|
||||
// - order.PaidAt = DateTime.UtcNow
|
||||
// - order.BankReferenceId = verifyResult.ReferenceCode
|
||||
//
|
||||
// 9. ذخیره همه تغییرات:
|
||||
// - await _context.SaveChangesAsync(cancellationToken)
|
||||
//
|
||||
// 10. Log و برگشت:
|
||||
// - _logger.LogInformation("Golden package verified for user {UserId}, order {OrderId}, wallet charged {Amount}",
|
||||
// order.UserId, order.Id, order.TotalPrice)
|
||||
// - return new VerifyGoldenPackagePurchaseResponseDto {
|
||||
// Success = true,
|
||||
// Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید",
|
||||
// OrderId = order.Id,
|
||||
// TransactionId = transaction.Id,
|
||||
// ReferenceCode = verifyResult.ReferenceCode,
|
||||
// WalletBalance = wallet.Balance
|
||||
// }
|
||||
//
|
||||
// نکته 1: کاربر هنوز عضو باشگاه نشده - باید ActivateClubMembership صدا بزند
|
||||
// نکته 2: TransactionType.DepositIpg را بررسی کنید موجود باشد
|
||||
// نکته 3: در صورت خطا، سفارش به PaymentFailed تغییر وضعیت میدهد
|
||||
// نکته 4: این فرآیند idempotent نیست - باید بررسی شود سفارش Pending باشد
|
||||
try
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Verifying golden package purchase. OrderId: {OrderId}, Authority: {Authority}, Status: {Status}",
|
||||
request.OrderId,
|
||||
request.Authority,
|
||||
request.Status);
|
||||
|
||||
throw new NotImplementedException("VerifyGoldenPackagePurchase needs implementation");
|
||||
// 1. اگر پرداخت از سمت درگاه موفق گزارش نشده باشد
|
||||
if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var pendingOrder = await _context.UserOrders
|
||||
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (pendingOrder != null && pendingOrder.PaymentStatus == PaymentStatus.Pending)
|
||||
{
|
||||
pendingOrder.PaymentStatus = PaymentStatus.Reject;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
throw new ValidationException("پرداخت توسط کاربر لغو شد.");
|
||||
}
|
||||
|
||||
// 2. پیدا کردن سفارش به همراه کاربر
|
||||
var order = await _context.UserOrders
|
||||
.Include(o => o.User)
|
||||
.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
_logger.LogWarning("Golden package order not found. OrderId: {OrderId}", request.OrderId);
|
||||
throw new NotFoundException(nameof(UserOrder), request.OrderId);
|
||||
}
|
||||
|
||||
// اگر قبلاً با موفقیت پرداخت شده، پاسخ idempotent برگردانیم
|
||||
if (order.PaymentStatus == PaymentStatus.Success && order.TransactionId.HasValue)
|
||||
{
|
||||
var existingWallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||
|
||||
var existingTransaction = await _context.Transactions
|
||||
.FirstOrDefaultAsync(t => t.Id == order.TransactionId.Value, cancellationToken);
|
||||
|
||||
return new VerifyGoldenPackagePurchaseResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "پرداخت قبلاً با موفقیت تایید شده است.",
|
||||
OrderId = order.Id,
|
||||
TransactionId = existingTransaction?.Id ?? order.TransactionId.Value,
|
||||
ReferenceCode = existingTransaction?.RefId ?? string.Empty,
|
||||
WalletBalance = existingWallet?.Balance ?? 0
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Verify با درگاه پرداخت
|
||||
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
||||
request.Authority,
|
||||
request.Authority,
|
||||
cancellationToken);
|
||||
|
||||
if (!verifyResult.IsSuccess)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Golden package payment verification failed. OrderId: {OrderId}, Message: {Message}",
|
||||
request.OrderId,
|
||||
verifyResult.Message);
|
||||
|
||||
order.PaymentStatus = PaymentStatus.Reject;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
throw new ValidationException($"تراکنش ناموفق: {verifyResult.Message}");
|
||||
}
|
||||
|
||||
// 4. شارژ کیف پول (Balance فقط طبق سناریوی پکیج)
|
||||
var wallet = await _context.UserWallets
|
||||
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||
|
||||
if (wallet == null)
|
||||
{
|
||||
_logger.LogError("Wallet not found for UserId: {UserId}", order.UserId);
|
||||
throw new NotFoundException($"کیف پول کاربر با شناسه {order.UserId} یافت نشد");
|
||||
}
|
||||
|
||||
var oldBalance = wallet.Balance;
|
||||
wallet.Balance += order.Amount;
|
||||
|
||||
_logger.LogInformation(
|
||||
"Charging wallet Balance for user {UserId} from {OldBalance} to {NewBalance}",
|
||||
order.UserId,
|
||||
oldBalance,
|
||||
wallet.Balance);
|
||||
|
||||
// 5. ثبت Transaction
|
||||
var transaction = new Transaction
|
||||
{
|
||||
Amount = order.Amount,
|
||||
Description = $"خرید پکیج طلایی از درگاه - سفارش #{order.Id}",
|
||||
PaymentStatus = PaymentStatus.Success,
|
||||
PaymentDate = DateTime.UtcNow,
|
||||
RefId = verifyResult.RefId,
|
||||
Type = TransactionType.DepositIpg
|
||||
};
|
||||
|
||||
_context.Transactions.Add(transaction);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. ثبت لاگ تغییر کیف پول
|
||||
var changeLog = new UserWalletChangeLog
|
||||
{
|
||||
WalletId = wallet.Id,
|
||||
CurrentBalance = wallet.Balance,
|
||||
ChangeValue = order.Amount,
|
||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||
ChangeNerworkValue = 0,
|
||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||
ChangeDiscountValue = 0,
|
||||
IsIncrease = true,
|
||||
RefrenceId = transaction.Id
|
||||
};
|
||||
|
||||
await _context.UserWalletChangeLogs.AddAsync(changeLog, cancellationToken);
|
||||
|
||||
// 7. بهروزرسانی سفارش و کاربر
|
||||
order.TransactionId = transaction.Id;
|
||||
order.PaymentStatus = PaymentStatus.Success;
|
||||
order.PaymentDate = DateTime.UtcNow;
|
||||
order.PaymentMethod = PaymentMethod.IPG;
|
||||
order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Golden package purchase verified successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}",
|
||||
order.Id,
|
||||
order.UserId,
|
||||
transaction.Id,
|
||||
verifyResult.RefId);
|
||||
|
||||
return new VerifyGoldenPackagePurchaseResponseDto
|
||||
{
|
||||
Success = true,
|
||||
Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.",
|
||||
OrderId = order.Id,
|
||||
TransactionId = transaction.Id,
|
||||
ReferenceCode = verifyResult.RefId,
|
||||
WalletBalance = wallet.Balance
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Error in VerifyGoldenPackagePurchaseCommand. OrderId: {OrderId}",
|
||||
request.OrderId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user