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/.gitea/workflows/kub-deploy.yml b/.gitea/workflows/kub-deploy.yml index 34633a9..7d393c6 100644 --- a/.gitea/workflows/kub-deploy.yml +++ b/.gitea/workflows/kub-deploy.yml @@ -73,10 +73,10 @@ jobs: # Apply manifests and restart sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} " - kubectl apply -f /tmp/frontoffice-deployment.yaml && + kubectl apply -n kbs-stage -f /tmp/frontoffice-deployment.yaml && crictl rmi ${REGISTRY}/${IMAGE_NAME}:latest 2>/dev/null || true && - kubectl rollout restart deployment/frontoffice && - kubectl rollout status deployment/frontoffice --timeout=180s && + kubectl -n kbs-stage rollout restart deployment/frontoffice && + kubectl -n kbs-stage rollout status deployment/frontoffice --timeout=180s && rm -f /tmp/frontoffice-deployment.yaml " echo "✅ Deployed!" diff --git a/.gitignore b/.gitignore index 0f13c79..bf1fd69 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ # ---> 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 - + # User-specific files *.rsuser *.suo diff --git a/k8s/staging/frontoffice-deployment.yaml b/k8s/staging/frontoffice-deployment.yaml index c5873b2..22d2154 100644 --- a/k8s/staging/frontoffice-deployment.yaml +++ b/k8s/staging/frontoffice-deployment.yaml @@ -4,7 +4,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: frontoffice - namespace: default + namespace: kbs-stage labels: app: frontoffice environment: staging @@ -21,7 +21,7 @@ spec: containers: - name: frontoffice image: 185.252.31.44:30080/admin/frontoffice:latest - imagePullPolicy: Always + imagePullPolicy: IfNotPresent ports: - containerPort: 80 name: http @@ -43,7 +43,7 @@ apiVersion: v1 kind: Service metadata: name: frontoffice-svc - namespace: default + namespace: kbs-stage labels: app: frontoffice spec: @@ -54,3 +54,31 @@ spec: targetPort: 80 name: http type: ClusterIP + +--- +# FrontOffice Ingress +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: frontoffice-ingress + namespace: kbs-stage + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + tls: + - hosts: + - frontoffice.se.kbs1.ir + secretName: frontoffice-tls + rules: + - host: frontoffice.se.kbs1.ir + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: frontoffice-svc + port: + number: 80 diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index 8d8f9e7..812bdb9 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -10,8 +10,7 @@ - - + @@ -23,6 +22,19 @@ + + + + + + + + + + + + + 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/Index.razor.cs b/src/FrontOffice.Main/Pages/Index.razor.cs index a66bc26..d019edc 100644 --- a/src/FrontOffice.Main/Pages/Index.razor.cs +++ b/src/FrontOffice.Main/Pages/Index.razor.cs @@ -162,7 +162,7 @@ public partial class Index : IDisposable (Icons.Material.Outlined.VerifiedUser, "ثبت‌نام سریع و ساده", "در چند گام کوتاه حساب بسازید و شروع کنید.", 0), (Icons.Material.Outlined.StarBorder, "پاداش‌های شفاف", "قوانین روشن، دسترسی آسان به سوابق و گزارش‌ها.", 100), (Icons.Material.Outlined.Devices, "طراحی واکنش‌گرا", "تجربه‌ای روان در موبایل و دسکتاپ.", 200), - (Icons.Material.Outlined.Groups, "تیم‌سازی هوشمند", "ساختار درختی شبکه و مدیریت تیم‌های فروش.", 300), + (Icons.Material.Outlined.Groups, "تیم‌سازی هوشمند", "ساختار درختی سازمان و مدیریت تیم‌های فروش.", 300), (Icons.Material.Outlined.Insights, "گزارش‌های لحظه‌ای", "داشبورد پویا برای مشاهده عملکرد و کمیسیون.", 400), (Icons.Material.Outlined.Lock, "امنیت بالا", "رمزنگاری اطلاعات و احراز هویت چندمرحله‌ای.", 500), }; diff --git a/src/FrontOffice.Main/Pages/Profile/Hub.razor b/src/FrontOffice.Main/Pages/Profile/Hub.razor index 83f24db..7e38ce3 100644 --- a/src/FrontOffice.Main/Pages/Profile/Hub.razor +++ b/src/FrontOffice.Main/Pages/Profile/Hub.razor @@ -46,6 +46,21 @@ + + + + + + + دعوت‌شدگان من + کسانی که با موبایل شما ثبت‌نام کرده‌اند + + + + + + + diff --git a/src/FrontOffice.Main/Pages/Profile/Index.razor b/src/FrontOffice.Main/Pages/Profile/Index.razor index 0189e5b..462721f 100644 --- a/src/FrontOffice.Main/Pages/Profile/Index.razor +++ b/src/FrontOffice.Main/Pages/Profile/Index.razor @@ -109,6 +109,13 @@ { @(_copyMessage) } + + مشاهده دعوت‌شدگان من + } else @@ -121,6 +128,13 @@ برای فعال‌سازی لینک دعوت، ابتدا یکی از پکیج‌ها را تهیه کنید و سپس در باشگاه مشتریان عضو شوید. + + دعوت‌شدگان من + از مزایای ویژه عضویت بهره‌مند شده و با فعالیت در زمینه توسعه فروشگاه‌ها پاداش دریافت کنید. 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/Invitees.razor b/src/FrontOffice.Main/Pages/Profile/Invitees.razor new file mode 100644 index 0000000..8c7c385 --- /dev/null +++ b/src/FrontOffice.Main/Pages/Profile/Invitees.razor @@ -0,0 +1,157 @@ +@attribute [Authorize] +@attribute [Route(RouteConstants.Profile.Invitees)] +@using CMSMicroservice.Protobuf.Protos.User +@using CMSMicroservice.Protobuf.Protos.City +@inject UserContract.UserContractClient UserClient +@inject ISnackbar Snackbar + +دعوت‌شدگان من | کارا بازار سلامت + + + + + + افرادی که با شماره موبایل شما به‌عنوان دعوت‌کننده ثبت‌نام کرده‌اند (متفاوت از سازمان فروش). + + + @if (_loading) + { + + + + } + else + { + + + + کل دعوت‌ها + @_stats.TotalInvitees + + + + + فعال در باشگاه + @_stats.ClubActiveCount + + + + + غیرفعال باشگاه + @_stats.ClubInactiveCount + + + + + + + همه + فعال باشگاه + غیرفعال باشگاه + + + + @if (!_invitees.Any()) + { + هنوز کسی با کد دعوت شما ثبت‌نام نکرده است. + } + else + { + + + نام + تاریخ ثبت‌نام + باشگاه + + + + @(string.IsNullOrWhiteSpace($"{context.FirstName} {context.LastName}".Trim()) + ? "—" + : $"{context.FirstName} {context.LastName}".Trim()) + + + @FormatDate(context.RegisteredAt) + + + @if (context.IsClubActive) + { + فعال + } + else + { + غیرفعال + } + + + + + @if (_totalPages > 1) + { + + + + } + } + } + + +@code { + private bool _loading = true; + private string _clubFilter = "ALL"; + private int _page = 1; + private const int PageSize = 10; + private int _totalPages; + private InviteeStats _stats = new(); + private List _invitees = new(); + + protected override async Task OnInitializedAsync() + { + await LoadAsync(); + } + + private async Task OnFilterChanged(string value) + { + _clubFilter = value; + _page = 1; + await LoadAsync(); + } + + private async Task OnPageChanged(int page) + { + _page = page; + await LoadAsync(); + } + + private async Task LoadAsync() + { + _loading = true; + try + { + var response = await UserClient.GetMyInviteesAsync(new GetMyInviteesRequest + { + PaginationState = new PaginationState { PageNumber = _page, PageSize = PageSize }, + ClubStatusFilter = _clubFilter + }); + + _stats = response.Stats ?? new InviteeStats(); + _invitees = response.Invitees.ToList(); + _totalPages = (int)(response.MetaData?.TotalPage ?? 0); + if (_totalPages < 1 && _invitees.Count > 0) _totalPages = 1; + } + catch (Exception ex) + { + Snackbar.Add($"خطا در بارگذاری دعوت‌شدگان: {ex.Message}", Severity.Error); + } + finally + { + _loading = false; + } + } + + private static string FormatDate(Google.Protobuf.WellKnownTypes.Timestamp? ts) + { + if (ts == null) return "—"; + return ts.ToDateTime().ToLocalTime().MiladiToJalali(); + } +} diff --git a/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor b/src/FrontOffice.Main/Pages/Profile/PaymentCallback.razor index 08c1794..9365398 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/Pages/RegisterWizard.razor.cs b/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs index 02891d0..c8c0e7c 100644 --- a/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs +++ b/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs @@ -319,17 +319,17 @@ public partial class RegisterWizard $"

