'missing order_id']); exit; } $api = $sandbox ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; $ch = curl_init("$api/v1/oauth2/token"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => 'grant_type=client_credentials', CURLOPT_HTTPHEADER => ['Accept: application/json'], CURLOPT_USERPWD => $client_id . ':' . $client_secret, ]); $tok = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http !== 200) { http_response_code(500); echo json_encode(['error'=>'oauth_fail']); exit; } $access = json_decode($tok, true)['access_token'] ?? null; $ch = curl_init("$api/v2/checkout/orders/$order_id/capture"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $access ], ]); $res = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curl_err = curl_error($ch); curl_close($ch); // Normalize response: ensure we always return valid JSON to the caller if ($res === false || $res === '') { // Curl-level failure or empty body http_response_code(502); $out = ['error' => 'paypal_empty_response', 'http' => $http, 'curl_error' => $curl_err]; echo json_encode($out); exit; } // Attempt to decode PayPal response $capture = json_decode($res, true); if (json_last_error() !== JSON_ERROR_NONE) { // PayPal returned non-JSON / malformed response — return it as raw string inside JSON http_response_code(502); $out = ['error' => 'paypal_invalid_json', 'http' => $http, 'raw' => $res]; echo json_encode($out); exit; } if ($http !== 201 && $http !== 200) { http_response_code($http); // Return structured JSON with PayPal's decoded response echo json_encode(['error' => 'paypal_capture_failed', 'http' => $http, 'response' => $capture]); exit; } // Extract payment details $txid = null; $captureStatus = $capture['status'] ?? 'UNKNOWN'; if (isset($capture['purchase_units'][0]['payments']['captures'][0])) { $txid = $capture['purchase_units'][0]['payments']['captures'][0]['id'] ?? null; } // Get custom_id (should be invoice_id from cart.php) $custom_id = $capture['purchase_units'][0]['custom_id'] ?? null; if ($captureStatus === 'COMPLETED' && $custom_id) { // Connect to database $db = createDatabaseConnection($db_host, $db_user, $db_pass, $db_name, $db_port); if (!$db) { error_log('capture_order.php: DB connection failed'); echo $res; exit; } // Find all invoices with status='due' for this user (cart session) // For now, we'll mark ALL due invoices for the logged-in user as paid // TODO: Improve to match specific invoice_id from custom_id if cart sends it session_start(); $user_id = isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0; if ($user_id > 0) { // Mark all due invoices for this user as paid $now = date('Y-m-d H:i:s'); $esc_txid = mysqli_real_escape_string($db, $txid); $updateInvoices = "UPDATE ogp_billing_invoices SET status='paid', paid_date='$now', payment_txid='$esc_txid', payment_method='paypal' WHERE user_id=$user_id AND status='due'"; mysqli_query($db, $updateInvoices); // Get all invoices we just marked paid $getInvoices = "SELECT * FROM ogp_billing_invoices WHERE user_id=$user_id AND payment_txid='$esc_txid'"; $invoicesResult = mysqli_query($db, $getInvoices); // For each invoice, create an order while ($inv = mysqli_fetch_assoc($invoicesResult)) { $invoice_id = intval($inv['invoice_id']); $service_id = intval($inv['service_id']); $home_name = mysqli_real_escape_string($db, $inv['home_name']); $ip = intval($inv['ip']); $max_players = intval($inv['max_players']); $qty = intval($inv['qty']); $duration = mysqli_real_escape_string($db, $inv['invoice_duration']); $amount = floatval($inv['amount']); $rcon_pw = mysqli_real_escape_string($db, $inv['remote_control_password']); $ftp_pw = mysqli_real_escape_string($db, $inv['ftp_password']); // Calculate end_date based on qty * duration $end_date = date('Y-m-d H:i:s', strtotime("+$qty $duration")); // Insert order $insertOrder = "INSERT INTO ogp_billing_orders ( user_id, service_id, home_name, ip, max_players, qty, invoice_duration, price, remote_control_password, ftp_password, status, order_date, end_date, payment_txid, paid_ts ) VALUES ( $user_id, $service_id, '$home_name', $ip, $max_players, $qty, '$duration', $amount, '$rcon_pw', '$ftp_pw', 'paid', '$now', '$end_date', '$esc_txid', '$now' )"; if (mysqli_query($db, $insertOrder)) { $new_order_id = mysqli_insert_id($db); // Link invoice to order $linkInvoice = "UPDATE ogp_billing_invoices SET order_id=$new_order_id WHERE invoice_id=$invoice_id"; mysqli_query($db, $linkInvoice); error_log("capture_order.php: Created order $new_order_id for invoice $invoice_id"); } else { error_log("capture_order.php: Failed to create order for invoice $invoice_id: " . mysqli_error($db)); } } mysqli_close($db); } } // Return the full PayPal response (normalized JSON) for proper processing echo json_encode($capture); ?>