diff --git a/README.md b/README.md
index 70283c2..1a3fdd9 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
# CMS Microservice
Documentation moved to [totalDoc/INDEX.md](../totalDoc/INDEX.md).
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/k8s/production/cms-config.yaml b/k8s/production/cms-config.yaml
index 6ce28ee..0835075 100644
--- a/k8s/production/cms-config.yaml
+++ b/k8s/production/cms-config.yaml
@@ -24,6 +24,23 @@ stringData:
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
"UseSandbox": false
},
+ "TwentyCard": {
+ "BaseUrl": "https://gateway.20card.ir/ipg",
+ "TerminalId": "165674",
+ "TerminalUsername": "ea5y1biehi",
+ "TerminalPassword": "SaLamAT@1404!",
+ "CreditPortalUrl": "https://userpanel.20card.ir/",
+ "Packages": {
+ "4": {
+ "GatewayAmountToman": 9700000,
+ "WalletCreditToman": 13000000
+ },
+ "1": {
+ "GatewayAmountToman": 97000000,
+ "WalletCreditToman": 130000000
+ }
+ }
+ },
"CmsBaseUrl": "https://cms.kbs2.ir",
"FrontOfficeBaseUrl": "https://kbs2.ir",
"JwtSecurityKey": "TvlZVx5TJaHs8e9HgUdGzhGP2CIidoI444nAj+8+g7c=",
diff --git a/k8s/staging/cms-config.yaml b/k8s/staging/cms-config.yaml
index c9c5562..17332ba 100644
--- a/k8s/staging/cms-config.yaml
+++ b/k8s/staging/cms-config.yaml
@@ -24,6 +24,23 @@ stringData:
"MerchantId": "4225d555-5fa9-4df0-9b61-1ce152cbbba8",
"UseSandbox": true
},
+ "TwentyCard": {
+ "BaseUrl": "https://gateway.20card.ir/ipg",
+ "TerminalId": "165674",
+ "TerminalUsername": "ea5y1biehi",
+ "TerminalPassword": "SaLamAT@1404!",
+ "CreditPortalUrl": "https://userpanel.20card.ir/",
+ "Packages": {
+ "4": {
+ "GatewayAmountToman": 9700000,
+ "WalletCreditToman": 13000000
+ },
+ "1": {
+ "GatewayAmountToman": 97000000,
+ "WalletCreditToman": 130000000
+ }
+ }
+ },
"CmsBaseUrl": "https://cms.se.kbs1.ir",
"FrontOfficeBaseUrl": "https://frontoffice.se.kbs1.ir",
"FMS": {
diff --git a/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs b/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs
index e411212..63403c6 100644
--- a/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs
+++ b/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs
@@ -108,6 +108,16 @@ public class PaymentRequest
/// شماره فاکتور / سفارش
///
public string Invoice { get; set; } = string.Empty;
+
+ ///
+ /// ایمیل (اختیاری — برای ۲۰کارت)
+ ///
+ public string? Email { get; set; }
+
+ ///
+ /// کد ملی (اختیاری — برای ۲۰کارت)
+ ///
+ public string? NationalId { get; set; }
}
///
diff --git a/src/CMSMicroservice.Infrastructure/Services/Payment/TwentyCardPaymentService.cs b/src/CMSMicroservice.Infrastructure/Services/Payment/TwentyCardPaymentService.cs
index 01396a3..9fec515 100644
--- a/src/CMSMicroservice.Infrastructure/Services/Payment/TwentyCardPaymentService.cs
+++ b/src/CMSMicroservice.Infrastructure/Services/Payment/TwentyCardPaymentService.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Net.Http;
-using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using CMSMicroservice.Application.Common.Interfaces;
@@ -11,7 +10,7 @@ namespace CMSMicroservice.Infrastructure.Services.Payment;
///
/// درگاه پرداخت بیستکارت (20Card IPG Merchant API).
-/// مبلغ ورودی به تومان است (مثل بقیهٔ سیستم).
+/// مبلغ ورودی به تومان است. درخواستها بهصورت multipart/form-data ارسال میشوند.
///
public class TwentyCardPaymentService : IPaymentGatewayService
{
@@ -55,12 +54,15 @@ public class TwentyCardPaymentService : IPaymentGatewayService
{
try
{
- var amount = (long)request.Amount;
+ var amount = ((long)request.Amount).ToString();
var payerName = string.IsNullOrWhiteSpace(request.Name)
? (string.IsNullOrWhiteSpace(request.Mobile) ? "کاربر" : request.Mobile)
: request.Name.Trim();
+ var invoice = string.IsNullOrWhiteSpace(request.Invoice)
+ ? request.UserId.ToString()
+ : request.Invoice;
- var body = new Dictionary
+ using var content = BuildForm(new Dictionary
{
["terminal_id"] = _terminalId,
["terminal_username"] = _terminalUsername,
@@ -68,19 +70,19 @@ public class TwentyCardPaymentService : IPaymentGatewayService
["amount"] = amount,
["name"] = payerName,
["mobile"] = request.Mobile ?? string.Empty,
- ["invoice"] = string.IsNullOrWhiteSpace(request.Invoice)
- ? request.UserId.ToString()
- : request.Invoice,
+ ["invoice"] = invoice,
["callback"] = request.CallbackUrl,
- ["description"] = request.Description
- };
+ ["description"] = request.Description ?? string.Empty
+ });
+
+ if (!string.IsNullOrWhiteSpace(request.Email))
+ content.Add(new StringContent(request.Email), "email");
+ if (!string.IsNullOrWhiteSpace(request.NationalId))
+ content.Add(new StringContent(request.NationalId), "national_id");
_logger.LogInformation(
"TwentyCard payment request: Amount={Amount} Toman, User={UserId}, Invoice={Invoice}",
- amount, request.UserId, body["invoice"]);
-
- using var content = new StringContent(
- JsonSerializer.Serialize(body, JsonOptions), Encoding.UTF8, "application/json");
+ amount, request.UserId, invoice);
var response = await _httpClient.PostAsync("api/pgw/merchant/request", content, cancellationToken);
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -108,7 +110,9 @@ public class TwentyCardPaymentService : IPaymentGatewayService
return new PaymentInitiateResult
{
IsSuccess = false,
- ErrorMessage = result?.Message ?? "ایجاد تراکنش ۲۰کارت ناموفق بود"
+ ErrorMessage = result?.Message
+ ?? result?.Error?.Message
+ ?? "ایجاد تراکنش ۲۰کارت ناموفق بود"
};
}
@@ -155,17 +159,14 @@ public class TwentyCardPaymentService : IPaymentGatewayService
};
}
- var body = new Dictionary
+ using var content = BuildForm(new Dictionary
{
["terminal_id"] = _terminalId,
["terminal_username"] = _terminalUsername,
["terminal_password"] = _terminalPassword,
["authority"] = refId,
- ["amount"] = (long)amountInToman
- };
-
- using var content = new StringContent(
- JsonSerializer.Serialize(body, JsonOptions), Encoding.UTF8, "application/json");
+ ["amount"] = ((long)amountInToman).ToString()
+ });
var response = await _httpClient.PostAsync("api/pgw/merchant/verify", content, cancellationToken);
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -236,10 +237,25 @@ public class TwentyCardPaymentService : IPaymentGatewayService
});
}
+ private static MultipartFormDataContent BuildForm(Dictionary fields)
+ {
+ var form = new MultipartFormDataContent();
+ foreach (var (key, value) in fields)
+ form.Add(new StringContent(value ?? string.Empty), key);
+ return form;
+ }
+
private sealed class TwentyCardRequestResponse
{
public string? Message { get; set; }
public TwentyCardRequestData? Data { get; set; }
+ public TwentyCardErrorBody? Error { get; set; }
+ }
+
+ private sealed class TwentyCardErrorBody
+ {
+ public string? Code { get; set; }
+ public string? Message { get; set; }
}
private sealed class TwentyCardRequestData
diff --git a/src/CMSMicroservice.WebApi/Services/PackageService.cs b/src/CMSMicroservice.WebApi/Services/PackageService.cs
index 2c2410d..f0ebdd2 100644
--- a/src/CMSMicroservice.WebApi/Services/PackageService.cs
+++ b/src/CMSMicroservice.WebApi/Services/PackageService.cs
@@ -223,7 +223,7 @@ public class PackageService : PackageContract.PackageContractBase
var user = await _context.Users
.AsNoTracking()
.Where(u => u.Id == userId)
- .Select(u => new { u.Mobile, u.FirstName, u.LastName })
+ .Select(u => new { u.Mobile, u.FirstName, u.LastName, u.Email, u.NationalCode })
.FirstOrDefaultAsync(cancellationToken);
// Callback URL از config — نه از ورودی کاربر (امنیت)
@@ -241,7 +241,9 @@ public class PackageService : PackageContract.PackageContractBase
Name = payerName,
Invoice = purchase.Id.ToString(),
Description = $"خرید پکیج {package.Title}",
- CallbackUrl = callbackUrl
+ CallbackUrl = callbackUrl,
+ Email = user?.Email,
+ NationalId = user?.NationalCode
}, cancellationToken);
if (!paymentResult.IsSuccess)
diff --git a/src/CMSMicroservice.WebApi/appsettings.Staging.json b/src/CMSMicroservice.WebApi/appsettings.Staging.json
index 3c7c05c..4009c39 100644
--- a/src/CMSMicroservice.WebApi/appsettings.Staging.json
+++ b/src/CMSMicroservice.WebApi/appsettings.Staging.json
@@ -5,11 +5,11 @@
"UseSandbox": true
},
"TwentyCard": {
- "BaseUrl": "https://gateway.20card.ir",
+ "BaseUrl": "https://gateway.20card.ir/ipg",
"TerminalId": "165674",
"TerminalUsername": "ea5y1biehi",
- "TerminalPassword": "ea5y1biehi",
- "CreditPortalUrl": "https://20card.ir",
+ "TerminalPassword": "SaLamAT@1404!",
+ "CreditPortalUrl": "https://userpanel.20card.ir/",
"Packages": {
"4": {
"GatewayAmountToman": 9700000,
diff --git a/src/CMSMicroservice.WebApi/appsettings.json b/src/CMSMicroservice.WebApi/appsettings.json
index 6b3d8e0..fc929bb 100644
--- a/src/CMSMicroservice.WebApi/appsettings.json
+++ b/src/CMSMicroservice.WebApi/appsettings.json
@@ -5,11 +5,11 @@
"UseSandbox": true
},
"TwentyCard": {
- "BaseUrl": "https://gateway.20card.ir",
+ "BaseUrl": "https://gateway.20card.ir/ipg",
"TerminalId": "165674",
"TerminalUsername": "ea5y1biehi",
- "TerminalPassword": "ea5y1biehi",
- "CreditPortalUrl": "https://20card.ir",
+ "TerminalPassword": "SaLamAT@1404!",
+ "CreditPortalUrl": "https://userpanel.20card.ir/",
"Packages": {
"4": {
"GatewayAmountToman": 9700000,