Fix invoice/order payment flow - implement process_payment_record and handle renewals
Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
30f862bb07
commit
c9e5e6d18a
2 changed files with 248 additions and 126 deletions
|
|
@ -78,7 +78,7 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
|
|||
$db = createDatabaseConnection($db_host, $db_user, $db_pass, $db_name, $db_port);
|
||||
if (!$db) {
|
||||
error_log('capture_order.php: DB connection failed');
|
||||
echo $res;
|
||||
echo json_encode(['error' => 'db_connection_failed', 'status' => $captureStatus]);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,9 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
|
|||
// 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;
|
||||
// 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);
|
||||
|
||||
if ($user_id > 0) {
|
||||
// Mark all due invoices for this user as paid
|
||||
|
|
@ -102,9 +104,10 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
|
|||
$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
|
||||
// For each invoice, either create a new order or extend existing one (renewal)
|
||||
while ($inv = mysqli_fetch_assoc($invoicesResult)) {
|
||||
$invoice_id = intval($inv['invoice_id']);
|
||||
$existing_order_id = intval($inv['order_id'] ?? 0);
|
||||
$service_id = intval($inv['service_id']);
|
||||
$home_name = mysqli_real_escape_string($db, $inv['home_name']);
|
||||
$ip = intval($inv['ip']);
|
||||
|
|
@ -115,30 +118,72 @@ if ($captureStatus === 'COMPLETED' && $custom_id) {
|
|||
$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);
|
||||
// Check if this is a renewal (existing order_id > 0) or new order (order_id = 0)
|
||||
if ($existing_order_id > 0) {
|
||||
// RENEWAL: Extend the existing order's end_date
|
||||
// Calculate months to add based on qty and duration
|
||||
$months = 0;
|
||||
$q = intval($qty);
|
||||
$invdur = strtolower(trim($duration));
|
||||
if (strpos($invdur, 'year') !== false) {
|
||||
$months = $q * 12;
|
||||
} else {
|
||||
// default to months for anything else (month, monthly, etc.)
|
||||
$months = $q;
|
||||
}
|
||||
|
||||
// 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");
|
||||
// Get current end_date and extend it
|
||||
$getEndDate = "SELECT end_date FROM ogp_billing_orders WHERE order_id=$existing_order_id LIMIT 1";
|
||||
$endDateResult = mysqli_query($db, $getEndDate);
|
||||
if ($endDateResult && mysqli_num_rows($endDateResult) === 1) {
|
||||
$endRow = mysqli_fetch_assoc($endDateResult);
|
||||
$current_end = $endRow['end_date'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
// Extend from current end_date or now (whichever is later)
|
||||
$extend_from = (strtotime($current_end) > time()) ? $current_end : date('Y-m-d H:i:s');
|
||||
$dt = new DateTime($extend_from);
|
||||
if ($months > 0) {
|
||||
$dt->modify('+' . intval($months) . ' months');
|
||||
}
|
||||
$new_end_date = $dt->format('Y-m-d H:i:s');
|
||||
|
||||
// Update order with new end_date and mark as paid/active
|
||||
$updateOrder = "UPDATE ogp_billing_orders
|
||||
SET end_date='$new_end_date', status='paid', payment_txid='$esc_txid', paid_ts='$now'
|
||||
WHERE order_id=$existing_order_id";
|
||||
if (mysqli_query($db, $updateOrder)) {
|
||||
error_log("capture_order.php: Extended order $existing_order_id end_date to $new_end_date for invoice $invoice_id");
|
||||
} else {
|
||||
error_log("capture_order.php: Failed to extend order $existing_order_id: " . mysqli_error($db));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error_log("capture_order.php: Failed to create order for invoice $invoice_id: " . mysqli_error($db));
|
||||
// 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"));
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,184 @@ session_start();
|
|||
require_once(__DIR__ . '/includes/header.php');
|
||||
require_once(__DIR__ . '/includes/config.inc.php');
|
||||
require_once(__DIR__ . '/../../includes/database_mysqli.php');
|
||||
require_once(__DIR__ . '/includes/log.php');
|
||||
|
||||
/**
|
||||
* Process payment record from webhook or capture
|
||||
* Marks invoices as paid and creates/extends orders
|
||||
*
|
||||
* @param array $record Payment record with invoice, custom, amount, txid, etc.
|
||||
* @return bool True if successful, false otherwise
|
||||
*/
|
||||
function process_payment_record($record) {
|
||||
global $db_host, $db_user, $db_pass, $db_name, $db_port, $table_prefix;
|
||||
|
||||
// Extract payment details
|
||||
$invoice = $record['invoice'] ?? null;
|
||||
$custom = $record['custom'] ?? null;
|
||||
$txid = $record['resource_id'] ?? null;
|
||||
$amount = $record['amount'] ?? 0;
|
||||
|
||||
// Require database connection
|
||||
$db = createDatabaseConnection($db_host, $db_user, $db_pass, $db_name, $db_port);
|
||||
if (!$db) {
|
||||
if (function_exists('site_log_error')) site_log_error('process_payment_db_fail', ['invoice'=>$invoice]);
|
||||
else error_log('[payment_success] DB connection failed for invoice=' . $invoice);
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$esc_txid = mysqli_real_escape_string($db, (string)$txid);
|
||||
|
||||
// Find invoices to mark as paid
|
||||
$invoices_to_process = [];
|
||||
|
||||
// Try to match by custom_id (which should be invoice_id for single-item carts)
|
||||
if ($custom && ctype_digit((string)$custom)) {
|
||||
$invoice_id = intval($custom);
|
||||
$stmt = $db->prepare("SELECT * FROM " . $table_prefix . "billing_invoices WHERE invoice_id = ? AND status = 'due' LIMIT 1");
|
||||
if ($stmt) {
|
||||
$stmt->bind_param('i', $invoice_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result && $row = $result->fetch_assoc()) {
|
||||
$invoices_to_process[] = $row;
|
||||
}
|
||||
$stmt->close();
|
||||
}
|
||||
}
|
||||
|
||||
// If no match by custom_id, try matching all unpaid invoices for this payment amount
|
||||
// (This handles multi-item carts where custom_id isn't a single invoice_id)
|
||||
if (empty($invoices_to_process) && $invoice) {
|
||||
// Match by invoice reference from PayPal
|
||||
$esc_invoice = mysqli_real_escape_string($db, $invoice);
|
||||
$query = "SELECT * FROM " . $table_prefix . "billing_invoices WHERE status = 'due' AND description LIKE '%$esc_invoice%'";
|
||||
$result = mysqli_query($db, $query);
|
||||
if ($result) {
|
||||
while ($row = mysqli_fetch_assoc($result)) {
|
||||
$invoices_to_process[] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process each invoice
|
||||
$processed_count = 0;
|
||||
foreach ($invoices_to_process as $inv) {
|
||||
$invoice_id = intval($inv['invoice_id']);
|
||||
$existing_order_id = intval($inv['order_id'] ?? 0);
|
||||
$user_id = intval($inv['user_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']);
|
||||
$invoice_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'] ?? '');
|
||||
|
||||
// Mark invoice as paid
|
||||
$upd_inv = $db->prepare("UPDATE " . $table_prefix . "billing_invoices SET status = 'paid', paid_date = ?, payment_txid = ?, payment_method = 'paypal' WHERE invoice_id = ? LIMIT 1");
|
||||
if ($upd_inv) {
|
||||
$upd_inv->bind_param('ssi', $now, $esc_txid, $invoice_id);
|
||||
$upd_inv->execute();
|
||||
$upd_inv->close();
|
||||
}
|
||||
|
||||
// Check if this is a renewal (existing order_id > 0) or new order (order_id = 0)
|
||||
if ($existing_order_id > 0) {
|
||||
// RENEWAL: Extend the existing order's end_date
|
||||
// Calculate months to add
|
||||
$months = 0;
|
||||
$q = intval($qty);
|
||||
$invdur = strtolower(trim($duration));
|
||||
if (strpos($invdur, 'year') !== false) {
|
||||
$months = $q * 12;
|
||||
} else {
|
||||
$months = $q;
|
||||
}
|
||||
|
||||
// Get current end_date and extend it
|
||||
$getEndDate = "SELECT end_date FROM " . $table_prefix . "billing_orders WHERE order_id = $existing_order_id LIMIT 1";
|
||||
$endDateResult = mysqli_query($db, $getEndDate);
|
||||
if ($endDateResult && mysqli_num_rows($endDateResult) === 1) {
|
||||
$endRow = mysqli_fetch_assoc($endDateResult);
|
||||
$current_end = $endRow['end_date'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
// Extend from current end_date or now (whichever is later)
|
||||
$extend_from = (strtotime($current_end) > time()) ? $current_end : date('Y-m-d H:i:s');
|
||||
$dt = new DateTime($extend_from);
|
||||
if ($months > 0) {
|
||||
$dt->modify('+' . intval($months) . ' months');
|
||||
}
|
||||
$new_end_date = $dt->format('Y-m-d H:i:s');
|
||||
|
||||
// Update order with new end_date and payment info
|
||||
$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";
|
||||
if (mysqli_query($db, $updateOrder)) {
|
||||
if (function_exists('site_log_info')) site_log_info('payment_renewal_processed', ['order_id'=>$existing_order_id, 'invoice_id'=>$invoice_id, 'new_end_date'=>$new_end_date]);
|
||||
else error_log("[payment_success] Extended order $existing_order_id to $new_end_date for invoice $invoice_id");
|
||||
$processed_count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NEW ORDER: Create a new order record
|
||||
// Calculate months for end_date
|
||||
$months = 0;
|
||||
$q = intval($qty);
|
||||
$invdur = strtolower(trim($duration));
|
||||
if (strpos($invdur, 'year') !== false) {
|
||||
$months = $q * 12;
|
||||
} else {
|
||||
$months = $q;
|
||||
}
|
||||
|
||||
$dt = new DateTime('now');
|
||||
if ($months > 0) {
|
||||
$dt->modify('+' . intval($months) . ' months');
|
||||
}
|
||||
$end_date = $dt->format('Y-m-d H:i:s');
|
||||
|
||||
// Insert order
|
||||
$insertOrder = "INSERT INTO " . $table_prefix . "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',
|
||||
$invoice_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 " . $table_prefix . "billing_invoices SET order_id = $new_order_id WHERE invoice_id = $invoice_id";
|
||||
mysqli_query($db, $linkInvoice);
|
||||
|
||||
if (function_exists('site_log_info')) site_log_info('payment_new_order_created', ['order_id'=>$new_order_id, 'invoice_id'=>$invoice_id, 'end_date'=>$end_date]);
|
||||
else error_log("[payment_success] Created order $new_order_id for invoice $invoice_id");
|
||||
$processed_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mysqli_close($db);
|
||||
|
||||
if ($processed_count > 0) {
|
||||
if (function_exists('site_log_info')) site_log_info('payment_success_processed', ['count'=>$processed_count,'invoice'=>$invoice,'custom'=>$custom]);
|
||||
else error_log('[payment_success] Processed ' . $processed_count . ' invoice(s) - invoice=' . $invoice . ' custom=' . $custom);
|
||||
return true;
|
||||
} else {
|
||||
if (function_exists('site_log_warn')) site_log_warn('payment_success_no_match', ['invoice'=>$invoice,'custom'=>$custom]);
|
||||
else error_log('[payment_success] No matching invoices found for invoice=' . $invoice . ' custom=' . $custom);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$invoice_ref = isset($_GET['invoice']) ? $_GET['invoice'] : '';
|
||||
$user_id = isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0;
|
||||
|
|
@ -88,104 +266,3 @@ $user_id = isset($_SESSION['user_id']) ? intval($_SESSION['user_id']) : 0;
|
|||
<?php include(__DIR__ . '/includes/footer.php'); ?>
|
||||
</body>
|
||||
</html>
|
||||
$end_date_val = null;
|
||||
if ($has_finish) {
|
||||
// Attempt to find the target order's qty/invoice_duration using the same where clause but without LIMIT
|
||||
$sel_sql = "SELECT qty, invoice_duration FROM ogp_billing_orders WHERE " . str_replace(' AND status <> \"paid\" LIMIT 1', '', $where_sql) . " LIMIT 1";
|
||||
// Note: this simple substitution assumes the where_sql is of the form 'col = ?' used earlier
|
||||
if ($sel_stmt = $db->prepare($sel_sql)) {
|
||||
// bind where params
|
||||
if ($bind_types) {
|
||||
$refs = [];
|
||||
$vals = $bind_vals;
|
||||
foreach ($vals as $k => $v) $refs[$k] = &$vals[$k];
|
||||
array_unshift($refs, $bind_types);
|
||||
call_user_func_array([$sel_stmt, 'bind_param'], $refs);
|
||||
}
|
||||
$sel_stmt->execute();
|
||||
$sel_stmt->bind_result($sel_qty, $sel_invdur);
|
||||
if ($sel_stmt->fetch()) {
|
||||
// compute months
|
||||
$months = 0;
|
||||
$q = intval($sel_qty ?? 0);
|
||||
$invdur = strtolower(trim($sel_invdur ?? ''));
|
||||
if (strpos($invdur, 'year') !== false) {
|
||||
$months = $q * 12;
|
||||
} else {
|
||||
$months = $q;
|
||||
}
|
||||
if ($months <= 0) $months = 0;
|
||||
$dt = new DateTime('now');
|
||||
if ($months > 0) $dt->modify('+' . intval($months) . ' months');
|
||||
$end_date_val = $dt->format('Y-m-d H:i:s');
|
||||
}
|
||||
$sel_stmt->close();
|
||||
}
|
||||
if ($end_date_val !== null) {
|
||||
$sql = str_replace(' WHERE ', ', end_date = ? WHERE ', $sql);
|
||||
}
|
||||
}
|
||||
|
||||
if ($stmt = $db->prepare($sql)) {
|
||||
// Build params: first any where params, then txid/ts values if present, then end_date if present
|
||||
$types = $bind_types;
|
||||
$vals = $bind_vals;
|
||||
if ($cols) {
|
||||
foreach ($cols as $c) {
|
||||
$types .= 's';
|
||||
if ($c === 'payment_txid') $vals[] = $txid;
|
||||
else $vals[] = $ts;
|
||||
}
|
||||
}
|
||||
if ($end_date_val !== null) {
|
||||
$types .= 's';
|
||||
$vals[] = $end_date_val;
|
||||
}
|
||||
// bind dynamically
|
||||
if ($types) {
|
||||
$refs = [];
|
||||
foreach ($vals as $k => $v) $refs[$k] = &$vals[$k];
|
||||
array_unshift($refs, $types);
|
||||
call_user_func_array([$stmt, 'bind_param'], $refs);
|
||||
}
|
||||
$stmt->execute();
|
||||
$affected = $stmt->affected_rows;
|
||||
$stmt->close();
|
||||
return $affected;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
$affected = 0;
|
||||
// Try match by invoice column (if present)
|
||||
if ($invoice) {
|
||||
// some invoices may include paths or file names; use exact match
|
||||
$affected = $update_paid('invoice = ?', 's', [$invoice]);
|
||||
}
|
||||
|
||||
// If not matched, try numeric custom (order_id)
|
||||
if (!$affected && $custom) {
|
||||
if (ctype_digit((string)$custom)) {
|
||||
$affected = $update_paid('order_id = ?', 'i', [(int)$custom]);
|
||||
}
|
||||
}
|
||||
|
||||
// If still not matched, try matching the custom text field
|
||||
if (!$affected && $custom) {
|
||||
$affected = $update_paid('custom = ?', 's', [$custom]);
|
||||
}
|
||||
|
||||
mysqli_close($db);
|
||||
|
||||
if ($affected) {
|
||||
if (function_exists('site_log_info')) site_log_info('payment_success_marked_paid', ['affected'=>intval($affected),'invoice'=>$invoice,'custom'=>$custom]);
|
||||
else error_log('[payment_success] Marked order paid (affected=' . intval($affected) . ') invoice=' . $invoice . ' custom=' . $custom);
|
||||
return true;
|
||||
} else {
|
||||
if (function_exists('site_log_warn')) site_log_warn('payment_success_no_match', ['invoice'=>$invoice,'custom'=>$custom]);
|
||||
else error_log('[payment_success] No matching order found for invoice=' . $invoice . ' custom=' . $custom);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue