'Invalid signature']); exit; } $event = json_decode($payload, true); if ($event['event'] === 'payment.captured') { $paymentId = $event['payload']['payment']['entity']['id'] ?? ''; $orderId = $event['payload']['payment']['entity']['order_id'] ?? ''; // Credit the wallet based on transaction record $tx = DB::row("SELECT * FROM transactions WHERE razorpay_order_id=? AND status='pending'", [$orderId]); if ($tx) { DB::query("UPDATE transactions SET status='completed', razorpay_payment_id=? WHERE id=?", [$paymentId, $tx['id']]); DB::query("UPDATE users SET credits=credits+? WHERE id=?", [$tx['amount'], $tx['user_id']]); } } echo json_encode(['ok' => true]); exit; } // ── Authenticated API ───────────────────────────────────────── if (!isLoggedIn()) { echo json_encode(['error' => 'Unauthenticated']); exit; } $user = currentUser(); $input = json_decode(file_get_contents('php://input'), true) ?? []; $action = $input['action'] ?? ''; // Load Razorpay keys $keyId = DB::getSetting('razorpay_key_id') ?: RAZORPAY_KEY_ID; $keySecret = DB::getSetting('razorpay_key_secret') ?: RAZORPAY_KEY_SECRET; if (!$keyId || !$keySecret) { echo json_encode(['error' => 'Payment gateway not configured.']); exit; } switch ($action) { // ── Create Razorpay order ───────────────────────────────── case 'create_order': { $amount = abs((float)($input['amount'] ?? 0)); $desc = substr(strip_tags($input['description'] ?? 'Wallet Top-up'), 0, 255); if ($amount < 100) { echo json_encode(['error' => 'Minimum ₹100']); exit; } $amountPaise = (int)($amount * 100); // Call Razorpay API $orderData = [ 'amount' => $amountPaise, 'currency' => 'INR', 'receipt' => 'survam_' . $user['id'] . '_' . time(), 'notes' => ['user_id' => $user['id'], 'description' => $desc], ]; $ch = curl_init('https://api.razorpay.com/v1/orders'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($orderData), CURLOPT_USERPWD => "$keyId:$keySecret", CURLOPT_HTTPHEADER => ['Content-Type: application/json'], ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $rzpOrder = json_decode($response, true); if ($httpCode !== 200 || empty($rzpOrder['id'])) { error_log('Razorpay order error: ' . $response); echo json_encode(['error' => 'Payment gateway error. Please try again.']); exit; } // Record pending transaction DB::insert("INSERT INTO transactions (user_id, type, amount, status, description, razorpay_order_id) VALUES (?,?,?,?,?,?)", [$user['id'], 'credit', $amount, 'pending', $desc, $rzpOrder['id']]); echo json_encode([ 'success' => true, 'order_id' => $rzpOrder['id'], 'amount_paise' => $amountPaise, ]); break; } // ── Verify payment ──────────────────────────────────────── case 'verify_payment': { $paymentId = $input['razorpay_payment_id'] ?? ''; $orderId = $input['razorpay_order_id'] ?? ''; $signature = $input['razorpay_signature'] ?? ''; if (!$paymentId || !$orderId || !$signature) { echo json_encode(['error' => 'Missing payment data.']); exit; } // Verify signature $expectedSig = hash_hmac('sha256', "$orderId|$paymentId", $keySecret); if (!hash_equals($expectedSig, $signature)) { echo json_encode(['error' => 'Payment signature mismatch. Contact support.']); exit; } // Credit wallet $tx = DB::row("SELECT * FROM transactions WHERE razorpay_order_id=? AND user_id=? AND status='pending'", [$orderId, $user['id']]); if (!$tx) { echo json_encode(['error' => 'Transaction not found or already processed.']); exit; } DB::query("UPDATE transactions SET status='completed', razorpay_payment_id=? WHERE id=?", [$paymentId, $tx['id']]); DB::query("UPDATE users SET credits=credits+? WHERE id=?", [$tx['amount'], $user['id']]); echo json_encode(['success' => true, 'credited' => $tx['amount']]); break; } // ── Apply plan (deduct credits) ─────────────────────────── case 'apply_plan': { $planId = (int)($input['plan_id'] ?? 0); $plan = DB::row("SELECT * FROM plans WHERE id=?", [$planId]); if (!$plan) { echo json_encode(['error' => 'Plan not found']); exit; } $price = (float)$plan['price_monthly']; if ($price === 0.0) { // Free plan — just switch DB::query("UPDATE users SET plan_id=? WHERE id=?", [$planId, $user['id']]); echo json_encode(['success' => true, 'message' => 'Switched to Free plan.']); exit; } if ((float)$user['credits'] < $price) { echo json_encode(['error' => 'Insufficient credits. Please top up your wallet.']); exit; } DB::query("UPDATE users SET plan_id=?, credits=credits-? WHERE id=?", [$planId, $price, $user['id']]); DB::insert("INSERT INTO transactions (user_id, type, amount, status, description) VALUES (?,?,?,?,?)", [$user['id'], 'debit', $price, 'completed', 'Plan: ' . $plan['name']]); echo json_encode(['success' => true, 'message' => 'Plan activated: ' . $plan['name']]); break; } default: echo json_encode(['error' => 'Unknown action']); }