5 Commits

Author SHA1 Message Date
masoodafar-web be80136ae8 fix: reduce discount order expiry from 30 to 15 minutes
Build and Deploy to Production / build-and-deploy (push) Successful in 9m8s
2026-02-24 00:24:30 +03:30
masoodafar-web 5e9984f0e5 fix: validate discount wallet balance before applying discount in PlaceOrderCommandHandler 2026-02-24 00:24:30 +03:30
masoodafar-web 1d07ae4f71 fix: add missing UserWalletChangeLog for discount shop purchases and discount wallet charge
Bug: when buying from discount shop, DiscountBalance was deducted from DB
but no UserWalletChangeLog was created — making it invisible in wallet history.
Same issue existed for discount wallet top-up (charge).

Fixed in 3 handlers:
- PlaceOrderCommandHandler (fully paid by discount balance path)
- CompleteOrderPaymentCommandHandler (gateway + discount balance path)
- VerifyDiscountWalletChargeCommandHandler (discount wallet charge)
2026-02-24 00:24:30 +03:30
masoodafar-web 775a9bd75d fix: update .gitignore to include environment-specific appsettings files 2026-02-24 00:18:42 +03:30
masoodafar-web 2620a24597 fix: correct production URLs from kbs1 to kbs2 in cms-config
CmsBaseUrl: cms.kbs1.ir → cms.kbs2.ir
FrontOfficeBaseUrl: kbs1.ir → kbs2.ir

This was causing ZarinPal callback to go to staging instead of production,
resulting in 401 errors (-1 unknown error).
2026-02-24 00:18:24 +03:30
6 changed files with 66 additions and 10 deletions
+3 -3
View File
@@ -494,6 +494,6 @@ fabric.properties
/src/.idea /src/.idea
# Environment-specific configuration files with sensitive data # Environment-specific configuration files with sensitive data
# **/appsettings.Staging.json **/appsettings.Staging.json
# **/appsettings.Production.json **/appsettings.Production.json
# **/appsettings.*.local.json **/appsettings.*.local.json
+2 -2
View File
@@ -23,8 +23,8 @@ stringData:
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8", "MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
"UseSandbox": false "UseSandbox": false
}, },
"CmsBaseUrl": "https://cms.kbs1.ir", "CmsBaseUrl": "https://cms.kbs2.ir",
"FrontOfficeBaseUrl": "https://kbs1.ir", "FrontOfficeBaseUrl": "https://kbs2.ir",
"ConnectionStrings": { "ConnectionStrings": {
"DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;" "DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;"
}, },
@@ -62,9 +62,23 @@ public class CompleteOrderPaymentCommandHandler : IRequestHandler<CompleteOrderP
var userWallet = await _context.UserWallets var userWallet = await _context.UserWallets
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
if (userWallet != null) if (userWallet != null && order.DiscountBalanceUsed > 0)
{ {
userWallet.DiscountBalance -= order.DiscountBalanceUsed; userWallet.DiscountBalance -= order.DiscountBalanceUsed;
// ثبت لاگ تغییرات کیف پول
_context.UserWalletChangeLogs.Add(new Domain.Entities.UserWalletChangeLog
{
WalletId = userWallet.Id,
CurrentBalance = userWallet.Balance,
ChangeValue = 0,
CurrentNetworkBalance = userWallet.NetworkBalance,
ChangeNerworkValue = 0,
CurrentDiscountBalance = userWallet.DiscountBalance,
ChangeDiscountValue = -order.DiscountBalanceUsed,
IsIncrease = false,
RefrenceId = transaction.Id
});
} }
// تایید فروش و کسر موجودی از طریق InventoryService // تایید فروش و کسر موجودی از طریق InventoryService
@@ -111,7 +111,18 @@ public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, Place
// Always apply maximum possible discount (100%) — user cannot choose less // Always apply maximum possible discount (100%) — user cannot choose less
var maxDiscountBalanceUsable = totalDiscountAmount; var maxDiscountBalanceUsable = totalDiscountAmount;
var actualDiscountBalanceUsed = Math.Min(maxDiscountBalanceUsable, userWallet.DiscountBalance);
// بررسی کافی بودن موجودی کیف پول اعتباری (تخفیفی)
if (userWallet.DiscountBalance < maxDiscountBalanceUsable)
{
return new PlaceOrderResponseDto
{
Success = false,
Message = $"موجودی کیف پول اعتباری کافی نیست. مبلغ مورد نیاز: {maxDiscountBalanceUsable:N0} ریال، موجودی شما: {userWallet.DiscountBalance:N0} ریال"
};
}
var actualDiscountBalanceUsed = maxDiscountBalanceUsable;
_logger.LogInformation( _logger.LogInformation(
"Discount auto-applied: max allowed={MaxAllowed}, wallet balance={WalletBalance}, used={Used}", "Discount auto-applied: max allowed={MaxAllowed}, wallet balance={WalletBalance}, used={Used}",
@@ -263,9 +274,25 @@ public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, Place
var walletForDeduct = await _context.UserWallets var walletForDeduct = await _context.UserWallets
.FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken); .FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken);
if (walletForDeduct != null) if (walletForDeduct != null && actualDiscountBalanceUsed > 0)
{
walletForDeduct.DiscountBalance -= actualDiscountBalanceUsed; walletForDeduct.DiscountBalance -= actualDiscountBalanceUsed;
// ثبت لاگ تغییرات کیف پول
_context.UserWalletChangeLogs.Add(new Domain.Entities.UserWalletChangeLog
{
WalletId = walletForDeduct.Id,
CurrentBalance = walletForDeduct.Balance,
ChangeValue = 0,
CurrentNetworkBalance = walletForDeduct.NetworkBalance,
ChangeNerworkValue = 0,
CurrentDiscountBalance = walletForDeduct.DiscountBalance,
ChangeDiscountValue = -actualDiscountBalanceUsed,
IsIncrease = false,
RefrenceId = transaction.Id
});
}
foreach (var cartItem in cartItems) foreach (var cartItem in cartItems)
{ {
await _inventoryService.ConfirmSaleAsync( await _inventoryService.ConfirmSaleAsync(
@@ -114,6 +114,21 @@ public class VerifyDiscountWalletChargeCommandHandler
_context.Transactions.Add(transaction); _context.Transactions.Add(transaction);
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
// ثبت لاگ تغییرات کیف پول (بعد از ایجاد Transaction برای داشتن TransactionId)
_context.UserWalletChangeLogs.Add(new Domain.Entities.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.SaveChangesAsync(cancellationToken);
// لینک PaymentTransaction به Transaction داخلی // لینک PaymentTransaction به Transaction داخلی
if (paymentTx != null) if (paymentTx != null)
{ {
@@ -17,9 +17,9 @@ public class ExpirePendingOrdersService : BackgroundService
private readonly ILogger<ExpirePendingOrdersService> _logger; private readonly ILogger<ExpirePendingOrdersService> _logger;
/// <summary> /// <summary>
/// مدت زمان انقضا (۳۰ دقیقه) /// مدت زمان انقضا (۱۵ دقیقه)
/// </summary> /// </summary>
private static readonly TimeSpan ExpirationTime = TimeSpan.FromMinutes(30); private static readonly TimeSpan ExpirationTime = TimeSpan.FromMinutes(15);
/// <summary> /// <summary>
/// هر ۵ دقیقه چک می‌شود /// هر ۵ دقیقه چک می‌شود