+ مبلغی که بیمار هنگام رزرو نوبت آنلاین پرداخت میکند. ۱۵۰٬۰۰۰ ریال = ۱۵٬۰۰۰ تومان.
+
+
+
{/* هزینه پنل پیامک */}
@@ -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'),
]);
}