feat: create core payment service microservice with Cashfree gateway integration and operations portal
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 36s

This commit is contained in:
vickytechkey 2026-10-18 22:00:00 +05:30
parent 369f9c0383
commit 493b204d32
5 changed files with 204 additions and 58 deletions

View file

@ -150,13 +150,32 @@ MAILERS = {
}
# Cashfree Payment Gateway Configuration
CASHFREE_APP_ID = os.environ.get('CASHFREE_APP_ID', 'TEST1118017027fc875a9485063df8b007108111')
CASHFREE_SECRET_KEY = os.environ.get('CASHFREE_SECRET_KEY', 'cfsk_ma_test_3701af3af410228b8e707765dbb68c32_1cf9fb86')
# Production API credentials (for requests originating from beta.tradhox.com & tradhox.com)
CASHFREE_PROD_APP_ID = os.environ.get('CASHFREE_PROD_APP_ID', '1370798f950e30514fa051e93868970731')
CASHFREE_PROD_SECRET_KEY = os.environ.get('CASHFREE_PROD_SECRET_KEY', 'cfsk_ma_prod_d441689923a71877068fee6b6e2e0d05_a626ee00')
# Sandbox / Test API credentials (for local testing and sandbox simulation)
CASHFREE_TEST_APP_ID = os.environ.get('CASHFREE_TEST_APP_ID', 'TEST1118017027fc875a9485063df8b007108111')
CASHFREE_TEST_SECRET_KEY = os.environ.get('CASHFREE_TEST_SECRET_KEY', 'cfsk_ma_test_3701af3af410228b8e707765dbb68c32_1cf9fb86')
# Default active fallback
CASHFREE_APP_ID = os.environ.get('CASHFREE_APP_ID', CASHFREE_PROD_APP_ID)
CASHFREE_SECRET_KEY = os.environ.get('CASHFREE_SECRET_KEY', CASHFREE_PROD_SECRET_KEY)
CASHFREE_ENVIRONMENT = os.environ.get('CASHFREE_ENVIRONMENT', 'sandbox') # 'sandbox' or 'production'
CASHFREE_API_VERSION = os.environ.get('CASHFREE_API_VERSION', '2023-08-01')
# Domains that trigger automatic Cashfree production API routing
TRADHOX_PRODUCTION_DOMAINS = [
'beta.tradhox.com',
'tradhox.com',
'www.tradhox.com',
'betapayments.tradhox.com',
'payments.tradhox.com',
]
if CASHFREE_ENVIRONMENT == 'production':
CASHFREE_BASE_URL = 'https://api.cashfree.com/pg'
else:
CASHFREE_BASE_URL = 'https://sandbox.cashfree.com/pg'

View file