شماره ملی: {_model.NationalCode?.PersianToEnglish()}

" + $"

شماره تماس: {_userAuthInfo.MobileNumber?.PersianToEnglish()}

" + "

کارابازار و کاربر که در این قرارداد جداگانه «طرف» و مجموعاً «طرفین» نامیده می‌شوند، با علم و آگاهی کامل، مفاد زیر را پذیرفته و امضا می‌نمایند.

ماده ۱ - موضوع قرارداد

" + - "

موضوع این قرارداد، تعیین حدود و چارچوب تعهدات و سلب مسئولیت کارابازار در قبال استفاده کاربر ازخدمات، باشگاه مشتریان، مزایا، طرح‌های همکاری، شبکه معرفی، خرید از فروشگاه‌های طرف‌قراردادو هرگونه امکانات و تسهیلات مرتبط است که حسب مورد توسط کارابازار یا از طریق پلتفرم‌ها و شرکت‌هایهمکار در اختیار کاربر قرار می‌گیرد، بدون اینکه جزئیات مدل کسب‌وکار، شیوه تأمین منابع مالی،شبکه همکاری یا ساختار داخلی آن در این قرارداد ذکر یا افشا شود.

" + + "

موضوع این قرارداد، تعیین حدود و چارچوب تعهدات و سلب مسئولیت کارابازار در قبال استفاده کاربر ازخدمات، باشگاه مشتریان، مزایا، طرح‌های همکاری، سازمان معرفی، خرید از فروشگاه‌های طرف‌قراردادو هرگونه امکانات و تسهیلات مرتبط است که حسب مورد توسط کارابازار یا از طریق پلتفرم‌ها و شرکت‌هایهمکار در اختیار کاربر قرار می‌گیرد، بدون اینکه جزئیات مدل کسب‌وکار، شیوه تأمین منابع مالی،سازمان همکاری یا ساختار داخلی آن در این قرارداد ذکر یا افشا شود.

