diff --git a/account/rewards.php b/account/rewards.php
index cb7f900..e22fd91 100644
--- a/account/rewards.php
+++ b/account/rewards.php
@@ -37,6 +37,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['redeem_points'])) {
redirect('/account/rewards.php');
}
+$extraHead = '';
require_once __DIR__ . '/../includes/header.php';
require_once __DIR__ . '/includes/sidebar.php';
?>
diff --git a/api/apply-wallet-credit.php b/api/apply-wallet-credit.php
new file mode 100644
index 0000000..aa1073e
--- /dev/null
+++ b/api/apply-wallet-credit.php
@@ -0,0 +1,58 @@
+ 'Method not allowed'], 405);
+}
+
+if (!CustomerAuth::isLoggedIn()) {
+ jsonResponse(['error' => 'Please log in to use wallet balance or a gift card'], 401);
+}
+
+$customer = CustomerAuth::getFullUser();
+$input = json_decode(file_get_contents('php://input'), true);
+
+$orderTotal = (float) ($input['order_total'] ?? 0);
+if ($orderTotal <= 0) {
+ jsonResponse(['error' => 'Invalid order total'], 400);
+}
+
+$giftCardCode = trim($input['gift_card_code'] ?? '');
+
+if (!empty($giftCardCode)) {
+ $redeemResult = loyalty()->redeemGiftCardToWallet($customer['customer_id'], $giftCardCode);
+ if (!$redeemResult['success']) {
+ jsonResponse(['error' => $redeemResult['error']], 400);
+ }
+ $walletBalance = $redeemResult['new_balance'];
+} else {
+ $fresh = db()->fetch("SELECT wallet_balance FROM customers WHERE customer_id = :id", ['id' => $customer['customer_id']]);
+ $walletBalance = (float) ($fresh['wallet_balance'] ?? 0);
+}
+
+$requested = isset($input['amount']) ? (float) $input['amount'] : $walletBalance;
+$applyAmount = max(0, round(min($requested, $walletBalance, $orderTotal), 2));
+
+jsonResponse([
+ 'success' => true,
+ 'apply_amount' => $applyAmount,
+ 'wallet_balance' => $walletBalance,
+ 'new_total' => round($orderTotal - $applyAmount, 2)
+]);
diff --git a/api/redeem-gift-card.php b/api/redeem-gift-card.php
index c3e6854..0e37830 100644
--- a/api/redeem-gift-card.php
+++ b/api/redeem-gift-card.php
@@ -7,6 +7,7 @@ header('Content-Type: application/json');
require_once __DIR__ . '/../includes/functions.php';
require_once __DIR__ . '/../includes/auth.php';
+require_once __DIR__ . '/../includes/loyalty.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
jsonResponse(['error' => 'Method not allowed'], 405);
@@ -19,79 +20,15 @@ if (!CustomerAuth::isLoggedIn()) {
$customer = CustomerAuth::getFullUser();
$input = json_decode(file_get_contents('php://input'), true);
-$code = strtoupper(str_replace(['-', ' '], '', trim($input['code'] ?? '')));
+$result = loyalty()->redeemGiftCardToWallet($customer['customer_id'], $input['code'] ?? '');
-if (empty($code) || strlen($code) < 8) {
- jsonResponse(['error' => 'Invalid gift card code'], 400);
+if (!$result['success']) {
+ jsonResponse(['error' => $result['error']], 400);
}
-// Find gift card
-$giftCard = db()->fetch(
- "SELECT * FROM gift_cards WHERE code = :code AND is_active = 1",
- ['code' => $code]
-);
-
-if (!$giftCard) {
- jsonResponse(['error' => 'Gift card not found or already used'], 404);
-}
-
-if ($giftCard['current_balance'] <= 0) {
- jsonResponse(['error' => 'This gift card has no remaining balance'], 400);
-}
-
-if ($giftCard['expires_at'] && strtotime($giftCard['expires_at']) < time()) {
- jsonResponse(['error' => 'This gift card has expired'], 400);
-}
-
-$amount = $giftCard['current_balance'];
-
-try {
- // Start transaction
- db()->query("START TRANSACTION");
-
- // Update gift card balance to 0
- db()->query(
- "UPDATE gift_cards SET current_balance = 0, is_active = 0, updated_at = NOW() WHERE gift_card_id = :id",
- ['id' => $giftCard['gift_card_id']]
- );
-
- // Log gift card transaction
- db()->insert('gift_card_transactions', [
- 'gift_card_id' => $giftCard['gift_card_id'],
- 'amount' => -$amount,
- 'balance_after' => 0,
- 'type' => 'redeem',
- 'description' => 'Redeemed by customer: ' . $customer['email']
- ]);
-
- // Add to customer wallet
- $newWalletBalance = ($customer['wallet_balance'] ?? 0) + $amount;
-
- db()->query(
- "UPDATE customers SET wallet_balance = :balance, updated_at = NOW() WHERE customer_id = :id",
- ['balance' => $newWalletBalance, 'id' => $customer['customer_id']]
- );
-
- // Log wallet transaction
- db()->insert('wallet_transactions', [
- 'transaction_id' => generateId('wt_'),
- 'customer_id' => $customer['customer_id'],
- 'amount' => $amount,
- 'balance_after' => $newWalletBalance,
- 'type' => 'gift_card',
- 'description' => 'Gift card redeemed: ' . $code
- ]);
-
- db()->query("COMMIT");
-
- jsonResponse([
- 'success' => true,
- 'amount' => $amount,
- 'new_balance' => $newWalletBalance,
- 'message' => formatCurrency($amount) . ' has been added to your wallet!'
- ]);
-
-} catch (Exception $e) {
- db()->query("ROLLBACK");
- jsonResponse(['error' => 'Failed to redeem gift card. Please try again.'], 500);
-}
+jsonResponse([
+ 'success' => true,
+ 'amount' => $result['amount'],
+ 'new_balance' => $result['new_balance'],
+ 'message' => formatCurrency($result['amount']) . ' has been added to your wallet!'
+]);
diff --git a/checkout.php b/checkout.php
index 5673ed0..e10f0b6 100644
--- a/checkout.php
+++ b/checkout.php
@@ -6,6 +6,7 @@
$pageTitle = "Checkout - Tom's Java Jive";
require_once __DIR__ . '/includes/functions.php';
require_once __DIR__ . '/includes/auth.php';
+require_once __DIR__ . '/includes/loyalty.php';
$cart = getCart();
if (empty($cart)) {
@@ -22,7 +23,7 @@ foreach ($cart as $productId => $quantity) {
"SELECT product_id, name, price, sale_price, stock, images FROM products WHERE product_id = :id AND is_active = 1",
['id' => $productId]
);
-
+
if ($product) {
$images = json_decode($product['images'] ?? '[]', true);
$product['image'] = !empty($images) ? $images[0] : '/assets/images/placeholder-product.svg';
@@ -50,10 +51,8 @@ if ($shippingSettings['flat_rate_enabled'] ?? true) {
}
}
-$total = $subtotal + $shippingCost;
-
-// Get Stripe publishable key
-$stripeKey = STRIPE_PUBLISHABLE_KEY;
+$preDiscountTotal = $subtotal + $shippingCost;
+$processor = defined('PAYMENT_PROCESSOR') ? PAYMENT_PROCESSOR : 'stripe';
$errors = [];
@@ -67,19 +66,31 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$city = trim($_POST['city'] ?? '');
$state = trim($_POST['state'] ?? '');
$zip = trim($_POST['zip'] ?? '');
-
+
if (empty($email)) $errors['email'] = 'Email is required';
if (empty($name)) $errors['name'] = 'Name is required';
if (empty($address)) $errors['address'] = 'Address is required';
if (empty($city)) $errors['city'] = 'City is required';
if (empty($state)) $errors['state'] = 'State is required';
if (empty($zip)) $errors['zip'] = 'ZIP code is required';
-
+
+ // Re-validate any requested wallet credit server-side against the
+ // customer's current balance - never trust the client-submitted amount.
+ $walletAmountUsed = 0;
+ if ($customer && !empty($_POST['wallet_amount_used'])) {
+ $requestedWallet = (float) $_POST['wallet_amount_used'];
+ $freshCustomer = db()->fetch("SELECT wallet_balance FROM customers WHERE customer_id = :id", ['id' => $customer['customer_id']]);
+ $currentBalance = (float) ($freshCustomer['wallet_balance'] ?? 0);
+ $walletAmountUsed = max(0, round(min($requestedWallet, $currentBalance, $preDiscountTotal), 2));
+ }
+
+ $total = round($preDiscountTotal - $walletAmountUsed, 2);
+
if (empty($errors)) {
// Create order
$orderId = generateId('ord_');
$orderNumber = generateOrderNumber();
-
+
// Get or create customer
$customerId = null;
if ($customer) {
@@ -87,7 +98,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} else {
$customerId = CustomerAuth::createGuest($email, $name, $phone);
}
-
+
// Prepare order items
$orderItems = [];
foreach ($cartItems as $item) {
@@ -99,7 +110,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'total' => $item['total']
];
}
-
+
// Insert order
db()->insert('orders', [
'order_id' => $orderId,
@@ -111,6 +122,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'items' => json_encode($orderItems),
'subtotal' => $subtotal,
'shipping_cost' => $shippingCost,
+ 'wallet_amount_used' => $walletAmountUsed,
'total' => $total,
'shipping_address' => json_encode([
'address' => $address,
@@ -120,11 +132,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'country' => 'USA'
]),
'shipping_method' => 'standard',
- 'payment_method' => 'stripe',
+ 'payment_method' => $processor,
'payment_status' => 'pending',
'order_status' => 'pending'
]);
-
+
// Insert order items for reporting
foreach ($orderItems as $item) {
db()->insert('order_items', [
@@ -136,7 +148,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
'total' => $item['total']
]);
}
-
+
// Reduce stock
foreach ($cartItems as $item) {
db()->query(
@@ -144,10 +156,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
['qty' => $item['quantity'], 'id' => $item['product_id']]
);
}
-
+
// Store order ID for payment
$_SESSION['pending_order_id'] = $orderId;
-
+
// Redirect to payment page
redirect('/payment.php?order=' . $orderId);
}
@@ -165,10 +177,11 @@ require_once __DIR__ . '/includes/header.php';
Checkout
-
+