The new updates of partners and user
Made-with: Cursor
This commit is contained in:
0
banking_operations/__init__.py
Normal file
0
banking_operations/__init__.py
Normal file
3
banking_operations/admin.py
Normal file
3
banking_operations/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
432
banking_operations/api.py
Normal file
432
banking_operations/api.py
Normal file
@@ -0,0 +1,432 @@
|
||||
import uuid
|
||||
from django.http import JsonResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.forms.models import model_to_dict
|
||||
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from banking_operations.models import PaymentGateway, PaymentGatewayCredentials
|
||||
from mobile_api.utils import validate_token_and_get_user
|
||||
|
||||
|
||||
def _payment_gateway_to_dict(gateway, request=None):
|
||||
"""Serialize PaymentGateway for JSON."""
|
||||
data = model_to_dict(
|
||||
gateway,
|
||||
fields=[
|
||||
"id",
|
||||
"payment_gateway_id",
|
||||
"payment_gateway_name",
|
||||
"payment_gateway_description",
|
||||
"payment_gateway_url",
|
||||
"payment_gateway_api_key",
|
||||
"payment_gateway_api_secret",
|
||||
"payment_gateway_api_url",
|
||||
"payment_gateway_api_version",
|
||||
"payment_gateway_api_method",
|
||||
"is_active",
|
||||
"created_date",
|
||||
"updated_date",
|
||||
"gateway_priority",
|
||||
],
|
||||
)
|
||||
# Add logo URL if exists
|
||||
if gateway.payment_gateway_logo:
|
||||
if request:
|
||||
data["payment_gateway_logo"] = request.build_absolute_uri(gateway.payment_gateway_logo.url)
|
||||
else:
|
||||
data["payment_gateway_logo"] = gateway.payment_gateway_logo.url
|
||||
else:
|
||||
data["payment_gateway_logo"] = None
|
||||
return data
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayCreateAPI(APIView):
|
||||
"""
|
||||
Create a new PaymentGateway.
|
||||
Body: token, username, payment_gateway_name, payment_gateway_description,
|
||||
payment_gateway_api_key, payment_gateway_api_secret, payment_gateway_api_version,
|
||||
payment_gateway_api_method (required);
|
||||
payment_gateway_logo (file upload), payment_gateway_url, payment_gateway_api_url,
|
||||
is_active, gateway_priority (optional).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
payment_gateway_name = data.get("payment_gateway_name")
|
||||
payment_gateway_description = data.get("payment_gateway_description")
|
||||
payment_gateway_api_key = data.get("payment_gateway_api_key")
|
||||
payment_gateway_api_secret = data.get("payment_gateway_api_secret")
|
||||
payment_gateway_api_version = data.get("payment_gateway_api_version")
|
||||
payment_gateway_api_method = data.get("payment_gateway_api_method")
|
||||
|
||||
if not all([
|
||||
payment_gateway_name,
|
||||
payment_gateway_description,
|
||||
payment_gateway_api_key,
|
||||
payment_gateway_api_secret,
|
||||
payment_gateway_api_version,
|
||||
payment_gateway_api_method,
|
||||
]):
|
||||
return JsonResponse(
|
||||
{
|
||||
"status": "error",
|
||||
"message": "payment_gateway_name, payment_gateway_description, payment_gateway_api_key, "
|
||||
"payment_gateway_api_secret, payment_gateway_api_version, and "
|
||||
"payment_gateway_api_method are required.",
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
||||
# Generate payment_gateway_id if not provided
|
||||
payment_gateway_id = data.get("payment_gateway_id")
|
||||
if not payment_gateway_id:
|
||||
payment_gateway_id = str(uuid.uuid4().hex[:10]).upper()
|
||||
|
||||
gateway = PaymentGateway.objects.create(
|
||||
payment_gateway_id=payment_gateway_id,
|
||||
payment_gateway_name=payment_gateway_name,
|
||||
payment_gateway_description=payment_gateway_description,
|
||||
payment_gateway_url=data.get("payment_gateway_url"),
|
||||
payment_gateway_api_key=payment_gateway_api_key,
|
||||
payment_gateway_api_secret=payment_gateway_api_secret,
|
||||
payment_gateway_api_url=data.get("payment_gateway_api_url"),
|
||||
payment_gateway_api_version=payment_gateway_api_version,
|
||||
payment_gateway_api_method=payment_gateway_api_method,
|
||||
is_active=data.get("is_active", True),
|
||||
gateway_priority=data.get("gateway_priority", 0),
|
||||
)
|
||||
|
||||
# Handle logo upload if provided
|
||||
if "payment_gateway_logo" in request.FILES:
|
||||
gateway.payment_gateway_logo = request.FILES["payment_gateway_logo"]
|
||||
gateway.save()
|
||||
|
||||
return JsonResponse(
|
||||
{"status": "success", "payment_gateway": _payment_gateway_to_dict(gateway, request)},
|
||||
status=201,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayListAPI(APIView):
|
||||
"""
|
||||
List PaymentGateways, optionally filtered by is_active.
|
||||
Body: token, username, is_active (optional).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
qs = PaymentGateway.objects.all().order_by("-gateway_priority", "-created_date")
|
||||
|
||||
is_active = data.get("is_active")
|
||||
if is_active is not None:
|
||||
qs = qs.filter(is_active=bool(is_active))
|
||||
|
||||
gateways = [_payment_gateway_to_dict(g, request) for g in qs]
|
||||
return JsonResponse({"status": "success", "payment_gateways": gateways}, status=200)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayUpdateAPI(APIView):
|
||||
"""
|
||||
Update an existing PaymentGateway.
|
||||
Body: token, username, payment_gateway_id (required);
|
||||
payment_gateway_name, payment_gateway_description, payment_gateway_url,
|
||||
payment_gateway_api_key, payment_gateway_api_secret, payment_gateway_api_url,
|
||||
payment_gateway_api_version, payment_gateway_api_method, is_active,
|
||||
gateway_priority (optional);
|
||||
payment_gateway_logo (file upload, optional).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
payment_gateway_id = data.get("payment_gateway_id")
|
||||
if not payment_gateway_id:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "payment_gateway_id is required."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
gateway = PaymentGateway.objects.get(payment_gateway_id=payment_gateway_id)
|
||||
except PaymentGateway.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGateway not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
# Update fields if provided
|
||||
if data.get("payment_gateway_name") is not None:
|
||||
gateway.payment_gateway_name = data["payment_gateway_name"]
|
||||
if data.get("payment_gateway_description") is not None:
|
||||
gateway.payment_gateway_description = data["payment_gateway_description"]
|
||||
if "payment_gateway_url" in data:
|
||||
gateway.payment_gateway_url = data["payment_gateway_url"] or None
|
||||
if data.get("payment_gateway_api_key") is not None:
|
||||
gateway.payment_gateway_api_key = data["payment_gateway_api_key"]
|
||||
if data.get("payment_gateway_api_secret") is not None:
|
||||
gateway.payment_gateway_api_secret = data["payment_gateway_api_secret"]
|
||||
if "payment_gateway_api_url" in data:
|
||||
gateway.payment_gateway_api_url = data["payment_gateway_api_url"] or None
|
||||
if data.get("payment_gateway_api_version") is not None:
|
||||
gateway.payment_gateway_api_version = data["payment_gateway_api_version"]
|
||||
if data.get("payment_gateway_api_method") is not None:
|
||||
gateway.payment_gateway_api_method = data["payment_gateway_api_method"]
|
||||
if data.get("is_active") is not None:
|
||||
gateway.is_active = bool(data["is_active"])
|
||||
if data.get("gateway_priority") is not None:
|
||||
try:
|
||||
gateway.gateway_priority = int(data["gateway_priority"])
|
||||
except (TypeError, ValueError):
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "gateway_priority must be an integer."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
# Handle logo upload if provided
|
||||
if "payment_gateway_logo" in request.FILES:
|
||||
gateway.payment_gateway_logo = request.FILES["payment_gateway_logo"]
|
||||
|
||||
gateway.save()
|
||||
return JsonResponse(
|
||||
{"status": "success", "payment_gateway": _payment_gateway_to_dict(gateway, request)},
|
||||
status=200,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayDeleteAPI(APIView):
|
||||
"""
|
||||
Delete an existing PaymentGateway.
|
||||
Body: token, username, payment_gateway_id.
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
payment_gateway_id = data.get("payment_gateway_id")
|
||||
if not payment_gateway_id:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "payment_gateway_id is required."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
gateway = PaymentGateway.objects.get(payment_gateway_id=payment_gateway_id)
|
||||
except PaymentGateway.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGateway not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
gateway_name = gateway.payment_gateway_name
|
||||
gateway.delete()
|
||||
return JsonResponse(
|
||||
{"status": "success", "message": f"PaymentGateway '{gateway_name}' deleted successfully."},
|
||||
status=200,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
def _payment_gateway_credentials_to_dict(credentials, request=None):
|
||||
"""Serialize PaymentGatewayCredentials for JSON."""
|
||||
data = model_to_dict(
|
||||
credentials,
|
||||
fields=[
|
||||
"id",
|
||||
"payment_gateway_credentials_value",
|
||||
"created_date",
|
||||
"updated_date",
|
||||
],
|
||||
)
|
||||
data["payment_gateway_id"] = credentials.payment_gateway_id
|
||||
data["payment_gateway_name"] = credentials.payment_gateway.payment_gateway_name
|
||||
data["payment_gateway_payment_gateway_id"] = credentials.payment_gateway.payment_gateway_id
|
||||
return data
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayCredentialsCreateAPI(APIView):
|
||||
"""
|
||||
Create a new PaymentGatewayCredentials.
|
||||
Body: token, username, payment_gateway_id (or payment_gateway_payment_gateway_id),
|
||||
payment_gateway_credentials_value (required).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
payment_gateway_id = data.get("payment_gateway_id") or data.get("payment_gateway_payment_gateway_id")
|
||||
payment_gateway_credentials_value = data.get("payment_gateway_credentials_value")
|
||||
|
||||
if not payment_gateway_id or not payment_gateway_credentials_value:
|
||||
return JsonResponse(
|
||||
{
|
||||
"status": "error",
|
||||
"message": "payment_gateway_id (or payment_gateway_payment_gateway_id) and payment_gateway_credentials_value are required.",
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
gateway = PaymentGateway.objects.get(payment_gateway_id=payment_gateway_id)
|
||||
except PaymentGateway.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGateway not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
credentials = PaymentGatewayCredentials.objects.create(
|
||||
payment_gateway=gateway,
|
||||
payment_gateway_credentials_value=payment_gateway_credentials_value,
|
||||
)
|
||||
|
||||
return JsonResponse(
|
||||
{"status": "success", "credentials": _payment_gateway_credentials_to_dict(credentials, request)},
|
||||
status=201,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayCredentialsListAPI(APIView):
|
||||
"""
|
||||
List PaymentGatewayCredentials, optionally filtered by payment_gateway_id.
|
||||
Body: token, username, payment_gateway_id (optional).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
qs = PaymentGatewayCredentials.objects.select_related("payment_gateway").all().order_by("-created_date")
|
||||
|
||||
payment_gateway_id = data.get("payment_gateway_id") or data.get("payment_gateway_payment_gateway_id")
|
||||
if payment_gateway_id:
|
||||
try:
|
||||
gateway = PaymentGateway.objects.get(payment_gateway_id=payment_gateway_id)
|
||||
qs = qs.filter(payment_gateway=gateway)
|
||||
except PaymentGateway.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGateway not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
credentials_list = [_payment_gateway_credentials_to_dict(c, request) for c in qs]
|
||||
return JsonResponse({"status": "success", "credentials": credentials_list}, status=200)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayCredentialsUpdateAPI(APIView):
|
||||
"""
|
||||
Update an existing PaymentGatewayCredentials.
|
||||
Body: token, username, credentials_id (required);
|
||||
payment_gateway_credentials_value (optional).
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
credentials_id = data.get("credentials_id")
|
||||
if not credentials_id:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "credentials_id is required."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
credentials = PaymentGatewayCredentials.objects.select_related("payment_gateway").get(id=credentials_id)
|
||||
except PaymentGatewayCredentials.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGatewayCredentials not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
# Update credentials value if provided
|
||||
if data.get("payment_gateway_credentials_value") is not None:
|
||||
credentials.payment_gateway_credentials_value = data["payment_gateway_credentials_value"]
|
||||
|
||||
credentials.save()
|
||||
return JsonResponse(
|
||||
{"status": "success", "credentials": _payment_gateway_credentials_to_dict(credentials, request)},
|
||||
status=200,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name="dispatch")
|
||||
class PaymentGatewayCredentialsDeleteAPI(APIView):
|
||||
"""
|
||||
Delete an existing PaymentGatewayCredentials.
|
||||
Body: token, username, credentials_id.
|
||||
"""
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
user, token, data, error_response = validate_token_and_get_user(request)
|
||||
if error_response:
|
||||
return error_response
|
||||
|
||||
credentials_id = data.get("credentials_id")
|
||||
if not credentials_id:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "credentials_id is required."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
credentials = PaymentGatewayCredentials.objects.select_related("payment_gateway").get(id=credentials_id)
|
||||
except PaymentGatewayCredentials.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"status": "error", "message": "PaymentGatewayCredentials not found."},
|
||||
status=404,
|
||||
)
|
||||
|
||||
gateway_name = credentials.payment_gateway.payment_gateway_name
|
||||
credentials.delete()
|
||||
return JsonResponse(
|
||||
{
|
||||
"status": "success",
|
||||
"message": f"PaymentGatewayCredentials for '{gateway_name}' deleted successfully.",
|
||||
},
|
||||
status=200,
|
||||
)
|
||||
except Exception as e:
|
||||
return JsonResponse({"status": "error", "message": str(e)}, status=500)
|
||||
6
banking_operations/apps.py
Normal file
6
banking_operations/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BankingOperationsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'banking_operations'
|
||||
69
banking_operations/migrations/0001_initial.py
Normal file
69
banking_operations/migrations/0001_initial.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# Generated by Django 4.2.27 on 2026-03-13 16:55
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PaymentGateway',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('payment_gateway_id', models.CharField(max_length=250)),
|
||||
('payment_gateway_name', models.CharField(max_length=250)),
|
||||
('payment_gateway_description', models.TextField()),
|
||||
('payment_gateway_logo', models.ImageField(blank=True, null=True, upload_to='payment_gateways/')),
|
||||
('payment_gateway_url', models.URLField(blank=True, null=True)),
|
||||
('payment_gateway_api_key', models.CharField(max_length=250)),
|
||||
('payment_gateway_api_secret', models.CharField(max_length=250)),
|
||||
('payment_gateway_api_url', models.URLField(blank=True, null=True)),
|
||||
('payment_gateway_api_version', models.CharField(max_length=250)),
|
||||
('payment_gateway_api_method', models.CharField(max_length=250)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
('created_date', models.DateField(auto_now_add=True)),
|
||||
('updated_date', models.DateField(auto_now=True)),
|
||||
('gateway_priority', models.IntegerField(default=0)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PaymentTransaction',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('payment_transaction_id', models.CharField(max_length=250)),
|
||||
('payment_type', models.CharField(choices=[('credit', 'Credit'), ('debit', 'Debit'), ('transfer', 'Transfer'), ('other', 'Other')], max_length=250)),
|
||||
('payment_sub_type', models.CharField(choices=[('online', 'Online'), ('offline', 'Offline'), ('other', 'Other')], max_length=250)),
|
||||
('payment_transaction_amount', models.DecimalField(decimal_places=2, max_digits=10)),
|
||||
('payment_transaction_currency', models.CharField(max_length=10)),
|
||||
('payment_transaction_status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('failed', 'Failed'), ('refunded', 'Refunded'), ('cancelled', 'Cancelled')], max_length=250)),
|
||||
('payment_transaction_date', models.DateField(auto_now_add=True)),
|
||||
('payment_transaction_time', models.TimeField(auto_now_add=True)),
|
||||
('payment_transaction_notes', models.TextField(blank=True, null=True)),
|
||||
('payment_transaction_raw_data', models.JSONField(blank=True, null=True)),
|
||||
('payment_transaction_response', models.JSONField(blank=True, null=True)),
|
||||
('payment_transaction_error', models.JSONField(blank=True, null=True)),
|
||||
('last_updated_date', models.DateField(blank=True, null=True)),
|
||||
('last_updated_time', models.TimeField(blank=True, null=True)),
|
||||
('last_updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('payment_gateway', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='banking_operations.paymentgateway')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PaymentGatewayCredentials',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('payment_gateway_credentials_value', models.TextField()),
|
||||
('created_date', models.DateField(auto_now_add=True)),
|
||||
('updated_date', models.DateField(auto_now=True)),
|
||||
('payment_gateway', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='banking_operations.paymentgateway')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
banking_operations/migrations/__init__.py
Normal file
0
banking_operations/migrations/__init__.py
Normal file
85
banking_operations/models.py
Normal file
85
banking_operations/models.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
import uuid
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
# Create your models here.
|
||||
class PaymentGateway(models.Model):
|
||||
payment_gateway_id = models.CharField(max_length=250)
|
||||
payment_gateway_name = models.CharField(max_length=250)
|
||||
payment_gateway_description = models.TextField()
|
||||
payment_gateway_logo = models.ImageField(upload_to='payment_gateways/', blank=True, null=True)
|
||||
payment_gateway_url = models.URLField(blank=True, null=True)
|
||||
payment_gateway_api_key = models.CharField(max_length=250)
|
||||
payment_gateway_api_secret = models.CharField(max_length=250)
|
||||
payment_gateway_api_url = models.URLField(blank=True, null=True)
|
||||
payment_gateway_api_version = models.CharField(max_length=250)
|
||||
payment_gateway_api_method = models.CharField(max_length=250)
|
||||
|
||||
is_active = models.BooleanField(default=True)
|
||||
|
||||
created_date = models.DateField(auto_now_add=True)
|
||||
updated_date = models.DateField(auto_now=True)
|
||||
|
||||
gateway_priority = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self):
|
||||
return self.payment_gateway_name
|
||||
|
||||
def __save__(self):
|
||||
if not self.payment_gateway_id:
|
||||
self.payment_gateway_id = str(uuid.uuid4().hex[:10]).upper()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class PaymentGatewayCredentials(models.Model):
|
||||
payment_gateway = models.ForeignKey(PaymentGateway, on_delete=models.CASCADE)
|
||||
payment_gateway_credentials_value = models.TextField()
|
||||
created_date = models.DateField(auto_now_add=True)
|
||||
updated_date = models.DateField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.payment_gateway.payment_gateway_name + " - " + self.payment_gateway_credentials_value
|
||||
|
||||
|
||||
class PaymentTransaction(models.Model):
|
||||
payment_transaction_id = models.CharField(max_length=250)
|
||||
payment_type = models.CharField(max_length=250, choices=[
|
||||
('credit', 'Credit'),
|
||||
('debit', 'Debit'),
|
||||
('transfer', 'Transfer'),
|
||||
('other', 'Other'),
|
||||
])
|
||||
payment_sub_type = models.CharField(max_length=250, choices=[
|
||||
('online', 'Online'),
|
||||
('offline', 'Offline'),
|
||||
('other', 'Other'),
|
||||
])
|
||||
payment_gateway = models.ForeignKey(PaymentGateway, on_delete=models.CASCADE)
|
||||
payment_transaction_amount = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
payment_transaction_currency = models.CharField(max_length=10)
|
||||
payment_transaction_status = models.CharField(max_length=250, choices=[
|
||||
('pending', 'Pending'),
|
||||
('completed', 'Completed'),
|
||||
('failed', 'Failed'),
|
||||
('refunded', 'Refunded'),
|
||||
('cancelled', 'Cancelled'),
|
||||
])
|
||||
payment_transaction_date = models.DateField(auto_now_add=True)
|
||||
payment_transaction_time = models.TimeField(auto_now_add=True)
|
||||
payment_transaction_notes = models.TextField(blank=True, null=True)
|
||||
payment_transaction_raw_data = models.JSONField(blank=True, null=True)
|
||||
payment_transaction_response = models.JSONField(blank=True, null=True)
|
||||
payment_transaction_error = models.JSONField(blank=True, null=True)
|
||||
|
||||
last_updated_date = models.DateField(blank=True, null=True)
|
||||
last_updated_time = models.TimeField(blank=True, null=True)
|
||||
last_updated_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.payment_gateway.payment_gateway_name + " - " + self.payment_transaction_id
|
||||
|
||||
def __save__(self):
|
||||
if not self.payment_transaction_id:
|
||||
self.payment_transaction_id = str(self.payment_gateway.payment_gateway_name[:3].upper()) + str(uuid.uuid4().hex[:10]).upper()
|
||||
super().save(*args, **kwargs)
|
||||
170
banking_operations/services.py
Normal file
170
banking_operations/services.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Banking/payment services. transaction_initiate is called by checkout (and others)
|
||||
to start a payment flow. Replace the stub with real gateway integration (e.g. Razorpay).
|
||||
"""
|
||||
from decimal import Decimal
|
||||
from django.contrib.auth import get_user_model
|
||||
from banking_operations.models import PaymentTransaction, PaymentGateway
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
def transaction_initiate(
|
||||
request,
|
||||
user,
|
||||
amount,
|
||||
currency="INR",
|
||||
reference_type="checkout",
|
||||
reference_id=None,
|
||||
bookings=None,
|
||||
extra_data=None,
|
||||
):
|
||||
"""
|
||||
Initiate a payment transaction (e.g. create Razorpay order and return payment URL).
|
||||
|
||||
Args:
|
||||
request: Django request (for building URLs, gateway config, etc.).
|
||||
user: User instance (customer).
|
||||
amount: Total amount in payment currency (Decimal or float).
|
||||
currency: Currency code, e.g. "INR".
|
||||
reference_type: Application reference type, e.g. "checkout".
|
||||
reference_id: Application reference id (e.g. booking_ids or order id).
|
||||
bookings: Optional list of Booking instances or IDs linked to this transaction.
|
||||
extra_data: Optional dict for gateway-specific data.
|
||||
|
||||
Returns:
|
||||
dict: On success: {"success": True, "transaction_id": "...", "payment_url": "...", "message": "..."}
|
||||
On failure: {"success": False, "message": "..."}
|
||||
"""
|
||||
# Stub: replace with real gateway call when banking_operations payment flow is implemented.
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Transaction initiation stub",
|
||||
"transaction_id": None,
|
||||
"payment_url": None,
|
||||
}
|
||||
|
||||
|
||||
def create_payment_transaction(
|
||||
payment_type,
|
||||
payment_sub_type,
|
||||
payment_gateway,
|
||||
transaction_amount,
|
||||
currency="INR",
|
||||
notes=None,
|
||||
raw_data=None,
|
||||
user=None,
|
||||
):
|
||||
"""
|
||||
Create a PaymentTransaction with pending status.
|
||||
|
||||
Args:
|
||||
payment_type: Payment type - 'credit', 'debit', 'transfer', or 'other'
|
||||
payment_sub_type: Payment sub type - 'online', 'offline', or 'other'
|
||||
payment_gateway: PaymentGateway instance or payment_gateway_id (str)
|
||||
transaction_amount: Transaction amount (Decimal, float, or string)
|
||||
currency: Currency code, e.g. "INR" (default: "INR")
|
||||
notes: Optional transaction notes (str)
|
||||
raw_data: Optional raw data dict to store in payment_transaction_raw_data (dict)
|
||||
user: Optional User instance for last_updated_by
|
||||
|
||||
Returns:
|
||||
tuple: (success: bool, transaction: PaymentTransaction or None, error_message: str or None)
|
||||
"""
|
||||
try:
|
||||
# Validate payment_type
|
||||
valid_payment_types = ['credit', 'debit', 'transfer', 'other']
|
||||
if payment_type not in valid_payment_types:
|
||||
return False, None, f"Invalid payment_type. Must be one of: {', '.join(valid_payment_types)}"
|
||||
|
||||
# Validate payment_sub_type
|
||||
valid_payment_sub_types = ['online', 'offline', 'other']
|
||||
if payment_sub_type not in valid_payment_sub_types:
|
||||
return False, None, f"Invalid payment_sub_type. Must be one of: {', '.join(valid_payment_sub_types)}"
|
||||
|
||||
# Get PaymentGateway instance
|
||||
if isinstance(payment_gateway, PaymentGateway):
|
||||
gateway = payment_gateway
|
||||
elif isinstance(payment_gateway, str):
|
||||
try:
|
||||
gateway = PaymentGateway.objects.get(payment_gateway_id=payment_gateway)
|
||||
except PaymentGateway.DoesNotExist:
|
||||
return False, None, f"PaymentGateway with id '{payment_gateway}' not found."
|
||||
else:
|
||||
return False, None, "payment_gateway must be a PaymentGateway instance or payment_gateway_id string."
|
||||
|
||||
# Validate transaction_amount
|
||||
try:
|
||||
amount = Decimal(str(transaction_amount))
|
||||
if amount <= 0:
|
||||
return False, None, "transaction_amount must be greater than zero."
|
||||
except (ValueError, TypeError):
|
||||
return False, None, "transaction_amount must be a valid number."
|
||||
|
||||
# Validate currency
|
||||
if not currency or len(currency) > 10:
|
||||
return False, None, "currency must be a valid currency code (max 10 characters)."
|
||||
|
||||
# Create PaymentTransaction
|
||||
transaction = PaymentTransaction.objects.create(
|
||||
payment_type=payment_type,
|
||||
payment_sub_type=payment_sub_type,
|
||||
payment_gateway=gateway,
|
||||
payment_transaction_amount=amount,
|
||||
payment_transaction_currency=currency,
|
||||
payment_transaction_status='pending', # Initial state as requested
|
||||
payment_transaction_notes=notes,
|
||||
payment_transaction_raw_data=raw_data,
|
||||
last_updated_by=user if isinstance(user, User) else None,
|
||||
)
|
||||
|
||||
return True, transaction.payment_transaction_id, None
|
||||
|
||||
except Exception as e:
|
||||
return False, None, str(e)
|
||||
|
||||
def update_payment_transaction(
|
||||
payment_transaction_id,
|
||||
payment_transaction_status,
|
||||
payment_transaction_notes=None,
|
||||
payment_transaction_raw_data=None,
|
||||
payment_transaction_response=None,
|
||||
payment_transaction_error=None,
|
||||
user=None,
|
||||
):
|
||||
"""
|
||||
Update a PaymentTransaction with the given status and notes.
|
||||
Args:
|
||||
payment_transaction_id: PaymentTransaction id (str)
|
||||
payment_transaction_status: PaymentTransaction status - 'pending', 'completed', 'failed', 'refunded', 'cancelled'
|
||||
payment_transaction_notes: Optional transaction notes (str)
|
||||
payment_transaction_raw_data: Optional raw data dict to store in payment_transaction_raw_data (dict)
|
||||
payment_transaction_response: Optional response dict to store in payment_transaction_response (dict)
|
||||
payment_transaction_error: Optional error dict to store in payment_transaction_error (dict)
|
||||
user: Optional User instance for last_updated_by
|
||||
"""
|
||||
try:
|
||||
# Get PaymentTransaction instance
|
||||
if isinstance(payment_transaction_id, PaymentTransaction):
|
||||
transaction = payment_transaction_id
|
||||
elif isinstance(payment_transaction_id, str):
|
||||
try:
|
||||
transaction = PaymentTransaction.objects.get(payment_transaction_id=payment_transaction_id)
|
||||
except PaymentTransaction.DoesNotExist:
|
||||
return False, None, f"PaymentTransaction with id '{payment_transaction_id}' not found."
|
||||
else:
|
||||
return False, None, "payment_transaction_id must be a PaymentTransaction instance or payment_transaction_id string."
|
||||
|
||||
# Update PaymentTransaction
|
||||
transaction.payment_transaction_status = payment_transaction_status
|
||||
transaction.payment_transaction_notes = payment_transaction_notes
|
||||
transaction.payment_transaction_raw_data = payment_transaction_raw_data
|
||||
transaction.payment_transaction_response = payment_transaction_response
|
||||
transaction.payment_transaction_error = payment_transaction_error
|
||||
transaction.last_updated_by = user if isinstance(user, User) else None
|
||||
transaction.save()
|
||||
|
||||
return True, transaction.payment_transaction_id, None
|
||||
|
||||
except Exception as e:
|
||||
return False, None, str(e)
|
||||
3
banking_operations/tests.py
Normal file
3
banking_operations/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
24
banking_operations/urls.py
Normal file
24
banking_operations/urls.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from django.urls import path
|
||||
|
||||
from banking_operations.api import (
|
||||
PaymentGatewayCreateAPI,
|
||||
PaymentGatewayListAPI,
|
||||
PaymentGatewayUpdateAPI,
|
||||
PaymentGatewayDeleteAPI,
|
||||
PaymentGatewayCredentialsCreateAPI,
|
||||
PaymentGatewayCredentialsListAPI,
|
||||
PaymentGatewayCredentialsUpdateAPI,
|
||||
PaymentGatewayCredentialsDeleteAPI,
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("payment-gateway/create/", PaymentGatewayCreateAPI.as_view(), name="payment_gateway_create"),
|
||||
path("payment-gateway/list/", PaymentGatewayListAPI.as_view(), name="payment_gateway_list"),
|
||||
path("payment-gateway/update/", PaymentGatewayUpdateAPI.as_view(), name="payment_gateway_update"),
|
||||
path("payment-gateway/delete/", PaymentGatewayDeleteAPI.as_view(), name="payment_gateway_delete"),
|
||||
path("payment-gateway-credentials/create/", PaymentGatewayCredentialsCreateAPI.as_view(), name="payment_gateway_credentials_create"),
|
||||
path("payment-gateway-credentials/list/", PaymentGatewayCredentialsListAPI.as_view(), name="payment_gateway_credentials_list"),
|
||||
path("payment-gateway-credentials/update/", PaymentGatewayCredentialsUpdateAPI.as_view(), name="payment_gateway_credentials_update"),
|
||||
path("payment-gateway-credentials/delete/", PaymentGatewayCredentialsDeleteAPI.as_view(), name="payment_gateway_credentials_delete"),
|
||||
]
|
||||
3
banking_operations/views.py
Normal file
3
banking_operations/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
Reference in New Issue
Block a user