Compare commits
4 Commits
3c216feb32
...
d59d829d5f
| Author | SHA1 | Date | |
|---|---|---|---|
| d59d829d5f | |||
| 81d139cdab | |||
| d44e99f4ab | |||
| cd835ae6f9 |
@@ -67,10 +67,12 @@
|
|||||||
| 4.5 | `WalletService.VerifyMagicChargeAsync` | `UserWalletContract.VerifyMagicCharge` | `/profile/payment-callback?type=magic-wallet` | تأیید شارژ Magic پس از درگاه |
|
| 4.5 | `WalletService.VerifyMagicChargeAsync` | `UserWalletContract.VerifyMagicCharge` | `/profile/payment-callback?type=magic-wallet` | تأیید شارژ Magic پس از درگاه |
|
||||||
| 4.6 | `WalletService.InitiateDiscountChargeAsync` | `UserWalletContract.InitiateDiscountCharge` | `/profile/charge-discount-wallet` | شروع شارژ کیف پول تخفیفی → درگاه |
|
| 4.6 | `WalletService.InitiateDiscountChargeAsync` | `UserWalletContract.InitiateDiscountCharge` | `/profile/charge-discount-wallet` | شروع شارژ کیف پول تخفیفی → درگاه |
|
||||||
| 4.7 | `WalletService.VerifyDiscountChargeAsync` | `UserWalletContract.VerifyDiscountCharge` | `/profile/payment-callback?type=discount-wallet` | تأیید شارژ کیف تخفیفی |
|
| 4.7 | `WalletService.VerifyDiscountChargeAsync` | `UserWalletContract.VerifyDiscountCharge` | `/profile/payment-callback?type=discount-wallet` | تأیید شارژ کیف تخفیفی |
|
||||||
| 4.8 | `WalletService.GetWithdrawalsAsync` | `UserWalletContract.GetCustomerWithdrawals` | `/profile/withdrawal-requests` | لیست درخواستهای برداشت |
|
| 4.8 | `WalletService.InitiateCreditChargeAsync` | `UserWalletContract.InitiateCreditCharge` | `/profile/charge-credit-wallet` | شروع شارژ کیف پول اصلی → درگاه |
|
||||||
| 4.9 | `WalletService.GetWithdrawalSettingsAsync` | `UserWalletContract.GetCustomerWithdrawalSettings` | `/profile/withdrawal-requests` | حداقل مبلغ برداشت |
|
| 4.9 | `WalletService.VerifyCreditChargeAsync` | `UserWalletContract.VerifyCreditCharge` | `/profile/payment-callback?type=credit-wallet` | تأیید شارژ کیف اصلی |
|
||||||
| 4.10 | `WalletService.RequestWithdrawalAsync` | `UserWalletContract.CustomerWithdrawBalance` | `/profile/withdrawal-requests` | ثبت درخواست برداشت پاداش (با payoutId کمیسیون) |
|
| 4.10 | `WalletService.GetWithdrawalsAsync` | `UserWalletContract.GetCustomerWithdrawals` | `/profile/withdrawal-requests` | لیست درخواستهای برداشت |
|
||||||
| 4.11 | `CommissionService.GetWithdrawablePayoutsAsync` | `CommissionContract.GetMyCommissionPayouts` | `/profile/withdrawal-requests` | پاداشهای قابل برداشت برای انتخاب |
|
| 4.11 | `WalletService.GetWithdrawalSettingsAsync` | `UserWalletContract.GetCustomerWithdrawalSettings` | `/profile/withdrawal-requests` | حداقل مبلغ برداشت |
|
||||||
|
| 4.12 | `WalletService.RequestWithdrawalAsync` | `UserWalletContract.CustomerWithdrawBalance` | `/profile/withdrawal-requests` | ثبت درخواست برداشت پاداش (با payoutId کمیسیون) |
|
||||||
|
| 4.13 | `CommissionService.GetWithdrawablePayoutsAsync` | `CommissionContract.GetMyCommissionPayouts` | `/profile/withdrawal-requests` | پاداشهای قابل برداشت برای انتخاب |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -81,8 +83,9 @@
|
|||||||
| 5.1 | `PackageContract.CustomerVerifyPackagePurchase` | `PackageContract` | `/profile/payment-callback` (type=package) | تأیید خرید پکیج |
|
| 5.1 | `PackageContract.CustomerVerifyPackagePurchase` | `PackageContract` | `/profile/payment-callback` (type=package) | تأیید خرید پکیج |
|
||||||
| 5.2 | `WalletService.VerifyMagicChargeAsync` | `UserWalletContract` | `/profile/payment-callback` (type=magic-wallet) | تأیید شارژ Magic |
|
| 5.2 | `WalletService.VerifyMagicChargeAsync` | `UserWalletContract` | `/profile/payment-callback` (type=magic-wallet) | تأیید شارژ Magic |
|
||||||
| 5.3 | `WalletService.VerifyDiscountChargeAsync` | `UserWalletContract` | `/profile/payment-callback` (type=discount-wallet) | تأیید شارژ تخفیفی |
|
| 5.3 | `WalletService.VerifyDiscountChargeAsync` | `UserWalletContract` | `/profile/payment-callback` (type=discount-wallet) | تأیید شارژ تخفیفی |
|
||||||
| 5.4 | `DiscountOrderService.VerifyDiscountOrderPaymentAsync` | `DiscountOrderContract.CustomerVerifyDiscountOrderPayment` | `/profile/payment-callback` (type=discount-order) | تأیید پرداخت سفارش فروشگاه تخفیفی |
|
| 5.4 | `WalletService.VerifyCreditChargeAsync` | `UserWalletContract` | `/profile/payment-callback` (type=credit-wallet) | تأیید شارژ کیف اصلی |
|
||||||
| 5.5 | `UserContract.GetUserForCustomer` + `AuthService.InitUserAuthInfo` | `UserContract` | `/profile/payment-callback` | بهروزرسانی claims کاربر پس از پرداخت موفق |
|
| 5.5 | `DiscountOrderService.VerifyDiscountOrderPaymentAsync` | `DiscountOrderContract.CustomerVerifyDiscountOrderPayment` | `/profile/payment-callback` (type=discount-order) | تأیید پرداخت سفارش فروشگاه تخفیفی |
|
||||||
|
| 5.6 | `UserContract.GetUserForCustomer` + `AuthService.InitUserAuthInfo` | `UserContract` | `/profile/payment-callback` | بهروزرسانی claims کاربر پس از پرداخت موفق |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
<PackageReference Include="DateTimeConverterCL" Version="1.0.0" />
|
||||||
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
<!-- Replace all FrontOffice.BFF protobuf packages with CMS protobuf -->
|
||||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.199" />
|
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.202" />
|
||||||
<!-- <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
<!-- <ProjectReference Include="../../../CMS/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj" />-->
|
||||||
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
<PackageReference Include="MudBlazor" Version="8.14.0" />
|
||||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
@attribute [Route(RouteConstants.Profile.ChargeCreditWallet)]
|
||||||
|
@attribute [Authorize]
|
||||||
|
|
||||||
|
<PageTitle>شارژ کیفپول اصلی</PageTitle>
|
||||||
|
|
||||||
|
<MudContainer MaxWidth="MaxWidth.Medium" Class="py-6">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<PageHeader Title="💳 شارژ کیفپول اصلی" BackHref="@RouteConstants.Profile.Wallet" />
|
||||||
|
|
||||||
|
@if (_paymentResult != null)
|
||||||
|
{
|
||||||
|
<MudAlert Severity="@(_paymentResult == "success" ? MudBlazor.Severity.Success : MudBlazor.Severity.Error)"
|
||||||
|
Class="rounded-lg" Variant="Variant.Filled">
|
||||||
|
@if (_paymentResult == "success")
|
||||||
|
{
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<span>شارژ کیف پول اصلی با موفقیت انجام شد! ✅</span>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Small"
|
||||||
|
StartIcon="@Icons.Material.Filled.ShoppingCart"
|
||||||
|
OnClick="ContinueShoppingAsync">
|
||||||
|
ادامه خرید
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
}
|
||||||
|
else if (_paymentResult == "cancelled")
|
||||||
|
{
|
||||||
|
<span>پرداخت توسط شما لغو شد.</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>پرداخت ناموفق بود. لطفاً دوباره تلاش کنید.</span>
|
||||||
|
}
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudPaper Elevation="2" Class="pa-5 rounded-lg">
|
||||||
|
<MudText Typo="Typo.h6" Class="mb-3">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.AccountBalanceWallet" Class="ml-1" />
|
||||||
|
شارژ موجودی اصلی
|
||||||
|
</MudText>
|
||||||
|
|
||||||
|
<MudAlert Severity="MudBlazor.Severity.Info" Dense="true" Class="mb-3" Icon="@Icons.Material.Filled.Info">
|
||||||
|
مبلغ وارد شده (به تومان) از طریق درگاه پرداخت به موجودی اصلی شما اضافه میشود.
|
||||||
|
از موجودی اصلی برای خرید از فروشگاه عادی و پرداخت سفارش استفاده میشود.
|
||||||
|
</MudAlert>
|
||||||
|
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudNumericField @bind-Value="_chargeAmount"
|
||||||
|
Label="مبلغ (تومان)"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
Min="10_000"
|
||||||
|
Max="1_000_000_000"
|
||||||
|
Format="N0"
|
||||||
|
Adornment="Adornment.Start"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Payments"
|
||||||
|
HelperText="حداقل ۱۰,۰۰۰ و حداکثر ۱,۰۰۰,۰۰۰,۰۰۰ تومان" />
|
||||||
|
|
||||||
|
<MudStack Row="true" Spacing="1" Class="flex-wrap">
|
||||||
|
@foreach (var preset in _presetAmounts)
|
||||||
|
{
|
||||||
|
<MudButton Variant="Variant.Outlined" Size="Size.Small"
|
||||||
|
Color="@(_chargeAmount == preset ? Color.Primary : Color.Default)"
|
||||||
|
OnClick="() => _chargeAmount = preset"
|
||||||
|
Class="rounded-pill">
|
||||||
|
@FormatToman(preset)
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
@if (_chargeAmount > 0)
|
||||||
|
{
|
||||||
|
<MudPaper Outlined="true" Class="pa-3 rounded-lg" Style="background: #eff6ff;">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2">مبلغ پرداخت:</MudText>
|
||||||
|
<MudText Typo="Typo.body2"><strong>@FormatToman(_chargeAmount)</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2">موجودی دریافتی:</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Primary"><strong>@FormatToman(_chargeAmount)</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Large"
|
||||||
|
FullWidth="true"
|
||||||
|
Disabled="@(_isProcessing || _chargeAmount < 10_000)"
|
||||||
|
OnClick="StartCreditCharge"
|
||||||
|
StartIcon="@Icons.Material.Filled.Payment"
|
||||||
|
Class="rounded-lg mt-2">
|
||||||
|
@if (_isProcessing)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="me-2" />
|
||||||
|
<span>در حال انتقال به درگاه...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>پرداخت با کارت بانکی</span>
|
||||||
|
}
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
|
||||||
|
<MudExpansionPanels Elevation="1" Class="rounded-lg">
|
||||||
|
<MudExpansionPanel Text="قوانین شارژ کیف پول اصلی" MaxHeight="500" IsInitiallyExpanded="false">
|
||||||
|
<MudList T="string" Dense="true">
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.CheckCircle" IconColor="Color.Success">
|
||||||
|
موجودی اصلی برای خرید از فروشگاه عادی و پرداخت سفارش قابل استفاده است
|
||||||
|
</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.CheckCircle" IconColor="Color.Success">
|
||||||
|
حداقل مبلغ شارژ: ۱۰,۰۰۰ تومان
|
||||||
|
</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.CheckCircle" IconColor="Color.Success">
|
||||||
|
مبلغ واریزی بدون ضریب به موجودی اضافه میشود (۱:۱)
|
||||||
|
</MudListItem>
|
||||||
|
<MudListItem T="string" Icon="@Icons.Material.Filled.Info" IconColor="Color.Info">
|
||||||
|
پرداخت از طریق درگاه بانکی انجام میشود
|
||||||
|
</MudListItem>
|
||||||
|
</MudList>
|
||||||
|
</MudExpansionPanel>
|
||||||
|
</MudExpansionPanels>
|
||||||
|
</MudStack>
|
||||||
|
</MudContainer>
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using FrontOffice.Main.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Pages.Profile;
|
||||||
|
|
||||||
|
public partial class ChargeCreditWallet : ComponentBase
|
||||||
|
{
|
||||||
|
private bool _isProcessing;
|
||||||
|
private long _chargeAmount;
|
||||||
|
private string? _paymentResult;
|
||||||
|
private string? _pendingReturnUrl;
|
||||||
|
|
||||||
|
private readonly long[] _presetAmounts = { 500_000, 1_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000 };
|
||||||
|
|
||||||
|
[SupplyParameterFromQuery(Name = "payment")]
|
||||||
|
public string? PaymentQueryParam { get; set; }
|
||||||
|
|
||||||
|
[SupplyParameterFromQuery(Name = "amount")]
|
||||||
|
public long? AmountQueryParam { get; set; }
|
||||||
|
|
||||||
|
[SupplyParameterFromQuery(Name = "returnUrl")]
|
||||||
|
public string? ReturnUrlQueryParam { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_paymentResult = PaymentQueryParam;
|
||||||
|
|
||||||
|
if (AmountQueryParam is > 0)
|
||||||
|
_chargeAmount = CreditChargeNavigation.NormalizeChargeAmount(AmountQueryParam.Value);
|
||||||
|
|
||||||
|
if (CreditChargeNavigation.IsValidReturnUrl(ReturnUrlQueryParam))
|
||||||
|
_pendingReturnUrl = ReturnUrlQueryParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender && !string.IsNullOrEmpty(_pendingReturnUrl))
|
||||||
|
{
|
||||||
|
await CreditChargeNavigation.SaveReturnUrlAsync(JSRuntime, _pendingReturnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartCreditCharge()
|
||||||
|
{
|
||||||
|
if (_chargeAmount <= 0 || _isProcessing) return;
|
||||||
|
|
||||||
|
_isProcessing = true;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_pendingReturnUrl))
|
||||||
|
await CreditChargeNavigation.SaveReturnUrlAsync(JSRuntime, _pendingReturnUrl);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var storedReturnUrl = await CreditChargeNavigation.GetReturnUrlAsync(JSRuntime);
|
||||||
|
if (CreditChargeNavigation.IsValidReturnUrl(storedReturnUrl))
|
||||||
|
_pendingReturnUrl = storedReturnUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (success, gatewayUrl, error) = await WalletService.InitiateCreditChargeAsync(_chargeAmount);
|
||||||
|
|
||||||
|
if (success && !string.IsNullOrEmpty(gatewayUrl))
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo(gatewayUrl, forceLoad: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Snackbar.Add(error ?? "خطا در ایجاد درخواست پرداخت", Severity.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Snackbar.Add($"خطا: {ex.Message}", Severity.Error);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isProcessing = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ContinueShoppingAsync()
|
||||||
|
{
|
||||||
|
var returnUrl = await CreditChargeNavigation.GetReturnUrlAsync(JSRuntime);
|
||||||
|
await CreditChargeNavigation.ClearReturnUrlAsync(JSRuntime);
|
||||||
|
|
||||||
|
if (CreditChargeNavigation.IsValidReturnUrl(returnUrl))
|
||||||
|
Navigation.NavigateTo(returnUrl!);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatToman(long toman)
|
||||||
|
=> string.Format("{0:N0} تومان", toman);
|
||||||
|
}
|
||||||
@@ -143,6 +143,9 @@
|
|||||||
case "discount-wallet":
|
case "discount-wallet":
|
||||||
await VerifyDiscountWalletCharge();
|
await VerifyDiscountWalletCharge();
|
||||||
break;
|
break;
|
||||||
|
case "credit-wallet":
|
||||||
|
await VerifyCreditWalletCharge();
|
||||||
|
break;
|
||||||
case "discount-order":
|
case "discount-order":
|
||||||
await VerifyDiscountOrderPayment();
|
await VerifyDiscountOrderPayment();
|
||||||
break;
|
break;
|
||||||
@@ -255,6 +258,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تأیید شارژ کیف پول اصلی
|
||||||
|
/// </summary>
|
||||||
|
private async Task VerifyCreditWalletCharge()
|
||||||
|
{
|
||||||
|
_successReturnUrl = "/profile/charge-credit-wallet?payment=success";
|
||||||
|
_successReturnText = "بازگشت به شارژ کیف پول اصلی";
|
||||||
|
_failReturnUrl = "/profile/charge-credit-wallet?payment=failed";
|
||||||
|
_failReturnText = "بازگشت به شارژ کیف پول اصلی";
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Authority))
|
||||||
|
{
|
||||||
|
_isSuccess = false;
|
||||||
|
_message = "کد Authority نامعتبر است";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (success, message) = await WalletService.VerifyCreditChargeAsync(
|
||||||
|
Authority, Status ?? "NOK");
|
||||||
|
|
||||||
|
_isSuccess = success;
|
||||||
|
_message = message;
|
||||||
|
|
||||||
|
if (_isSuccess)
|
||||||
|
{
|
||||||
|
await UpdateWalletBalance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// تأیید پرداخت سفارش فروشگاه تخفیفی
|
/// تأیید پرداخت سفارش فروشگاه تخفیفی
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -308,6 +340,7 @@
|
|||||||
{
|
{
|
||||||
"magic-wallet" => "/profile/magic-wallet",
|
"magic-wallet" => "/profile/magic-wallet",
|
||||||
"discount-wallet" => "/profile/charge-discount-wallet",
|
"discount-wallet" => "/profile/charge-discount-wallet",
|
||||||
|
"credit-wallet" => "/profile/charge-credit-wallet",
|
||||||
"discount-order" => "/discount-store",
|
"discount-order" => "/discount-store",
|
||||||
_ => "/profile"
|
_ => "/profile"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,17 @@
|
|||||||
درخواستهای برداشت
|
درخواستهای برداشت
|
||||||
</MudButton>
|
</MudButton>
|
||||||
|
|
||||||
|
<!-- دکمه شارژ کیفپول اصلی -->
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
Size="Size.Large"
|
||||||
|
FullWidth="true"
|
||||||
|
StartIcon="@Icons.Material.Filled.AccountBalanceWallet"
|
||||||
|
Href="@RouteConstants.Profile.ChargeCreditWallet"
|
||||||
|
Class="rounded-lg">
|
||||||
|
شارژ کیفپول اصلی
|
||||||
|
</MudButton>
|
||||||
|
|
||||||
<!-- دکمه شارژ کیفپول اعتباری -->
|
<!-- دکمه شارژ کیفپول اعتباری -->
|
||||||
<MudButton Variant="Variant.Filled"
|
<MudButton Variant="Variant.Filled"
|
||||||
Color="Color.Info"
|
Color="Color.Info"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
using CMSMicroservice.Protobuf.Protos.UserAddress;
|
||||||
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
using CMSMicroservice.Protobuf.Protos.UserOrder;
|
||||||
|
using FrontOffice.Main.Shared;
|
||||||
using FrontOffice.Main.Utilities;
|
using FrontOffice.Main.Utilities;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using MudBlazor;
|
using MudBlazor;
|
||||||
@@ -33,7 +34,6 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
{
|
{
|
||||||
await AuthDialogService.ShowAuthDialogAsync();
|
await AuthDialogService.ShowAuthDialogAsync();
|
||||||
}
|
}
|
||||||
// لود سبد خرید (فقط اگر کاربر لاگین کرده باشد)
|
|
||||||
await Cart.EnsureInitializedAsync();
|
await Cart.EnsureInitializedAsync();
|
||||||
await LoadAddresses();
|
await LoadAddresses();
|
||||||
await LoadWalletBalance();
|
await LoadWalletBalance();
|
||||||
@@ -43,7 +43,6 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
// بارگذاری نرخ VAT
|
|
||||||
await VAT.LoadAsync();
|
await VAT.LoadAsync();
|
||||||
}
|
}
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
@@ -52,9 +51,7 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
private async Task LoadWalletBalance()
|
private async Task LoadWalletBalance()
|
||||||
{
|
{
|
||||||
var walletResult = await WalletService.GetBalancesAsync();
|
var walletResult = await WalletService.GetBalancesAsync();
|
||||||
walletBalance = walletResult.CreditBalance
|
walletBalance = walletResult.CreditBalance;
|
||||||
// + walletResult.NetworkBalance
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadAddresses()
|
private async Task LoadAddresses()
|
||||||
@@ -93,11 +90,15 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var totalRequired = VAT.AddVAT(Cart.Total);
|
||||||
|
if (await TryHandleInsufficientBalanceAsync(totalRequired))
|
||||||
|
return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var request = new SubmitShopBuyOrderRequest
|
var request = new SubmitShopBuyOrderRequest
|
||||||
{
|
{
|
||||||
TotalAmount = VAT.AddVAT(Cart.Total)
|
TotalAmount = totalRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = await UserOrderContract.SubmitShopBuyOrderAsync(request);
|
var response = await UserOrderContract.SubmitShopBuyOrderAsync(request);
|
||||||
@@ -107,15 +108,54 @@ public partial class CheckoutSummary : ComponentBase
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (CreditChargeNavigation.IsInsufficientWalletBalance(ex))
|
||||||
|
{
|
||||||
|
await LoadWalletBalance();
|
||||||
|
await TryHandleInsufficientBalanceAsync(totalRequired);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Snackbar.Add($"خطا در ثبت سفارش: {ex.Message}", Severity.Error);
|
Snackbar.Add($"خطا در ثبت سفارش: {ex.Message}", Severity.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> TryHandleInsufficientBalanceAsync(long totalRequired)
|
||||||
|
{
|
||||||
|
if (walletBalance >= totalRequired)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var shortfall = totalRequired - walletBalance;
|
||||||
|
var parameters = new DialogParameters<InsufficientCreditDialog>
|
||||||
|
{
|
||||||
|
{ x => x.CurrentBalance, walletBalance },
|
||||||
|
{ x => x.RequiredAmount, totalRequired },
|
||||||
|
{ x => x.ShortfallAmount, shortfall }
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<InsufficientCreditDialog>(
|
||||||
|
"موجودی کافی نیست",
|
||||||
|
parameters,
|
||||||
|
new DialogOptions
|
||||||
|
{
|
||||||
|
CloseOnEscapeKey = true,
|
||||||
|
MaxWidth = MaxWidth.Small,
|
||||||
|
FullWidth = true
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (result is { Canceled: false } && result.Data is long chargeShortfall)
|
||||||
|
{
|
||||||
|
var chargeAmount = CreditChargeNavigation.NormalizeChargeAmount(chargeShortfall);
|
||||||
|
Navigation.NavigateTo(CreditChargeNavigation.BuildChargeUrl(
|
||||||
|
chargeAmount,
|
||||||
|
RouteConstants.Store.CheckoutSummary));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// محاسبه مالیات بر ارزش افزوده
|
|
||||||
/// </summary>
|
|
||||||
private long CalculateVAT() => VAT.CalculateVAT(Cart.Total);
|
private long CalculateVAT() => VAT.CalculateVAT(Cart.Total);
|
||||||
|
|
||||||
private static string GetProductImageUrl(string? imageUrl)
|
private static string GetProductImageUrl(string? imageUrl)
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
<MudDialog>
|
||||||
|
<TitleContent>
|
||||||
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||||
|
<MudIcon Icon="@Icons.Material.Filled.AccountBalanceWallet" Color="Color.Warning" />
|
||||||
|
<MudText Typo="Typo.h6">موجودی کیف پول اصلی کافی نیست</MudText>
|
||||||
|
</MudStack>
|
||||||
|
</TitleContent>
|
||||||
|
<DialogContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||||
|
برای تکمیل این سفارش، موجودی اصلی شما کافی نیست. میتوانید کیف پول را شارژ کنید و سپس سفارش را ثبت کنید.
|
||||||
|
</MudText>
|
||||||
|
<MudPaper Outlined="true" Class="pa-3 rounded-lg">
|
||||||
|
<MudStack Spacing="1">
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2">موجودی فعلی:</MudText>
|
||||||
|
<MudText Typo="Typo.body2"><strong>@FormatPrice(CurrentBalance)</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2">مبلغ سفارش:</MudText>
|
||||||
|
<MudText Typo="Typo.body2"><strong>@FormatPrice(RequiredAmount)</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
<MudDivider Class="my-1" />
|
||||||
|
<MudStack Row="true" Justify="Justify.SpaceBetween">
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Error">کمبود:</MudText>
|
||||||
|
<MudText Typo="Typo.body2" Color="Color.Error"><strong>@FormatPrice(ShortfallAmount)</strong></MudText>
|
||||||
|
</MudStack>
|
||||||
|
</MudStack>
|
||||||
|
</MudPaper>
|
||||||
|
</MudStack>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">بستن</MudButton>
|
||||||
|
<MudButton Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary"
|
||||||
|
StartIcon="@Icons.Material.Filled.Payment"
|
||||||
|
OnClick="GoToCharge">
|
||||||
|
شارژ حساب اصلی
|
||||||
|
</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public long CurrentBalance { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public long RequiredAmount { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public long ShortfallAmount { get; set; }
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
private void GoToCharge() => MudDialog.Close(DialogResult.Ok(ShortfallAmount));
|
||||||
|
|
||||||
|
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using Grpc.Core;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
|
namespace FrontOffice.Main.Utilities;
|
||||||
|
|
||||||
|
public static class CreditChargeNavigation
|
||||||
|
{
|
||||||
|
public const long MinChargeAmount = 10_000;
|
||||||
|
public const string ReturnUrlStorageKey = "credit_charge_return_url";
|
||||||
|
private const string InsufficientBalanceMarker = "موجودی کیف پول کافی نیست";
|
||||||
|
|
||||||
|
public static long NormalizeChargeAmount(long shortfall) =>
|
||||||
|
Math.Max(shortfall, MinChargeAmount);
|
||||||
|
|
||||||
|
public static string BuildChargeUrl(long amount, string? returnUrl)
|
||||||
|
{
|
||||||
|
var url = $"{RouteConstants.Profile.ChargeCreditWallet}?amount={amount}";
|
||||||
|
if (IsValidReturnUrl(returnUrl))
|
||||||
|
url += $"&returnUrl={Uri.EscapeDataString(returnUrl!)}";
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsValidReturnUrl(string? url) =>
|
||||||
|
!string.IsNullOrWhiteSpace(url)
|
||||||
|
&& url.StartsWith('/')
|
||||||
|
&& !url.StartsWith("//", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public static bool IsInsufficientWalletBalanceMessage(string? message) =>
|
||||||
|
message?.Contains(InsufficientBalanceMarker, StringComparison.Ordinal) == true;
|
||||||
|
|
||||||
|
public static bool IsInsufficientWalletBalance(Exception ex) =>
|
||||||
|
ex switch
|
||||||
|
{
|
||||||
|
RpcException rpc => IsInsufficientWalletBalanceMessage(rpc.Status.Detail)
|
||||||
|
|| IsInsufficientWalletBalanceMessage(rpc.Message),
|
||||||
|
_ => IsInsufficientWalletBalanceMessage(ex.Message)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static async Task SaveReturnUrlAsync(IJSRuntime js, string returnUrl)
|
||||||
|
{
|
||||||
|
if (!IsValidReturnUrl(returnUrl)) return;
|
||||||
|
await js.InvokeVoidAsync("sessionStorage.setItem", ReturnUrlStorageKey, returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string?> GetReturnUrlAsync(IJSRuntime js) =>
|
||||||
|
await js.InvokeAsync<string?>("sessionStorage.getItem", ReturnUrlStorageKey);
|
||||||
|
|
||||||
|
public static async Task ClearReturnUrlAsync(IJSRuntime js) =>
|
||||||
|
await js.InvokeVoidAsync("sessionStorage.removeItem", ReturnUrlStorageKey);
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ public static class RouteConstants
|
|||||||
public const string Wallet = "/profile/wallet";
|
public const string Wallet = "/profile/wallet";
|
||||||
public const string MagicWallet = "/profile/magic-wallet";
|
public const string MagicWallet = "/profile/magic-wallet";
|
||||||
public const string ChargeDiscountWallet = "/profile/charge-discount-wallet";
|
public const string ChargeDiscountWallet = "/profile/charge-discount-wallet";
|
||||||
|
public const string ChargeCreditWallet = "/profile/charge-credit-wallet";
|
||||||
public const string WithdrawalRequests = "/profile/withdrawal-requests";
|
public const string WithdrawalRequests = "/profile/withdrawal-requests";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,26 @@ public class WalletService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<(bool Success, string? GatewayUrl, string? Error)> InitiateCreditChargeAsync(long amount)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _client.InitiateCreditChargeAsync(new InitiateCreditChargeRequest
|
||||||
|
{
|
||||||
|
Amount = amount
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.IsSuccess)
|
||||||
|
return (true, response.GatewayUrl, null);
|
||||||
|
|
||||||
|
return (false, null, response.ErrorMessage);
|
||||||
|
}
|
||||||
|
catch (Grpc.Core.RpcException ex)
|
||||||
|
{
|
||||||
|
return (false, null, ex.Status.Detail ?? "خطا در ارتباط با سرور");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ============= Wallet Verify Methods =============
|
// ============= Wallet Verify Methods =============
|
||||||
|
|
||||||
public async Task<(bool Success, string Message)> VerifyMagicChargeAsync(string authority, string status)
|
public async Task<(bool Success, string Message)> VerifyMagicChargeAsync(string authority, string status)
|
||||||
@@ -304,6 +324,24 @@ public class WalletService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<(bool Success, string Message)> VerifyCreditChargeAsync(string authority, string status)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await _client.VerifyCreditChargeAsync(new VerifyWalletChargeRequest
|
||||||
|
{
|
||||||
|
Authority = authority,
|
||||||
|
Status = status
|
||||||
|
});
|
||||||
|
|
||||||
|
return (response.Success, response.Message);
|
||||||
|
}
|
||||||
|
catch (Grpc.Core.RpcException ex)
|
||||||
|
{
|
||||||
|
return (false, ex.Status.Detail ?? "خطا در بررسی وضعیت پرداخت");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int GetWalletMode()
|
public int GetWalletMode()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
Reference in New Issue
Block a user