Complete port of all 163 API endpoints from Lucee/CFML to PHP 8.3. Shared helpers in api/helpers.php (DB, auth, request/response, security). PDO prepared statements throughout. Same JSON response shapes as CFML.
119 lines
4.5 KiB
PHP
119 lines
4.5 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../helpers.php';
|
|
runAuth();
|
|
|
|
/**
|
|
* Submit Cash Order
|
|
* POST: { OrderID: int, CashAmount: float, Tip?: float }
|
|
*/
|
|
|
|
$data = readJsonBody();
|
|
$OrderID = (int) ($data['OrderID'] ?? 0);
|
|
$CashAmount = (float) ($data['CashAmount'] ?? 0);
|
|
$Tip = (float) ($data['Tip'] ?? 0);
|
|
|
|
if ($OrderID <= 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'missing_orderid', 'MESSAGE' => 'OrderID is required.']);
|
|
}
|
|
|
|
if ($CashAmount <= 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'missing_amount', 'MESSAGE' => 'CashAmount is required.']);
|
|
}
|
|
|
|
try {
|
|
$qOrder = queryOne("
|
|
SELECT o.ID, o.StatusID, o.UserID, o.BusinessID, o.ServicePointID, o.PaymentID,
|
|
o.DeliveryFee, o.OrderTypeID,
|
|
b.PayfritFee, b.TaxRate
|
|
FROM Orders o
|
|
INNER JOIN Businesses b ON b.ID = o.BusinessID
|
|
WHERE o.ID = ? LIMIT 1
|
|
", [$OrderID]);
|
|
|
|
if (!$qOrder) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'not_found', 'MESSAGE' => 'Order not found.']);
|
|
}
|
|
|
|
if ((int) $qOrder['StatusID'] !== 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'bad_state', 'MESSAGE' => 'Order is not in cart state.']);
|
|
}
|
|
|
|
// Calculate platform fee
|
|
$feeRate = (is_numeric($qOrder['PayfritFee']) && (float) $qOrder['PayfritFee'] > 0) ? (float) $qOrder['PayfritFee'] : 0;
|
|
|
|
$qSubtotal = queryOne(
|
|
"SELECT COALESCE(SUM(Price * Quantity), 0) AS Subtotal FROM OrderLineItems WHERE OrderID = ? AND IsDeleted = 0",
|
|
[$OrderID]
|
|
);
|
|
$subtotal = (float) ($qSubtotal['Subtotal'] ?? 0);
|
|
$platformFee = $subtotal * $feeRate;
|
|
|
|
// Calculate full order total
|
|
$taxRate = (is_numeric($qOrder['TaxRate']) && (float) $qOrder['TaxRate'] > 0) ? (float) $qOrder['TaxRate'] : 0;
|
|
$taxAmount = $subtotal * $taxRate;
|
|
$deliveryFee = ((int) $qOrder['OrderTypeID'] === 3) ? (float) $qOrder['DeliveryFee'] : 0;
|
|
$orderTotal = round(($subtotal + $taxAmount + $platformFee + $Tip + $deliveryFee) * 100) / 100;
|
|
|
|
// Auto-apply user balance
|
|
$balanceApplied = 0;
|
|
$cashNeeded = $orderTotal;
|
|
$userID = (int) $qOrder['UserID'];
|
|
|
|
if ($userID > 0) {
|
|
$qBalance = queryOne("SELECT Balance FROM Users WHERE ID = ?", [$userID]);
|
|
$userBalance = (float) ($qBalance['Balance'] ?? 0);
|
|
if ($userBalance > 0) {
|
|
$balanceToApply = round(min($userBalance, $orderTotal) * 100) / 100;
|
|
// Atomic deduct
|
|
$stmt = queryTimed(
|
|
"UPDATE Users SET Balance = Balance - ? WHERE ID = ? AND Balance >= ?",
|
|
[$balanceToApply, $userID, $balanceToApply]
|
|
);
|
|
// queryTimed returns PDOStatement for UPDATE; check rowCount
|
|
if ($stmt instanceof \PDOStatement && $stmt->rowCount() > 0) {
|
|
$balanceApplied = $balanceToApply;
|
|
$cashNeeded = max(0, $orderTotal - $balanceApplied);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create Payment record
|
|
$CashAmountCents = (int) round($cashNeeded * 100);
|
|
queryTimed(
|
|
"INSERT INTO Payments (PaymentPaidInCash, PaymentFromPayfritBalance, PaymentFromCreditCard, PaymentSentByUserID, PaymentReceivedByUserID, PaymentOrderID, PaymentPayfritsCut, PaymentAddedOn)
|
|
VALUES (?, ?, 0, ?, 0, ?, ?, NOW())",
|
|
[$cashNeeded, $balanceApplied, $userID, $OrderID, $platformFee]
|
|
);
|
|
$PaymentID = lastInsertId();
|
|
|
|
// Update order
|
|
$fullyPaidByBalance = ($cashNeeded <= 0);
|
|
$paymentStatus = $fullyPaidByBalance ? 'paid' : 'pending';
|
|
|
|
queryTimed("
|
|
UPDATE Orders
|
|
SET StatusID = 1, PaymentID = ?, PaymentStatus = ?, PlatformFee = ?,
|
|
TipAmount = ?, BalanceApplied = ?, SubmittedOn = NOW(), LastEditedOn = NOW(),
|
|
PaymentCompletedOn = CASE WHEN ? = 1 THEN NOW() ELSE PaymentCompletedOn END
|
|
WHERE ID = ?
|
|
", [$PaymentID, $paymentStatus, $platformFee, $Tip, $balanceApplied, $fullyPaidByBalance ? 1 : 0, $OrderID]);
|
|
|
|
$response = [
|
|
'OK' => true,
|
|
'OrderID' => $OrderID,
|
|
'PaymentID' => (int) $PaymentID,
|
|
'CashAmountCents' => $CashAmountCents,
|
|
'MESSAGE' => 'Order submitted with cash payment.',
|
|
];
|
|
|
|
if ($balanceApplied > 0) {
|
|
$response['BalanceApplied'] = round($balanceApplied * 100) / 100;
|
|
$response['BalanceAppliedCents'] = (int) round($balanceApplied * 100);
|
|
$response['FullyPaidByBalance'] = ($cashNeeded <= 0);
|
|
}
|
|
|
|
jsonResponse($response);
|
|
|
|
} catch (Exception $e) {
|
|
jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => 'Failed to submit cash order', 'DETAIL' => $e->getMessage()]);
|
|
}
|