" + "

ماده ۲ - اقرار و آگاهی کاربر

۲-۱. کاربر اقرار می‌نماید که کلیه اطلاعات کلی لازم در خصوص نحوه فعالیت، مزایا، ریسک‌های احتمالی," + "سازوکار کلی استفاده از خدمات، و چارچوب کلی همکاری را از مراجع رسمی معرفی‌شده توسط کارابازاردریافت کرده، سؤالات خود را مطرح نموده و پاسخ مناسب را دریافت نموده است.

۲-۲. کاربر با امضای این قرارداد اقرار می‌نماید که:

  • با اراده آزاد و بدون هرگونه اجبار، اکراه، فریب یا سوءاستفاده از ناآگاهی وارد این همکاری شده است.
  • از احتمال وجود ریسک‌های مالی، اعتباری، قانونی و شبکه‌ای مرتبط با فعالیت خود آگاه بوده و آن را می‌پذیرد.
  • " + - "
  • هرگونه تصمیم برای استفاده از تسهیلات، مزایا، خدمات باشگاه مشتریان، فعالیت در شبکه معرفی، خرید از فروشگاه‌ها و جذب افراد جدید، تصمیمی شخصی و آگاهانه است و به‌عنوان تعهد یا ضمانت بازده مشخص از سوی کارابازار تلقی نمی‌شود.
