Files
boursecu_server/docs/WALLET_ONLINE_PAYMENT.md
T

13 KiB
Raw Blame History

شارژ آنلاین کیف پول (Wallet Online Payment)

مستند پیاده‌سازی داخلی پروژه برای شارژ کیف پول از طریق درگاه. برای پروتکل Montify (HMAC، initiate، webhook) به سند جداگانه مراجعه کنید:

→ EXTERNAL_PAYMENT_PARTNER.md


۱. هدف

  • کاربر بتواند علاوه بر واریز دستی (کارت/شبا)، از پرداخت آنلاین کیف را شارژ کند.
  • تمام credentialها و انتخاب درگاه فقط سمت سرور بماند.
  • معماری طوری باشد که بعداً چند نوع درگاه (Montify پارتنر، درگاه مستقیم، …) اضافه شود.

۲. جریان کلی (از دید کاربر و سیستم)

اپ کاربر (khaneghi_user)
  │  مبلغ را وارد می‌کند → مدال راهنمای شارژ
  │  GET  /customer/wallet/payment/options     → { online_enabled }
  │  POST /customer/wallet/payment/initiate    → { payment_url }
  ▼
سرور ما
  │  Withdraw (sts=1) می‌سازد
  │  درگاه فعال را از Settings انتخاب می‌کند (مثلاً montify)
  │  با HMAC به Montify: POST /external-payment/initiate
  ▼
Montify → payment_url → درگاه بانکی
  │
  ├─► Browser return → GET  /customer/wallet/payment/return/{provider}
  └─► Webhook        → POST /customer/wallet/payment/webhook/{provider}
                         │
                         └─► شارژ idempotent کیف (sts=2 + Transaction)

منبع حقیقت شارژ: webhook (یا verify امن سمت سرور). فقط به query برگشت مرورگر اعتماد نکنید؛ امضا را verify کنید و مبلغ/reference_id را با Withdraw تطبیق دهید.


۳. امنیت — قوانین اجباری

درست غلط
client_id / client_secret / base_url فقط در Settings سرور گذاشتن secret در JS فرانت
فیلتر api/get_settings برای کلیدهای محرمانه برگرداندن external_payment_* به اپ کاربر
اپ فقط online_enabled و بعد payment_url می‌بیند خواندن provider یا credential از settings در کلاینت
initiate فقط با JWT مشتری ساخت لینک پرداخت در مرورگر
verify امضای return و webhook اعتماد کور به query بدون signature
تطبیق amount با Withdraw.cash قبول هر مبلغ برگشتی

۳.۱. چه چیزی به اپ کاربر می‌رسد؟

  • GET /customer/wallet/payment/options → فقط { "online_enabled": true|false }
  • POST /customer/wallet/payment/initiate → فقط { "payment_url": "https://..." }
  • هیچ client_id، client_secret، base_url، نام درگاه داخلی، یا wallet_payment_provider در Network تب اپ نباید دیده شود.

۳.۲. فیلتر get_settings

در App\Models\Setting::isSecretOrServerOnlyKey() و SettingsController::authorizeSettingsPayload():

  • درخواست عمومی (api/get_settings بدون auth ادمین) → کلیدهای محرمانه حذف می‌شوند.
  • پنل ادمین (admin/get_settings با auth:user) → همه کلیدها قابل مشاهده/ویرایش طبق can_edit.

کلیدهای مرتبط با پرداخت که به اپ عمومی نمی‌روند شامل:

  • همه external_payment_*
  • wallet_payment_provider
  • همه payping_*
  • الگوهای *_secret / *_token / *_password و لیست ثابت توکن‌ها

۴. Settings (جدول settings)

پیکربندی از پنل ادمین → گروه تنظیمات درگاه (can_edit=1).

