170 lines
7.0 KiB
Python
170 lines
7.0 KiB
Python
|
|
"""
|
||
|
|
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)
|