" + + "
  • هرگونه تصمیم برای استفاده از تسهیلات، مزایا، خدمات باشگاه مشتریان، فعالیت در سازمان معرفی، خرید از فروشگاه‌ها و جذب افراد جدید، تصمیمی شخصی و آگاهانه است و به‌عنوان تعهد یا ضمانت بازده مشخص از سوی کارابازار تلقی نمی‌شود.
  • " + "

    ماده ۳ - سلب مسئولیت کارابازار

    ۳-۱. کاربر صراحتاً می‌پذیرد که کارابازار صرفاً در چارچوب قوانین و مقررات حاکم و مطابق قراردادها و تفاهم‌نامه‌های خود با شرکای تجاری و پلتفرم‌های همکار، خدمات و امکانات مشخصی را در اختیار کاربر قرار می‌دهد و هیچ‌گونه تعهدی نسبت به:

    • تحقق سود یا بازده مالی مشخص،
    • ثبات ارزش هرگونه اعتبار، امتیاز، یا منافع غیرنقدی که ممکن است در قالب طرح‌ها به کاربر تخصیص یابد،
    • " + "
    • عملکرد اشخاص ثالث (از جمله پلتفرم‌ها، فروشگاه‌ها، شرکت‌های بیمه، ارائه‌دهندگان خدمات سلامت و هر شخص حقیقی یا حقوقی همکار)،
    • تصمیمات و اقدامات کاربر در جذب افراد جدید و ایجاد زیرمجموعه،

    نداشته و نخواهد داشت مگر در حدودی که به‌صراحت در قرارداد یا قوانین آمره بر عهده کارابازار گذاشته شده است.

    " + "۳-۲. هرگونه خسارت، دعوی، تهدید، مطالبه، ادعا، شکایت یا پیگیری حقوقی که ناشی از تصمیمات شخصی کاربر،نحوه استفاده وی از تسهیلات و مزایا، جذب دیگران، خرید کالا و خدمات، یا تعاملات او با اشخاص ثالث باشد،مستقیماً متوجه خود کاربر است و کارابازار نسبت به آن هیچ‌گونه مسئولیت مدنی، کیفری، مالی یا قراردادی ندارد," + "مگر در مواردی که طبق قوانین آمره، مسئولیت صریح و غیرقابل‌اسقاط بر عهده کارابازار قرار گرفته باشد.

    ۳-۳. کاربر می‌پذیرد که هیچ‌گونه ادعایی مبنی بر فریب، اغفال، ارائه اطلاعات نادرست یا وعده‌های خارج ازچهارچوب رسمی از سوی کارابازار یا نمایندگان رسمی آن، پس از امضای این قرارداد، از وی مسموع نخواهد بود،مگر آنکه طبق رأی قطعی مرجع قضایی صالح، خلاف آن ثابت شود.

    ماده ۴ - مسئولیت کاربر در قبال تهدید، ادعا و شکایت

    " + "

    ۴-۱. کاربر متعهد است در صورت طرح هرگونه تهدید، ادعا، مطالبه، شکایت یا پیگیری حقوقی از سوی اشخاص ثالث(از جمله افراد جذب‌شده توسط کاربر، زیرمجموعه‌ها، خریداران معرفی‌شده یا سایر اشخاص)، که ناشی از عملکردشخصی کاربر، معرفی‌های وی، شیوه تبلیغ، وعده‌های خارج از چارچوب رسمی کارابازار، یا نقض قوانین و مقرراتباشد، خود رأساً پاسخگو باشد و کارابازار را از هرگونه مسئولیت و هزینه احتمالی در این خصوص مصون بدارد.

    ۴-۲. کاربر می‌پذیرد که:

    • هرگونه تهدید یا ادعای بی‌جا، غیرمنطقی، غیرمستند یا مغایر با مفاد این قرارداد، متوجه شخص کاربر است.
    • " + "
    • در صورت وارد آمدن خسارت مادی یا معنوی به کارابازار ناشی از چنین تهدیدات یا ادعاهایی، کارابازار حق دارد نسبت به مطالبه خسارت از کاربر، طبق قوانین و از طریق مراجع صالح اقدام نماید.
    " + - "

    ماده ۵ - محرمانگی و عدم افشای جزئیات کسب‌وکار

    ۵-۱. کاربر می‌پذیرد که جزئیات فنی، مالی، ساختار شبکه، شیوه توزیع مزایا، قراردادهای داخلی کارابازار باشرکای تجاری و پلتفرم‌های همکار، اطلاعات محرمانه تجاری محسوب شده و خارج از این قرارداد است و افشای آن‌هاتوسط کاربر بدون مجوز کتبی کارابازار، مجاز نمی‌باشد.

    ۵-۲. کاربر حق استناد به عدم درج جزئیات ساختار کسب‌وکار در این قرارداد را به‌عنوان دلیل بطلان، فسخ یاادعای فریب علیه کارابازار از خود سلب می‌نماید.

    " + + "

    ماده ۵ - محرمانگی و عدم افشای جزئیات کسب‌وکار

    ۵-۱. کاربر می‌پذیرد که جزئیات فنی، مالی، ساختار سازمان، شیوه توزیع مزایا، قراردادهای داخلی کارابازار باشرکای تجاری و پلتفرم‌های همکار، اطلاعات محرمانه تجاری محسوب شده و خارج از این قرارداد است و افشای آن‌هاتوسط کاربر بدون مجوز کتبی کارابازار، مجاز نمی‌باشد.

    ۵-۲. کاربر حق استناد به عدم درج جزئیات ساختار کسب‌وکار در این قرارداد را به‌عنوان دلیل بطلان، فسخ یاادعای فریب علیه کارابازار از خود سلب می‌نماید.

    " + "

    ماده ۶ - کد تأیید یکبارمصرف (OTP) و شناسه قرارداد

    ۶-۱. به منظور تأیید هویت کاربر و جایگزین کردن امضا در محیط غیرحضوری، برای این قرارداد یک شناسه یکتا(GUID) تولید شده و از طریق سامانه کارابازار، کد تأیید یکبارمصرف (OTP) مرتبط با آن برای کاربر ارسالمی‌گردد.

    " + "

    ۶-۲. کاربر با وارد کردن صحیح کد تأیید (OTP) در سامانه، اقرار می‌نماید که:

    • مالک شماره تماس ثبت‌شده در این قرارداد است یا با اجازه قانونی مالک از آن استفاده می‌کند،
    • از محتوای قرارداد به‌طور کامل آگاه است و آن را بدون ابهام و با رضایت کامل می‌پذیرد،
    • شناسه یکتا و OTP ارسال‌شده، معادل امضای الکترونیکی معتبر وی بر روی این قرارداد است.
    " + "

    ماده ۷ - مدت، اصلاح و حاکمیت قانون

    " + diff --git a/src/FrontOffice.Main/Shared/AuthDialog.razor b/src/FrontOffice.Main/Shared/AuthDialog.razor index d4e7b30..a99e45a 100644 --- a/src/FrontOffice.Main/Shared/AuthDialog.razor +++ b/src/FrontOffice.Main/Shared/AuthDialog.razor @@ -24,7 +24,9 @@ OnChangePhone="ChangePhoneAsync" OnResendOtp="ResendOtpAsync" ReferralCode="@_referralCode" - ReferralCodeChanged="OnReferralCodeChanged" /> + ReferralCodeChanged="OnReferralCodeChanged" + InviterMobile="@_inviterMobile" + InviterMobileChanged="OnInviterMobileChanged" />
    } else @@ -55,7 +57,9 @@ else OnChangePhone="ChangePhoneAsync" OnResendOtp="ResendOtpAsync" ReferralCode="@_referralCode" - ReferralCodeChanged="OnReferralCodeChanged" /> + ReferralCodeChanged="OnReferralCodeChanged" + InviterMobile="@_inviterMobile" + InviterMobileChanged="OnInviterMobileChanged" /> @if (_currentStep == AuthStep.Phone) diff --git a/src/FrontOffice.Main/Shared/AuthDialog.razor.cs b/src/FrontOffice.Main/Shared/AuthDialog.razor.cs index 0ce1755..23da086 100644 --- a/src/FrontOffice.Main/Shared/AuthDialog.razor.cs +++ b/src/FrontOffice.Main/Shared/AuthDialog.razor.cs @@ -44,6 +44,8 @@ public partial class AuthDialog : IDisposable // Referral code field private string? _referralCode; + // Inviter mobile (دعوت‌کننده) + private string? _inviterMobile; [Inject] private ILocalStorageService LocalStorage { get; set; } = default!; [Inject] private UserContract.UserContractClient UserClient { get; set; } = default!; @@ -71,6 +73,7 @@ public partial class AuthDialog : IDisposable } _referralCode = await LocalStorage.GetItemAsync("referral:code"); + _inviterMobile = await LocalStorage.GetItemAsync("inviter:mobile"); // await LocalStorage.RemoveItemAsync(TokenStorageKey); } @@ -83,6 +86,15 @@ public partial class AuthDialog : IDisposable await LocalStorage.RemoveItemAsync("referral:code"); } + private async Task OnInviterMobileChanged(string? value) + { + _inviterMobile = value; + if (!string.IsNullOrWhiteSpace(value)) + await LocalStorage.SetItemAsync("inviter:mobile", value); + else + await LocalStorage.RemoveItemAsync("inviter:mobile"); + } + private void GenerateCaptcha() { _captchaCode = Guid.NewGuid().ToString("N")[..6].ToUpperInvariant(); @@ -204,6 +216,15 @@ public partial class AuthDialog : IDisposable _verifyRequest.ParentReferralCode = effectiveReferralCode; } + var storedInviterMobile = await LocalStorage.GetItemAsync("inviter:mobile"); + var effectiveInviterMobile = !string.IsNullOrWhiteSpace(_inviterMobile) + ? _inviterMobile + : storedInviterMobile; + if (!string.IsNullOrWhiteSpace(effectiveInviterMobile)) + { + _verifyRequest.InviterMobile = effectiveInviterMobile; + } + var validationResult = true; // _verifyRequestValidator.Validate(_verifyRequest); if (!validationResult) { @@ -236,6 +257,7 @@ public partial class AuthDialog : IDisposable await LocalStorage.RemoveItemAsync(PhoneStorageKey); await LocalStorage.RemoveItemAsync(RedirectStorageKey); await LocalStorage.RemoveItemAsync("referral:code"); + await LocalStorage.RemoveItemAsync("inviter:mobile"); _attemptsLeft = MaxVerificationAttempts; _verifyRequest.Code = string.Empty; 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/PhoneVerifyForm.razor b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor index 3ca37a4..a56cc58 100644 --- a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor +++ b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor @@ -38,11 +38,21 @@ + Placeholder="کد معرف ۱۰ رقمی" + Class="mb-3" + HelperText="جایگاه شما در سازمان — با لینک معرف پر می‌شود" /> + + @if (!string.IsNullOrWhiteSpace(ErrorMessage)) { diff --git a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs index e7f353e..f917d88 100644 --- a/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs +++ b/src/FrontOffice.Main/Shared/PhoneVerifyForm.razor.cs @@ -33,6 +33,10 @@ public partial class PhoneVerifyForm [Parameter] public string? ReferralCode { get; set; } [Parameter] public EventCallback ReferralCodeChanged { get; set; } + // ── Inviter (دعوت‌کننده) ── + [Parameter] public string? InviterMobile { get; set; } + [Parameter] public EventCallback InviterMobileChanged { get; set; } + // ── Verify actions ── [Parameter] public EventCallback OnChangePhone { get; set; } [Parameter] public EventCallback OnResendOtp { get; set; } 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/Utilities/RouteConstants.cs b/src/FrontOffice.Main/Utilities/RouteConstants.cs index 7000f3f..cc1dddd 100644 --- a/src/FrontOffice.Main/Utilities/RouteConstants.cs +++ b/src/FrontOffice.Main/Utilities/RouteConstants.cs @@ -21,6 +21,7 @@ public static class RouteConstants public const string Settings = "/profile/settings"; public const string ChangePassword = "/profile/change-password"; public const string Tree = "/profile/tree"; + public const string Invitees = "/profile/invitees"; public const string Wallet = "/profile/wallet"; public const string MagicWallet = "/profile/magic-wallet"; public const string ChargeDiscountWallet = "/profile/charge-discount-wallet"; diff --git a/src/FrontOffice.Main/Utilities/Seo/SeoMetadataProvider.cs b/src/FrontOffice.Main/Utilities/Seo/SeoMetadataProvider.cs index 6909b42..c0b73ab 100644 --- a/src/FrontOffice.Main/Utilities/Seo/SeoMetadataProvider.cs +++ b/src/FrontOffice.Main/Utilities/Seo/SeoMetadataProvider.cs @@ -208,6 +208,6 @@ public class SeoMetadataProvider private static string GetHomeCrawlableContent() => "کارابازار سلامت (KBS) — باشگاه مشتریان و فروشگاه محصولات سلامت. " + - "با عضویت در باشگاه مشتریان KBS از پاداش شفاف، کیف پول جادویی، فروشگاه اعتباری و شبکه فروش بهره‌مند شوید. " + + "با عضویت در باشگاه مشتریان KBS از پاداش شفاف، کیف پول جادویی، فروشگاه اعتباری و سازمان فروش بهره‌مند شوید. " + "ثبت‌نام رایگان در kbs2.ir."; } diff --git a/src/FrontOffice.Main/Utilities/Seo/SeoSettings.cs b/src/FrontOffice.Main/Utilities/Seo/SeoSettings.cs index 89d19e9..bf8f2f4 100644 --- a/src/FrontOffice.Main/Utilities/Seo/SeoSettings.cs +++ b/src/FrontOffice.Main/Utilities/Seo/SeoSettings.cs @@ -7,5 +7,5 @@ public class SeoSettings public string SiteUrl { get; set; } = "https://kbs2.ir"; public string SiteName { get; set; } = "کارابازار سلامت"; public string DefaultDescription { get; set; } = - "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و شبکه فروش."; + "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و سازمان فروش."; } diff --git a/src/FrontOffice.Main/appsettings.Staging.json b/src/FrontOffice.Main/appsettings.Staging.json index f07c714..9eeec81 100644 --- a/src/FrontOffice.Main/appsettings.Staging.json +++ b/src/FrontOffice.Main/appsettings.Staging.json @@ -2,10 +2,13 @@ "Seo": { "SiteUrl": "https://kbs1.ir", "SiteName": "کارابازار سلامت", - "DefaultDescription": "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و شبکه فروش." + "DefaultDescription": "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و سازمان فروش." }, "GwUrl": "https://cms.se.kbs1.ir", "DownloadUrl": "", + "TwentyCard": { + "CreditPortalUrl": "https://userpanel.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..33d9fbb 100644 --- a/src/FrontOffice.Main/appsettings.json +++ b/src/FrontOffice.Main/appsettings.json @@ -2,10 +2,13 @@ "Seo": { "SiteUrl": "http://localhost:5268", "SiteName": "کارابازار سلامت", - "DefaultDescription": "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و شبکه فروش." + "DefaultDescription": "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و سازمان فروش." }, "GwUrl": "https://cms.se.kbs1.ir", "DownloadUrl": "", + "TwentyCard": { + "CreditPortalUrl": "https://userpanel.20card.ir/" + }, "EncryptionSettings": { "Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=", "IV": "1wyF3Tt142MOkCpIyCxh/g=="