Files
clinicpro/docs/api/admin.md
T
hamed 7814bcc0de feat(logging): implement log pruning functionality
- Add LogPruneService to handle the deletion of old logs based on retention settings.
- Create PruneLogsCommand to provide a console command for log pruning.
- Introduce PruneLogsMessage and PruneLogsHandler for message handling related to log pruning.
- Update the AST cache with new classes and their relationships.
2026-07-01 21:50:51 +03:30

30 KiB
Raw Blame History

Admin API

Prefix: /api/v1/admin
Permission: ALL endpoints in this file require ROLE_ADMIN
Headers: Authorization: Bearer <admin_jwt_token>


Dashboard

GET /api/v1/admin/dashboard/stats

Get key performance indicators (KPIs) for the dashboard.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "total_users": 1200,
    "active_doctors": 85,
    "total_doctors": 92,
    "total_clinics": 34,
    "today_appointments": 47,
    "total_appointments": 8540,
    "today_payments_count": 30,
    "today_payments_amount": 15000000,
    "total_payments_amount": 425000000,
    "pending_comments": 12,
    "pending_settlements": 5,
    "this_month_revenue": 52000000,
    "this_month_appointments": 620
  }
}

GET /api/v1/admin/dashboard/charts

Get chart data for the last 30 days.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "appointments_30d": [
      { "date": "2024-06-01", "count": 42 }
    ],
    "revenue_30d": [
      { "date": "2024-06-01", "amount_rials": 21000000 }
    ],
    "appointment_status": {
      "confirmed": 350,
      "completed": 180,
      "cancelled": 45,
      "pending": 20,
      "no_show": 25
    },
    "top_specialties": [
      { "name": "قلب و عروق", "count": 120 }
    ]
  }
}

GET /api/v1/admin/dashboard/recent

Get recent activity (last 10 of each type).

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "appointments": [
      {
        "uuid": "...",
        "doctor_title": "دکتر علی احمدی",
        "patient_name": "محمد رضایی",
        "slot_start": 1718438400,
        "status": "confirmed"
      }
    ],
    "payments": [
      {
        "uuid": "...",
        "amount_rials": 500000,
        "gateway": "mellat",
        "status": "paid",
        "created_at": 1717000000
      }
    ],
    "users": [
      {
        "uuid": "...",
        "real_name": "محمد رضایی",
        "mobile_number": "09...",
        "roles": ["ROLE_USER"],
        "created_at": 1717000000
      }
    ]
  }
}

User Management

GET /api/v1/admin/users

List all users with pagination and filters.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search by name or mobile
role string Filter: ROLE_USER, ROLE_DOCTOR, ROLE_ADMIN, etc.
status string "active" or "inactive"
sort string "created_at" (default desc)

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "real_name": "علی احمدی",
      "mobile_number": "09123456789",
      "roles": ["ROLE_USER"],
      "status": "active",
      "created_at": 1717000000
    }
  ],
  "meta": { "totalRecords": 1200, "totalPages": 60, "currentPage": 1 }
}

GET /api/v1/admin/users/{uuid}

Get detailed user info.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "uuid": "...",
    "real_name": "علی احمدی",
    "mobile_number": "09123456789",
    "roles": ["ROLE_USER"],
    "status": "active",
    "wallet_balance_rials": 500000,
    "appointments_count": 5,
    "created_at": 1717000000
  }
}

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 User not found

GET /api/v1/admin/users/stats

Get user statistics.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "total": 1200,
    "active": 1150,
    "inactive": 50,
    "admins": 3,
    "doctors": 92,
    "patients": 1100
  }
}

PUT /api/v1/admin/users/{uuid}

Update user info (name, email, password).

Permission: ROLE_ADMIN

Request Body (application/json)

{
  "real_name": "علی احمدی جدید",
  "password": "newPassword123"
}

Response 200

Updated user object.


PUT /api/v1/admin/users/{uuid}/role

Change a user's role.

Permission: ROLE_ADMIN

Request Body (application/json)

{
  "role": "ROLE_DOCTOR"
}
Field Type Required Allowed Values
role string ROLE_USER, ROLE_DOCTOR, ROLE_CLINIC, ROLE_SECRETARY, ROLE_ADMIN

