Files
2026-05-16 23:00:37 -05:00

288 lines
11 KiB
PHP

<?php
/**
* Tom's Java Jive - Payment Page (Stripe)
* Supports both PaymentIntent (card element) and Checkout Session (redirect) flows
*/
$pageTitle = "Payment - Tom's Java Jive";
require_once __DIR__ . '/includes/functions.php';
require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/includes/stripe.php';
$orderId = $_GET['order'] ?? $_SESSION['pending_order_id'] ?? '';
$cancelled = isset($_GET['cancelled']);
if (empty($orderId)) {
redirect('/cart.php');
}
// Get order
$order = db()->fetch(
"SELECT * FROM orders WHERE order_id = :id",
['id' => $orderId]
);
if (!$order) {
redirect('/cart.php');
}
// If already paid, redirect to confirmation
if ($order['payment_status'] === 'paid') {
clearCart();
redirect('/order-confirmation.php?order=' . $orderId);
}
$stripePublishableKey = STRIPE_PUBLISHABLE_KEY;
$stripeConfigured = isStripeConfigured();
$total = $order['total'];
require_once __DIR__ . '/includes/header.php';
?>
<section class="section" style="padding-top: 2rem;">
<div class="container">
<div class="card" style="max-width: 500px; margin: 0 auto;">
<div class="card-header">
<h2 style="margin: 0;">Complete Payment</h2>
</div>
<div class="card-body">
<?php if ($cancelled): ?>
<div class="alert alert-warning mb-2">
<i class="fas fa-exclamation-triangle"></i> Payment was cancelled. Please try again.
</div>
<?php endif; ?>
<div style="background: var(--color-background); padding: 1rem; border-radius: var(--radius-md); margin-bottom: 1.5rem;">
<div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem;">
<span>Order #<?= htmlspecialchars($order['order_number']) ?></span>
<strong><?= formatCurrency($total) ?></strong>
</div>
<p class="text-muted mb-0" style="font-size: 0.875rem;">
<?= htmlspecialchars($order['customer_email']) ?>
</p>
</div>
<?php if (!$stripeConfigured): ?>
<!-- Demo Mode - No Stripe Keys -->
<div class="alert alert-info mb-2">
<i class="fas fa-info-circle"></i> <strong>Demo Mode:</strong> Stripe is not configured. Click below to simulate a successful payment.
</div>
<form id="demo-payment-form">
<button type="submit" id="demo-submit" class="btn btn-primary btn-lg btn-block">
<span id="demo-text">Complete Demo Payment <?= formatCurrency($total) ?></span>
<span id="demo-spinner" style="display: none;">
<span class="loading"></span> Processing...
</span>
</button>
</form>
<?php else: ?>
<!-- Stripe Payment Options -->
<div class="payment-options mb-2">
<button type="button" id="checkout-btn" class="btn btn-primary btn-lg btn-block mb-1">
<i class="fas fa-credit-card"></i> Pay with Stripe Checkout
</button>
<p class="text-muted text-center" style="font-size: 0.875rem;">or enter card details below</p>
</div>
<form id="payment-form">
<div class="form-group">
<label class="form-label">Card Details</label>
<div id="card-element" style="padding: 0.75rem; border: 1px solid var(--color-border); border-radius: var(--radius-md);"></div>
<div id="card-errors" class="form-error" style="margin-top: 0.5rem;"></div>
</div>
<button type="submit" id="submit-button" class="btn btn-secondary btn-lg btn-block">
<span id="button-text">Pay <?= formatCurrency($total) ?></span>
<span id="spinner" style="display: none;">
<span class="loading"></span> Processing...
</span>
</button>
</form>
<?php endif; ?>
<div id="payment-message" style="display: none; margin-top: 1rem;"></div>
<p class="text-muted text-center mt-2" style="font-size: 0.75rem;">
<i class="fas fa-lock"></i> Your payment is secure and encrypted
</p>
</div>
</div>
</div>
</section>
<script>
const orderId = '<?= $orderId ?>';
const stripeConfigured = <?= $stripeConfigured ? 'true' : 'false' ?>;
const messageEl = document.getElementById('payment-message');
function showMessage(message, type = 'info') {
messageEl.style.display = 'block';
messageEl.className = `alert alert-${type === 'error' ? 'error' : 'success'}`;
messageEl.innerHTML = `<i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'check-circle'}"></i> ${message}`;
}
<?php if (!$stripeConfigured): ?>
// Demo mode payment
const demoForm = document.getElementById('demo-payment-form');
demoForm.addEventListener('submit', async function(e) {
e.preventDefault();
const btn = document.getElementById('demo-submit');
const text = document.getElementById('demo-text');
const spinner = document.getElementById('demo-spinner');
btn.disabled = true;
text.style.display = 'none';
spinner.style.display = 'inline';
try {
const response = await fetch('/api/create-payment-intent.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order_id: orderId })
});
const data = await response.json();
if (data.demo_mode && data.redirect) {
showMessage('Payment simulated successfully! Redirecting...', 'success');
setTimeout(() => window.location.href = data.redirect, 1000);
} else if (data.error) {
showMessage(data.error, 'error');
btn.disabled = false;
text.style.display = 'inline';
spinner.style.display = 'none';
}
} catch (err) {
showMessage('Payment failed. Please try again.', 'error');
btn.disabled = false;
text.style.display = 'inline';
spinner.style.display = 'none';
}
});
<?php else: ?>
// Stripe initialized
const stripe = Stripe('<?= $stripePublishableKey ?>');
const elements = stripe.elements();
const cardElement = elements.create('card', {
style: {
base: {
fontSize: '16px',
color: '#1B1B1B',
'::placeholder': { color: '#9CA3AF' }
}
}
});
cardElement.mount('#card-element');
// Handle validation errors
cardElement.on('change', function(event) {
const displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Stripe Checkout button (redirect to hosted page)
document.getElementById('checkout-btn').addEventListener('click', async function() {
this.disabled = true;
this.innerHTML = '<span class="loading"></span> Redirecting to Stripe...';
try {
const response = await fetch('/api/create-checkout-session.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_id: orderId,
origin_url: window.location.origin
})
});
const data = await response.json();
if (data.demo_mode && data.redirect) {
window.location.href = data.redirect;
} else if (data.url) {
window.location.href = data.url;
} else if (data.error) {
showMessage(data.error, 'error');
this.disabled = false;
this.innerHTML = '<i class="fas fa-credit-card"></i> Pay with Stripe Checkout';
}
} catch (err) {
showMessage('Failed to create checkout session.', 'error');
this.disabled = false;
this.innerHTML = '<i class="fas fa-credit-card"></i> Pay with Stripe Checkout';
}
});
// PaymentIntent form (inline card element)
const form = document.getElementById('payment-form');
const submitButton = document.getElementById('submit-button');
const buttonText = document.getElementById('button-text');
const spinner = document.getElementById('spinner');
form.addEventListener('submit', async function(e) {
e.preventDefault();
submitButton.disabled = true;
buttonText.style.display = 'none';
spinner.style.display = 'inline';
try {
const response = await fetch('/api/create-payment-intent.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order_id: orderId })
});
const data = await response.json();
if (data.demo_mode && data.redirect) {
window.location.href = data.redirect;
return;
}
if (data.error) {
showMessage(data.error, 'error');
submitButton.disabled = false;
buttonText.style.display = 'inline';
spinner.style.display = 'none';
return;
}
// Confirm payment with Stripe
const { error, paymentIntent } = await stripe.confirmCardPayment(data.client_secret, {
payment_method: {
card: cardElement,
billing_details: {
name: '<?= addslashes($order['customer_name']) ?>',
email: '<?= addslashes($order['customer_email']) ?>'
}
}
});
if (error) {
showMessage(error.message, 'error');
} else if (paymentIntent.status === 'succeeded') {
showMessage('Payment successful! Redirecting...', 'success');
setTimeout(() => window.location.href = '/order-confirmation.php?order=' + orderId, 1000);
}
} catch (err) {
showMessage('Payment failed. Please try again.', 'error');
console.error(err);
} finally {
submitButton.disabled = false;
buttonText.style.display = 'inline';
spinner.style.display = 'none';
}
});
<?php endif; ?>
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>