docs(api): document booking lock, patient fields, and payment confirm

appointment.md: for_self + patient_* request fields, expires_at and
patient_* response fields, the 15-minute lock/expiry behavior, and the
atomic-conflict 409. payment.md: successful appointment payment confirms
the booking and SMSes the patient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-15 18:01:36 +03:30
co-authored by Claude Opus 4.8
parent a52f412cfc
commit ff90ffb9ca
2 changed files with 28 additions and 6 deletions
+26 -6
View File
@@ -78,6 +78,12 @@ Book an appointment slot.
"doctor_uuid": "550e8400-e29b-41d4-a716-446655440000",
"slot_start": 1718438400,
"slot_end": 1718439600,
"for_self": false,
"patient_name": "علی احمدی",
"patient_mobile": "09120000000",
"patient_national_code": "0012345678",
"patient_gender": "male",
"patient_reason": "چکاپ",
"note": "لطفاً سریع ویزیت شوم"
}
```
@@ -87,21 +93,35 @@ Book an appointment slot.
| `doctor_uuid` | string (UUID) | ✅ | Doctor UUID |
| `slot_start` | integer | ✅ | Slot start (Unix timestamp) |
| `slot_end` | integer | ✅ | Slot end (Unix timestamp) |
| `for_self` | boolean | ❌ | `true` (default) = patient is the logged-in payer; `false` = booking for someone else |
| `patient_name` | string | ⚠️ | Required when `for_self=false`; otherwise filled from the payer's profile |
| `patient_mobile` | string | ⚠️ | Required when `for_self=false`; otherwise the payer's mobile |
| `patient_national_code` | string | ❌ | Patient national code (only when for another person) |
| `patient_gender` | string | ❌ | `male` / `female` |
| `patient_reason` | string | ❌ | Reason for visit |
| `note` | string | ❌ | Patient note |
> **Payer vs patient:** the authenticated user (`user`) is always the payer; the `patient_*` fields describe who the visit is for and are stored separately. **Temporary lock:** the slot is held by the new `pending` booking for **15 minutes** (`expires_at = created_at + 900`). If payment is not completed in time, the booking is moved to `expired` and the slot is freed (see `app:cancel-expired-appointments`). An expired pending booking no longer blocks the slot even before the cron runs.
### Response `201`
```json
{
"success": true,
"data": {
"uuid": "appt-uuid-...",
"doctor": { "uuid": "...", "title": "دکتر علی احمدی" },
"user": { "uuid": "...", "real_name": "..." },
"doctor": { "uuid": "...", "name": "دکتر علی احمدی" },
"user": { "uuid": "...", "mobile": "..." },
"slot_start": 1718438400,
"slot_end": 1718439600,
"status": "pending",
"note": "...",
"price": 500000,
"expires_at": 1718438100,
"patient_name": "علی احمدی",
"patient_mobile": "09120000000",
"patient_national_code": "0012345678",
"patient_gender": "male",
"patient_reason": "چکاپ",
"version": 1,
"created_at": 1717000000
}
}
@@ -120,9 +140,9 @@ Book an appointment slot.
| Code | HTTP | Description |
|------|------|-------------|
| `ERR_AUTH_001` | 401 | Missing token |
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
| `ERR_CONFLICT_001` | 409 | Slot already booked |
| `ERR_VALIDATION_001` | 422 | Invalid slot times |
| `ERR_VALIDATION_002` | 404 | Doctor not found |
| `ERR_CONFLICT_001` | 409 | Slot already booked (incl. concurrent booking — the booking is atomic) |
| `ERR_VALIDATION_001` | 422 | Invalid slot times, past slot, or missing patient name/mobile when `for_self=false` |
---
+2
View File
@@ -62,6 +62,8 @@ Initiate payment for an appointment. Returns a redirect URL to the payment gatew
}
```
> **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.
### Errors
| Code | HTTP | Description |
|------|------|-------------|