کلید نوع توضیح
wallet_payment_provider select none = خاموش، montify = پارتنر Montify
external_payment_enabled switch فعال‌سازی شارژ آنلاین (همراه با پیکربندی کامل)
external_payment_base_url text BASE_URL API Montify (بدون اسلش انتهایی)
external_payment_client_id text client_id پارتنر
external_payment_client_secret text client_secret — محرمانه، فقط سرور
external_payment_gateway select zarinpall / zibal / payping / sepal
external_payment_return_url text اختیاری؛ خالی = پیش‌فرض سرور
external_payment_webhook_url text اختیاری؛ خالی = پیش‌فرض سرور
min_deposit price حداقل مبلغ واریز (گروه کارمزد و محدودیت‌ها)

مقادیر پیشنهادی برای abaiegold

فیلد Settings مقدار
external_payment_return_url https://abaeigold.ir/wallet/payment-result
external_payment_webhook_url https://abaeigold.ir/server/customer/wallet/payment/webhook/montify

در whitelist پارتنر Montify حداقل این پیشوندها را ثبت کنید:

  • Return: https://abaeigold.ir/wallet/payment-result یا https://abaeigold.ir/wallet/
  • Webhook: همان URL کامل webhook بالا

جریان برگشت فرانت:

  1. Montify کاربر را به return_url با query امضاشده می‌فرستد
  2. صفحه khaneghi_user → /wallet/payment-result
  3. فرانت POST /customer/wallet/payment/verify-return می‌زند (امضا فقط سمت سرور verify می‌شود)
  4. webhook هم موازی برای شارژ قطعی به بک‌اند می‌آید (idempotent)

Migrationها

  • 2026_07_24_000001_add_external_payment_settings.php
  • 2026_07_24_000002_add_wallet_payment_provider_setting.php
  • 2026_07_26_000001_expose_gateway_settings_in_admin_panel.php
php artisan migrate

۵. APIهای سرور ما

پیشوند: /customer
Auth مشتری: auth:customer + customer.active (به‌جز return/webhook)

متد مسیر Auth نقش
GET /wallet/payment/options مشتری { online_enabled }
POST /wallet/payment/initiate مشتری Body: { value } (ریال) → { payment_url }
GET /wallet/payment/return/{provider} عمومی برگشت مرورگر؛ verify + گزارش
POST /wallet/payment/webhook/{provider} عمومی (HMAC) شارژ کیف

سازگاری قدیمی

مسیر قدیمی معادل
POST /wallet/external-payment/initiate همان initiate
GET /wallet/external-payment/return return/montify
POST /wallet/external-payment/webhook webhook/montify

Initiate — جزئیات

  1. بررسی WalletPaymentManager::isOnlineDepositAvailable()
  2. اعتبارسنجی مبلغ (≥ ۱۰۰۰ و ≥ min_deposit)
  3. ساخت Withdraw با sts=1، note=واریز آنلاین
  4. reference_id = WD-{withdraw_id}
  5. فراخوانی gateway->initiate(...)
  6. ذخیره session_id در tracked_number در صورت وجود
  7. پاسخ فقط payment_url

Webhook / Return — شارژ

منطق مشترک در App\Services\Payment\WalletDepositCreditor:

  • قفل ردیف Withdraw (idempotent)
  • اگر قبلاً sts=2 باشد، دوباره شارژ نمی‌کند
  • TransactionInterface نوع واریز نقدی
  • همگام‌سازی حسابداری (Pargar) در صورت امکان
  • SMS ادمین با سوییچ sms_notif_gateway_charge_admin

۶. معماری کد (چنددرگاهی)

app/Services/Payment/
  Contracts/
    WalletPaymentGatewayInterface.php   # قرارداد درگاه
  Gateways/
    MontifyPartnerGateway.php           # پیاده‌سازی فعلی Montify
    # PaypingDirectGateway.php          # آینده
  Support/
    MontifyHmacSigner.php               # HMAC + stableStringify
  WalletPaymentManager.php              # انتخاب درگاه از Settings
  WalletDepositCreditor.php             # شارژ idempotent کیف

app/Http/Controllers/pay/
  WalletPaymentController.php           # options / initiate / return / webhook

قرارداد درگاه

هر درگاه باید این‌ها را پیاده کند:

  • key(): string — مثلاً montify
  • isAvailable(): bool — فعال + پیکربندی کامل
  • initiate(array $payload): array — حداقل payment_url
  • returnUrl() / webhookUrl()
  • handleReturn(Request): array
  • handleWebhook(Request): array