Response 200

{ "success": true, "data": { "message": "نقش کاربر تغییر کرد", "roles": ["ROLE_DOCTOR"] } }

POST /api/v1/admin/users/{uuid}/status

Toggle user active/inactive status.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "status": "inactive" } }

DELETE /api/v1/admin/users/{uuid}

Delete a user.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "message": "کاربر حذف شد" } }

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 User not found

Doctor Management

نماینده (ROLE_REPRESENTATION): افزودن پزشک و کلینیک برای نماینده از طریق endpointهای جدا انجام می‌شود — POST /api/v1/representation/doctor و POST /api/v1/representation/clinic (به docs/api/representation.md مراجعه کنید). در نسخه‌ی نماینده، representation_id پزشک خودکار روی نماینده‌ی کاربر جاری ست می‌شود. endpointهای /api/v1/admin/* همچنان فقط ROLE_ADMIN هستند.

POST /api/v1/admin/doctors

ساخت پزشک جدید (در صورت نبودِ کاربر با این موبایل، یک User هم ساخته می‌شود).

Permission: ROLE_ADMIN

Request Body (application/json)

Field Type Required Description
mobile string موبایل ورود؛ باید فرمت معتبر موبایل ایران داشته باشد (^09\d{9}$) — ارقام فارسی/عربی به انگلیسی نرمال می‌شوند
name string نام پزشک

Errors

Code HTTP Description
VALIDATION 422 mobile یا name خالی
VALIDATION 422 mobile فرمت معتبر موبایل ایران ندارد (field: mobile)

POST /api/v1/admin/clinic

ساخت کلینیک جدید (در صورت نبودِ کاربرِ صاحب با این موبایل، یک User هم ساخته می‌شود).

Permission: ROLE_ADMIN

Request Body (application/json)

Field Type Required Description
owner_mobile string موبایل صاحب کلینیک؛ باید فرمت معتبر موبایل ایران داشته باشد (^09\d{9}$)
name string نام کلینیک

Errors

Code HTTP Description
VALIDATION 422 owner_mobile خالی
VALIDATION 422 owner_mobile فرمت معتبر موبایل ایران ندارد (field: owner_mobile)
VALIDATION 422 name کلینیک خالی

GET /api/v1/admin/doctors

List all doctors with pagination.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search in title
status string "active" or "inactive"
gender string "male" or "female"
specialty_id integer Filter by specialty
sort string Sort field

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "title": "دکتر علی احمدی",
      "degree": "متخصص",
      "gender": "male",
      "doctor_rate": 4.5,
      "active_doctor_appointment": true
    }
  ],
  "meta": { "totalRecords": 92, "totalPages": 5, "currentPage": 1 }
}

GET /api/v1/admin/doctors/stats

Get doctor statistics.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": {
    "total": 92,
    "active": 85,
    "inactive": 7,
    "male": 60,
    "female": 32,
    "top_specialty": "قلب و عروق"
  }
}

POST /api/v1/admin/doctors/{uuid}/status

Toggle doctor active status.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "active": false } }

Clinic Management

GET /api/v1/admin/clinics

List all clinics with pagination.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search by clinic name
status string "active" or "inactive"

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "name": "کلینیک الوند",
      "city": "تهران",
      "telephone": "02112345678",
      "is_active": true,
      "created_at": 1717000000
    }
  ],
  "meta": { "totalRecords": 34, "totalPages": 2, "currentPage": 1 }
}

PATCH /api/v1/admin/clinic/{uuid}/status

Toggle clinic active/inactive.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "is_active": false } }

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 Clinic not found

DELETE /api/v1/admin/clinic/{uuid}

Delete a clinic.

Permission: ROLE_ADMIN

Side effect: the clinic's insurance configuration (tenant_insurances, entity_insurance_pricing, and their tenant_service_coverages) is purged in the same request — polymorphic entity_id, cleaned up at the application level.

Response 200

{ "success": true, "data": { "message": "کلینیک حذف شد" } }

Appointment Management

GET /api/v1/admin/appointments/today-stats

Get appointment statistics for a specific date (defaults to today).

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
date string (YYYY-MM-DD) Default: today

Response 200

{
  "success": true,
  "data": {
    "total": 47,
    "completed": 20,
    "waiting": 18,
    "cancelled": 9
  }
}

GET /api/v1/admin/appointments

List appointments filtered by date and/or doctor. Sorted by slot_start ASC.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 15, max: 500
search string Search by patient name/mobile or doctor name
status string Filter by status
date string (YYYY-MM-DD) Filter by slot date
doctor_uuid string Filter by doctor UUID

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "appt-uuid",
      "patient_name": "محمد رضایی",
      "patient_mobile": "09123456789",
      "doctor_uuid": "doctor-uuid",
      "doctor_name": "دکتر علی احمدی",
      "slot_start": 1718438400,
      "slot_end": 1718439600,
      "appointment_date": "2025-06-15",
      "appointment_time": "09:00",
      "end_time": "09:20",
      "status": "confirmed",
      "version": 1,
      "created_at": "2025-06-14T10:30:00+03:30"
    }
  ],
  "meta": { "totalRecords": 47, "totalPages": 1, "currentPage": 1 }
}

Status values: pending | confirmed | completed | cancelled_by_doctor | cancelled_by_user | no_show | expired


POST /api/v1/admin/appointment

Create a new appointment for a patient. If no user exists with the given mobile, a new user account is created automatically.

Permission: ROLE_ADMIN

Request Body

{
  "doctor_uuid": "doctor-uuid",
  "slot_start": 1718438400,
  "slot_end": 1718439600,
  "patient_mobile": "09123456789",
  "patient_name": "علی محمدی",
  "note": "optional note"
}

patient_mobile و patient_name هر دو اجباری هستند. اگر کاربری با این شماره موبایل نداشته باشیم، یک کاربر جدید با نقش ROLE_USER ساخته می‌شود.

Response 201

{
  "success": true,
  "data": {
    "uuid": "appt-uuid",
    "slot_start": 1718438400,
    "slot_end": 1718439600,
    "status": "pending"
  }
}

Error Responses

Code HTTP Description
VALIDATION 422 Missing required fields (doctor_uuid, slot_start, slot_end, patient_mobile, patient_name)
DOCTOR_NOT_FOUND 404 Doctor UUID not found
SLOT_TAKEN 409 Slot already booked

Payment Management

GET /api/v1/admin/payments

List all payments.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
status string "pending", "paid", "failed", "cancelled"

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "order_id": "CLINICPRO-...",
      "amount_rials": 500000,
      "status": "paid",
      "gateway": "mellat",
      "created_at": 1717000000
    }
  ],
  "meta": { "totalRecords": 7800, "totalPages": 390, "currentPage": 1 }
}

Settlement Management

GET /api/v1/admin/settlements

List all settlement requests.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
status string "pending", "approved", "rejected"

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "user": { "uuid": "...", "real_name": "دکتر علی احمدی" },
      "amount_rials": 1000000,
      "status": "pending",
      "bank_account": { "bank_name": "بانک ملت", "owner_name": "..." },
      "created_at": 1717000000
    }
  ],
  "meta": { "totalRecords": 45, "totalPages": 3, "currentPage": 1 }
}

To approve or reject, use the Settlement API: POST /api/v1/settlement/{uuid}/approve or /reject


Representation Management

GET /api/v1/admin/representations

List all representations.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 15
search string Search by name or mobile (representation's or linked user's)
city_id integer Filter by city

Response 200

Paginated representation list. Each item:

{
  "id": 3,
  "uuid": "...",
  "full_name": "حامد حسینی",
  "mobile_number": "09120671756",
  "city_id": 132,
  "city": "یزد",
  "commission_percent": 10.0,
  "wallet_balance": 0,
  "is_active": true,
  "created_at": "2026-06-18T..."
}

mobile_number falls back to the linked user's mobile when the representation's own mobile_number column is empty.

Deactivation, not deletion: the admin panel deactivates a representation via PATCH /api/v1/representation/{uuid} with { "active": false } rather than calling DELETE.


Secretary Management

GET /api/v1/admin/secretaries

List all secretaries.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search by mobile

Response 200

Paginated secretary list with linked doctor info.


Rating & Comment Management

GET /api/v1/admin/rates

List all ratings.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search by doctor/patient

Ratings are multi-dimensional (five 0100 dimensions). Each row's overall is the mean of the five dimensions (0100) and score is that mean on a 05 scale (overall / 20).


GET /api/v1/admin/comments

List all comments (all statuses).

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 20
search string Search in body
status string "pending", "approved", "rejected"

To approve/reject comments, use the Rating API: POST /api/v1/admin/comment/{uuid}/approve or /reject


SMS Management (Admin)

GET /api/v1/admin/sms/logs

List SMS send logs.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 15, max 100
tag string فیلتر بر اساس تگ: global | otp | payment | clinic_invitation | pre_registration | notification_mobile | user_template

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "recipient": "09123456789",
      "message": "کد تأیید: 123456",
      "status": "sent",
      "provider": "kavenegar",
      "tag": "otp",
      "sent_at": "2026-06-19T...",
      "created_at": "2026-06-19T..."
    }
  ],
  "meta": { "totalRecords": 5000, "totalPages": 334, "currentPage": 1 }
}

tag نوع پیامک را مشخص می‌کند؛ پیش‌فرض پیامک‌های سیستمیِ بی‌برچسب global است.


GET /api/v1/admin/sms/templates

List all SMS templates.

Permission: ROLE_ADMIN

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "name": "تأیید نوبت",
      "status": "approved",
      "provider_code": "verify_appointment",
      "created_at": 1717000000
    }
  ]
}

To create/approve/reject templates, see sms.md


Clinic Invitation Management

See clinic-invitation.md for full endpoint details.

Endpoint Description
POST /api/v1/admin/clinic/{uuid}/invite-doctor Send invitation
GET /api/v1/admin/clinic/{uuid}/invitations List invitations
POST /api/v1/admin/clinic/invitation/{invUuid}/resend Resend SMS
PATCH /api/v1/admin/clinic/invitation/{invUuid}/status Change status
DELETE /api/v1/admin/clinic/invitation/{invUuid} Delete

Settings

GET /api/v1/admin/settings

Returns all site configuration values.

Response 200

{
  "success": true,
  "data": {
    "commission_enabled": "0",
    "commission_percent": "0",
    "site_name": "ClinicPro",
    "support_phone": "",
    "max_cancel_hours_before": "24",
    "appointment_reminder_hours": "2",
    "log_retention_days": "90",
    "payment_test_mode": "0",
    "mellat_terminal_id": "",
    "mellat_username": "",
    "mellat_password": "",
    "sep_terminal_id": "",
    "sms_provider": "kavenegar",
    "kavenegar_api_key": "",
    "kavenegar_sender": "",
    "sms_price_rials": "500"
  }
}

All values are strings. Missing keys return their default values.

PATCH /api/v1/admin/settings

Update one or more settings. Unknown keys are silently ignored.

Request body (partial update — send only keys to change):

{
  "commission_enabled": "1",
  "commission_percent": "5",
  "site_name": "کلینیک‌پرو",
  "payment_test_mode": "1",
  "mellat_terminal_id": "12345678",
  "mellat_username": "user",
  "mellat_password": "pass",
  "sep_terminal_id": "87654321",
  "sms_provider": "kavenegar",
  "kavenegar_api_key": "your-api-key",
  "kavenegar_sender": "10008664",
  "sms_price_rials": "500"
}

Response 200 — same shape as GET, returns all settings after save.

Commission rules:

  • commission_enabled"1" = active, "0" = inactive
  • commission_percent — integer string, 0100
  • Commission applies only to regular users (booked_by = user); secretaries are exempt

Payment gateway rules:

  • payment_test_mode"1" = all payments use MockGateway (no real bank calls), "0" = real gateways
  • payment_allowed_frontend_hosts — comma-separated hosts allowed as a payment frontend_address (origin site to return to). Falls back to the ALLOWED_FRONTEND_HOSTS env var when empty. Add a consumer site's host here to permit its payments.
  • Gateway credentials (mellat/sep) read from DB first, fallback to env vars if DB value is empty
  • MockGateway callback: same URL pattern + &mock=1&ResCode=0&RefId=MOCK-{orderId} (add &cancel=1 to simulate a user cancellation → canceled)

SMS provider rules:

  • sms_provider"kavenegar" or "rangineh"
  • Kavenegar API key and sender read from DB first, fallback to env vars KAVENEGAR_API_KEY, KAVENEGAR_SENDER

Pre-Registration Management

GET /api/v1/admin/pre-registrations

List pre-registration requests. Permission: ROLE_ADMIN

Query params:

Param Default Notes
page 1
limit 20 max 50
status pending pending | approved | rejected | all

Response 200 (paginated):

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "type": "independent_doctor",
      "name": "دکتر احمدی",
      "mobile": "09121234567",
      "info": "متخصص داخلی",
      "status": "pending",
      "admin_note": null,
      "created_at": 1718000000
    }
  ],
  "meta": { "totalRecords": 5, "totalPages": 1, "currentPage": 1 }
}

POST /api/v1/admin/pre-registrations/{uuid}/approve

Approve a pending request. Creates User + Doctor/Clinic entity based on type, resets password, sends SMS. Permission: ROLE_ADMIN

Response 200:

{ "success": true, "data": { "message": "تأیید شد و اطلاعات ورود ارسال گردید" } }

Error Codes:

Code HTTP Meaning
NOT_FOUND 404 UUID not found
ALREADY_PROCESSED 409 Status is not pending

POST /api/v1/admin/pre-registrations/{uuid}/reject

Reject a pending request. Permission: ROLE_ADMIN

Request body (optional):

{ "note": "مدارک ناقص است" }

Response 200:

{ "success": true, "data": { "message": "درخواست رد شد" } }

موتور مالی نمایندگی

تنظیمات مالی از طریق GET/PATCH /api/v1/admin/settings کنترل می‌شوند (کلیدها در whitelist SiteConfigController::ALLOWED_KEYS):

کلید پیش‌فرض شرح
appointment_commission_enabled 0 فعال‌سازی پورسانت نوبت (درصد از Representation.commission_percent هر نماینده)
upgrade_commission_enabled 0 فعال‌سازی پورسانت ارتقاء اشتراک
upgrade_commission_percent 20 درصد پورسانت ارتقاء (سراسری)
tax_enabled 0 فعال‌سازی مالیات بر ارزش افزوده
tax_percent 10 درصد مالیات
sms_panel_fee_rials 1500000 هزینه ثابت پنل پیامک به ریال (از نوبت و اشتراک کسر می‌شود)
appointment_fee_rials 150000 مبلغ هر نوبت به ریال؛ مبلغی که بیمار هنگام رزرو آنلاین پرداخت می‌کند. backend از همین کلید می‌خواند و در GET /api/v1/payment/config expose می‌شود
log_retention_days 90 مدت نگهداری لاگ‌ها (روز)؛ کاماند روزانه app:prune-logs لاگ‌های قدیمی‌تر را حذف می‌کند. 0 = نگهداری نامحدود

ترتیب محاسبه (در CommissionService): ۱) کسر sms_panel_fee_rials ۲) مالیاتِ استخراجی afterSms × tax/(100+tax) ۳) پورسانت = netAfterTax × percent/100. سهم نماینده به کیف‌پولش (WalletTransaction credit) واریز و یک ردیف FinancialBreakdown ثبت می‌شود (idempotent بر اساس payment_id).

هر تغییر tax_percent/tax_enabled در یک ردیف TaxRateHistory ثبت و از GET /api/v1/admin/settings/tax-history قابل مشاهده است.

GET /api/v1/admin/financial-breakdowns

لیست تفکیک مالی تراکنش‌ها (paginated). Permission: ROLE_ADMIN

Query Parameters:

Param Type Description
page integer پیش‌فرض 1
limit integer پیش‌فرض 15، حداکثر 100
representation_id integer فیلتر نماینده
source string appointment یا subscription
from integer Unix timestamp شروع بازه
to integer Unix timestamp پایان بازه

Response 200 (paginated):

{
  "success": true,
  "data": [
    {
      "uuid": "…", "order_id": "ORD-…", "source": "appointment",
      "gross_rials": 2000000, "sms_fee_rials": 1500000,
      "tax_percent": 10, "tax_rials": 45455, "net_after_tax_rials": 454545,
      "commission_percent": 20, "representation_share_rials": 90909,
      "system_share_rials": 363636,
      "representation_id": 3, "representation_name": "نماینده یزد",
      "doctor_id": 12, "clinic_id": null, "created_at": "2026-06-24T…"
    }
  ],
  "meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
}

GET /api/v1/admin/financial-summary

جمع کل مبالغ. Permission: ROLE_ADMIN

Response 200:

{
  "success": true,
  "data": {
    "total_gross": 2000000,
    "total_representation_income": 90909,
    "total_tax_collected": 45455,
    "total_sms_fee": 1500000,
    "total_system_share": 363636
  }
}

GET /api/v1/admin/settings/tax-history

تاریخچه‌ی تغییرات مالیات بر ارزش افزوده (۵۰ ردیف آخر، نزولی). هر بار که tax_percent یا tax_enabled از طریق PATCH /api/v1/admin/settings تغییر کند، یک ردیف با کاربرِ تغییردهنده ثبت می‌شود. Permission: ROLE_ADMIN

Response 200:

{
  "success": true,
  "data": [
    { "tax_percent": 10, "enabled": true, "changed_by_name": "مدیر سیستم", "changed_at": 1782800000 }
  ]
}

GET /api/v1/admin/settlement/{uuid}

جزئیات یک درخواست تسویه (برای صفحه‌ی /admin/settlements/{uuid}). Permission: ROLE_ADMIN

Response 200:

{
  "success": true,
  "data": {
    "uuid": "...",
    "representation_name": "نماینده یزد",
    "representation_mobile": "09390036732",
    "amount": 500000,
    "status": "pending",
    "bank_card": "6037...", "bank_name": "ملت", "bank_iban": "IR...", "bank_owner": "...",
    "reject_reason": null,
    "requested_at": "2026-06-24T...", "processed_at": null
  }
}

تأیید/رد از طریق POST /api/v1/settlement/{uuid}/approve|reject (در docs/api/settlement.md).

Errors: NOT_FOUND (404) — درخواست یافت نشد.


Application Logs

Persisted application logs (warning level and above). Written by the DbLogger decorator over the logger service into the app_log table — every LoggerInterface::warning()/error()/critical()/... call across the backend lands here, while info/debug go to stderr only.

GET /api/v1/admin/logs

List persisted logs with pagination and filters.

Permission: ROLE_ADMIN

Query Parameters

Param Type Required Description
page integer Default: 1
limit integer Default: 25 (max 100)
level string Exact PSR level: warning, error, critical, alert, emergency
search string Substring match on the message
from integer Unix timestamp lower bound (created_at >=)
to integer Unix timestamp upper bound (created_at <=)

Ordered by newest first (id DESC).

Response 200

{
  "success": true,
  "data": [
    {
      "id": 4213,
      "level": "error",
      "message": "Unhandled exception: RuntimeException: boom @ /var/www/html/src/Foo.php:42 [path=/oauth/userinfo]",
      "context": "{\"exception\":\"RuntimeException: boom @ /var/www/html/src/Foo.php:42\"}",
      "channel": "app",
      "path": "/oauth/userinfo",
      "created_at": 1717000000
    }
  ],
  "meta": { "totalRecords": 137, "totalPages": 6, "currentPage": 1 }
}

Notes:

  • context is a JSON string (or null); a Throwable in the context is stored as a compact Class: message @ file:line string, never the raw object.
  • created_at is a Unix timestamp (integer).

DELETE /api/v1/admin/logs

Delete all persisted logs (truncate the app_log table). Irreversible.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "deleted": 137 } }
  • deleted — number of rows removed.

Log Retention

Logs are pruned automatically based on the log_retention_days setting (see Site SettingsGET/PATCH /api/v1/admin/settings, whitelisted key log_retention_days, default 90, 0 = keep forever).

  • A daily scheduled task (App\Shared\Logging\Message\PruneLogsMessage, registered in src/Schedule.php, routed to scheduler_default) deletes logs older than log_retention_days.
  • Manual prune: php bin/console app:prune-logs (reads the same setting, deletes older-than-retention rows, prints the count).
  • Requires the scheduler worker: php bin/console messenger:consume scheduler_default.