diff --git a/.gitattributes b/.gitattributes index 82b5185..679f26c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -# Keep production config from current branch during merges — never overwrite +# Keep production config from current branch during merges — never ov erwrite src/FrontOffice.Main/appsettings.Production.json merge=ours diff --git a/.gitignore b/.gitignore index 0f13c79..28a74b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # ---> VisualStudio ## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. +## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index 8d8f9e7..eac8e04 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -10,8 +10,8 @@ - - + + @@ -23,6 +23,10 @@ + + + + diff --git a/src/FrontOffice.Main/Pages/Checkout.razor b/src/FrontOffice.Main/Pages/Checkout.razor index f138e5b..c268589 100644 --- a/src/FrontOffice.Main/Pages/Checkout.razor +++ b/src/FrontOffice.Main/Pages/Checkout.razor @@ -140,7 +140,18 @@ OnClick="ProcessPayment" Disabled="@(!CanProceedToPayment || _isProcessingPayment || !_selectedPackage.SupportsDirectPurchase)" Class="mt-2"> - @(_isProcessingPayment ? "در حال پردازش..." : "پرداخت آنلاین") + @(_isProcessingPayment ? "در حال پردازش..." : "پرداخت آنلاین (زرین‌پال)") + + + + پرداخت با ۲۰کارت @if (_selectedPackage.SupportsDayaPurchase) diff --git a/src/FrontOffice.Main/Pages/Checkout.razor.cs b/src/FrontOffice.Main/Pages/Checkout.razor.cs index 5e30af5..a15bf17 100644 --- a/src/FrontOffice.Main/Pages/Checkout.razor.cs +++ b/src/FrontOffice.Main/Pages/Checkout.razor.cs @@ -107,6 +107,33 @@ public partial class Checkout } private async Task ProcessPayment() + => await StartGatewayPayment(gatewayProvider: null); + + private async Task ProcessTwentyCardPayment() + { + if (!CanProceedToPayment || _selectedPackage == null) + { + Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning); + return; + } + + var options = new DialogOptions + { + MaxWidth = MaxWidth.Small, + FullWidth = true, + CloseOnEscapeKey = true, + BackdropClick = true + }; + + var dialog = await DialogService.ShowAsync( + string.Empty, options); + var result = await dialog.Result; + if (result.Canceled) return; + + await StartGatewayPayment(gatewayProvider: "20card"); + } + + private async Task StartGatewayPayment(string? gatewayProvider) { if (_isProcessingPayment) return; @@ -120,11 +147,15 @@ public partial class Checkout try { - var response = await PackageClient.CustomerPurchasePackageAsync(new CustomerPurchasePackageRequest + var request = new CustomerPurchasePackageRequest { PackageId = _selectedPackage.Id, PurchaseMethod = PurchaseMethodEnum.PurchaseMethodGateway - }); + }; + if (!string.IsNullOrWhiteSpace(gatewayProvider)) + request.GatewayProvider = gatewayProvider; + + var response = await PackageClient.CustomerPurchasePackageAsync(request); if (!response.Success) { diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs index 2f4a266..09708a7 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor.cs @@ -421,7 +421,10 @@ public partial class Index switch (purchase.Method) { case Shared.PackagePurchaseDialog.PaymentMethodType.DirectPayment: - await ProcessDirectPayment(purchase.Package.Id); + await ProcessDirectPayment(purchase.Package.Id, gatewayProvider: null); + break; + case Shared.PackagePurchaseDialog.PaymentMethodType.TwentyCard: + await ProcessTwentyCardPayment(purchase.Package.Id); break; case Shared.PackagePurchaseDialog.PaymentMethodType.DayaLoan: await ProcessDayaLoanPayment(); @@ -430,7 +433,25 @@ public partial class Index } } - private async Task ProcessDirectPayment(long packageId) + private async Task ProcessTwentyCardPayment(long packageId) + { + var options = new DialogOptions + { + MaxWidth = MaxWidth.Small, + FullWidth = true, + CloseOnEscapeKey = true, + BackdropClick = true + }; + + var dialog = await DialogService.ShowAsync( + string.Empty, options); + var result = await dialog.Result; + if (result.Canceled) return; + + await ProcessDirectPayment(packageId, gatewayProvider: "20card"); + } + + private async Task ProcessDirectPayment(long packageId, string? gatewayProvider = null) { if (_isProcessingPayment) return; @@ -439,11 +460,15 @@ public partial class Index try { - var response = await PackageContract.CustomerPurchasePackageAsync(new CustomerPurchasePackageRequest + var request = new CustomerPurchasePackageRequest { PackageId = packageId, PurchaseMethod = PurchaseMethodEnum.PurchaseMethodGateway - }); + }; + if (!string.IsNullOrWhiteSpace(gatewayProvider)) + request.GatewayProvider = gatewayProvider; + + var response = await PackageContract.CustomerPurchasePackageAsync(request); if (!response.Success) { diff --git a/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor b/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor index c70178f..3ca2632 100644 --- a/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor +++ b/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor @@ -85,6 +85,10 @@ [SupplyParameterFromQuery(Name = "Status")] private string? Status { get; set; } + + /// ۲۰کارت callback — هم‌معنی Status زرین‌پال + [SupplyParameterFromQuery(Name = "PaymentStatus")] + private string? PaymentStatus { get; set; } private bool _isLoading = true; private bool _isSuccess; @@ -132,6 +136,10 @@ { try { + // ۲۰کارت: PaymentStatus؛ زرین‌پال: Status + if (string.IsNullOrWhiteSpace(Status) && !string.IsNullOrWhiteSpace(PaymentStatus)) + Status = PaymentStatus; + // تعیین نوع پرداخت — اگر type نباشد، پیش‌فرض خرید پکیج var type = PaymentType?.ToLowerInvariant() ?? "package"; diff --git a/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor b/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor index c997ff1..5230825 100644 --- a/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor +++ b/src/FrontOffice.Main/Shared/PackagePurchaseDialog.razor @@ -52,7 +52,7 @@ روش پرداخت خود را انتخاب کنید: - @* Direct Payment — always available *@ + @* Direct Payment (ZarinPal) — always available *@ @@ -61,13 +61,29 @@ پرداخت مستقیم - پرداخت آنی از طریق درگاه بانکی + پرداخت آنی از طریق درگاه بانکی (زرین‌پال) + @* TwentyCard IPG *@ + + + + + + پرداخت با ۲۰کارت + پرداخت از طریق درگاه ۲۰کارت (نیاز به تأمین اعتبار) + + + + + + @* Daya Loan — only for base package AND first purchase cycle *@ @if (_selectedPackage.SupportsDayaPurchase && PurchaseCycleCount == 0) { @@ -229,6 +245,6 @@ : "background:rgba(16,185,129,.12); color:#10b981;"; } - public enum PaymentMethodType { DirectPayment, DayaLoan } + public enum PaymentMethodType { DirectPayment, TwentyCard, DayaLoan } public record PackagePurchaseResult(PackageDto Package, PaymentMethodType Method); } diff --git a/src/FrontOffice.Main/Shared/TwentyCardCreditDialog.razor b/src/FrontOffice.Main/Shared/TwentyCardCreditDialog.razor new file mode 100644 index 0000000..1f9b13f --- /dev/null +++ b/src/FrontOffice.Main/Shared/TwentyCardCreditDialog.razor @@ -0,0 +1,60 @@ +@inject IConfiguration Configuration +@inject IJSRuntime JSRuntime + + + + + + تأمین اعتبار ۲۰کارت + + + + + + برای پرداخت از طریق درگاه ۲۰کارت، ابتدا باید اعتبار حساب ۲۰کارت خود را تأمین کنید. + پس از تأمین اعتبار، روی «ادامه پرداخت» بزنید تا به درگاه هدایت شوید. + + + + اگر قبلاً اعتبار را تأمین کرده‌اید، می‌توانید مستقیم ادامه دهید. + + + + + انصراف + + تأمین اعتبار + + + ادامه پرداخت + + + + +@code { + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = default!; + + private string _creditPortalUrl = string.Empty; + + protected override void OnInitialized() + { + _creditPortalUrl = Configuration["TwentyCard:CreditPortalUrl"] ?? string.Empty; + } + + private async Task OpenCreditPortal() + { + if (string.IsNullOrWhiteSpace(_creditPortalUrl)) return; + await JSRuntime.InvokeVoidAsync("open", _creditPortalUrl, "_blank"); + } + + private void ContinuePayment() => MudDialog.Close(DialogResult.Ok(true)); + + private void Cancel() => MudDialog.Cancel(); +} diff --git a/src/FrontOffice.Main/appsettings.Production.json b/src/FrontOffice.Main/appsettings.Production.json index 6f08557..3411fe9 100644 --- a/src/FrontOffice.Main/appsettings.Production.json +++ b/src/FrontOffice.Main/appsettings.Production.json @@ -1,6 +1,9 @@ { "GwUrl": "https://cms.kbs1.ir", "DownloadUrl": "", + "TwentyCard": { + "CreditPortalUrl": "https://20card.ir" + }, "EncryptionSettings": { "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=", "IV": "1wyF3Tt142MOkCpIyCxh/g==" diff --git a/src/FrontOffice.Main/appsettings.Staging.json b/src/FrontOffice.Main/appsettings.Staging.json index f07c714..038c752 100644 --- a/src/FrontOffice.Main/appsettings.Staging.json +++ b/src/FrontOffice.Main/appsettings.Staging.json @@ -6,6 +6,9 @@ }, "GwUrl": "https://cms.se.kbs1.ir", "DownloadUrl": "", + "TwentyCard": { + "CreditPortalUrl": "https://20card.ir" + }, "EncryptionSettings": { "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=", "IV": "1wyF3Tt142MOkCpIyCxh/g==" diff --git a/src/FrontOffice.Main/appsettings.json b/src/FrontOffice.Main/appsettings.json index a8b7510..9ecc7ab 100644 --- a/src/FrontOffice.Main/appsettings.json +++ b/src/FrontOffice.Main/appsettings.json @@ -6,6 +6,9 @@ }, "GwUrl": "https://cms.se.kbs1.ir", "DownloadUrl": "", + "TwentyCard": { + "CreditPortalUrl": "https://20card.ir" + }, "EncryptionSettings": { "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=", "IV": "1wyF3Tt142MOkCpIyCxh/g=="