Add comprehensive logging to PayPal payment flow for debugging errors

Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-10-29 21:38:02 +00:00
parent de3db4e247
commit 4dba46d11c
4 changed files with 542 additions and 68 deletions

View file

@ -11,12 +11,49 @@ ini_set('display_errors', '0');
error_reporting(E_ALL);
header('Content-Type: application/json');
$in = json_decode(file_get_contents('php://input'), true) ?: [];
// Read and parse input
$rawInput = file_get_contents('php://input');
capture_log('RAW_INPUT', substr($rawInput, 0, 1000));
$in = json_decode($rawInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
capture_log('JSON_DECODE_ERROR', [
'error' => json_last_error_msg(),
'raw_input_length' => strlen($rawInput),
'raw_input_preview' => substr($rawInput, 0, 500)
]);
http_response_code(400);
echo json_encode(['error' => 'invalid_json', 'message' => json_last_error_msg(), 'request_id' => $requestId]);
exit;
}
if (!$in) {
$in = [];
}
$order_id = $in['order_id'] ?? null;
if (!$order_id) { http_response_code(400); echo json_encode(['error'=>'missing order_id']); exit; }
capture_log('PARSED_INPUT', ['order_id' => $order_id]);
if (!$order_id) {
capture_log('MISSING_ORDER_ID', ['input' => $in]);
http_response_code(400);
echo json_encode(['error' => 'missing_order_id', 'request_id' => $requestId]);
exit;
}
$api = $sandbox ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
capture_log('PAYPAL_API_CONFIG', [
'sandbox_mode' => $sandbox,
'api_base' => $api,
'has_client_id' => !empty($client_id),
'has_client_secret' => !empty($client_secret)
]);
// Step 1: Get OAuth token
capture_log('OAUTH_REQUEST_START', ['endpoint' => "$api/v1/oauth2/token"]);
$ch = curl_init("$api/v1/oauth2/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
@ -27,9 +64,47 @@ curl_setopt_array($ch, [
]);
$tok = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$oauth_curl_errno = curl_errno($ch);
$oauth_curl_error = curl_error($ch);
curl_close($ch);
if ($http !== 200) { http_response_code(500); echo json_encode(['error'=>'oauth_fail']); exit; }
capture_log('OAUTH_RESPONSE', [
'http_code' => $http,
'curl_errno' => $oauth_curl_errno,
'curl_error' => $oauth_curl_error,
'response_length' => strlen($tok),
'response_preview' => substr($tok, 0, 200)
]);
if ($oauth_curl_errno !== 0) {
capture_log('OAUTH_CURL_ERROR', ['errno' => $oauth_curl_errno, 'error' => $oauth_curl_error]);
http_response_code(502);
echo json_encode(['error' => 'oauth_curl_fail', 'details' => $oauth_curl_error, 'request_id' => $requestId]);
exit;
}
if ($http !== 200) {
capture_log('OAUTH_HTTP_ERROR', ['http_code' => $http, 'response' => $tok]);
http_response_code(500);
echo json_encode(['error' => 'oauth_fail', 'http_code' => $http, 'request_id' => $requestId]);
exit;
}
$access = json_decode($tok, true)['access_token'] ?? null;
if (!$access) {
capture_log('OAUTH_NO_TOKEN', ['response' => $tok]);
http_response_code(500);
echo json_encode(['error' => 'oauth_no_token', 'request_id' => $requestId]);
exit;
}
capture_log('OAUTH_SUCCESS', ['token_length' => strlen($access)]);
// Step 2: Capture the PayPal order
capture_log('CAPTURE_REQUEST_START', [
'endpoint' => "$api/v2/checkout/orders/$order_id/capture",
'order_id' => $order_id
]);
$ch = curl_init("$api/v2/checkout/orders/$order_id/capture");
curl_setopt_array($ch, [
@ -40,30 +115,33 @@ curl_setopt_array($ch, [
$res = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_err = curl_error($ch);
$curl_errno = curl_errno($ch);
curl_close($ch);
// Ensure logs folder exists and provide a helper to write debug info
$logDir = __DIR__ . '/../logs';
if (!is_dir($logDir)) @mkdir($logDir, 0755, true);
$logFile = $logDir . '/paypal_capture.log';
function capture_log($label, $data) {
global $logFile;
$entry = '[' . date('Y-m-d H:i:s') . '] ' . $label . "\n";
if (is_array($data) || is_object($data)) $entry .= print_r($data, true);
else $entry .= (string)$data;
$entry .= "\n---\n";
@file_put_contents($logFile, $entry, FILE_APPEND | LOCK_EX);
}
// Log the raw curl response for debugging
capture_log('paypal_curl_response_http_' . $http, $res === false ? "(curl failed) " . $curl_err : $res);
capture_log('CAPTURE_RESPONSE', [
'http_code' => $http,
'curl_errno' => $curl_errno,
'curl_error' => $curl_err,
'response_length' => strlen($res),
'response_preview' => substr($res, 0, 1000)
]);
// Check for curl-level errors
if ($curl_errno !== 0) {
capture_log('CAPTURE_CURL_ERROR', ['errno' => $curl_errno, 'error' => $curl_err]);
http_response_code(502);
$out = ['error' => 'capture_curl_fail', 'details' => $curl_err, 'request_id' => $requestId];
echo json_encode($out);
exit;
}
// Normalize response: ensure we always return valid JSON to the caller
if ($res === false || $res === '') {
// Curl-level failure or empty body
capture_log('CAPTURE_EMPTY_RESPONSE', ['http' => $http]);
http_response_code(502);
$out = ['error' => 'paypal_empty_response', 'http' => $http, 'curl_error' => $curl_err];
capture_log('paypal_empty_response', $out);
$out = ['error' => 'paypal_empty_response', 'http' => $http, 'request_id' => $requestId];
echo json_encode($out);
exit;
}
@ -72,25 +150,36 @@ if ($res === false || $res === '') {
$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
capture_log('CAPTURE_INVALID_JSON', [
'json_error' => json_last_error_msg(),
'http' => $http,
'raw_preview' => substr($res, 0, 500)
]);
http_response_code(502);
$out = ['error' => 'paypal_invalid_json', 'http' => $http, 'raw' => $res];
capture_log('paypal_invalid_json', $out);
$out = ['error' => 'paypal_invalid_json', 'http' => $http, 'request_id' => $requestId];
echo json_encode($out);
exit;
}
if ($http !== 201 && $http !== 200) {
capture_log('CAPTURE_HTTP_ERROR', [
'http_code' => $http,
'response' => $capture
]);
http_response_code($http);
// Return structured JSON with PayPal's decoded response
$out = ['error' => 'paypal_capture_failed', 'http' => $http, 'response' => $capture];
capture_log('paypal_capture_failed', $out);
$out = ['error' => 'paypal_capture_failed', 'http' => $http, 'paypal_error' => $capture, 'request_id' => $requestId];
echo json_encode($out);
exit;
}
// Extract payment details
$txid = null;
capture_log('paypal_capture_success', $capture);
capture_log('CAPTURE_SUCCESS', [
'status' => $capture['status'] ?? 'UNKNOWN',
'id' => $capture['id'] ?? 'UNKNOWN'
]);
if (isset($capture['purchase_units'][0]['payments']['captures'][0])) {
$txid = $capture['purchase_units'][0]['payments']['captures'][0]['id'] ?? null;
}
@ -99,28 +188,48 @@ if (isset($capture['purchase_units'][0]['payments']['captures'][0])) {
$custom_id = $capture['purchase_units'][0]['custom_id'] ?? null;
$captureStatus = $capture['status'] ?? null;
capture_log('PAYMENT_DETAILS', [
'txid' => $txid,
'custom_id' => $custom_id,
'status' => $captureStatus
]);
if ($captureStatus === 'COMPLETED' && $custom_id) {
capture_log('STARTING_DB_PROCESSING', ['custom_id' => $custom_id, 'status' => $captureStatus]);
// Connect to database using mysqli (standalone - no panel dependencies)
$db = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
if (!$db) {
error_log('capture_order.php: DB connection failed - ' . mysqli_connect_error());
echo json_encode(['error' => 'db_connection_failed', 'status' => $captureStatus]);
$dbError = mysqli_connect_error();
capture_log('DB_CONNECTION_FAILED', [
'error' => $dbError,
'host' => $db_host,
'db_name' => $db_name
]);
error_log('capture_order.php: DB connection failed - ' . $dbError);
echo json_encode(['error' => 'db_connection_failed', 'status' => $captureStatus, 'request_id' => $requestId]);
exit;
}
capture_log('DB_CONNECTED', ['database' => $db_name]);
// Get coupon information from session if available
session_start();
$applied_coupon = isset($_SESSION['applied_coupon']) ? $_SESSION['applied_coupon'] : null;
$coupon_id = $applied_coupon ? intval($applied_coupon['coupon_id']) : null;
// 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();
// Check both website_user_id and user_id for compatibility
$user_id = isset($_SESSION['website_user_id']) ? intval($_SESSION['website_user_id']) :
(isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0);
capture_log('SESSION_INFO', [
'user_id' => $user_id,
'coupon_id' => $coupon_id,
'session_keys' => array_keys($_SESSION)
]);
if ($user_id > 0) {
capture_log('PROCESSING_INVOICES', ['user_id' => $user_id, 'custom_id' => $custom_id]);
// Mark all due invoices for this user as paid, including coupon_id if applicable
$now = date('Y-m-d H:i:s');
$esc_txid = mysqli_real_escape_string($db, $txid);
@ -131,26 +240,52 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
$updateInvoices .= ", coupon_id=$coupon_id";
}
$updateInvoices .= " WHERE user_id=$user_id AND status='due'";
mysqli_query($db, $updateInvoices);
capture_log('UPDATE_INVOICES_QUERY', ['sql' => $updateInvoices]);
$updateResult = mysqli_query($db, $updateInvoices);
if (!$updateResult) {
capture_log('UPDATE_INVOICES_FAILED', ['error' => mysqli_error($db)]);
} else {
$affectedRows = mysqli_affected_rows($db);
capture_log('UPDATE_INVOICES_SUCCESS', ['affected_rows' => $affectedRows]);
}
// Update coupon usage count if a coupon was applied
if ($coupon_id) {
$updateCoupon = "UPDATE {$table_prefix}billing_coupons
SET current_uses = current_uses + 1
WHERE coupon_id = $coupon_id";
mysqli_query($db, $updateCoupon);
capture_log('UPDATE_COUPON_QUERY', ['sql' => $updateCoupon]);
$couponResult = mysqli_query($db, $updateCoupon);
if (!$couponResult) {
capture_log('UPDATE_COUPON_FAILED', ['error' => mysqli_error($db)]);
} else {
capture_log('UPDATE_COUPON_SUCCESS', ['affected_rows' => mysqli_affected_rows($db)]);
}
// Clear coupon from session after use (for one-time coupons)
if ($applied_coupon && $applied_coupon['usage_type'] === 'one_time') {
unset($_SESSION['applied_coupon']);
capture_log('COUPON_CLEARED', ['type' => 'one_time']);
}
}
// Get all invoices we just marked paid
$getInvoices = "SELECT * FROM {$table_prefix}billing_invoices WHERE user_id=$user_id AND payment_txid='$esc_txid'";
capture_log('GET_INVOICES_QUERY', ['sql' => $getInvoices]);
$invoicesResult = mysqli_query($db, $getInvoices);
if (!$invoicesResult) {
capture_log('GET_INVOICES_FAILED', ['error' => mysqli_error($db)]);
} else {
$invoiceCount = mysqli_num_rows($invoicesResult);
capture_log('GET_INVOICES_SUCCESS', ['count' => $invoiceCount]);
}
// For each invoice, either create a new order or extend existing one (renewal)
$processedInvoices = 0;
while ($inv = mysqli_fetch_assoc($invoicesResult)) {
$invoice_id = intval($inv['invoice_id']);
$existing_order_id = intval($inv['order_id'] ?? 0);
@ -166,8 +301,17 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
$ftp_pw = mysqli_real_escape_string($db, $inv['ftp_password']);
$inv_coupon_id = intval($inv['coupon_id'] ?? 0);
capture_log('PROCESSING_INVOICE', [
'invoice_id' => $invoice_id,
'existing_order_id' => $existing_order_id,
'service_id' => $service_id,
'home_name' => $home_name,
'amount' => $amount
]);
// Check if this is a renewal (existing order_id > 0) or new order (order_id = 0)
if ($existing_order_id > 0) {
capture_log('RENEWAL_DETECTED', ['order_id' => $existing_order_id, 'invoice_id' => $invoice_id]);
// RENEWAL: Extend the existing order's end_date
// Calculate months to add based on qty and duration
$months = 0;
@ -199,13 +343,27 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
$updateOrder = "UPDATE {$table_prefix}billing_orders
SET end_date='$new_end_date', status='paid', payment_txid='$esc_txid', paid_ts='$now'
WHERE order_id=$existing_order_id";
capture_log('UPDATE_ORDER_QUERY', ['sql' => $updateOrder]);
if (mysqli_query($db, $updateOrder)) {
capture_log('ORDER_EXTENDED_SUCCESS', [
'order_id' => $existing_order_id,
'new_end_date' => $new_end_date,
'invoice_id' => $invoice_id
]);
error_log("capture_order.php: Extended order $existing_order_id end_date to $new_end_date for invoice $invoice_id");
$processedInvoices++;
} else {
error_log("capture_order.php: Failed to extend order $existing_order_id: " . mysqli_error($db));
$dbError = mysqli_error($db);
capture_log('ORDER_EXTENDED_FAILED', [
'order_id' => $existing_order_id,
'error' => $dbError
]);
error_log("capture_order.php: Failed to extend order $existing_order_id: " . $dbError);
}
}
} else {
capture_log('NEW_ORDER_DETECTED', ['invoice_id' => $invoice_id]);
// NEW ORDER: Create a new order record
// Calculate end_date based on qty * duration
$end_date = date('Y-m-d H:i:s', strtotime("+$qty $duration"));
@ -221,25 +379,58 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
'$esc_txid', '$now'" . ($inv_coupon_id ? ", $inv_coupon_id" : "") . "
)";
capture_log('INSERT_ORDER_QUERY', ['sql' => substr($insertOrder, 0, 500)]);
if (mysqli_query($db, $insertOrder)) {
$new_order_id = mysqli_insert_id($db);
capture_log('ORDER_CREATED_SUCCESS', [
'new_order_id' => $new_order_id,
'invoice_id' => $invoice_id
]);
// Link invoice to order
$linkInvoice = "UPDATE {$table_prefix}billing_invoices SET order_id=$new_order_id WHERE invoice_id=$invoice_id";
mysqli_query($db, $linkInvoice);
capture_log('LINK_INVOICE_QUERY', ['sql' => $linkInvoice]);
if (mysqli_query($db, $linkInvoice)) {
capture_log('INVOICE_LINKED_SUCCESS', ['invoice_id' => $invoice_id, 'order_id' => $new_order_id]);
} else {
capture_log('INVOICE_LINK_FAILED', ['error' => mysqli_error($db)]);
}
error_log("capture_order.php: Created order $new_order_id for invoice $invoice_id");
$processedInvoices++;
} else {
error_log("capture_order.php: Failed to create order for invoice $invoice_id: " . mysqli_error($db));
$dbError = mysqli_error($db);
capture_log('ORDER_CREATE_FAILED', [
'invoice_id' => $invoice_id,
'error' => $dbError
]);
error_log("capture_order.php: Failed to create order for invoice $invoice_id: " . $dbError);
}
}
}
capture_log('PROCESSING_COMPLETE', [
'processed_invoices' => $processedInvoices,
'user_id' => $user_id
]);
mysqli_close($db);
} else {
capture_log('NO_USER_ID', ['session_data' => $_SESSION]);
}
} else {
capture_log('SKIP_PROCESSING', [
'captureStatus' => $captureStatus,
'custom_id' => $custom_id,
'reason' => !$captureStatus ? 'no_status' : (!$custom_id ? 'no_custom_id' : 'status_not_completed')
]);
}
// Return the full PayPal response (normalized JSON) for proper processing
capture_log('REQUEST_COMPLETE', ['returning_status' => $captureStatus]);
echo json_encode($capture);
?>

View file

@ -1,13 +1,65 @@
<?php
/**
* PayPal Create Order API Endpoint
* Enhanced with comprehensive logging for debugging
*/
// Ensure all errors are logged, not displayed (to prevent JSON corruption)
ini_set('display_errors', '0');
error_reporting(E_ALL);
require_once(__DIR__ . '/../includes/config.inc.php');
// create_order for PayPal — adapted to run from _website/api
$sandbox = true; // flip to false for Live
$client_id = 'AfvY_C2zA_hTHxHq7TIhtOeub4xBdySYrt_Hjj3d_WYQwjWI9NfOAVOTeResx2rgZ_nP5tOoxQSAHw8c';
$client_secret = 'EJ216np9cAj9n7KSddez3fLVxGe-zi4oKKKl1YGqPp88XIikr4Qzbxh0XW2as-V6LgdX-upjtQAg9dC0';
// Setup comprehensive logging
$logDir = __DIR__ . '/../logs';
@mkdir($logDir, 0755, true);
$logFile = $logDir . '/paypal_create_order.log';
$requestId = uniqid('req_', true); // Unique request identifier for tracking
function create_order_log($label, $data) {
global $logFile, $requestId;
$timestamp = date('Y-m-d H:i:s');
$entry = "[$timestamp] [$requestId] $label\n";
if (is_array($data) || is_object($data)) {
$entry .= print_r($data, true);
} else {
$entry .= (string)$data;
}
$entry .= "\n" . str_repeat('-', 80) . "\n";
@file_put_contents($logFile, $entry, FILE_APPEND | LOCK_EX);
}
create_order_log('REQUEST_START', [
'method' => $_SERVER['REQUEST_METHOD'] ?? 'UNKNOWN',
'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? 'UNKNOWN',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN',
]);
header('Content-Type: application/json');
$in = json_decode(file_get_contents('php://input'), true) ?: [];
// Read and parse input
$rawInput = file_get_contents('php://input');
create_order_log('RAW_INPUT', substr($rawInput, 0, 2000)); // Log first 2000 chars
$in = json_decode($rawInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
create_order_log('JSON_DECODE_ERROR', [
'error' => json_last_error_msg(),
'raw_input_length' => strlen($rawInput),
'raw_input_preview' => substr($rawInput, 0, 500)
]);
http_response_code(400);
echo json_encode(['error' => 'invalid_json', 'message' => json_last_error_msg(), 'request_id' => $requestId]);
exit;
}
if (!$in) {
$in = [];
}
$amount_in = $in['amount'] ?? '0.00';
$currency = $in['currency'] ?? 'USD';
@ -19,6 +71,15 @@ $cancel_url = $in['cancel_url'] ?? null;
$items = (isset($in['items']) && is_array($in['items'])) ? $in['items'] : null;
$line_invoices= (isset($in['line_invoices']) && is_array($in['line_invoices'])) ? $in['line_invoices'] : null;
create_order_log('PARSED_INPUT', [
'amount' => $amount_in,
'currency' => $currency,
'invoice_id' => $invoice_id,
'custom_id' => $custom_id,
'items_count' => $items ? count($items) : 0,
'line_invoices_count' => $line_invoices ? count($line_invoices) : 0
]);
$amount_value = number_format((float)$amount_in, 2, '.', '');
if ($items) {
$sum = 0.00;
@ -28,9 +89,23 @@ if ($items) {
$sum += $qty * $val;
}
$amount_value = number_format($sum, 2, '.', '');
create_order_log('AMOUNT_CALCULATED', [
'original_amount' => $amount_in,
'calculated_from_items' => $amount_value,
'items_sum' => $sum
]);
}
$api = $sandbox ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
create_order_log('PAYPAL_API_CONFIG', [
'sandbox_mode' => $sandbox,
'api_base' => $api,
'has_client_id' => !empty($client_id),
'has_client_secret' => !empty($client_secret)
]);
// Step 1: Get OAuth token
create_order_log('OAUTH_REQUEST_START', ['endpoint' => "$api/v1/oauth2/token"]);
$ch = curl_init("$api/v1/oauth2/token");
curl_setopt_array($ch, [
@ -42,14 +117,51 @@ curl_setopt_array($ch, [
]);
$tok = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_errno = curl_errno($ch);
$curl_error = curl_error($ch);
curl_close($ch);
if ($http !== 200) { http_response_code(500); echo json_encode(['error'=>'oauth_fail']); exit; }
create_order_log('OAUTH_RESPONSE', [
'http_code' => $http,
'curl_errno' => $curl_errno,
'curl_error' => $curl_error,
'response_length' => strlen($tok),
'response_preview' => substr($tok, 0, 200)
]);
if ($curl_errno !== 0) {
create_order_log('OAUTH_CURL_ERROR', ['errno' => $curl_errno, 'error' => $curl_error]);
http_response_code(502);
echo json_encode(['error' => 'oauth_curl_fail', 'details' => $curl_error, 'request_id' => $requestId]);
exit;
}
if ($http !== 200) {
create_order_log('OAUTH_HTTP_ERROR', ['http_code' => $http, 'response' => $tok]);
http_response_code(500);
echo json_encode(['error' => 'oauth_fail', 'http_code' => $http, 'request_id' => $requestId]);
exit;
}
$access = json_decode($tok, true)['access_token'] ?? null;
if (!$access) { http_response_code(500); echo json_encode(['error'=>'oauth_no_token']); exit; }
if (!$access) {
create_order_log('OAUTH_NO_TOKEN', ['response' => $tok]);
http_response_code(500);
echo json_encode(['error' => 'oauth_no_token', 'request_id' => $requestId]);
exit;
}
create_order_log('OAUTH_SUCCESS', ['token_length' => strlen($access)]);
// Update site base URL to exclude 'modules/billing'
$siteBaseUrl = 'http://gameservers.world';
create_order_log('URL_PROCESSING_BEFORE', [
'return_url' => $return_url,
'cancel_url' => $cancel_url,
'site_base' => $siteBaseUrl
]);
// Ensure return_url and cancel_url are absolute URLs (relative to site root)
if (strpos($return_url, 'http') !== 0) {
$return_url = $siteBaseUrl . '/' . ltrim($return_url, '/');
@ -58,6 +170,11 @@ if (strpos($cancel_url, 'http') !== 0) {
$cancel_url = $siteBaseUrl . '/' . ltrim($cancel_url, '/');
}
create_order_log('URL_PROCESSING_AFTER', [
'return_url' => $return_url,
'cancel_url' => $cancel_url
]);
$purchaseUnit = [
'amount' => [ 'currency_code' => $currency, 'value' => $amount_value ],
'description' => $description,
@ -75,17 +192,10 @@ $body = [
'application_context' => [ 'return_url'=>$return_url, 'cancel_url'=>$cancel_url, 'user_action'=>'PAY_NOW' ]
];
// Log the payload for debugging
$logDir = __DIR__ . '/../data';
@mkdir($logDir, 0775, true);
$logFile = $logDir . '/create_order_payload.log';
$logEntry = date('Y-m-d H:i:s') . "\n" . json_encode($body, JSON_PRETTY_PRINT) . "\n\n";
@file_put_contents($logFile, $logEntry, FILE_APPEND);
create_order_log('PAYPAL_ORDER_PAYLOAD', $body);
// Log corrected URLs for debugging
$logFile = $logDir . '/corrected_urls.log';
$logEntry = date('Y-m-d H:i:s') . "\nReturn URL: $return_url\nCancel URL: $cancel_url\n\n";
@file_put_contents($logFile, $logEntry, FILE_APPEND);
// Step 2: Create PayPal order
create_order_log('CREATE_ORDER_REQUEST_START', ['endpoint' => "$api/v2/checkout/orders"]);
$ch = curl_init("$api/v2/checkout/orders");
curl_setopt_array($ch, [
@ -96,20 +206,61 @@ curl_setopt_array($ch, [
]);
$res = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_errno = curl_errno($ch);
$curl_error = curl_error($ch);
curl_close($ch);
if ($http !== 201) {
// Log error for debugging
$logDir = __DIR__ . '/../data';
@mkdir($logDir, 0775, true);
$logFile = $logDir . '/create_order_errors.log';
$logEntry = date('Y-m-d H:i:s') . " HTTP $http: " . substr($res, 0, 500) . "\n";
@file_put_contents($logFile, $logEntry, FILE_APPEND);
http_response_code($http);
echo $res;
exit;
create_order_log('CREATE_ORDER_RESPONSE', [
'http_code' => $http,
'curl_errno' => $curl_errno,
'curl_error' => $curl_error,
'response_length' => strlen($res),
'response' => substr($res, 0, 1000) // First 1000 chars of response
]);
if ($curl_errno !== 0) {
create_order_log('CREATE_ORDER_CURL_ERROR', ['errno' => $curl_errno, 'error' => $curl_error]);
http_response_code(502);
echo json_encode(['error' => 'create_order_curl_fail', 'details' => $curl_error, 'request_id' => $requestId]);
exit;
}
if ($http !== 201) {
create_order_log('CREATE_ORDER_HTTP_ERROR', [
'http_code' => $http,
'response' => $res,
'payload_sent' => $body
]);
// Try to parse PayPal error response
$errorData = json_decode($res, true);
http_response_code($http);
echo json_encode([
'error' => 'create_order_failed',
'http_code' => $http,
'paypal_error' => $errorData,
'request_id' => $requestId
]);
exit;
}
// Success - parse and validate response
$orderData = json_decode($res, true);
if (json_last_error() !== JSON_ERROR_NONE) {
create_order_log('CREATE_ORDER_INVALID_JSON', [
'json_error' => json_last_error_msg(),
'response' => $res
]);
http_response_code(502);
echo json_encode(['error' => 'invalid_paypal_response', 'request_id' => $requestId]);
exit;
}
create_order_log('CREATE_ORDER_SUCCESS', [
'order_id' => $orderData['id'] ?? 'UNKNOWN',
'status' => $orderData['status'] ?? 'UNKNOWN'
]);
echo $res;
?>

View file

@ -0,0 +1,44 @@
<?php
/**
* Client-side error logging endpoint
* Logs JavaScript errors from the cart page for debugging
*/
// Ensure all errors are logged, not displayed
ini_set('display_errors', '0');
error_reporting(E_ALL);
header('Content-Type: application/json');
// Setup logging
$logDir = __DIR__ . '/../logs';
@mkdir($logDir, 0755, true);
$logFile = $logDir . '/client_errors.log';
function log_client_error($data) {
global $logFile;
$timestamp = date('Y-m-d H:i:s');
$entry = "[$timestamp] CLIENT ERROR\n";
$entry .= "IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'UNKNOWN') . "\n";
$entry .= "User Agent: " . ($_SERVER['HTTP_USER_AGENT'] ?? 'UNKNOWN') . "\n";
if (is_array($data) || is_object($data)) {
$entry .= print_r($data, true);
} else {
$entry .= (string)$data;
}
$entry .= "\n" . str_repeat('-', 80) . "\n";
@file_put_contents($logFile, $entry, FILE_APPEND | LOCK_EX);
}
// Read and parse input
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput, true);
if ($data) {
log_client_error($data);
echo json_encode(['status' => 'logged']);
} else {
log_client_error(['raw_input' => $rawInput, 'error' => 'Invalid JSON']);
echo json_encode(['status' => 'error', 'message' => 'Invalid JSON']);
}
?>