افزودن درگاه جدید (چک‌لیست)

  1. کلاس جدید در Gateways/ با WalletPaymentGatewayInterface
  2. ثبت در WalletPaymentManager::drivers()
  3. افزودن مقدار به optionsی wallet_payment_provider در migration/admin (settingTypes.ts)
  4. Settings مخصوص آن درگاه (و اضافه کردن به فیلتر secret در Setting::isSecretOrServerOnlyKey)
  5. مسیرهای return/{provider} و webhook/{provider} از قبل generic هستند
  6. تست: options → initiate → webhook → موجودی کیف

۷. فرانت‌اند (khaneghi_user)

فایل نقش
src/api/wallet.ts getWalletPaymentOptions / initiateWalletPayment
src/views/base/wallet/composables/useWalletPage.ts بارگذاری online_enabled، payOnline()
src/views/base/wallet/components/WalletDepositGuideModal.vue دکمه «پرداخت آنلاین از درگاه»
src/views/base/wallet/index.vue اتصال props/events مدال

رفتار UI:

  1. کاربر مبلغ را وارد می‌کند و واریز می‌زند → مدال راهنما (کارت/شبا + پشتیبانی)
  2. اگر online_enabled باشد دکمه درگاه نمایش داده می‌شود
  3. کلیک → initiate → window.location = payment_url

نکته: وضعیت دکمه از endpoint سرور می‌آید، نه از settingsStore و کلیدهای external_payment_*.


۸. پنل ادمین

  • گروه تنظیمات: admin/.../configuration/settingGroups.ts → id external_payment
  • انواع/برچسب/قفل: settingTypes.ts
  • ویرایش credentialها فقط در ادمین (و کلیدهای can_edit=0 فقط دیپلوی)

۹. مدل داده مرتبط

مدل نقش
Withdraw درخواست واریز؛ sts: 1=در انتظار، 2=موفق، 3=ناموفق
reference_id WD-{id} برای Montify
Wallet TransactionInterface واریز نقدی پس از موفقیت

واریز دستی تغییری نکرده؛ فقط راهنما + کپی کارت/شبا از Settings عمومی (deposit_card_number و …) است.


۱۰. راه‌اندازی عملیاتی

  1. php artisan migrate
  2. در پنل ادمین / دیپلوی:
    • wallet_payment_provider = montify
    • external_payment_base_url / client_id / client_secret / gateway
  3. در پنل Montify:
    • ثبت allowed_return_urls و webhook_url مطابق URLهای سرور
  4. تست با مبلغ ≥ min_deposit
  5. تأیید: Network اپ کاربر فاقد secret باشد؛ فقط options و initiate دیده شود

۱۱. عیب‌یابی سریع

مشکل بررسی
دکمه درگاه نیست options.online_enabled؛ provider؛ کامل بودن credential؛ isAvailable()
مشکل در ارتباط با درگاه ساعت سرور (NTP)، stableStringify، PATH دقیق، body خام همان hash
شارژ دوبل باید idempotent باشد (sts=2 + lock)
secret در Network مسیر باید api/get_settings باشد نه admin؛ فیلتر Setting::forPublicApi

جزئیات امضای HMAC و خطاهای Montify: EXTERNAL_PAYMENT_PARTNER.md


۱۲. ارجاع فایل‌های کلیدی

server/docs/WALLET_ONLINE_PAYMENT.md          ← این سند
server/docs/EXTERNAL_PAYMENT_PARTNER.md      ← پروتکل Montify
server/app/Services/Payment/...
server/app/Http/Controllers/pay/WalletPaymentController.php
server/routes/customer.php
server/app/Models/Setting.php                ← فیلتر secret
server/database/migrations/2026_07_24_000001_add_external_payment_settings.php
server/database/migrations/2026_07_24_000002_add_wallet_payment_provider_setting.php
khaneghi_user/src/api/wallet.ts
khaneghi_user/src/views/base/wallet/...
admin/src/pages/menuPages/configuration/settingTypes.ts
admin/src/pages/menuPages/configuration/settingGroups.ts