diff --git a/assets/admin/pages/SettingsPage.tsx b/assets/admin/pages/SettingsPage.tsx index a0c36da2..0ed9e46b 100644 --- a/assets/admin/pages/SettingsPage.tsx +++ b/assets/admin/pages/SettingsPage.tsx @@ -29,6 +29,7 @@ const schema = z.object({ tax_enabled: z.string(), tax_percent: z.string(), sms_panel_fee_rials: z.string(), + appointment_fee_rials: z.string(), // payment gateways payment_test_mode: z.string(), mellat_terminal_id: z.string(), @@ -55,6 +56,7 @@ interface Settings { tax_enabled: string; tax_percent: string; sms_panel_fee_rials: string; + appointment_fee_rials: string; payment_test_mode: string; mellat_terminal_id: string; mellat_username: string; @@ -112,6 +114,7 @@ export default function SettingsPage() { tax_enabled: settings.tax_enabled ?? '0', tax_percent: settings.tax_percent ?? '10', sms_panel_fee_rials: settings.sms_panel_fee_rials ?? '1500000', + appointment_fee_rials: settings.appointment_fee_rials ?? '150000', payment_test_mode: settings.payment_test_mode ?? '0', mellat_terminal_id: settings.mellat_terminal_id ?? '', mellat_username: settings.mellat_username ?? '', @@ -313,6 +316,16 @@ export default function SettingsPage() { )} + {/* مبلغ هر نوبت */} +
+ + +

+ مبلغی که بیمار هنگام رزرو نوبت آنلاین پرداخت می‌کند. ۱۵۰٬۰۰۰ ریال = ۱۵٬۰۰۰ تومان. +

+
+ {/* هزینه پنل پیامک */}
@@ -522,6 +535,7 @@ export default function SettingsPage() { tax_enabled: settings.tax_enabled, tax_percent: settings.tax_percent, sms_panel_fee_rials: settings.sms_panel_fee_rials, + appointment_fee_rials: settings.appointment_fee_rials, payment_test_mode: settings.payment_test_mode, mellat_terminal_id: settings.mellat_terminal_id, mellat_username: settings.mellat_username, diff --git a/docs/api/admin.md b/docs/api/admin.md index c69e2e42..3ded8611 100644 --- a/docs/api/admin.md +++ b/docs/api/admin.md @@ -981,6 +981,7 @@ Reject a pending request. **Permission:** `ROLE_ADMIN` | `tax_enabled` | `0` | فعال‌سازی مالیات بر ارزش افزوده | | `tax_percent` | `10` | درصد مالیات | | `sms_panel_fee_rials` | `1500000` | هزینه ثابت پنل پیامک به ریال (از نوبت و اشتراک کسر می‌شود) | +| `appointment_fee_rials` | `150000` | مبلغ هر نوبت به ریال؛ مبلغی که بیمار هنگام رزرو آنلاین پرداخت می‌کند. backend از همین کلید می‌خواند و در `GET /api/v1/payment/config` expose می‌شود | **ترتیب محاسبه** (در `CommissionService`): ۱) کسر `sms_panel_fee_rials` ۲) مالیاتِ استخراجی `afterSms × tax/(100+tax)` ۳) پورسانت = `netAfterTax × percent/100`. سهم نماینده به کیف‌پولش (`WalletTransaction` credit) واریز و یک ردیف `FinancialBreakdown` ثبت می‌شود (idempotent بر اساس `payment_id`). diff --git a/docs/api/payment.md b/docs/api/payment.md index bb8d53cb..9735b0e4 100644 --- a/docs/api/payment.md +++ b/docs/api/payment.md @@ -16,7 +16,8 @@ { "success": true, "data": { - "test_mode": true + "test_mode": true, + "appointment_fee_rials": 150000 } } ``` @@ -24,6 +25,7 @@ | Field | Type | Description | |-------|------|-------------| | `test_mode` | boolean | `true` = درگاه آزمایشی فعال است — backend از MockGateway استفاده می‌کند و پول واقعی کسر نمی‌شود | +| `appointment_fee_rials` | integer | مبلغ هر نوبت به ریال (از تنظیمات سایت، کلید `appointment_fee_rials`). frontend برای نمایش «مبلغ قابل پرداخت» از این می‌خواند؛ مبلغِ واقعیِ تراکنش هم در backend از همین کلید خوانده می‌شود (نه از client) | **نکته:** این endpoint هیچ اطلاعات حساسی (terminal_id، password، ...) را expose نمی‌کند. تنها یک boolean برای مصرف frontend است. @@ -105,6 +107,8 @@ Initiate payment for an appointment. Returns a redirect URL to the payment gatew } ``` +> **مبلغ نوبت:** مبلغِ پرداخت از کلید `appointment_fee_rials` تنظیمات سایت خوانده می‌شود (نه از client و نه hardcode). برای تغییر، در `/admin/settings` ویرایش کنید. +> > **On successful callback** for an appointment payment, the booking is transitioned `pending → confirmed` (its 15-minute `expires_at` is cleared) and a confirmation SMS is dispatched to the patient's mobile. If the booking already lapsed to `expired` before payment confirmed, it is **not** re-confirmed (the transition is rejected) — handle refund out of band. > > **پورسانت نماینده:** اگر پزشک نوبت `representation_id` داشته باشد و `appointment_commission_enabled=1` باشد، پس از confirm شدن `CommissionService` هزینه پنل پیامک و مالیات را کسر و سهم نماینده را به کیف‌پولش واریز می‌کند (ردیف `FinancialBreakdown` ثبت می‌شود). برای پرداخت اشتراک هم اگر `upgrade_commission_enabled=1` و پزشک/کلینیک `representation_id` داشته باشد همین منطق با درصد `upgrade_commission_percent` اعمال می‌شود. کلیدهای تنظیمات و ترتیب محاسبه در `docs/api/admin.md`. diff --git a/src/Config/Controller/SiteConfigController.php b/src/Config/Controller/SiteConfigController.php index 8f3f6fab..69968689 100644 --- a/src/Config/Controller/SiteConfigController.php +++ b/src/Config/Controller/SiteConfigController.php @@ -23,6 +23,7 @@ class SiteConfigController extends BaseController 'tax_enabled', 'tax_percent', 'sms_panel_fee_rials', + 'appointment_fee_rials', 'site_name', 'support_phone', 'max_cancel_hours_before', diff --git a/src/Config/Repository/SiteConfigRepository.php b/src/Config/Repository/SiteConfigRepository.php index df283fe8..81865529 100644 --- a/src/Config/Repository/SiteConfigRepository.php +++ b/src/Config/Repository/SiteConfigRepository.php @@ -16,7 +16,8 @@ class SiteConfigRepository extends ServiceEntityRepository 'upgrade_commission_percent' => '20', 'tax_enabled' => '0', 'tax_percent' => '10', - 'sms_panel_fee_rials' => '1500000', + 'sms_panel_fee_rials' => '150', + 'appointment_fee_rials' => '15000', 'site_name' => 'ClinicPro', 'support_phone' => '', 'max_cancel_hours_before' => '24', diff --git a/src/Payment/Controller/PaymentController.php b/src/Payment/Controller/PaymentController.php index cc321a02..1f0b7020 100644 --- a/src/Payment/Controller/PaymentController.php +++ b/src/Payment/Controller/PaymentController.php @@ -186,7 +186,8 @@ class PaymentController extends BaseController return $this->error(ErrorCodes::ERR_PAYMENT_001, ErrorCodes::message(ErrorCodes::ERR_PAYMENT_001), 503); } - $payment = new Payment($user, 150000, $gatewayName, Payment::TYPE_APPOINTMENT, $frontendAddress); + $feeRials = (int) $this->configRepo->get('appointment_fee_rials'); + $payment = new Payment($user, $feeRials, $gatewayName, Payment::TYPE_APPOINTMENT, $frontendAddress); $payment->setAppointment($appointment); $this->paymentRepo->save($payment); @@ -519,7 +520,8 @@ class PaymentController extends BaseController public function config(): JsonResponse { return $this->success([ - 'test_mode' => $this->configRepo->get('payment_test_mode') === '1', + 'test_mode' => $this->configRepo->get('payment_test_mode') === '1', + 'appointment_fee_rials' => (int) $this->configRepo->get('appointment_fee_rials'), ]); }