fix: remove double ×10 Rial conversion in ZarinPalPaymentService
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
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:
@@ -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;"
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-3
@@ -74,12 +74,11 @@ public class VerifyMagicWalletChargeCommandHandler
|
|||||||
throw new BadRequestException("پرداخت توسط کاربر لغو شد");
|
throw new BadRequestException("پرداخت توسط کاربر لغو شد");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Verify با درگاه (زرینپال نیاز به مبلغ دارد)
|
// 3. Verify با درگاه (مبلغ به ریال)
|
||||||
var amountInToman = depositAmount / 10m;
|
|
||||||
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
||||||
request.Authority,
|
request.Authority,
|
||||||
request.Status,
|
request.Status,
|
||||||
amountInToman,
|
depositAmount,
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ public class ZarinPalPaymentService : IPaymentGatewayService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// زرینپال مبلغ را به ریال میخواهد — تبدیل تومان به ریال
|
// مبلغ از caller به ریال میرسد — مستقیم ارسال به زرینپال
|
||||||
var amountInRials = (long)(request.Amount * 10);
|
var amountInRials = (long)request.Amount;
|
||||||
|
|
||||||
var zarinPalRequest = new ZarinPalPaymentRequest
|
var zarinPalRequest = new ZarinPalPaymentRequest
|
||||||
{
|
{
|
||||||
@@ -85,12 +85,26 @@ public class ZarinPalPaymentService : IPaymentGatewayService
|
|||||||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"ZarinPal payment request: Amount={AmountToman} Toman ({AmountRial} Rial), User={UserId}, Sandbox={Sandbox}",
|
"ZarinPal payment request: Amount={AmountRial} Rial ({AmountToman} Toman), User={UserId}, Sandbox={Sandbox}",
|
||||||
request.Amount, amountInRials, request.UserId, _useSandbox);
|
amountInRials, amountInRials / 10m, request.UserId, _useSandbox);
|
||||||
|
|
||||||
var response = await _httpClient.PostAsync(RequestEndpoint, content, cancellationToken);
|
var response = await _httpClient.PostAsync(RequestEndpoint, content, cancellationToken);
|
||||||
var responseBody = await response.Content.ReadAsStringAsync(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}",
|
_logger.LogDebug("ZarinPal request response: {StatusCode} - {Body}",
|
||||||
response.StatusCode, responseBody);
|
response.StatusCode, responseBody);
|
||||||
|
|
||||||
@@ -153,21 +167,21 @@ public class ZarinPalPaymentService : IPaymentGatewayService
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// تأیید پرداخت با مبلغ — نسخه اصلی برای زرینپال
|
/// تأیید پرداخت با مبلغ — نسخه اصلی برای زرینپال
|
||||||
/// refId = Authority، verificationToken = Status (OK/NOK)، amountInToman = مبلغ به تومان
|
/// refId = Authority، verificationToken = Status (OK/NOK)، amount = مبلغ به ریال
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Task<PaymentVerificationResult> VerifyPaymentAsync(
|
public Task<PaymentVerificationResult> VerifyPaymentAsync(
|
||||||
string refId,
|
string refId,
|
||||||
string verificationToken,
|
string verificationToken,
|
||||||
decimal amountInToman,
|
decimal amount,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return VerifyPaymentWithAmountAsync(refId, verificationToken, amountInToman, cancellationToken);
|
return VerifyPaymentWithAmountAsync(refId, verificationToken, amount, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PaymentVerificationResult> VerifyPaymentWithAmountAsync(
|
private async Task<PaymentVerificationResult> VerifyPaymentWithAmountAsync(
|
||||||
string refId,
|
string refId,
|
||||||
string verificationToken,
|
string verificationToken,
|
||||||
decimal amountInToman,
|
decimal amountInRials,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -184,21 +198,18 @@ public class ZarinPalPaymentService : IPaymentGatewayService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// تبدیل تومان → ریال (×۱۰)
|
|
||||||
var amountInRials = (long)(amountInToman * 10);
|
|
||||||
|
|
||||||
var verifyRequest = new ZarinPalVerifyRequest
|
var verifyRequest = new ZarinPalVerifyRequest
|
||||||
{
|
{
|
||||||
MerchantId = _merchantId,
|
MerchantId = _merchantId,
|
||||||
Authority = refId,
|
Authority = refId,
|
||||||
Amount = amountInRials
|
Amount = (long)amountInRials
|
||||||
};
|
};
|
||||||
|
|
||||||
var jsonContent = JsonSerializer.Serialize(verifyRequest, JsonOptions);
|
var jsonContent = JsonSerializer.Serialize(verifyRequest, JsonOptions);
|
||||||
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
_logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={Amount} Rial",
|
_logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={Amount} Rial ({AmountToman} Toman)",
|
||||||
refId, amountInRials);
|
refId, (long)amountInRials, amountInRials / 10m);
|
||||||
|
|
||||||
var response = await _httpClient.PostAsync(VerifyEndpoint, content, cancellationToken);
|
var response = await _httpClient.PostAsync(VerifyEndpoint, content, cancellationToken);
|
||||||
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
@@ -220,7 +231,7 @@ public class ZarinPalPaymentService : IPaymentGatewayService
|
|||||||
IsSuccess = true,
|
IsSuccess = true,
|
||||||
RefId = refId,
|
RefId = refId,
|
||||||
TrackingCode = result.Data.RefId?.ToString(),
|
TrackingCode = result.Data.RefId?.ToString(),
|
||||||
Amount = (result.Data.Amount ?? 0) / 10m, // ریال → تومان
|
Amount = result.Data.Amount ?? 0, // ریال — بدون تبدیل
|
||||||
CardPan = result.Data.CardPan,
|
CardPan = result.Data.CardPan,
|
||||||
CardHash = result.Data.CardHash,
|
CardHash = result.Data.CardHash,
|
||||||
VerificationCode = result.Data.Code,
|
VerificationCode = result.Data.Code,
|
||||||
|
|||||||
@@ -82,11 +82,11 @@ public class PaymentCallbackController : ControllerBase
|
|||||||
if (string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.IsNullOrEmpty(authority))
|
&& !string.IsNullOrEmpty(authority))
|
||||||
{
|
{
|
||||||
// Verify با مبلغ از دیتابیس (تومان)
|
// Verify با مبلغ از دیتابیس (ریال)
|
||||||
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
var verifyResult = await _paymentGateway.VerifyPaymentAsync(
|
||||||
authority,
|
authority,
|
||||||
status!,
|
status!,
|
||||||
order.GatewayAmountPaid, // مبلغ به تومان
|
order.GatewayAmountPaid, // مبلغ به ریال
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
paymentSuccess = verifyResult.IsSuccess;
|
paymentSuccess = verifyResult.IsSuccess;
|
||||||
|
|||||||
Reference in New Issue
Block a user