Files
clinicpro/.claude/prompt/my-payments-list-endpoint.md
T
hamedandClaude Opus 4.8 038cb73ea7 feat(payment): add GET /api/v1/my/payments for the logged-in user
Add PaymentRepository::findByUser/countByUser and a paginated
my/payments endpoint that returns the authenticated user's own payments
(derived from the token, never a userId in the URL). Public dashboard's
transactions tab can now list payments instead of hitting a nonexistent
route.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:21:31 +03:30

134 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# افزودن endpoint لیست پرداخت‌های کاربر لاگین‌شده (`GET /api/v1/my/payments`)
## پروژه
`clinicpro` (Backend). **این پرامپت اول اجرا شود.**
> **Cross-repo:** سایت عمومی (`nobat724_front`) این endpoint را در تب «تراکنش‌ها»ی داشبورد مصرف می‌کند. پرامپت همتای frontend:
> `nobat724_front/.claude/prompt/fix-dashboard-uuid-and-lists.md`
## زمینه
داشبورد کاربر در سایت عمومی تب «تراکنش‌ها» دارد که `request.getMyPayments(userId)` را صدا می‌زند → `GET /api/v1/payment/my-payments/{userId}`. اما **چنین route‌ای در backend وجود ندارد** (تأییدشده با `debug:router`): تنها endpointهای پرداخت، `GET /api/v1/payment/{uuid}` (تک پرداخت) و `GET /api/v1/admin/payments` (ادمین) هستند. در نتیجه تب تراکنش‌ها همیشه **404** می‌گیرد.
`Payment` به `user` (پرداخت‌کننده) لینک دارد و `toArray()` دارد، ولی `PaymentRepository` متد `findByUser` ندارد.
## مشکل / هدف
یک endpoint عمومی-برای-کاربر بساز: `GET /api/v1/my/payments` که **پرداخت‌های خودِ کاربر لاگین‌شده** را (از روی توکن، نه از روی userId در URL) به‌صورت لیست برمی‌گرداند. الگو دقیقاً مثل `MyAppointmentsController` و قرارداد پاسخ مثل بقیه‌ی listهای کاربر.
## فایل‌های مرتبط
| فایل | نقش |
|------|-----|
| `src/Payment/Controller/PaymentController.php` | افزودن متد `myPayments()` (یا کنترلر `MyPaymentsController` جدید) |
| `src/Payment/Repository/PaymentRepository.php` | افزودن `findByUser(User $user, ?string $status, int $page, int $limit)` |
| `src/Payment/Entity/Payment.php` | `getUser()`, `toArray()` موجود |
| `src/Appointment/Controller/MyAppointmentsController.php` | مرجع الگو (CurrentUser, paginated) |
| `docs/api/payment.md` | مستندسازی endpoint جدید |
## وضعیت فعلی (کد واقعی)
### `PaymentRepository` — بدون findByUser
```php
public function findByUuid(string $uuid): ?Payment { ... }
public function findByOrderId(string $orderId): ?Payment { ... }
public function save(Payment $entity, bool $flush = true): void { ... }
```
### `Payment` — لینک user + toArray
```php
#[ORM\JoinColumn(name: 'user_id', ...)] private User $user;
public function getUser(): User { return $this->user; }
public function getOrderId(): string { ... }
public function getAmountRials(): int { ... }
public function getStatus(): string { ... }
public function toArray(): array { ... } // شکل پرداخت
```
### route ناموجود که فرانت صدا می‌زند
```
GET /api/v1/payment/my-payments/{userId} → 404 (وجود ندارد)
```
## وظایف
### ۱. `findByUser` در `PaymentRepository`
```php
/** @return Payment[] */
public function findByUser(User $user, ?string $status = null, int $page = 1, int $limit = 20): array
{
$qb = $this->createQueryBuilder('p')
->where('p.user = :user')->setParameter('user', $user)
->orderBy('p.createdAt', 'DESC')
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit);
if ($status !== null) {
$qb->andWhere('p.status = :status')->setParameter('status', $status);
}
return $qb->getQuery()->getResult();
}
public function countByUser(User $user, ?string $status = null): int
{
$qb = $this->createQueryBuilder('p')->select('COUNT(p.id)')
->where('p.user = :user')->setParameter('user', $user);
if ($status !== null) {
$qb->andWhere('p.status = :status')->setParameter('status', $status);
}
return (int) $qb->getQuery()->getSingleScalarResult();
}
```
> از `User` به‌جای `user_id` در DQL استفاده کن (`App\Auth\Entity\User` را import کن). status اختیاری (`pending`/`paid`/`failed`/...).
### ۲. endpoint `GET /api/v1/my/payments`
در `PaymentController` (یا کنترلر جدید `MyPaymentsController` هم‌سبک با `MyAppointmentsController`):
```php
#[Route('/api/v1/my/payments', methods: ['GET'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function myPayments(Request $request, #[CurrentUser] User $user): JsonResponse
{
$page = max(1, (int) $request->query->get('page', 1));
$limit = min(100, max(1, (int) $request->query->get('limit', 20)));
$status = $request->query->get('status');
$items = array_map(
fn(Payment $p) => $p->toArray(),
$this->paymentRepo->findByUser($user, $status, $page, $limit)
);
$total = $this->paymentRepo->countByUser($user, $status);
return $this->paginated($items, $total, $page, $limit);
}
```
> از `paginated()` استفاده کن (نه success تودرتو) تا فرانت items را از `data` و total را از `meta.totalRecords` بخواند — هم‌خوان با بقیه‌ی listها. **کاربر فقط پرداخت‌های خودش را می‌بیند** (از `#[CurrentUser]`؛ هیچ userId از URL گرفته نمی‌شود).
### ۳. مطمئن شو route عمومی/auth درست است
- `/api/v1/my/payments` زیر فایروال `^/api` با `IS_AUTHENTICATED_FULLY` می‌افتد (نیاز به whitelist عمومی ندارد چون کاربر لاگین است). نیازی به تغییر `security.yaml` نیست. تأیید کن با `debug:router`.
### ۴. مستندسازی
در `docs/api/payment.md`: endpoint `GET /api/v1/my/payments` با permission، query params (`page`, `limit`, `status`)، و نمونه‌ی پاسخ paginated واقعی (items + meta).
## نکات مهم
- **هیچ migration لازم نیست** (فقط query + route).
- **امنیت:** هرگز `userId` از URL نگیر؛ فقط `#[CurrentUser]`. کاربر نباید پرداخت دیگران را ببیند.
- پاسخ‌ها از `BaseController`؛ این endpoint باید `paginated()` بدهد (نه `success(['data'=>...])`) — فرانت `data` (آرایه) و `meta.totalRecords` می‌خواند.
- `toArray()` پرداخت شکل واقعی را می‌دهد؛ اگر فیلدی برای نمایش لازم است (مبلغ، وضعیت، تاریخ، نوع، order_id، appointment_uuid) و در `toArray` نیست، اضافه کن.
- تست:
- `ddev exec php -l` روی فایل‌های تغییر یافته
- `ddev exec php bin/console cache:clear`
- `ddev exec php bin/console debug:router | grep "my/payments"`
- با توکن یک کاربر (مثلاً `09210651788` که یک پرداخت آزمایشی دارد): `GET /api/v1/my/payments` → ۲۰۰ با لیست پرداخت‌های همان کاربر؛ بدون توکن → ۴۰۱.
- `docs/api/payment.md` به‌روز شود.