feat(payment): unify payment flow with new pure redirect entry and update related endpoints
This commit is contained in:
@@ -1033,6 +1033,7 @@ Reject a pending request. **Permission:** `ROLE_ADMIN`
|
||||
| `tax_enabled` | `0` | فعالسازی مالیات بر ارزش افزوده |
|
||||
| `tax_percent` | `10` | درصد مالیات |
|
||||
| `sms_panel_fee_rials` | `1500000` | هزینه ثابت پنل پیامک به ریال (از نوبت و اشتراک کسر میشود) |
|
||||
| `sms_price_rials` | `500` | هزینه هر پیامک ارسالی به ریال؛ مبنای محاسبهٔ تعداد پیامک از موجودی کیفپول (`GET /api/v1/sms/wallet/balance`). قابل ویرایش در `/admin/settings` → بخش پیامک |
|
||||
| `appointment_fee_rials` | `150000` | مبلغ هر نوبت به ریال؛ مبلغی که بیمار هنگام رزرو آنلاین پرداخت میکند. backend از همین کلید میخواند و در `GET /api/v1/payment/config` expose میشود |
|
||||
| `log_retention_days` | `90` | مدت نگهداری لاگها (روز)؛ کاماند روزانه `app:prune-logs` لاگهای قدیمیتر را حذف میکند. `0` = نگهداری نامحدود |
|
||||
|
||||
|
||||
@@ -185,6 +185,31 @@ Initiate payment for an appointment. Returns a redirect URL to the payment gatew
|
||||
|
||||
---
|
||||
|
||||
## GET `/api/v1/payment/order/{appointmentUuid}`
|
||||
|
||||
**entry ریدایرکتِ خالص (بدون XHR)** برای شروع پرداخت نوبت. مرورگر مستقیماً به این آدرس هدایت میشود؛ Backend همهٔ کار را انجام میدهد: اعتبارسنجی سفارش → ساخت `Payment` → ارتباط با بانک → ریدایرکت به شاپرک.
|
||||
|
||||
**Permission:** `PUBLIC` (بدون JWT).
|
||||
|
||||
### Query Parameters
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `gateway` | string | ✅ | نام درگاه فعال (`mellat`/`sep`) — از `GET /payment/config` |
|
||||
| `return` | string | ❌ | آدرس بازگشت (دامنهٔ مبدأ)؛ باید در `payment_allowed_frontend_hosts` مجاز باشد |
|
||||
|
||||
### رفتار
|
||||
- نوبت یافت نشد → `302` به `return?status=notfound` (یا `422` اگر return نبود/نامجاز).
|
||||
- نوبت قابلپرداخت نیست (نه `pending`/`confirmed`) → `302` `return?status=invalid`.
|
||||
- درگاه نامعتبر/غیرفعال → `302` `return?status=gateway`.
|
||||
- موفق → ساخت/ادامهٔ `Payment` pending (بدون pending تکراری via `findPendingByAppointment`) و `302` به بانک (یا فرم auto-submit POST برای ملت).
|
||||
|
||||
### امنیت مالکیت
|
||||
چون entry عمومی و full-page cross-domain است، JWT در دسترس نیست؛ `appointmentUuid` (UUID غیرقابلحدس) نقش capability را دارد و پرداخت فقط به نفع صاحب نوبت است. برای مالکیت سختگیرانه میتوان پارامتر امضاشدهٔ `sig` (HMAC) افزود.
|
||||
|
||||
> **صفحهٔ نتیجه:** این endpointهای مرورگرمحور (`order`/`pay`/`callback`) هرگز JSON به کاربر نمیدهند. اگر `return` معتبر باشد → `302` به همان دامنه با `?status=`؛ در غیر اینصورت (نبود/نامعتبر بودن `return`، یافتنشدن سفارش، نبود آدرس بازگشت) یک صفحهٔ **Twig** (`templates/payment/result.html.twig`, RTL، `noindex`) با پیام وضعیت رندر میشود.
|
||||
|
||||
---
|
||||
|
||||
## GET `/api/v1/payment/pay/{orderId}`
|
||||
|
||||
انتقال مرورگر به درگاه پرداخت برای یک پرداختِ `pending`. **عمومی (بدون JWT)** — مرورگر مستقیماً به این آدرس هدایت میشود. صلاحیت اوردر قبلاً در `POST /api/v1/payment/appointment` (نیازمند JWT) بررسی و پرداخت ساخته شده است. **ارتباط با بانک (init درگاه) در همین endpoint انجام میشود.**
|
||||
|
||||
+4
-4
@@ -9,8 +9,8 @@
|
||||
- **کلید API کاوهنگار فقط از متغیر محیطی `KAVENEGAR_API_KEY` خوانده میشود** — نه از دیتابیس و نه از پنل. در پنل ادمین فقط وضعیت read-only «تنظیمشده/نشده» نمایش داده میشود.
|
||||
- شماره فرستنده تنظیم نمیشود؛ کاوهنگار از خط پیشفرض حساب استفاده میکند.
|
||||
- endpoint `GET /api/v1/admin/settings` یک فیلد read-only به نام `sms_api_key_configured` (boolean) برمیگرداند.
|
||||
- `PATCH /api/v1/admin/settings` دیگر کلیدهای `sms_provider`، `kavenegar_api_key`، `kavenegar_sender`، `rangineh_api_key`، `rangineh_sender`، `sms_price_rials` را نمیپذیرد (از `ALLOWED_KEYS` حذف شدهاند).
|
||||
- قیمت هر پیامک ثابت است: `SmsWalletController::SMS_PRICE_RIALS = 500` ریال.
|
||||
- `PATCH /api/v1/admin/settings` کلیدهای `sms_provider`، `kavenegar_api_key`، `kavenegar_sender`، `rangineh_api_key`، `rangineh_sender` را نمیپذیرد (از `ALLOWED_KEYS` حذف شدهاند).
|
||||
- **قیمت هر پیامک** از کلید تنظیمات `sms_price_rials` خوانده میشود (قابل ویرایش در `/admin/settings` → بخش پیامک، و از طریق `PATCH /api/v1/admin/settings`). اگر تنظیم نشده باشد، مقدار پیشفرض `SmsWalletController::SMS_PRICE_RIALS = 500` ریال بهعنوان fallback استفاده میشود. `GET /api/v1/sms/wallet/balance` این مقدار را در `sms_price_rials` و تعداد تخمینی پیامک را در `estimated_sms_count` برمیگرداند.
|
||||
|
||||
---
|
||||
|
||||
@@ -348,13 +348,13 @@ Updated template with `status: "rejected"`.
|
||||
"success": true,
|
||||
"data": {
|
||||
"payment_uuid": "...",
|
||||
"redirect_url": "https://gateway...",
|
||||
"pay_url": "{APP_BASE_URL}/api/v1/payment/pay/ORD-...",
|
||||
"order_id": "ORD-..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
پس از پرداخت موفق، موجودی کیف خودکار شارژ میشود.
|
||||
> این endpoint فقط `Payment` (type=`sms_wallet`) میسازد و `pay_url` میدهد؛ **ارتباط با بانک اینجا انجام نمیشود** و از flow واحد پرداخت (`GET /payment/pay/{orderId}` → callback → `PaymentManager`) عبور میکند. کلاینت باید مرورگر را به `pay_url` هدایت کند. پس از پرداخت موفق، `PaymentManager` موجودی کیف را خودکار شارژ میکند.
|
||||
|
||||
### GET /api/v1/sms/wallet/logs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user