Files
HotLunches/paypal_checkout_capture.php
2026-10-19 15:22:52 -07:00

410 lines
18 KiB
PHP

<?php
/**
* PayPal Checkout - Capture Payment Endpoint
*
* Called by the PayPal JS SDK after user approves payment.
* Captures the payment and updates your database.
*
* Returns JSON with transaction details or error
*/
header('Content-Type: application/json');
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Include database and required files
if (file_exists(__DIR__ . '/db.php')) {
require_once __DIR__ . '/db.php';
}
if (file_exists(__DIR__ . '/include/school-functions.php')) {
include_once __DIR__ . '/include/school-functions.php';
}
if (file_exists(__DIR__ . '/include/date-time.php')) {
include_once __DIR__ . '/include/date-time.php';
}
require_once __DIR__ . '/paypal_checkout_api.php';
try {
// Get the order ID from the request
$input = json_decode(file_get_contents('php://input'), true);
$orderId = $input['orderID'] ?? $_POST['orderID'] ?? null;
if (!$orderId) {
throw new Exception('Order ID is required');
}
// Verify this is the order we created (prevent replay attacks)
if (!isset($_SESSION['paypal_order_id']) || $_SESSION['paypal_order_id'] !== $orderId) {
throw new Exception('Order ID mismatch. Please try again.');
}
$expectedTrxnum = $_SESSION['paypal_order_trxnum'] ?? null;
$expectedAmount = $_SESSION['paypal_order_amount'] ?? null;
// Capture the payment
$paypal = new PayPalCheckoutAPI();
$captureResult = $paypal->captureOrder($orderId);
// Check capture status
if ($captureResult['status'] !== 'COMPLETED') {
throw new Exception('Payment was not completed. Status: ' . $captureResult['status']);
}
// Extract payment details
$purchaseUnit = $captureResult['purchase_units'][0] ?? [];
$capture = $purchaseUnit['payments']['captures'][0] ?? [];
$txnId = $capture['id'] ?? $orderId;
$amount = $capture['amount']['value'] ?? 0;
$currency = $capture['amount']['currency_code'] ?? '';
$customId = $purchaseUnit['custom_id'] ?? $expectedTrxnum; // This is your TRXnum
$payerEmail = $captureResult['payer']['email_address'] ?? '';
$payerName = ($captureResult['payer']['name']['given_name'] ?? '') . ' ' . ($captureResult['payer']['name']['surname'] ?? '');
// Get fee info if available
$fee = 0;
if (isset($capture['seller_receivable_breakdown']['paypal_fee']['value'])) {
$fee = floatval($capture['seller_receivable_breakdown']['paypal_fee']['value']);
}
// Validate amount matches what we expected
if ($expectedAmount && abs(floatval($amount) - floatval($expectedAmount)) > 0.01) {
// Log this discrepancy but still process (PayPal has already captured)
error_log("PayPal amount mismatch: expected $expectedAmount, got $amount");
}
// =========================================================================
// UPDATE YOUR DATABASE
// This follows the same pattern as ppPayment.php but adapted for REST API
// =========================================================================
$TRXnum = intval($customId);
$ptime = time();
if ($TRXnum > 0 && function_exists('db_query')) {
// Get user info from online payments record
$query = "SELECT * FROM user_online_payments WHERE record_num = $TRXnum";
$r = db_query($query);
if (db_num_rows($r) == 1) {
$paymentRow = db_fetch_assoc($r);
$p_school_num = $paymentRow['school_num'];
$p_user_num = $paymentRow['user_num'];
// Update user_online_payments
$query = "UPDATE user_online_payments SET status = 'Paid', payment_amount = balance_owing, reference = '$txnId', trx_fee = $fee, completed_time = $ptime WHERE record_num = $TRXnum";
db_query($query);
// Get school info for timezone
$schoolRow = db_fetch_assoc(db_query("SELECT * FROM schools WHERE record_num = $p_school_num"));
$timezone = $schoolRow['timezone'] ?? 'Canada/Pacific';
if (function_exists('mapTimeZone')) {
date_default_timezone_set(mapTimeZone($timezone));
}
$today = date("Y-m-d", $ptime);
$trx_time = date("Y-m-d H:i:s", $ptime);
// Update hotlunch_order_header - mark orders as paid
$query = "SELECT hloh.* FROM hotlunch_order_header as hloh, people as p " .
"WHERE hloh.school_num = $p_school_num " .
"AND ordered_by_num = p.record_num " .
"AND p.user_num = $p_user_num " .
"AND hloh.status NOT IN ('Paid','Complimentary','Concession')";
$orders = db_query($query);
$order_payments = "";
$snum = 0;
if (db_num_rows($orders) > 0) {
while ($row = db_fetch_array($orders)) {
$onum = $row['record_num'];
$ototal = $row['order_total'];
$opaid = $row['amount_paid'];
$snum = $row['schedule_num'];
$amt = number_format($ototal - $opaid, 2, '.', '');
if (strlen($order_payments) > 0) $order_payments .= "|";
$order_payments .= "{" . $onum . ":" . $amt . "}";
$query = "UPDATE hotlunch_order_header SET amount_paid = order_total, status = 'Paid', paid_date = '$today', payment_method = 'PayPal' WHERE record_num = $onum";
db_query($query);
}
}
// Check for duplicate entry in user_account
$query = "SELECT * FROM user_account WHERE school_num = $p_school_num AND user_num = $p_user_num AND payment_reference = '$txnId'";
$existing = db_query($query);
if (db_num_rows($existing) == 0) {
// Post payment to user_account
$paymentAmount = floatval($amount) * -1;
$query = "INSERT INTO user_account (school_num, user_num, schedule_num, transaction_datetime, transaction_type, note, transaction_amount, transaction_fee, payment_method, payment_reference, order_payments, deleted, entered_time) " .
"VALUES ($p_school_num, $p_user_num, $snum, '$trx_time', 'Payment', 'PayPal Checkout Payment', $paymentAmount, $fee, 'PayPal', '$txnId', '$order_payments', 'N', $ptime)";
db_query($query);
// Close all open transactions
$query = "UPDATE user_account SET transaction_status = 'Closed' WHERE school_num = $p_school_num AND user_num = $p_user_num AND transaction_status != 'Closed'";
db_query($query);
// Reset user flags
$query = "UPDATE users SET trx_status = NULL, can_order = 'Yes' WHERE school_num = $p_school_num AND record_num = $p_user_num AND can_order = 'Balance'";
db_query($query);
}
}
}
// Clear session variables
unset($_SESSION['paypal_order_id']);
unset($_SESSION['paypal_order_trxnum']);
unset($_SESSION['paypal_order_amount']);
// Return success response
echo json_encode([
'success' => true,
'transactionId' => $txnId,
'amount' => $amount,
'currency' => $currency,
'status' => $captureResult['status'],
'payerEmail' => $payerEmail,
'payerName' => trim($payerName)
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
<?php
/**
* PayPal Checkout - Capture Payment Endpoint
*
* Called by the PayPal JS SDK after user approves payment.
* Captures the payment and updates your database.
*
* Returns JSON with transaction details or error
*/
header('Content-Type: application/json');
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Include database and required files
if (file_exists(__DIR__ . '/db.php')) {
require_once __DIR__ . '/db.php';
}
if (file_exists(__DIR__ . '/include/school-functions.php')) {
include_once __DIR__ . '/include/school-functions.php';
}
if (file_exists(__DIR__ . '/include/date-time.php')) {
include_once __DIR__ . '/include/date-time.php';
}
require_once __DIR__ . '/paypal_checkout_api.php';
try {
// Get the order ID from the request
$input = json_decode(file_get_contents('php://input'), true);
$orderId = $input['orderID'] ?? $_POST['orderID'] ?? null;
if (!$orderId) {
throw new Exception('Order ID is required');
}
// Verify this is the order we created (prevent replay attacks)
if (!isset($_SESSION['paypal_order_id']) || $_SESSION['paypal_order_id'] !== $orderId) {
throw new Exception('Order ID mismatch. Please try again.');
}
$expectedTrxnum = $_SESSION['paypal_order_trxnum'] ?? null;
$expectedAmount = $_SESSION['paypal_order_amount'] ?? null;
// Capture the payment
$paypal = new PayPalCheckoutAPI();
$captureResult = $paypal->captureOrder($orderId);
// Check capture status
if ($captureResult['status'] !== 'COMPLETED') {
throw new Exception('Payment was not completed. Status: ' . $captureResult['status']);
}
// Extract payment details
$purchaseUnit = $captureResult['purchase_units'][0] ?? [];
$capture = $purchaseUnit['payments']['captures'][0] ?? [];
$txnId = $capture['id'] ?? $orderId;
$amount = $capture['amount']['value'] ?? 0;
$currency = $capture['amount']['currency_code'] ?? '';
$customId = $purchaseUnit['custom_id'] ?? $expectedTrxnum; // This is your TRXnum
$payerEmail = $captureResult['payer']['email_address'] ?? '';
$payerName = ($captureResult['payer']['name']['given_name'] ?? '') . ' ' . ($captureResult['payer']['name']['surname'] ?? '');
// Get fee info if available
$fee = 0;
if (isset($capture['seller_receivable_breakdown']['paypal_fee']['value'])) {
$fee = floatval($capture['seller_receivable_breakdown']['paypal_fee']['value']);
}
// Validate amount matches what we expected
if ($expectedAmount && abs(floatval($amount) - floatval($expectedAmount)) > 0.01) {
// Log this discrepancy but still process (PayPal has already captured)
error_log("PayPal amount mismatch: expected $expectedAmount, got $amount");
}
// =========================================================================
// UPDATE YOUR DATABASE
// This follows the same pattern as ppPayment.php but adapted for REST API
// =========================================================================
$TRXnum = intval($customId);
$ptime = time();
$time = date("Y-m-d H:i:s", $ptime);
$trxID = null; // Will hold record_num from user_paypal_transactions
if ($TRXnum > 0 && function_exists('db_query')) {
// Get user info from online payments record
$query = "SELECT * FROM user_online_payments WHERE record_num = $TRXnum";
$r = db_query($query);
if (db_num_rows($r) == 1) {
$paymentRow = db_fetch_assoc($r);
$p_school_num = $paymentRow['school_num'];
$p_user_num = $paymentRow['user_num'];
// =====================================================================
// INSERT INTO user_paypal_transactions (same as ppPayment.php)
// =====================================================================
$payment_type = 'instant';
$payment_status = $captureResult['status']; // COMPLETED
// Check for duplicate in user_paypal_transactions
$escapedTxnId = function_exists('db_escape_string') ? db_escape_string($txnId) : addslashes($txnId);
$dupQuery = "SELECT * FROM user_paypal_transactions WHERE school_num = $p_school_num AND payment_type = '$payment_type' AND payment_status = '$payment_status' AND txn_id = '$escapedTxnId' AND current_year != 'N'";
$dupResult = db_query($dupQuery);
if (db_num_rows($dupResult) == 0) {
// Build insert for user_paypal_transactions following ppPayment.php pattern
$escPayerEmail = function_exists('db_escape_string') ? db_escape_string($payerEmail) : addslashes($payerEmail);
$escPayerName = function_exists('db_escape_string') ? db_escape_string($payerName) : addslashes($payerName);
$firstName = function_exists('db_escape_string') ? db_escape_string($captureResult['payer']['name']['given_name'] ?? '') : addslashes($captureResult['payer']['name']['given_name'] ?? '');
$lastName = function_exists('db_escape_string') ? db_escape_string($captureResult['payer']['name']['surname'] ?? '') : addslashes($captureResult['payer']['name']['surname'] ?? '');
$receiverEmail = function_exists('db_escape_string') ? db_escape_string($captureResult['purchase_units'][0]['payee']['email_address'] ?? '') : addslashes($captureResult['purchase_units'][0]['payee']['email_address'] ?? '');
$trxInsertQuery = "INSERT INTO user_paypal_transactions
(school_num, user_num, trx_num, posting_time, ptime, error_handled,
transaction_subject, payment_date, txn_type, first_name, last_name, payer_email, payer_status,
item_name, business, receiver_email, mc_currency, mc_gross, mc_fee, reference,
payment_type, payment_status, pending_reason, txn_id, receipt_id, current_year,
posting_result, posting_message)
VALUES
($p_school_num, $p_user_num, $TRXnum, '$time', $ptime, 'N',
'PayPal Checkout Payment', '$time', 'capture', '$firstName', '$lastName', '$escPayerEmail', 'verified',
'Order Payment', '', '$receiverEmail', '$currency', $amount, $fee, '$escapedTxnId',
'$payment_type', '$payment_status', '', '$escapedTxnId', '', 'Y',
'Posted', 'Payment processed ok')";
db_query($trxInsertQuery);
$trxID = function_exists('db_insert_id') ? db_insert_id() : null;
}
// Update user_online_payments
$query = "UPDATE user_online_payments SET status = 'Paid', payment_amount = balance_owing, reference = '$txnId', trx_fee = $fee, completed_time = $ptime WHERE record_num = $TRXnum";
db_query($query);
// Get school info for timezone
$schoolRow = db_fetch_assoc(db_query("SELECT * FROM schools WHERE record_num = $p_school_num"));
$timezone = $schoolRow['timezone'] ?? 'Canada/Pacific';
if (function_exists('mapTimeZone')) {
date_default_timezone_set(mapTimeZone($timezone));
}
$today = date("Y-m-d", $ptime);
$trx_time = date("Y-m-d H:i:s", $ptime);
// Update hotlunch_order_header - mark orders as paid
$query = "SELECT hloh.* FROM hotlunch_order_header as hloh, people as p " .
"WHERE hloh.school_num = $p_school_num " .
"AND ordered_by_num = p.record_num " .
"AND p.user_num = $p_user_num " .
"AND hloh.status NOT IN ('Paid','Complimentary','Concession')";
$orders = db_query($query);
$order_payments = "";
$snum = 0;
if (db_num_rows($orders) > 0) {
while ($row = db_fetch_array($orders)) {
$onum = $row['record_num'];
$ototal = $row['order_total'];
$opaid = $row['amount_paid'];
$snum = $row['schedule_num'];
$amt = number_format($ototal - $opaid, 2, '.', '');
if (strlen($order_payments) > 0) $order_payments .= "|";
$order_payments .= "{" . $onum . ":" . $amt . "}";
$query = "UPDATE hotlunch_order_header SET amount_paid = order_total, status = 'Paid', paid_date = '$today', payment_method = 'PayPal' WHERE record_num = $onum";
db_query($query);
}
}
// Check for duplicate entry in user_account
$query = "SELECT * FROM user_account WHERE school_num = $p_school_num AND user_num = $p_user_num AND payment_reference = '$txnId'";
$existing = db_query($query);
if (db_num_rows($existing) == 0) {
// Post payment to user_account
$paymentAmount = floatval($amount) * -1;
$query = "INSERT INTO user_account (school_num, user_num, schedule_num, transaction_datetime, transaction_type, note, transaction_amount, transaction_fee, payment_method, payment_reference, order_payments, deleted, entered_time) " .
"VALUES ($p_school_num, $p_user_num, $snum, '$trx_time', 'Payment', 'PayPal Checkout Payment', $paymentAmount, $fee, 'PayPal', '$txnId', '$order_payments', 'N', $ptime)";
db_query($query);
// Close all open transactions
$query = "UPDATE user_account SET transaction_status = 'Closed' WHERE school_num = $p_school_num AND user_num = $p_user_num AND transaction_status != 'Closed'";
db_query($query);
// Reset user flags
$query = "UPDATE users SET trx_status = NULL, can_order = 'Yes' WHERE school_num = $p_school_num AND record_num = $p_user_num AND can_order = 'Balance'";
db_query($query);
}
}
}
// Clear session variables
unset($_SESSION['paypal_order_id']);
unset($_SESSION['paypal_order_trxnum']);
unset($_SESSION['paypal_order_amount']);
// Return success response
echo json_encode([
'success' => true,
'transactionId' => $txnId,
'amount' => $amount,
'currency' => $currency,
'status' => $captureResult['status'],
'payerEmail' => $payerEmail,
'payerName' => trim($payerName)
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}