feat: unify wallet with real payment methods, full transaction transparency, pay-session-from-wallet
Address wallet feedback: use the clinic's real payment infrastructure,
redesign the tab to match the admin panel, and make every wallet movement
fully auditable.
Backend:
- WalletTransaction: add createdBy (acting user) + createdByName, payment_method,
reference, status; toArray exposes them (migration Version20260716083939).
- WalletService (Settlement): balance/charge/withdraw + settleSessionFromWallet,
records actor/method/reason; insufficient balance throws ERR_WALLET_INSUFFICIENT.
- PatientController: charge/withdraw delegate to WalletService and accept
payment_method/reference; PATCH /session/{uuid} with payment_method=wallet
debits the patient's final share from the wallet (reference=session:{uuid}).
- docs/api/patient.md updated.
Frontend:
- Wallet modal redesigned to panel style (no gradient); payment method now uses
the clinic's real bank accounts + POS devices (usePaymentMethods) plus cash.
- Wallet tab: panel balance card + DataTable ledger with columns مبلغ/نوع/روش/
دلیل/ثبتکننده/تاریخ/ساعت/وضعیت + همه/واریزی/برداشت filters.
- Session card «تکمیل پرداخت» opens a payment-method chooser incl. کیف پول.
Tests: backend transparency + session-from-wallet (success/insufficient/cash);
frontend modal (real methods, toman→rials) + wallet tab + settle chooser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-6
@@ -589,24 +589,29 @@ When an appointment's status changes to `confirmed` via `PATCH /api/v1/appointme
|
||||
لیست پرداختهای درگاهیِ بیمار (paginated). Query: `page`, `limit` (≤100)، `status` (اختیاری: `pending|success|failed|canceled|refunded`).
|
||||
Response: `{ success, data: [{ uuid, order_id, amount_rials, status, gateway, type, reference_id, appointment_uuid, created_at }], meta: { totalRecords, totalPages, currentPage } }`
|
||||
|
||||
هر تراکنش برای شفافیت این فیلدها را دارد: `type` (credit/debit)، `payment_method` (card/pos/cash/gateway/wallet یا null)، `description` (دلیل)، `reference` (مرجعِ ماشینی مثل `session:{uuid}`)، `created_by_name` (کاربرِ ثبتکننده)، `status` (`confirmed`)، `balance_after`، `created_at`.
|
||||
|
||||
### GET `/api/v1/patient/{uuid}/wallet`
|
||||
موجودی + ۱۰ تراکنش اخیر (تب کیفپول). `balance_rials` = مجموع credit − debit.
|
||||
Response: `{ success, data: { balance_rials, recent_transactions: [{ uuid, amount_rials, type, description, balance_after, created_at }] } }`
|
||||
Response: `{ success, data: { balance_rials, recent_transactions: [{ uuid, amount_rials, type, description, balance_after, created_by_name, payment_method, reference, status, created_at }] } }`
|
||||
|
||||
### POST `/api/v1/patient/{uuid}/wallet/charge`
|
||||
شارژ دستی کیفپول (مثلاً بیعانهٔ حضوری). یک تراکنش `credit` برای کاربرِ صاحب رکورد میسازد.
|
||||
شارژ دستی کیفپول (مثلاً بیعانهٔ حضوری). یک تراکنش `credit` برای کاربرِ صاحب رکورد میسازد؛ کاربرِ درخواستکننده بهعنوان `created_by` ثبت میشود.
|
||||
```json
|
||||
{ "amount_rials": 300000, "description": "بیعانه نوبت (اختیاری، پیشفرض «شارژ کیف پول»)" }
|
||||
{ "amount_rials": 300000, "description": "بیعانه نوبت (اختیاری)", "payment_method": "card", "reference": "(اختیاری)" }
|
||||
```
|
||||
`amount_rials` باید > 0 باشد وگرنه `422`. Response `201`: `{ success, data: { transaction, balance_rials } }`
|
||||
`amount_rials` باید > 0 باشد وگرنه `422`. `payment_method` ناشناخته نادیده گرفته میشود (null). Response `201`: `{ success, data: { transaction, balance_rials } }`
|
||||
|
||||
### POST `/api/v1/patient/{uuid}/wallet/withdraw`
|
||||
برداشت دستی از کیفپول (مثلاً عودت وجه حضوری). یک تراکنش `debit` برای کاربرِ صاحب رکورد میسازد.
|
||||
برداشت دستی از کیفپول (مثلاً عودت وجه حضوری). یک تراکنش `debit` با ثبتِ کاربرِ عامل و روش پرداخت میسازد.
|
||||
```json
|
||||
{ "amount_rials": 200000, "description": "عودت (اختیاری، پیشفرض «برداشت از کیف پول»)" }
|
||||
{ "amount_rials": 200000, "description": "عودت (اختیاری)", "payment_method": "cash" }
|
||||
```
|
||||
`amount_rials` باید > 0 باشد وگرنه `422`. اگر مبلغ از موجودی فعلی بیشتر باشد `422` با کد `ERR_WALLET_INSUFFICIENT`. Response `201`: `{ success, data: { transaction, balance_rials } }`
|
||||
|
||||
### PATCH `/api/v1/session/{uuid}` — پرداخت مراجعه از کیف پول
|
||||
با `{"payment_method": "wallet"}` سهمِ نهاییِ بیمار (`final_price_rials`) از کیف پول کسر میشود: یک تراکنشِ `debit` با `payment_method=wallet`، `reference=session:{uuid}` و دلیلِ «پرداخت سرویس: …» ثبت میگردد. فقط وقتی مراجعه هنوز تسویه نشده و مبلغ > 0 باشد. موجودیِ ناکافی → `422` `ERR_WALLET_INSUFFICIENT` (مراجعه تسویه نمیشود).
|
||||
|
||||
### GET `/api/v1/patient/{uuid}/wallet/transactions`
|
||||
دفترِ کاملِ تراکنشهای کیفپول (paginated). Query: `page`, `limit` (≤100).
|
||||
Response: `{ success, data: [{ uuid, amount_rials, type, description, balance_after, created_at }], meta: { totalRecords, totalPages, currentPage } }`
|
||||
|
||||
Reference in New Issue
Block a user