fix: remove double ×10 Rial conversion in ZarinPalPaymentService
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled

Root cause: FrontOffice already converts Toman→Rial (×10) before sending
to CMS. ZarinPalPaymentService was multiplying by 10 AGAIN, causing
amounts to be 10x too large.

Example: user enters 500K Toman → FO sends 5M Rial → CMS did ×10 → 50M
Rial sent to ZarinPal → showed 5M Toman instead of 500K.

Changes:
- ZarinPalPaymentService.InitiatePaymentAsync: remove ×10 (amount already Rial)
- ZarinPalPaymentService.VerifyPaymentWithAmountAsync: remove ×10 + accept Rial
- ZarinPalPaymentService.VerifyResult.Amount: return Rial (no /10 conversion)
- VerifyMagicWalletChargeCommandHandler: remove /10 before calling Verify
- PaymentCallbackController: update comments (amount is Rial)
- Also includes: improved HTTP error logging for non-200 responses
- Also includes: production URL fix (kbs1→kbs2)
This commit is contained in:
masoodafar-web
2026-02-24 00:49:16 +03:30
parent e206b71186
commit cbaa20f339
4 changed files with 32 additions and 22 deletions
+2 -2
View File
@@ -23,8 +23,8 @@ stringData:
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
"UseSandbox": false
},
"CmsBaseUrl": "https://cms.kbs1.ir",
"FrontOfficeBaseUrl": "https://kbs1.ir",
"CmsBaseUrl": "https://cms.kbs2.ir",
"FrontOfficeBaseUrl": "https://kbs2.ir",
"ConnectionStrings": {
"DefaultConnection": "Server=mssql-svc;Database=KBS;User Id=sa;Password=YourStrong@Passw0rd;TrustServerCertificate=True;"
},
@@ -74,12 +74,11 @@ public class VerifyMagicWalletChargeCommandHandler
throw new BadRequestException("پرداخت توسط کاربر لغو شد");
}
// 3. Verify با درگاه (زرین‌پال نیاز به مبلغ دارد)
var amountInToman = depositAmount / 10m;
// 3. Verify با درگاه (مبلغ به ریال)
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
request.Authority,
request.Status,
amountInToman,
depositAmount,
cancellationToken
);
@@ -66,8 +66,8 @@ public class ZarinPalPaymentService : IPaymentGatewayService
{
try
{
// زرین‌پال مبلغ را به ریال می‌خواهد — تبدیل تومان به ریال
var amountInRials = (long)(request.Amount * 10);
// مبلغ از caller به ریال می‌رسد — مستقیم ارسال به زرین‌پال
var amountInRials = (long)request.Amount;
var zarinPalRequest = new ZarinPalPaymentRequest
{
@@ -85,12 +85,26 @@ public class ZarinPalPaymentService : IPaymentGatewayService
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
_logger.LogInformation(
"ZarinPal payment request: Amount={AmountToman} Toman ({AmountRial} Rial), User={UserId}, Sandbox={Sandbox}",
request.Amount, amountInRials, request.UserId, _useSandbox);
"ZarinPal payment request: Amount={AmountRial} Rial ({AmountToman} Toman), User={UserId}, Sandbox={Sandbox}",
amountInRials, amountInRials / 10m, request.UserId, _useSandbox);
var response = await _httpClient.PostAsync(RequestEndpoint, content, cancellationToken);
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
// لاگ HTTP status — مهم برای دیباگ
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"ZarinPal HTTP error: StatusCode={StatusCode}, Body={Body}",
(int)response.StatusCode, responseBody);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = $"خطای ارتباط با زرین‌پال (HTTP {(int)response.StatusCode}). لطفاً MerchantId و IP سرور در پنل زرین‌پال بررسی شود."
};
}
_logger.LogDebug("ZarinPal request response: {StatusCode} - {Body}",
response.StatusCode, responseBody);
@@ -153,21 +167,21 @@ public class ZarinPalPaymentService : IPaymentGatewayService
/// <summary>
/// تأیید پرداخت با مبلغ — نسخه اصلی برای زرین‌پال
/// refId = Authority، verificationToken = Status (OK/NOK)، amountInToman = مبلغ به تومان
/// refId = Authority، verificationToken = Status (OK/NOK)، amount = مبلغ به ریال
/// </summary>
public Task<PaymentVerificationResult> VerifyPaymentAsync(
string refId,
string verificationToken,
decimal amountInToman,
decimal amount,
CancellationToken cancellationToken = default)
{
return VerifyPaymentWithAmountAsync(refId, verificationToken, amountInToman, cancellationToken);
return VerifyPaymentWithAmountAsync(refId, verificationToken, amount, cancellationToken);
}
private async Task<PaymentVerificationResult> VerifyPaymentWithAmountAsync(
string refId,
string verificationToken,
decimal amountInToman,
decimal amountInRials,
CancellationToken cancellationToken)
{
try
@@ -184,21 +198,18 @@ public class ZarinPalPaymentService : IPaymentGatewayService
};
}
// تبدیل تومان → ریال (×۱۰)
var amountInRials = (long)(amountInToman * 10);
var verifyRequest = new ZarinPalVerifyRequest
{
MerchantId = _merchantId,
Authority = refId,
Amount = amountInRials
Amount = (long)amountInRials
};
var jsonContent = JsonSerializer.Serialize(verifyRequest, JsonOptions);
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
_logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={Amount} Rial",
refId, amountInRials);
_logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={Amount} Rial ({AmountToman} Toman)",
refId, (long)amountInRials, amountInRials / 10m);
var response = await _httpClient.PostAsync(VerifyEndpoint, content, cancellationToken);
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -220,7 +231,7 @@ public class ZarinPalPaymentService : IPaymentGatewayService
IsSuccess = true,
RefId = refId,
TrackingCode = result.Data.RefId?.ToString(),
Amount = (result.Data.Amount ?? 0) / 10m, // ریال → تومان
Amount = result.Data.Amount ?? 0, // ریال — بدون تبدیل
CardPan = result.Data.CardPan,
CardHash = result.Data.CardHash,
VerificationCode = result.Data.Code,
@@ -82,11 +82,11 @@ public class PaymentCallbackController : ControllerBase
if (string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrEmpty(authority))
{
// Verify با مبلغ از دیتابیس (تومان)
// Verify با مبلغ از دیتابیس (ریال)
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
authority,
status!,
order.GatewayAmountPaid, // مبلغ به تومان
order.GatewayAmountPaid, // مبلغ به ریال
cancellationToken);
paymentSuccess = verifyResult.IsSuccess;