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,