feat: add payment callback and referral link features
Build and Deploy / build (push) Failing after 1m21s
Build and Deploy / build (push) Failing after 1m21s
This commit is contained in:
@@ -21,43 +21,74 @@
|
||||
</div>
|
||||
</MudStack>
|
||||
<MudSpacer/>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="Financing">تامین اعتبار خرید</MudButton>
|
||||
@* <MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.ShoppingCart"
|
||||
OnClick="OpenPurchaseOptions">تامین اعتبار</MudButton> *@
|
||||
</MudStack>
|
||||
|
||||
|
||||
<!-- Referral Code Section -->
|
||||
<MudDivider />
|
||||
<MudStack Spacing="3">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.subtitle1">کد دعوت شما</MudText>
|
||||
<MudSpacer />
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Share"
|
||||
OnClick="ShareReferralCode">
|
||||
اشتراکگذاری
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
این کد را با دوستان خود به اشتراک بگذارید تا از خریدهای آنها پاداش دریافت کنید.
|
||||
</MudText>
|
||||
|
||||
@if (CanShowReferralLink)
|
||||
{
|
||||
<MudStack Spacing="3">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.subtitle1">کد دعوت شما</MudText>
|
||||
<MudSpacer />
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Share"
|
||||
OnClick="ShareReferralCode">
|
||||
اشتراکگذاری
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
این کد را با دوستان خود به اشتراک بگذارید تا از خریدهای آنها پاداش دریافت کنید.
|
||||
</MudText>
|
||||
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudTextField @bind-Value="_userProfile.ReferralCode"
|
||||
Label="کد دعوت"
|
||||
Variant="Variant.Outlined"
|
||||
ReadOnly="true"
|
||||
Class="flex-grow-1"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentIcon="@Icons.Material.Filled.ContentCopy"
|
||||
OnAdornmentClick="CopyReferralCode" />
|
||||
</MudStack>
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudTextField @bind-Value="_userProfile.ReferralCode"
|
||||
Label="کد دعوت"
|
||||
Variant="Variant.Outlined"
|
||||
ReadOnly="true"
|
||||
Class="flex-grow-1"
|
||||
Adornment="Adornment.End"
|
||||
AdornmentIcon="@Icons.Material.Filled.ContentCopy"
|
||||
OnAdornmentClick="CopyReferralCode" />
|
||||
</MudStack>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(_copyMessage))
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Success">@(_copyMessage)</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
@if (!string.IsNullOrWhiteSpace(_copyMessage))
|
||||
{
|
||||
<MudText Typo="Typo.caption" Color="Color.Success">@(_copyMessage)</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudPaper Class="my-3 pa-3 border border-secondary">
|
||||
<MudStack Spacing="3" AlignItems="AlignItems.Center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Lock" Size="Size.Large" Color="Color.Info" />
|
||||
<MudText Typo="Typo.h6" Align="Align.Center" Color="Color.Info">
|
||||
لینک دعوت شما هنوز فعال نشده است
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body1" Align="Align.Center" Class="mud-text-secondary">
|
||||
برای فعالسازی لینک دعوت و دعوت دوستان خود به جمع مشتریان ویژه کارابازار، کافی است ابتدا <strong>پکیج پایه ۵۶ میلیونی</strong> را تهیه کنید و سپس در <strong>باشگاه مشتریان</strong> عضو شوید.
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2" Align="Align.Center" Class="mud-text-secondary">
|
||||
پس از تکمیل این مراحل، میتوانید از مزایای ویژه عضویت در شبکه فروش بهرهمند شده و با معرفی دوستان، کمیسیون دریافت کنید.
|
||||
</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
Size="Size.Large"
|
||||
StartIcon="@Icons.Material.Filled.ShoppingCart"
|
||||
OnClick="OpenPurchaseOptions">
|
||||
شروع فرآیند تامین اعتبار
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
}
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
@@ -199,3 +230,67 @@
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudContainer>
|
||||
|
||||
<!-- Purchase Options Bottom Sheet -->
|
||||
<MudDialog @bind-Visible="_showPurchaseBottomSheet">
|
||||
<DialogContent>
|
||||
<MudStack Spacing="3" Class="pa-2">
|
||||
<MudText Typo="Typo.h6" Align="Align.Center">
|
||||
خرید پکیج پایه ۵۶ میلیون تومان
|
||||
</MudText>
|
||||
|
||||
<MudDivider />
|
||||
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
برای خرید پکیج پایه، یکی از روشهای زیر را انتخاب کنید:
|
||||
</MudText>
|
||||
|
||||
<!-- Direct Payment Option -->
|
||||
<MudPaper Elevation="0" Class="pa-4 rounded-lg" Style="border: 2px solid var(--mud-palette-primary);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@Icons.Material.Filled.CreditCard" Color="Color.Primary" Size="Size.Medium" />
|
||||
<MudText Typo="Typo.subtitle1" Color="Color.Primary"><b>پرداخت مستقیم</b></MudText>
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
پرداخت آنی از طریق درگاه بانکی
|
||||
</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
FullWidth="true"
|
||||
StartIcon="@Icons.Material.Filled.Payment"
|
||||
OnClick="DirectPayment">
|
||||
پرداخت با کارت بانکی
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
<!-- Daya Loan Option -->
|
||||
<MudPaper Elevation="0" Class="pa-4 rounded-lg" Style="border: 2px solid var(--mud-palette-tertiary);">
|
||||
<MudStack Spacing="2">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Diamond" Color="Color.Tertiary" Size="Size.Medium" />
|
||||
<MudText Typo="Typo.subtitle1" Color="Color.Tertiary"><b>اعتبار الماسی دایا</b></MudText>
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.body2" Class="mud-text-secondary">
|
||||
تأمین اعتبار از طریق خرید توکن الماس و دریافت وام
|
||||
</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Tertiary"
|
||||
FullWidth="true"
|
||||
StartIcon="@Icons.Material.Filled.AccountBalance"
|
||||
OnClick="DayaLoanPayment">
|
||||
تأمین اعتبار الماسی
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
<MudButton Variant="Variant.Text"
|
||||
Color="Color.Default"
|
||||
FullWidth="true"
|
||||
OnClick="ClosePurchaseOptions">
|
||||
بستن
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
</MudDialog>
|
||||
|
||||
@@ -3,6 +3,7 @@ using FrontOffice.BFF.User.Protobuf.Protos.User;
|
||||
using FrontOffice.BFF.User.Protobuf.Validator;
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Protos.UserAddress;
|
||||
using FrontOffice.BFF.UserAddress.Protobuf.Validator;
|
||||
using FrontOffice.BFF.Package.Protobuf.Protos.Package;
|
||||
using FrontOffice.Main.Pages.Profile.Components;
|
||||
using FrontOffice.Main.Utilities;
|
||||
using Mapster;
|
||||
@@ -18,6 +19,8 @@ public partial class Index
|
||||
{
|
||||
[Inject] private UserContract.UserContractClient UserContract { get; set; } = default!;
|
||||
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
|
||||
[Inject] private PackageContract.PackageContractClient PackageContract { get; set; } = default!;
|
||||
[Inject] private AuthService AuthService { get; set; } = default!;
|
||||
|
||||
private GetUserResponse _userProfile = new();
|
||||
private UpdateUserRequest _updateUserRequest = new();
|
||||
@@ -32,6 +35,15 @@ public partial class Index
|
||||
|
||||
private DateTime? _date;
|
||||
|
||||
// Referral Link Control
|
||||
private bool _hasPurchasedPackage;
|
||||
private bool _isClubMemberActive;
|
||||
private bool CanShowReferralLink => _hasPurchasedPackage && _isClubMemberActive;
|
||||
|
||||
// Purchase Options Bottom Sheet
|
||||
private bool _showPurchaseBottomSheet;
|
||||
private bool _isProcessingPayment;
|
||||
|
||||
// Address management
|
||||
private List<GetAllUserAddressByFilterResponseModel> _addresses = new();
|
||||
private bool _isLoadingAddresses;
|
||||
@@ -44,12 +56,29 @@ public partial class Index
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
if (firstRender)
|
||||
{
|
||||
await LoadUserAuthInfo();
|
||||
await LoadUserProfile();
|
||||
await LoadAddresses();
|
||||
await LoadWallet();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadUserAuthInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userInfo = await AuthService.GetUserAuthInfo();
|
||||
_hasPurchasedPackage = userInfo.HasPurchasedPackage;
|
||||
_isClubMemberActive = userInfo.IsClubMemberActive;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_hasPurchasedPackage = false;
|
||||
_isClubMemberActive = false;
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private string _walletCredit = "-";
|
||||
private string _walletDiscount = "-";
|
||||
private string _walletNetwork = "-";
|
||||
@@ -272,33 +301,96 @@ public partial class Index
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Financing()
|
||||
private void OpenPurchaseOptions()
|
||||
{
|
||||
_showPurchaseBottomSheet = true;
|
||||
}
|
||||
|
||||
private void ClosePurchaseOptions()
|
||||
{
|
||||
_showPurchaseBottomSheet = false;
|
||||
}
|
||||
|
||||
private async Task DirectPayment()
|
||||
{
|
||||
if (_isProcessingPayment) return;
|
||||
|
||||
_isProcessingPayment = true;
|
||||
_showPurchaseBottomSheet = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Create callback URL for payment verification
|
||||
var callbackUrl = $"{Navigation.BaseUri}profile/payment-callback";
|
||||
|
||||
// Call BFF to initiate payment (UserId is taken from JWT token in BFF)
|
||||
var response = await PackageContract.InitiateBasePackagePaymentAsync(new InitiateBasePackagePaymentRequest
|
||||
{
|
||||
CallbackUrl = callbackUrl
|
||||
});
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
Snackbar.Add(response.Message ?? "خطا در آغاز فرآیند پرداخت", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirect to payment gateway
|
||||
if (!string.IsNullOrEmpty(response.PaymentGatewayUrl))
|
||||
{
|
||||
Navigation.NavigateTo(response.PaymentGatewayUrl, forceLoad: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add("خطا در دریافت آدرس درگاه پرداخت", Severity.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"خطا در فرآیند پرداخت: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isProcessingPayment = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DayaLoanPayment()
|
||||
{
|
||||
// Validate user info before redirecting
|
||||
if (string.IsNullOrWhiteSpace(_updateUserRequest.FirstName))
|
||||
{
|
||||
_showPurchaseBottomSheet = false;
|
||||
Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (نام وارد نشده)", Severity.Error);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_updateUserRequest.LastName))
|
||||
{
|
||||
_showPurchaseBottomSheet = false;
|
||||
Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (نام خانوادگی وارد نشده)", Severity.Error);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_updateUserRequest.NationalCode))
|
||||
{
|
||||
_showPurchaseBottomSheet = false;
|
||||
Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (کدملی وارد نشده)", Severity.Error);
|
||||
return;
|
||||
}
|
||||
if (_updateUserRequest.BirthDate == null)
|
||||
{
|
||||
_showPurchaseBottomSheet = false;
|
||||
Snackbar.Add($"لطفا اطلاعات شخصی خود را تکمیل کنید. (تاریخ تولد وارد نشده)", Severity.Error);
|
||||
return;
|
||||
}
|
||||
if (!_addresses.Any())
|
||||
{
|
||||
Snackbar.Add($"آدرس محل سکونت شما الزمای است!", Severity.Error);
|
||||
_showPurchaseBottomSheet = false;
|
||||
Snackbar.Add($"آدرس محل سکونت شما الزامی است!", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_showPurchaseBottomSheet = false;
|
||||
var url = "https://dayadiamond.ir/profile/creditpurchase/?merchantcode=56146364";
|
||||
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
@page "/profile/payment-callback"
|
||||
@attribute [Authorize]
|
||||
@using Blazored.LocalStorage
|
||||
@using FrontOffice.BFF.Package.Protobuf.Protos.Package
|
||||
@using FrontOffice.BFF.User.Protobuf.Protos.User
|
||||
|
||||
<PageTitle>نتیجه پرداخت | کارا بازار سلامت</PageTitle>
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.Small" Class="py-8">
|
||||
<MudPaper Class="pa-6 text-center">
|
||||
@if (_isLoading)
|
||||
{
|
||||
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
|
||||
<MudText Typo="Typo.h6" Class="mt-4">در حال بررسی وضعیت پرداخت...</MudText>
|
||||
}
|
||||
else if (_isSuccess)
|
||||
{
|
||||
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success" Size="Size.Large" Style="font-size: 80px" />
|
||||
<MudText Typo="Typo.h5" Color="Color.Success" Class="mt-4">پرداخت موفق</MudText>
|
||||
<MudText Typo="Typo.body1" Class="mt-2">@_message</MudText>
|
||||
|
||||
@if (!string.IsNullOrEmpty(_refId))
|
||||
{
|
||||
<MudText Typo="Typo.body2" Class="mt-3">
|
||||
<strong>کد پیگیری:</strong> @_refId
|
||||
</MudText>
|
||||
}
|
||||
|
||||
<MudDivider Class="my-4" />
|
||||
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
موجودی کیف پول: @FormatPrice(_walletBalance)
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
موجودی اعتبار تخفیف: @FormatPrice(_discountBalance)
|
||||
</MudText>
|
||||
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="mt-6"
|
||||
Href="/profile">
|
||||
بازگشت به پروفایل
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIcon Icon="@Icons.Material.Filled.Error" Color="Color.Error" Size="Size.Large" Style="font-size: 80px" />
|
||||
<MudText Typo="Typo.h5" Color="Color.Error" Class="mt-4">پرداخت ناموفق</MudText>
|
||||
<MudText Typo="Typo.body1" Class="mt-2">@_message</MudText>
|
||||
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="mt-6"
|
||||
Href="/profile">
|
||||
بازگشت به پروفایل
|
||||
</MudButton>
|
||||
|
||||
<MudButton Color="Color.Secondary" Variant="Variant.Outlined" Class="mt-3 mr-2"
|
||||
OnClick="RetryPayment">
|
||||
تلاش مجدد
|
||||
</MudButton>
|
||||
}
|
||||
</MudPaper>
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
[Inject] private PackageContract.PackageContractClient PackageContractClient { get; set; } = default!;
|
||||
[Inject] private UserContract.UserContractClient UserContractClient { get; set; } = default!;
|
||||
[Inject] private ILocalStorageService LocalStorage { get; set; } = default!;
|
||||
[Inject] private AuthService AuthService { get; set; } = default!;
|
||||
[Inject] private NavigationManager NavManager { get; set; } = default!;
|
||||
|
||||
private const string TokenStorageKey = "auth:token";
|
||||
|
||||
[SupplyParameterFromQuery(Name = "orderId")]
|
||||
private long OrderId { get; set; }
|
||||
|
||||
[SupplyParameterFromQuery(Name = "transactionId")]
|
||||
private long TransactionId { get; set; }
|
||||
|
||||
[SupplyParameterFromQuery(Name = "Authority")]
|
||||
private string? Authority { get; set; }
|
||||
|
||||
[SupplyParameterFromQuery(Name = "Status")]
|
||||
private string? Status { get; set; }
|
||||
|
||||
private bool _isLoading = true;
|
||||
private bool _isSuccess;
|
||||
private string _message = string.Empty;
|
||||
private string? _refId;
|
||||
private long _walletBalance;
|
||||
private long _discountBalance;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await VerifyPayment();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task VerifyPayment()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OrderId <= 0 || TransactionId <= 0 || string.IsNullOrEmpty(Authority))
|
||||
{
|
||||
_isSuccess = false;
|
||||
_message = "پارامترهای پرداخت نامعتبر است";
|
||||
_isLoading = false;
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
var response = await PackageContractClient.VerifyBasePackagePaymentAsync(new VerifyBasePackagePaymentRequest
|
||||
{
|
||||
OrderId = OrderId,
|
||||
TransactionId = TransactionId,
|
||||
Authority = Authority,
|
||||
Status = Status ?? "NOK"
|
||||
});
|
||||
|
||||
_isSuccess = response.Success;
|
||||
_message = response.Message;
|
||||
_refId = response.RefId;
|
||||
_walletBalance = response.WalletBalance;
|
||||
_discountBalance = response.DiscountBalance;
|
||||
|
||||
// اگر پرداخت موفق بود، توکن را refresh میکنیم
|
||||
// چون PackagePurchaseMethod تغییر کرده و باید در claims جدید باشد
|
||||
if (_isSuccess)
|
||||
{
|
||||
await RefreshTokenAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_isSuccess = false;
|
||||
_message = $"خطا در بررسی وضعیت پرداخت: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void RetryPayment()
|
||||
{
|
||||
NavManager.NavigateTo("/profile");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh token after successful payment to update PackagePurchaseMethod claim
|
||||
/// </summary>
|
||||
private async Task RefreshTokenAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userResponse = await UserContractClient.GetUserAsync(new Google.Protobuf.WellKnownTypes.Empty());
|
||||
if (!string.IsNullOrWhiteSpace(userResponse.Token))
|
||||
{
|
||||
// ذخیره توکن جدید در localStorage
|
||||
await LocalStorage.SetItemAsync(TokenStorageKey, userResponse.Token);
|
||||
|
||||
// بهروزرسانی UserAuthInfo با claims جدید
|
||||
await AuthService.InitUserAuthInfo();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// اگر refresh ناموفق بود، پرداخت همچنان موفق است
|
||||
// کاربر میتواند با logout/login دوباره token جدید بگیرد
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatPrice(long price) => string.Format("{0:N0} تومان", price);
|
||||
}
|
||||
Reference in New Issue
Block a user