Browse Source
feat(transaction): implement receipt management for transactions
feat(transaction): implement receipt management for transactions
- Added `TransactionReceipt` model to store payment receipts uploaded by users. - Introduced endpoints for uploading receipts and listing receipts associated with transactions. - Updated `TransactionParticipant` model to include a new status 'waiting_approval'. - Enhanced serializers to handle receipt uploads and retrievals, including validation for file uploads. - Updated admin interface to manage transaction receipts effectively. - Added comprehensive API documentation for transaction receipt operations.master
9 changed files with 968 additions and 26 deletions
-
21apps/library/migrations/0009_alter_book_language.py
-
59apps/transaction/admin.py
-
529apps/transaction/doc.py
-
35apps/transaction/migrations/0003_alter_transactionparticipant_status_and_more.py
-
38apps/transaction/models.py
-
51apps/transaction/serializers.py
-
3apps/transaction/urls.py
-
153apps/transaction/views.py
-
79templates/docs.html
@ -0,0 +1,21 @@ |
|||
# Generated by Django 5.1.8 on 2025-12-03 23:32 |
|||
|
|||
import dj_language.field |
|||
import django.db.models.deletion |
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('dj_language', '0002_auto_20220120_1344'), |
|||
('library', '0008_auto_20251203_1533'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='book', |
|||
name='language', |
|||
field=dj_language.field.LanguageField(blank=True, default=69, limit_choices_to={'status': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='dj_language.language', verbose_name='Language'), |
|||
), |
|||
] |
|||
@ -0,0 +1,529 @@ |
|||
def doc_upload_transaction_receipts(): |
|||
return """ |
|||
# 🐈 Scenario |
|||
🛠️ آپلود رسید پرداخت برای تراکنش |
|||
|
|||
این API برای آپلود یک یا چند رسید پرداخت برای یک تراکنش استفاده میشود. |
|||
پس از آپلود موفقیتآمیز، وضعیت تراکنش به 'waiting_approval' (در انتظار تایید) تغییر میکند. |
|||
|
|||
--- |
|||
|
|||
## 🚀 روند آپلود (دو مرحلهای) |
|||
|
|||
### مرحله 1️⃣: آپلود فایل به سرور موقت |
|||
ابتدا باید فایلهای خود را به endpoint زیر آپلود کنید: |
|||
|
|||
``` |
|||
POST /upload-tmp-media/ |
|||
Content-Type: multipart/form-data |
|||
|
|||
Body: |
|||
- file: [فایل رسید] |
|||
``` |
|||
|
|||
**پاسخ:** |
|||
```json |
|||
{ |
|||
"url": "/static/tmp/xyz123-receipt.jpg", |
|||
"name": "receipt.jpg", |
|||
"size": "1024000", |
|||
"mime_type": "image/jpeg" |
|||
} |
|||
``` |
|||
|
|||
### مرحله 2️⃣: ثبت URL فایلها در تراکنش |
|||
سپس URL های دریافتی را به این endpoint ارسال کنید: |
|||
|
|||
``` |
|||
POST /api/transactions/<transaction_id>/receipts/upload/ |
|||
Content-Type: application/json |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 🚀 درخواست API (مرحله 2) |
|||
|
|||
### URL: |
|||
``` |
|||
POST /api/transactions/<transaction_id>/receipts/upload/ |
|||
``` |
|||
|
|||
### پارامترهای URL: |
|||
| کلید | نوع داده | توضیحات | |
|||
|------------------|-----------|----------------------------------------------------------| |
|||
| `transaction_id` | Integer | شناسه تراکنش که میخواهید رسید برای آن ثبت کنید | |
|||
|
|||
### پارامترهای درخواست (JSON Body): |
|||
| کلید | نوع داده | الزامی | توضیحات | |
|||
|---------------|-----------|--------|----------------------------------------------------------| |
|||
| `files` | String[] | بله | لیست URL های فایلهای آپلود شده از مرحله 1 (حداکثر 10 فایل) | |
|||
| `description` | String | خیر | توضیحات اختیاری درباره رسیدها | |
|||
|
|||
--- |
|||
|
|||
## 💡 نکات مهم: |
|||
1. **روند دو مرحلهای**: |
|||
- **مرحله 1**: ابتدا فایلها را به `/upload-tmp-media/` آپلود کنید |
|||
- **مرحله 2**: سپس URL های دریافتی را به این API ارسال کنید |
|||
|
|||
2. **محدودیت فایلها**: |
|||
- حداکثر 10 فایل میتوانید در هر درخواست ثبت کنید |
|||
|
|||
3. **وضعیت تراکنش**: |
|||
- فقط میتوانید برای تراکنشهایی با وضعیت 'pending' یا 'waiting_approval' رسید آپلود کنید |
|||
- پس از ثبت موفقیتآمیز، وضعیت تراکنش به 'waiting_approval' تغییر میکند |
|||
|
|||
4. **احراز هویت**: |
|||
- باید توکن احراز هویت را در هدر درخواست ارسال کنید |
|||
- فقط میتوانید برای تراکنشهای خودتان رسید آپلود کنید |
|||
|
|||
--- |
|||
|
|||
## 📊 پاسخها |
|||
|
|||
| کد وضعیت | توضیحات | |
|||
|---------------|-----------------------------------------------------------| |
|||
| `201` | موفقیتآمیز - رسیدها با موفقیت ثبت شدند | |
|||
| `400` | دادههای نامعتبر یا تراکنش قادر به دریافت رسید نیست | |
|||
| `403` | عدم دسترسی - شما صاحب این تراکنش نیستید | |
|||
| `404` | تراکنش یافت نشد | |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست کامل (JSON): |
|||
|
|||
```json |
|||
{ |
|||
"files": [ |
|||
"/static/tmp/xyz123-receipt1.jpg", |
|||
"/static/tmp/abc456-receipt2.jpg" |
|||
], |
|||
"description": "Payment receipt for Python course" |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ موفقیتآمیز |
|||
|
|||
```json |
|||
{ |
|||
"success": true, |
|||
"message": "Receipts uploaded successfully", |
|||
"transaction_status": "waiting_approval", |
|||
"receipts": [ |
|||
{ |
|||
"id": 1, |
|||
"file": "http://example.com/media/receipts/1/receipt1.jpg", |
|||
"description": "Payment receipt for course enrollment", |
|||
"uploaded_at": "2025-12-03T10:30:00Z" |
|||
}, |
|||
{ |
|||
"id": 2, |
|||
"file": "http://example.com/media/receipts/1/receipt2.jpg", |
|||
"description": "Payment receipt for course enrollment", |
|||
"uploaded_at": "2025-12-03T10:30:05Z" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست کامل (cURL): |
|||
|
|||
### مرحله 1 - آپلود فایل: |
|||
```bash |
|||
curl -X POST \\ |
|||
'http://your-api.com/upload-tmp-media/' \\ |
|||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \\ |
|||
-F 'file=@/path/to/receipt1.jpg' |
|||
``` |
|||
|
|||
### مرحله 2 - ثبت رسید: |
|||
```bash |
|||
curl -X POST \\ |
|||
'http://your-api.com/api/transactions/123/receipts/upload/' \\ |
|||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \\ |
|||
-H 'Content-Type: application/json' \\ |
|||
-d '{ |
|||
"files": ["/static/tmp/xyz123-receipt1.jpg"], |
|||
"description": "Payment receipt for Python course" |
|||
}' |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ خطا (403 - عدم دسترسی): |
|||
|
|||
```json |
|||
{ |
|||
"message": "You don't have permission to upload receipts for this transaction" |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ خطا (400 - وضعیت نامعتبر): |
|||
|
|||
```json |
|||
{ |
|||
"message": "Cannot upload receipts for transaction with status 'success'" |
|||
} |
|||
``` |
|||
""" |
|||
|
|||
|
|||
def doc_list_transaction_receipts(): |
|||
return """ |
|||
# 🐈 Scenario |
|||
🛠️ لیست رسیدهای پرداخت یک تراکنش |
|||
|
|||
این API برای دریافت لیست تمام رسیدهای آپلود شده برای یک تراکنش خاص استفاده میشود. |
|||
|
|||
--- |
|||
|
|||
## 🚀 درخواست API |
|||
|
|||
### URL: |
|||
``` |
|||
GET /api/transactions/<transaction_id>/receipts/ |
|||
``` |
|||
|
|||
### پارامترهای URL: |
|||
| کلید | نوع داده | توضیحات | |
|||
|------------------|-----------|----------------------------------------------------------| |
|||
| `transaction_id` | Integer | شناسه تراکنش که میخواهید رسیدهای آن را مشاهده کنید | |
|||
|
|||
--- |
|||
|
|||
## 💡 نکات مهم: |
|||
1. **احراز هویت**: |
|||
- باید توکن احراز هویت را در هدر درخواست ارسال کنید |
|||
- فقط میتوانید رسیدهای تراکنشهای خودتان را مشاهده کنید |
|||
|
|||
2. **مرتبسازی**: |
|||
- رسیدها بر اساس تاریخ آپلود (جدیدترین اول) مرتب میشوند |
|||
|
|||
--- |
|||
|
|||
## 📊 پاسخها |
|||
|
|||
| کد وضعیت | توضیحات | |
|||
|---------------|-----------------------------------------------------------| |
|||
| `200` | موفقیتآمیز - لیست رسیدها بازگردانده شد | |
|||
| `403` | عدم دسترسی - شما صاحب این تراکنش نیستید | |
|||
| `404` | تراکنش یافت نشد | |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ موفقیتآمیز |
|||
|
|||
```json |
|||
[ |
|||
{ |
|||
"id": 1, |
|||
"file": "http://example.com/media/receipts/1/receipt1.jpg", |
|||
"description": "Payment receipt for course enrollment", |
|||
"uploaded_at": "2025-12-03T10:30:00Z" |
|||
}, |
|||
{ |
|||
"id": 2, |
|||
"file": "http://example.com/media/receipts/1/receipt2.jpg", |
|||
"description": "Second payment receipt", |
|||
"uploaded_at": "2025-12-03T10:25:00Z" |
|||
} |
|||
] |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 توضیحات مقادیر پاسخ |
|||
|
|||
| کلید | نوع داده | توضیحات | |
|||
|---------------|------------|----------------------------------------------------------| |
|||
| `id` | Integer | شناسه یکتای رسید | |
|||
| `file` | String | URL کامل فایل رسید آپلود شده | |
|||
| `description` | String | توضیحات اختیاری درباره رسید (ممکن است خالی باشد) | |
|||
| `uploaded_at` | DateTime | تاریخ و زمان آپلود رسید | |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست (cURL): |
|||
|
|||
```bash |
|||
curl -X GET \\ |
|||
'http://your-api.com/api/transactions/123/receipts/' \\ |
|||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ خطا (403 - عدم دسترسی): |
|||
|
|||
```json |
|||
{ |
|||
"message": "You don't have permission to view receipts for this transaction" |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ خطا (404 - تراکنش یافت نشد): |
|||
|
|||
```json |
|||
{ |
|||
"message": "Transaction not found" |
|||
} |
|||
``` |
|||
""" |
|||
|
|||
|
|||
def doc_transaction_list(): |
|||
return """ |
|||
# 🐈 Scenario |
|||
🛠️ لیست تراکنشهای کاربر |
|||
|
|||
این API برای دریافت لیست تمام تراکنشهای کاربر احراز هویت شده استفاده میشود. |
|||
|
|||
--- |
|||
|
|||
## 🚀 درخواست API |
|||
|
|||
### URL: |
|||
``` |
|||
GET /api/transactions/list/ |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 💡 نکات مهم: |
|||
1. **احراز هویت**: |
|||
- باید توکن احراز هویت را در هدر درخواست ارسال کنید |
|||
- فقط تراکنشهای خودتان را مشاهده میکنید |
|||
|
|||
2. **فیلترینگ خودکار**: |
|||
- تراکنشهای حذف شده (soft deleted) نمایش داده نمیشوند |
|||
|
|||
3. **وضعیتهای تراکنش**: |
|||
- `pending`: در انتظار پرداخت |
|||
- `waiting_approval`: در انتظار تایید (رسید آپلود شده) |
|||
- `success`: پرداخت موفق و تایید شده |
|||
- `failed`: پرداخت ناموفق |
|||
|
|||
--- |
|||
|
|||
## 📊 پاسخها |
|||
|
|||
| کد وضعیت | توضیحات | |
|||
|---------------|-----------------------------------------------------------| |
|||
| `200` | موفقیتآمیز - لیست تراکنشها بازگردانده شد | |
|||
| `401` | عدم احراز هویت | |
|||
|
|||
--- |
|||
|
|||
## 📄 توضیحات مقادیر پاسخ |
|||
|
|||
| کلید | نوع داده | توضیحات | |
|||
|---------------|------------|----------------------------------------------------------| |
|||
| `id` | Integer | شناسه یکتای تراکنش | |
|||
| `course` | Object | اطلاعات دوره مرتبط با تراکنش | |
|||
| `status` | String | وضعیت تراکنش (pending, waiting_approval, success, failed) | |
|||
| `price` | Decimal | مبلغ تراکنش | |
|||
| `created_at` | DateTime | تاریخ و زمان ایجاد تراکنش | |
|||
| `updated_at` | DateTime | تاریخ و زمان آخرین بهروزرسانی تراکنش | |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ موفقیتآمیز |
|||
|
|||
```json |
|||
[ |
|||
{ |
|||
"id": 1, |
|||
"course": { |
|||
"id": 5, |
|||
"title": "Python Programming Basics", |
|||
"slug": "python-programming-basics", |
|||
"thumbnail": "http://example.com/media/courses/thumbnails/python.jpg", |
|||
"price": "99.00", |
|||
"final_price": "79.00" |
|||
}, |
|||
"status": "waiting_approval", |
|||
"price": "79.00", |
|||
"created_at": "2025-12-01T10:00:00Z", |
|||
"updated_at": "2025-12-03T10:30:00Z" |
|||
}, |
|||
{ |
|||
"id": 2, |
|||
"course": { |
|||
"id": 8, |
|||
"title": "Django Web Development", |
|||
"slug": "django-web-development", |
|||
"thumbnail": "http://example.com/media/courses/thumbnails/django.jpg", |
|||
"price": "149.00", |
|||
"final_price": "149.00" |
|||
}, |
|||
"status": "success", |
|||
"price": "149.00", |
|||
"created_at": "2025-11-28T14:20:00Z", |
|||
"updated_at": "2025-11-29T09:15:00Z" |
|||
} |
|||
] |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست (cURL): |
|||
|
|||
```bash |
|||
curl -X GET \\ |
|||
'http://your-api.com/api/transactions/list/' \\ |
|||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' |
|||
``` |
|||
""" |
|||
|
|||
|
|||
def doc_create_transaction(): |
|||
return """ |
|||
# 🐈 Scenario |
|||
🛠️ ثبتنام در دوره و ایجاد تراکنش |
|||
|
|||
این API برای ثبتنام کاربر در یک دوره و ایجاد تراکنش استفاده میشود. |
|||
|
|||
--- |
|||
|
|||
## 🚀 درخواست API |
|||
|
|||
### URL: |
|||
``` |
|||
POST /api/transactions/<slug>/join/ |
|||
``` |
|||
|
|||
### پارامترهای URL: |
|||
| کلید | نوع داده | توضیحات | |
|||
|---------|-----------|----------------------------------------------------------| |
|||
| `slug` | String | اسلاگ دورهای که میخواهید در آن ثبتنام کنید | |
|||
|
|||
### پارامترهای درخواست (JSON Body): |
|||
| کلید | نوع داده | الزامی | توضیحات | |
|||
|---------------------|-----------|--------|----------------------------------------------------------| |
|||
| `participant_infos` | Array | بله | لیست اطلاعات شرکتکنندگان | |
|||
|
|||
### ساختار `participant_infos`: |
|||
| کلید | نوع داده | الزامی | توضیحات | |
|||
|---------------|-----------|--------|----------------------------------------------------------| |
|||
| `fullname` | String | بله | نام کامل شرکتکننده | |
|||
| `email` | String | بله | ایمیل شرکتکننده (برای دوره رایگان باید با ایمیل کاربر احراز هویت شده یکسان باشد) | |
|||
| `phone_number`| String | خیر | شماره تلفن شرکتکننده | |
|||
| `gender` | String | خیر | جنسیت شرکتکننده (male, female) | |
|||
| `birthdate` | Date | خیر | تاریخ تولد شرکتکننده (فرمت: YYYY-MM-DD) | |
|||
|
|||
--- |
|||
|
|||
## 💡 نکات مهم: |
|||
1. **دوره رایگان**: |
|||
- اگر دوره رایگان باشد و فقط یک شرکتکننده در لیست باشد و ایمیل او با کاربر احراز هویت شده یکسان باشد، تراکنش به صورت خودکار تایید میشود (status = 'success') |
|||
- کاربر به صورت خودکار به عنوان دانشجو در دوره ثبت میشود |
|||
|
|||
2. **دوره پولی**: |
|||
- تراکنش با وضعیت 'pending' ایجاد میشود |
|||
- کاربر باید رسید پرداخت خود را آپلود کند |
|||
- پس از آپلود رسید، وضعیت به 'waiting_approval' تغییر میکند |
|||
- پس از تایید توسط ادمین، وضعیت به 'success' تغییر میکند |
|||
|
|||
3. **احراز هویت**: |
|||
- باید توکن احراز هویت را در هدر درخواست ارسال کنید |
|||
|
|||
--- |
|||
|
|||
## 📊 پاسخها |
|||
|
|||
| کد وضعیت | توضیحات | |
|||
|---------------|-----------------------------------------------------------| |
|||
| `201` | موفقیتآمیز - تراکنش ایجاد شد | |
|||
| `400` | دادههای نامعتبر | |
|||
| `404` | دوره یافت نشد | |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست (JSON Body): |
|||
|
|||
```json |
|||
{ |
|||
"participant_infos": [ |
|||
{ |
|||
"fullname": "علی رضایی", |
|||
"email": "ali@example.com", |
|||
"phone_number": "+989123456789", |
|||
"gender": "male", |
|||
"birthdate": "1995-05-15" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ موفقیتآمیز (دوره رایگان): |
|||
|
|||
```json |
|||
{ |
|||
"message": "Transaction Participant created successfully.", |
|||
"transaction_id": 123, |
|||
"participant_infos": [ |
|||
{ |
|||
"fullname": "علی رضایی", |
|||
"email": "ali@example.com", |
|||
"phone_number": "+989123456789", |
|||
"gender": "male", |
|||
"birthdate": "1995-05-15" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه پاسخ موفقیتآمیز (دوره پولی): |
|||
|
|||
```json |
|||
{ |
|||
"message": "Transaction Participant created successfully.", |
|||
"transaction_id": 124, |
|||
"participant_infos": [ |
|||
{ |
|||
"fullname": "سارا احمدی", |
|||
"email": "sara@example.com", |
|||
"phone_number": "+989123456789", |
|||
"gender": "female", |
|||
"birthdate": "1998-03-20" |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
توجه: برای دوره پولی، شما باید با استفاده از `transaction_id` بازگشتی، رسید پرداخت خود را از طریق API آپلود رسید آپلود کنید. |
|||
|
|||
--- |
|||
|
|||
## 📄 نمونه درخواست (cURL): |
|||
|
|||
```bash |
|||
curl -X POST \\ |
|||
'http://your-api.com/api/transactions/python-programming-basics/join/' \\ |
|||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \\ |
|||
-H 'Content-Type: application/json' \\ |
|||
-d '{ |
|||
"participant_infos": [ |
|||
{ |
|||
"fullname": "علی رضایی", |
|||
"email": "ali@example.com", |
|||
"phone_number": "+989123456789", |
|||
"gender": "male", |
|||
"birthdate": "1995-05-15" |
|||
} |
|||
] |
|||
}' |
|||
``` |
|||
""" |
|||
@ -0,0 +1,35 @@ |
|||
# Generated by Django 5.1.8 on 2025-12-03 23:32 |
|||
|
|||
import apps.transaction.models |
|||
import django.db.models.deletion |
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('transaction', '0002_remove_transactionparticipant_is_paid_and_more'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='transactionparticipant', |
|||
name='status', |
|||
field=models.CharField(choices=[('pending', 'Pending'), ('waiting_approval', 'Waiting for Approval'), ('success', 'Success'), ('failed', 'Failed')], default='pending', max_length=20, verbose_name='Transaction Status'), |
|||
), |
|||
migrations.CreateModel( |
|||
name='TransactionReceipt', |
|||
fields=[ |
|||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('file', models.FileField(help_text='Upload payment receipt image or document', upload_to=apps.transaction.models.receipt_file_upload_to, verbose_name='Receipt File')), |
|||
('uploaded_at', models.DateTimeField(auto_now_add=True, verbose_name='Uploaded At')), |
|||
('description', models.TextField(blank=True, help_text='Optional description or notes about the receipt', null=True, verbose_name='Description')), |
|||
('transaction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='receipts', to='transaction.transactionparticipant', verbose_name='Transaction')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Transaction Receipt', |
|||
'verbose_name_plural': 'Transaction Receipts', |
|||
'ordering': ['-uploaded_at'], |
|||
}, |
|||
), |
|||
] |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue