feat: Implement Magic Wallet functionality
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m36s

- Added ClubMembershipCycle entity and DbSet to IApplicationDbContext.
- Updated VAT rate from 9% to 10% in VatCalculator and related areas.
- Introduced Magic Wallet settings in SystemConstants.
- Enhanced UserWallet entity to support Magic Wallet features.
- Updated TransactionType enum to include Magic Wallet transactions.
- Configured UserWallet to handle Magic Wallet properties in ApplicationDbContext.
- Implemented OrmCommissionCalculationStrategy to exclude Magic Wallet users from commission calculations.
- Added Protobuf definitions for Magic Wallet methods and responses.
- Created PaymentCallbackController endpoint for handling Magic Wallet charge callbacks.
- Updated UserOrderService to manage Magic Wallet state transitions.
- Developed UserWalletService to support Magic Wallet operations.
- Created ChargeMagicWalletCommand and its handler for initiating Magic Wallet charges.
- Implemented VerifyMagicWalletChargeCommand and handler for payment verification.
- Added validation for ChargeMagicWalletCommand.
- Established ClubMembershipCycle configuration for EF Core.
- Introduced WalletMode enum to differentiate between Normal and Magic modes.
This commit is contained in:
masoodafar-web
2026-02-21 19:50:28 +03:30
parent 04e8c49fa7
commit 2a569a024f
23 changed files with 914 additions and 22 deletions
@@ -6,7 +6,9 @@ using CMSMicroservice.Application.UserOrderCQ.Queries.GetCustomerOrderHistory;
using CMSMicroservice.Application.OrderManagementCQ.Commands.UpdateOrderStatus;
using CMSMicroservice.Application.OrderManagementCQ.Commands.CancelOrderByAdmin;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Club;
using CMSMicroservice.Domain.Entities.Order;
using CMSMicroservice.Domain.Enums;
using AppModels = CMSMicroservice.Application.Common.Models;
@@ -272,7 +274,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
}
// Calculate amounts
const decimal vatRate = 0.09m;
const decimal vatRate = 0.10m;
long baseAmount = cartItems.Sum(c => c.Product.Price * c.Count);
long vatAmount = (long)(baseAmount * vatRate);
long totalAmount = baseAmount + vatAmount;
@@ -334,7 +336,54 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
};
_context.UserWalletChangeLogs.Add(walletLog);
// ═══ Magic Wallet: Entry / Exit Trigger ═══
if (wallet.Balance == 0)
{
var user = await _context.Users
.Include(u => u.ClubMembership)
.FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken);
if (wallet.WalletMode == WalletMode.Magic
&& wallet.MagicTotalDeposited >= SystemConstants.MagicWalletMaxDeposit)
{
// ── EXIT Magic Mode ──
// هر دو شرط: Balance=0 و سقف 100M پر شده
wallet.WalletMode = WalletMode.Normal;
wallet.MagicCompletedAt = DateTime.UtcNow;
var currentCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle,
context.CancellationToken);
if (currentCycle != null)
{
currentCycle.MagicCompletedAt = DateTime.UtcNow;
}
}
else if (wallet.WalletMode == WalletMode.Normal
&& user != null
&& user.PackagePurchaseMethod != PackagePurchaseMethod.None
&& user.ClubMembership?.IsActive == true)
{
// ── ENTER Magic Mode ──
wallet.WalletMode = WalletMode.Magic;
wallet.MagicActivatedAt = DateTime.UtcNow;
wallet.MagicCompletedAt = null;
wallet.MagicTotalDeposited = 0;
wallet.MagicTotalCredited = 0;
var currentCycle = await _context.ClubMembershipCycles
.FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle,
context.CancellationToken);
if (currentCycle != null)
{
currentCycle.MagicStartedAt = DateTime.UtcNow;
}
}
// ⚠️ اگه Magic باشه و Balance=0 ولی سقف پر نشده → هنوز Magic!
// کاربر میتونه دوباره شارژ کنه
}
// Create order
var order = new UserOrder
{
@@ -935,11 +984,11 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
public override Task<GetVATRateResponse> GetVATRate(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
{
// VAT Rate for Iran: 9% (نرخ مالیات بر ارزش افزوده ایران)
// VAT Rate for Iran: 10% (نرخ مالیات بر ارزش افزوده ایران)
return Task.FromResult(new GetVATRateResponse
{
VatRate = 0.09,
VatPercentage = 9,
VatRate = 0.10,
VatPercentage = 10,
IsEnabled = true
});
}