203 lines
5.2 KiB
Markdown
203 lines
5.2 KiB
Markdown
# Payment API
|
|
|
|
> **Prefix:** `/api/v1/payment`, `/api/v1/subscription-payment`
|
|
> **Supported Gateways:** `mellat` (Mellat Bank SOAP) | `sep` (SEP REST)
|
|
|
|
---
|
|
|
|
## GET `/api/v1/payment/config`
|
|
|
|
دریافت تنظیمات عمومی پرداخت — برای نمایش وضعیت درگاه آزمایشی در frontend.
|
|
|
|
**Permission:** `IS_AUTHENTICATED_FULLY`
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"test_mode": true
|
|
}
|
|
}
|
|
```
|
|
|
|
| Field | Type | Description |
|
|
|-------|------|-------------|
|
|
| `test_mode` | boolean | `true` = درگاه آزمایشی فعال است — backend از MockGateway استفاده میکند و پول واقعی کسر نمیشود |
|
|
|
|
**نکته:** این endpoint هیچ اطلاعات حساسی (terminal_id، password، ...) را expose نمیکند. تنها یک boolean برای مصرف frontend است.
|
|
|
|
---
|
|
|
|
## POST `/api/v1/payment/appointment`
|
|
|
|
Initiate payment for an appointment. Returns a redirect URL to the payment gateway.
|
|
|
|
**Permission:** `AUTH` — must be the appointment owner (patient)
|
|
|
|
### Request Body (`application/json`)
|
|
```json
|
|
{
|
|
"appointment_uuid": "appt-uuid-...",
|
|
"gateway": "mellat",
|
|
"frontend_address": "https://myapp.com/payment/result"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `appointment_uuid` | string (UUID) | ✅ | Appointment to pay for |
|
|
| `gateway` | string | ✅ | `"mellat"` or `"sep"` |
|
|
| `frontend_address` | string | ❌ | Redirect URL after payment (overrides default) |
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"payment_uuid": "pay-uuid-...",
|
|
"redirect_url": "https://bpm.shaparak.ir/pgwchannel/...",
|
|
"order_id": "CLINICPRO-1717000000-ABC123"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_FORBIDDEN_001` | 403 | Not the patient |
|
|
| `ERR_PAYMENT_003` | 422 | Appointment not in payable state |
|
|
| `ERR_PAYMENT_002` | 422 | Invalid amount |
|
|
| `ERR_PAYMENT_001` | 503 | Payment gateway unavailable |
|
|
|
|
---
|
|
|
|
## POST `/api/v1/payment/callback/{gateway}`
|
|
## GET `/api/v1/payment/callback/{gateway}`
|
|
|
|
Payment gateway callback. Called by the bank after user completes (or cancels) payment.
|
|
|
|
**Permission:** `PUBLIC` — called by the gateway, not the user
|
|
|
|
### Path Parameters
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `gateway` | string | `mellat` or `sep` |
|
|
|
|
### Request (varies by gateway)
|
|
**Mellat POST fields:**
|
|
```
|
|
ResCode=0&SaleOrderId=...&SaleReferenceId=...&RefId=...
|
|
```
|
|
|
|
**SEP POST fields:**
|
|
```
|
|
Status=2&RRN=...&RefNum=...&TerminalId=...&TraceNo=...
|
|
```
|
|
|
|
### Response
|
|
- If `ResCode=0` (success): appointment confirmed, redirect to `frontend_address?success=1&uuid=...`
|
|
- If failed: redirect to `frontend_address?success=0&error=...`
|
|
|
|
### Notes
|
|
- On success: appointment status → `confirmed`, wallet credited with doctor's share
|
|
- Payment record stored with: `order_id`, `amount_rials`, `status`, `gateway`, `ref_id`
|
|
|
|
---
|
|
|
|
## POST `/api/v1/subscription-payment`
|
|
|
|
Initiate a subscription / wallet top-up payment (not tied to a specific appointment).
|
|
|
|
**Permission:** `AUTH`
|
|
|
|
### Request Body
|
|
```json
|
|
{
|
|
"gateway": "mellat",
|
|
"amount_rials": 1000000,
|
|
"frontend_address": "https://myapp.com/wallet/result"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `gateway` | string | ✅ | `"mellat"` or `"sep"` |
|
|
| `amount_rials` | integer | ✅ | Amount in Rials (min: 10,000) |
|
|
| `frontend_address` | string | ❌ | Redirect URL after payment |
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"payment_uuid": "pay-uuid-...",
|
|
"redirect_url": "https://bpm.shaparak.ir/pgwchannel/...",
|
|
"order_id": "CLINICPRO-SUB-1717000000-XYZ"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_PAYMENT_002` | 422 | Invalid amount |
|
|
| `ERR_PAYMENT_001` | 503 | Gateway unavailable |
|
|
|
|
---
|
|
|
|
## POST/GET `/api/v1/subscription-payment/callback/{gateway}`
|
|
|
|
Callback for subscription payments. Same behavior as appointment callback but credits wallet instead.
|
|
|
|
**Permission:** `PUBLIC`
|
|
|
|
---
|
|
|
|
## GET `/api/v1/payment/{uuid}`
|
|
|
|
Get payment status and details.
|
|
|
|
**Permission:** `AUTH` — must be the payment owner or `ROLE_ADMIN`
|
|
|
|
### Path Parameters
|
|
| Param | Type | Description |
|
|
|-------|------|-------------|
|
|
| `uuid` | string (UUID) | Payment UUID |
|
|
|
|
### Response `200`
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"uuid": "pay-uuid-...",
|
|
"order_id": "CLINICPRO-1717000000-ABC123",
|
|
"amount_rials": 500000,
|
|
"status": "paid",
|
|
"gateway": "mellat",
|
|
"ref_id": "123456789",
|
|
"appointment_uuid": "appt-uuid-...",
|
|
"created_at": 1717000000,
|
|
"paid_at": 1717000120
|
|
}
|
|
}
|
|
```
|
|
|
|
**Payment Status Values:**
|
|
| Value | Description |
|
|
|-------|-------------|
|
|
| `pending` | Created, not paid yet |
|
|
| `paid` | Successfully paid |
|
|
| `failed` | Gateway returned failure |
|
|
| `cancelled` | User cancelled at gateway |
|
|
| `refunded` | Refunded |
|
|
|
|
### Errors
|
|
| Code | HTTP | Description |
|
|
|------|------|-------------|
|
|
| `ERR_AUTH_001` | 401 | Missing token |
|
|
| `ERR_FORBIDDEN_001` | 403 | Not the owner |
|
|
| `ERR_NOT_FOUND_001` | 404 | Payment not found |
|