@ -26,9 +26,11 @@ class PaymentEngine:
"""Allows dynamic registration of gateway adapters (e.g. Razorpay)."""
self.gateway_registry[name.upper()] = gateway_class
def get_adapter(self, config: PaymentGatewayConfig) -> Optional[BasePaymentGateway]:
def get_adapter(self, config: PaymentGatewayConfig, request=None, environment=None) -> Optional[BasePaymentGateway]:
gateway_class = self.gateway_registry.get(config.name.upper())
if gateway_class:
if config.name.upper() == 'CASHFREE':
return gateway_class(config=config, request=request, environment=environment)
return gateway_class(config=config)
logger.warning(f"No adapter registered for gateway: {config.name}")
return None
@ -77,6 +79,8 @@ class PaymentEngine:
notify_url: str,
order_note: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None,
request=None,
environment=None,
) -> Tuple[bool, PaymentOrder, Optional[PaymentAttempt], Optional[str]]:
"""
Creates an order with automatic failover across registered gateways.
@ -103,12 +107,13 @@ class PaymentEngine:
last_error = "No active gateways available"
for config in candidates:
adapter = self.get_adapter(config)
adapter = self.get_adapter(config, request=request, environment=environment)
if not adapter:
continue
logger.info(f"Attempting order {order_id} via {adapter.gateway_name} (Priority {config.priority})")
# Execute order creation with gateway
resp: GatewayOrderResponse = adapter.create_order(
order_id=order_id,

View file

@ -17,27 +17,54 @@ class CashfreeGateway(BasePaymentGateway):
GATEWAY_NAME = 'CASHFREE'
def __init__(self, config=None):
def __init__(self, config=None, environment: Optional[str] = None, request=None):
super().__init__(config)
# Use database config if provided, else fall back to django settings
if config:
self.app_id = config.app_id or getattr(settings, 'CASHFREE_APP_ID', '')
self.secret_key = config.secret_key or getattr(settings, 'CASHFREE_SECRET_KEY', '')
self.environment = config.environment or getattr(settings, 'CASHFREE_ENVIRONMENT', 'sandbox')
self.api_version = config.api_version or getattr(settings, 'CASHFREE_API_VERSION', '2023-08-01')
else:
self.app_id = getattr(settings, 'CASHFREE_APP_ID', '')
self.secret_key = getattr(settings, 'CASHFREE_SECRET_KEY', '')
self.environment = getattr(settings, 'CASHFREE_ENVIRONMENT', 'sandbox')
self.api_version = getattr(settings, 'CASHFREE_API_VERSION', '2023-08-01')
# 1. Determine target environment (production vs sandbox)
resolved_env = None
if environment:
resolved_env = environment
elif request:
req_data = getattr(request, 'data', {}) or {}
if isinstance(req_data, dict) and req_data.get('environment') in ('production', 'sandbox'):
resolved_env = req_data.get('environment')
else:
# Inspect Origin, Referer, Host headers for Tradhox production domains
origin = request.META.get('HTTP_ORIGIN', '').lower()
referer = request.META.get('HTTP_REFERER', '').lower()
host = request.META.get('HTTP_HOST', '').lower()
prod_domains = getattr(settings, 'TRADHOX_PRODUCTION_DOMAINS', [
'beta.tradhox.com', 'tradhox.com', 'www.tradhox.com', 'betapayments.tradhox.com'
])
for domain in prod_domains:
if domain in origin or domain in referer or domain in host:
resolved_env = 'production'
break
if not resolved_env:
if config and config.environment:
resolved_env = config.environment
else:
resolved_env = getattr(settings, 'CASHFREE_ENVIRONMENT', 'sandbox')
self.environment = resolved_env
self.api_version = (config.api_version if config else None) or getattr(settings, 'CASHFREE_API_VERSION', '2023-08-01')
# 2. Select credentials according to resolved environment
if self.environment == 'production':
self.app_id = (config.app_id if config and config.environment == 'production' and config.app_id else getattr(settings, 'CASHFREE_PROD_APP_ID', '1370798f950e30514fa051e93868970731'))
self.secret_key = (config.secret_key if config and config.environment == 'production' and config.secret_key else getattr(settings, 'CASHFREE_PROD_SECRET_KEY', 'cfsk_ma_prod_d441689923a71877068fee6b6e2e0d05_a626ee00'))
self.base_url = 'https://api.cashfree.com/pg'
self.checkout_base = 'https://payments.cashfree.com/order/#'
else:
self.app_id = (config.app_id if config and config.environment == 'sandbox' and config.app_id else getattr(settings, 'CASHFREE_TEST_APP_ID', 'TEST1118017027fc875a9485063df8b007108111'))
self.secret_key = (config.secret_key if config and config.environment == 'sandbox' and config.secret_key else getattr(settings, 'CASHFREE_TEST_SECRET_KEY', 'cfsk_ma_test_3701af3af410228b8e707765dbb68c32_1cf9fb86'))
self.base_url = 'https://sandbox.cashfree.com/pg'
self.checkout_base = 'https://payments-test.cashfree.com/order/#'
logger.info(f"CashfreeGateway initialized in [{self.environment.upper()}] mode (App ID: {self.app_id[:8]}...)")
@property
def gateway_name(self) -> str:
return self.GATEWAY_NAME
@ -183,13 +210,28 @@ class CashfreeGateway(BasePaymentGateway):
).digest()
computed_signature = base64.b64encode(computed_hmac).decode('utf-8')
if not hmac.compare_digest(computed_signature, signature):
is_matched = hmac.compare_digest(computed_signature, signature)
if not is_matched:
# Try alternate key (production vs test) in case order was placed under different environment
alt_key = getattr(settings, 'CASHFREE_PROD_SECRET_KEY', '') if self.environment != 'production' else getattr(settings, 'CASHFREE_TEST_SECRET_KEY', '')
if alt_key and alt_key != self.secret_key:
alt_hmac = hmac.new(
key=alt_key.encode('utf-8'),
msg=payload_to_sign,
digestmod=hashlib.sha256
).digest()
alt_sig = base64.b64encode(alt_hmac).decode('utf-8')
if hmac.compare_digest(alt_sig, signature):
is_matched = True
if not is_matched:
return GatewayVerifyResult(
is_valid=False,
event_type="SIGNATURE_MISMATCH",
error_message="Computed signature does not match header signature"
)
# Parse JSON
try:
body_json = json.loads(raw_body.decode('utf-8'))

View file

@ -37,6 +37,7 @@ class CreatePaymentOrderView(APIView):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
data = serializer.validated_data
requested_env = request.data.get('environment')
success, order, attempt, error_msg = payment_engine.initiate_payment_order(
order_id=data['order_id'],
amount=float(data['amount']),
@ -49,6 +50,8 @@ class CreatePaymentOrderView(APIView):
notify_url=data.get('notify_url', ''),
order_note=data.get('order_note', ''),
metadata=data.get('metadata', {}),
request=request,
environment=requested_env,
)
if not success:
@ -64,8 +67,12 @@ class CreatePaymentOrderView(APIView):
if attempt and attempt.raw_response:
checkout_url = attempt.raw_response.get("payment_session_id")
if checkout_url and attempt.gateway_name == 'CASHFREE':
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
base = 'https://payments.cashfree.com/order/#' if (config and config.environment == 'production') else 'https://payments-test.cashfree.com/order/#'
origin = request.META.get('HTTP_ORIGIN', '').lower()
referer = request.META.get('HTTP_REFERER', '').lower()
host = request.META.get('HTTP_HOST', '').lower()
prod_domains = getattr(settings, 'TRADHOX_PRODUCTION_DOMAINS', ['beta.tradhox.com', 'tradhox.com'])
is_prod = (requested_env == 'production') or any(d in origin or d in referer or d in host for d in prod_domains)
base = 'https://payments.cashfree.com/order/#' if is_prod else 'https://payments-test.cashfree.com/order/#'
checkout_url = f"{base}{attempt.payment_session_id}"
return Response({
@ -441,7 +448,7 @@ class CashfreeDirectPaySessionView(APIView):
return Response({"error": "payment_session_id is required"}, status=status.HTTP_400_BAD_REQUEST)
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.data.get('environment'))
url = f"{gateway.base_url}/orders/sessions"
method_payload = {"channel": channel}
@ -473,7 +480,7 @@ class CashfreeDirectPaymentsListView(APIView):
def get(self, request, order_id):
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.query_params.get('environment'))
url = f"{gateway.base_url}/orders/{order_id}/payments"
import requests
@ -494,7 +501,7 @@ class CashfreeDirectSettlementsView(APIView):
def get(self, request, order_id):
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.query_params.get('environment'))
url = f"{gateway.base_url}/orders/{order_id}/settlements"
import requests
@ -509,7 +516,7 @@ class CashfreeDirectSettlementsView(APIView):
class CashfreeDirectCreateOrderView(APIView):
"""
POST /api/payments/cashfree/orders/create/
Creates an order directly against Cashfree Sandbox, bypassing orchestrator routing.
Creates an order directly against Cashfree, bypassing orchestrator routing.
Records the order and attempt in the database for tracking and refunds.
"""
permission_classes = [permissions.AllowAny]
@ -529,7 +536,7 @@ class CashfreeDirectCreateOrderView(APIView):
return Response({"error": "order_id and amount are required"}, status=status.HTTP_400_BAD_REQUEST)
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.data.get('environment'))
# Call Cashfree API directly
resp = gateway.create_order(
@ -598,7 +605,7 @@ class CashfreeDirectRawOrderView(APIView):
def get(self, request, order_id):
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.query_params.get('environment'))
raw_order = gateway.get_order(order_id)
return Response(raw_order, status=status.HTTP_200_OK)
@ -606,7 +613,7 @@ class CashfreeDirectRawOrderView(APIView):
class CashfreeQuickSessionView(APIView):
"""
POST /api/payments/cashfree/quick-session/
Automatically generates a live Cashfree Sandbox order and returns the payment_session_id.
Automatically generates a live Cashfree order and returns the payment_session_id.
Requires zero manual parameters, enabling 1-click dynamic session creation.
"""
permission_classes = [permissions.AllowAny]
@ -615,7 +622,7 @@ class CashfreeQuickSessionView(APIView):
amount = float(request.data.get('amount') or 100.00)
rand_id = f"CF-UPI-{int(timezone.now().timestamp())}"
config = PaymentGatewayConfig.objects.filter(name='CASHFREE').first()
gateway = CashfreeGateway(config=config)
gateway = CashfreeGateway(config=config, request=request, environment=request.data.get('environment'))
resp = gateway.create_order(
order_id=rand_id,

View file

@ -985,20 +985,43 @@
<div class="section-header">
<h2 class="section-title" style="color: var(--cashfree-color);">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
Cashfree Dedicated Vendor Studio (Direct Sandbox)
Cashfree Dedicated Vendor Studio
</h2>
<span class="badge" style="background: var(--cashfree-glow); color: #F472B6; border: 1px solid var(--border-cashfree);">Isolated Gateway Engine</span>
<div style="display: flex; gap: 10px; align-items: center;">
<span class="badge" id="cf-env-badge" style="background: var(--cashfree-glow); color: #F472B6; border: 1px solid var(--border-cashfree);">Mode: Sandbox</span>
<span class="badge" style="background: rgba(16, 185, 129, 0.15); color: #34D399; border: 1px solid rgba(16, 185, 129, 0.3);">
🌐 Auto-Routing: beta.tradhox.com & tradhox.com ➔ Production
</span>
</div>
</div>
<!-- Cashfree Direct Live Config & Connectivity -->
<div style="background: rgba(236, 72, 153, 0.08); border: 1px solid var(--border-cashfree); border-radius: var(--radius-md); padding: 18px 24px; margin-bottom: 24px; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 16px;">
<div style="display: flex; flex-wrap: wrap; gap: 24px; font-size: 13px;">
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">App ID:</span> <code style="color: #F472B6; font-family: var(--font-mono);">TEST1118017027fc875a9485063df8b007108111</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">Secret:</span> <code style="color: #F472B6; font-family: var(--font-mono);">cfsk_ma_test_3701...1cf9fb86</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">API Base:</span> <code style="color: #F472B6; font-family: var(--font-mono);">https://sandbox.cashfree.com/pg</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">Version:</span> <code style="color: #F472B6; font-family: var(--font-mono);">2023-08-01</code></div>
<!-- Cashfree Direct Live Config & Environment Switcher -->
<div style="background: rgba(236, 72, 153, 0.08); border: 1px solid var(--border-cashfree); border-radius: var(--radius-md); padding: 18px 24px; margin-bottom: 24px; display: flex; flex-direction: column; gap: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 14px;">
<div style="display: flex; align-items: center; gap: 12px;">
<label style="font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0;">Target Environment:</label>
<select id="cf-studio-env-select" onchange="onCfStudioEnvChange()" class="form-control" style="width: auto; padding: 6px 14px; font-size: 13px; font-weight: 600; background: #0A0D14; border-color: var(--border-cashfree); color: #F472B6; border-radius: var(--radius-sm); cursor: pointer;">
<option value="sandbox" selected>Sandbox Mode (Test Simulator)</option>
<option value="production">Production Mode (api.cashfree.com/pg)</option>
</select>
</div>
<div style="display: flex; gap: 10px; align-items: center;">
<button onclick="pingCashfreeDirectApi()" class="btn btn-cashfree btn-sm" id="btn-ping-cf">⚡ Ping Active Gateway</button>
</div>
</div>
<div style="display: flex; flex-wrap: wrap; gap: 20px; font-size: 12px; background: rgba(0,0,0,0.3); padding: 12px 16px; border-radius: var(--radius-sm); border: 1px solid rgba(255,255,255,0.05);">
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">Active App ID:</span> <code id="cf-disp-appid" style="color: #F472B6; font-family: var(--font-mono); font-weight: 600;">TEST1118017027fc875a9485063df8b007108111</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">Secret Key:</span> <code id="cf-disp-secret" style="color: #F472B6; font-family: var(--font-mono);">cfsk_ma_test_3701...1cf9fb86</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">API Base URL:</span> <code id="cf-disp-baseurl" style="color: #A5B4FC; font-family: var(--font-mono);">https://sandbox.cashfree.com/pg</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">Checkout Base:</span> <code id="cf-disp-checkout" style="color: #34D399; font-family: var(--font-mono);">https://payments-test.cashfree.com/order/#</code></div>
<div><span style="color: var(--text-muted); font-size: 11px; text-transform: uppercase; font-weight: 600;">API Version:</span> <code style="color: #F472B6; font-family: var(--font-mono);">2023-08-01</code></div>
</div>
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; font-size: 11px; color: var(--text-dim);">
<span>🔒 <strong>Credentials in Memory:</strong> Prod: <code>1370798f...970731</code> (api.cashfree.com) | Sandbox: <code>TEST1118...8111</code> (sandbox.cashfree.com)</span>
<span style="color: #34D399;">✓ Automatic origin-based routing active for beta.tradhox.com & tradhox.com</span>
</div>
<button onclick="pingCashfreeDirectApi()" class="btn btn-cashfree btn-sm" id="btn-ping-cf">⚡ Ping Cashfree Direct API</button>
</div>
<div class="sandbox-container" style="border-color: var(--border-cashfree);">
@ -1653,15 +1676,57 @@
}
}
// ==========================================
// ==========================================
// CASHFREE DEDICATED VENDOR STUDIO HANDLERS
// ==========================================
function getCfStudioEnv() {
const el = document.getElementById('cf-studio-env-select');
return el ? el.value : 'sandbox';
}
function onCfStudioEnvChange() {
const env = getCfStudioEnv();
const badge = document.getElementById('cf-env-badge');
const appIdEl = document.getElementById('cf-disp-appid');
const secretEl = document.getElementById('cf-disp-secret');
const baseEl = document.getElementById('cf-disp-baseurl');
const checkoutEl = document.getElementById('cf-disp-checkout');
if (env === 'production') {
if (badge) {
badge.textContent = 'Mode: Production (Live)';
badge.style.background = 'rgba(239, 68, 68, 0.15)';
badge.style.color = '#F87171';
badge.style.borderColor = 'rgba(239, 68, 68, 0.3)';
}
if (appIdEl) appIdEl.textContent = '1370798f950e30514fa051e93868970731';
if (secretEl) secretEl.textContent = 'cfsk_ma_prod_d441...a626ee00';
if (baseEl) baseEl.textContent = 'https://api.cashfree.com/pg';
if (checkoutEl) checkoutEl.textContent = 'https://payments.cashfree.com/order/#';
showToast('Switched Cashfree Studio to Production Mode (api.cashfree.com)', 'info');
} else {
if (badge) {
badge.textContent = 'Mode: Sandbox';
badge.style.background = 'var(--cashfree-glow)';
badge.style.color = '#F472B6';
badge.style.borderColor = 'var(--border-cashfree)';
}
if (appIdEl) appIdEl.textContent = 'TEST1118017027fc875a9485063df8b007108111';
if (secretEl) secretEl.textContent = 'cfsk_ma_test_3701...1cf9fb86';
if (baseEl) baseEl.textContent = 'https://sandbox.cashfree.com/pg';
if (checkoutEl) checkoutEl.textContent = 'https://payments-test.cashfree.com/order/#';
showToast('Switched Cashfree Studio to Sandbox Mode', 'info');
}
}
async function handleDirectCashfreeOrder(e) {
e.preventDefault();
const btn = document.getElementById('btn-cf-direct-create');
btn.disabled = true;
const env = getCfStudioEnv();
const payload = {
order_id: document.getElementById('cf-direct-order-id').value,
amount: document.getElementById('cf-direct-amount').value,
@ -1670,12 +1735,13 @@
customer_name: document.getElementById('cf-direct-cust-name').value,
customer_email: document.getElementById('cf-direct-email').value,
customer_phone: document.getElementById('cf-direct-phone').value,
order_note: 'Direct Cashfree Vendor Sandbox Order',
return_url: window.location.origin + '/portal/?order_id={order_id}'
order_note: `Direct Cashfree Vendor Order [${env.toUpperCase()}]`,
return_url: window.location.origin + '/portal/?order_id={order_id}',
environment: env
};
try {
logConsole('Sending Direct Order to Cashfree API...', payload);
logConsole(`Sending Direct Order to Cashfree API [${env.toUpperCase()}]...`, payload);
const res = await fetch('/api/payments/cashfree/orders/create/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@ -1688,12 +1754,12 @@
currentOrderId = data.order_id;
currentSessionId = data.payment_session_id;
showToast(`Cashfree order ${data.order_id} created successfully!`, 'success');
showToast(`Cashfree order ${data.order_id} created successfully [${env.toUpperCase()}]!`, 'success');
const drawer = document.getElementById('cf-direct-drawer');
drawer.style.display = 'block';
document.getElementById('cf-drawer-info').textContent =
`Cashfree Order ID: ${data.gateway_order_id} | Session ID: ${data.payment_session_id}`;
`Order: ${data.gateway_order_id || data.order_id} | Session: ${data.payment_session_id} [${env}]`;
document.getElementById('btn-cf-direct-hosted').href = data.checkout_url || '#';
document.getElementById('upi-session-id').value = data.payment_session_id;
@ -1799,11 +1865,12 @@
}
try {
logConsole(`Auto-generating live Cashfree Payment Session for ₹${amount}...`);
const env = getCfStudioEnv();
logConsole(`Auto-generating live Cashfree Payment Session for ₹${amount} [${env}]...`);
const res = await fetch('/api/payments/cashfree/quick-session/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: amount })
body: JSON.stringify({ amount: amount, environment: env })
});
const data = await res.json();
@ -1816,9 +1883,9 @@
if (statusEl) {
statusEl.style.color = 'var(--success)';
statusEl.textContent = `✓ Generated Session for ${data.order_id} (₹${amount})`;
statusEl.textContent = `✓ Generated Session for ${data.order_id} (₹${amount}) [${env}]`;
}
showToast(`Live Session ID generated for order ${data.order_id}!`, 'success');
showToast(`Live Session ID generated for order ${data.order_id} [${env}]!`, 'success');
logConsole('Cashfree Quick Session Generated:', data);
refreshDashboard();
return data.payment_session_id;
@ -1871,13 +1938,15 @@
out.textContent = 'Calling Cashfree /orders/sessions endpoint...';
try {
const env = getCfStudioEnv();
const res = await fetch('/api/payments/cashfree/pay/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment_session_id: sessionId,
channel: channel,
upi_id: upiId
upi_id: upiId,
environment: env
})
});
const data = await res.json();
@ -1916,15 +1985,16 @@
if (statusEl) {
statusEl.style.display = 'inline';
statusEl.style.color = '#FBBF24';
statusEl.textContent = 'Creating sandbox order on Cashfree...';
statusEl.textContent = 'Creating order on Cashfree...';
}
try {
logConsole('Auto-generating Cashfree test order for inspection...');
const env = getCfStudioEnv();
logConsole(`Auto-generating Cashfree test order for inspection [${env}]...`);
const res = await fetch('/api/payments/cashfree/quick-session/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount: 150 })
body: JSON.stringify({ amount: 150, environment: env })
});
const data = await res.json();
if (res.ok && data.success) {
@ -1937,9 +2007,9 @@
if (statusEl) {
statusEl.style.color = 'var(--success)';
statusEl.textContent = `✓ Generated ${data.order_id}`;
statusEl.textContent = `✓ Generated ${data.order_id} [${env}]`;
}
showToast(`Generated order ${data.order_id} on Cashfree!`, 'success');
showToast(`Generated order ${data.order_id} on Cashfree [${env}]!`, 'success');
refreshDashboard();
if (target === 'inspector') {
@ -2007,11 +2077,12 @@
return;
}
const env = getCfStudioEnv();
const out = document.getElementById('cf-inspector-output');
out.textContent = `Querying Cashfree API for raw order ${orderId}...`;
out.textContent = `Querying Cashfree API for raw order ${orderId} [${env}]...`;
try {
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/raw/`);
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/raw/?environment=${env}`);
const data = await res.json();
out.textContent = JSON.stringify(data, null, 2);
logConsole('Raw Cashfree Server Order Object:', data);
@ -2027,11 +2098,12 @@
return;
}
const env = getCfStudioEnv();
const out = document.getElementById('cf-inspector-output');
out.textContent = `Querying Cashfree /orders/${orderId}/payments directly...`;
out.textContent = `Querying Cashfree /orders/${orderId}/payments directly [${env}]...`;
try {
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/payments/`);
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/payments/?environment=${env}`);
const data = await res.json();
out.textContent = JSON.stringify(data, null, 2);
logConsole('Direct Cashfree Payments Array:', data);
@ -2048,11 +2120,12 @@
return;
}
const env = getCfStudioEnv();
const out = document.getElementById('cf-inspector-output');
out.textContent = `Querying Cashfree /orders/${orderId}/settlements directly...`;
out.textContent = `Querying Cashfree /orders/${orderId}/settlements directly [${env}]...`;
try {
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/settlements/`);
const res = await fetch(`/api/payments/cashfree/orders/${orderId}/settlements/?environment=${env}`);
const data = await res.json();
out.textContent = JSON.stringify(data, null, 2);
logConsole('Direct Cashfree Settlement Details:', data);