![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/public_html/ |
<?php
session_start();
// Check if user is logged in
$is_logged_in = isset($_SESSION['user_id']);
// If not logged in, show checkout with login/register options instead of redirecting
if (!$is_logged_in) {
// Allow guest checkout or show login/register options
$guest_checkout = true;
} else {
$guest_checkout = false;
}
// Check if cart has items (either credit cart, music cart, or ticket cart)
$has_credits = !empty($_SESSION['credit_cart']);
$has_music = !empty($_SESSION['cart']);
$has_tickets = !empty($_SESSION['ticket_cart']);
if (!$has_credits && !$has_music && !$has_tickets) {
header('Location: /cart.php');
exit;
}
require_once 'config/database.php';
require_once 'includes/site_settings_helper.php';
require_once 'includes/translations.php';
require_once 'utils/subscription_helpers.php';
$ticket_purchase_limit = 10;
// Only try to get user if logged in
$user = null;
$has_active_subscription = false;
if (isset($_SESSION['user_id'])) {
$user = getUserById($_SESSION['user_id']);
// Check if user has active subscription (required for credit purchases)
$has_active_subscription = hasActiveSubscription($_SESSION['user_id']) !== false;
}
// IMPORTANT: Credit purchases require active subscription (minimum Essential $5/month)
// If user has credits in cart but no active subscription, redirect to subscription page
if ($has_credits && $is_logged_in && !$has_active_subscription) {
$_SESSION['error_message'] = t('checkout.subscription_required_message');
header('Location: /account_settings.php?tab=subscription&require_subscription=1');
exit;
}
// Check cart contents and separate items
$creditItems = $_SESSION['credit_cart'] ?? [];
$musicItems = $_SESSION['cart'] ?? [];
$ticketItems = $_SESSION['ticket_cart'] ?? [];
// Always merge all arrays, regardless of which is present
$cart = array_merge($creditItems, $musicItems, $ticketItems);
// Use merged cart items for display
$allItems = $cart;
// Calculate totals
$total = 0;
$totalCredits = 0;
$totalTracks = 0;
$totalTickets = 0;
foreach ($cart as $item) {
$total += ($item['price'] ?? 0) * ($item['quantity'] ?? 1);
if (isset($item['credits'])) {
$totalCredits += $item['credits'] * $item['quantity'];
}
if (isset($item['type']) && $item['type'] === 'track') {
$totalTracks += $item['quantity'] ?? 1;
}
if (isset($item['type']) && $item['type'] === 'ticket') {
$totalTickets += $item['quantity'] ?? 1;
}
}
// Set page variables for header
$page_title = t('checkout.page_title');
$page_description = t('checkout.page_description');
$current_page = 'checkout';
include 'includes/header.php';
if (isset($_GET['success'])) {
// Send confirmation email if payment was successful
if (isset($_SESSION['user_id']) && isset($_SESSION['last_payment_data'])) {
require_once 'config/email.php';
$user = getUserById($_SESSION['user_id']);
$payment_data = $_SESSION['last_payment_data'];
if ($user && $payment_data) {
// Generate and send confirmation email
$email_data = generateOrderConfirmationEmail(
$user['name'],
$payment_data['order_details'],
$payment_data['billing_address']
);
$email_sent = sendEmail(
$payment_data['billing_address']['billing_email'],
$user['name'],
$email_data['subject'],
$email_data['html'],
$email_data['text']
);
if ($email_sent) {
error_log("Confirmation email sent successfully to: " . $payment_data['billing_address']['billing_email']);
} else {
error_log("Failed to send confirmation email to: " . $payment_data['billing_address']['billing_email']);
}
// Clear the payment data from session
unset($_SESSION['last_payment_data']);
}
// Clear all carts after successful payment
if (isset($_SESSION['credit_cart'])) {
$_SESSION['credit_cart'] = [];
}
if (isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
}
if (isset($_SESSION['ticket_cart'])) {
$_SESSION['ticket_cart'] = [];
}
}
// Determine purchase type for redirect
$purchase_type = 'mixed';
$cart_summary = $_SESSION['last_payment_data']['order_details']['cart_summary'] ?? [];
$has_tracks = false;
$has_credits = false;
$has_tickets = false;
foreach ($cart_summary as $item) {
if (isset($item['type'])) {
if ($item['type'] === 'track') $has_tracks = true;
if ($item['type'] === 'credit') $has_credits = true;
if ($item['type'] === 'ticket') $has_tickets = true;
}
}
// Determine single purchase type
if ($has_tracks && !$has_credits && !$has_tickets) {
$purchase_type = 'tracks';
} elseif ($has_credits && !$has_tracks && !$has_tickets) {
$purchase_type = 'credits';
} elseif ($has_tickets && !$has_tracks && !$has_credits) {
$purchase_type = 'tickets';
}
// Get payment intent ID if available
$payment_intent_id = $_SESSION['last_payment_data']['order_details']['payment_intent_id'] ?? null;
// Redirect to order success page with purchase type
$redirect_url = '/order_success.php?type=' . urlencode($purchase_type);
if ($payment_intent_id) {
$redirect_url .= '&payment_intent=' . urlencode($payment_intent_id);
}
$is_free_order = isset($_GET['free']) && $_GET['free'] == '1';
if ($is_free_order) {
echo '<script>document.addEventListener("DOMContentLoaded", function() { showSuccessMessage("' . addslashes(t('checkout.order_processed')) . '"); setTimeout(() => { window.location.href = "/order_success.php?type=' . urlencode($purchase_type) . '"; }, 2000); });</script>';
} else {
echo '<script>document.addEventListener("DOMContentLoaded", function() { showSuccessMessage("' . addslashes(t('checkout.payment_successful')) . '"); setTimeout(() => { window.location.href = "' . $redirect_url . '"; }, 2000); });</script>';
}
}
?>
<style>
/* Premium Checkout Page Styles */
.checkout-container {
max-width: 1400px;
margin: 0 auto;
padding: 3rem 2rem;
position: relative;
min-height: 100vh;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%);
box-sizing: border-box;
width: 100%;
}
.checkout-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
pointer-events: none;
}
.checkout-header {
text-align: center;
margin-bottom: 4rem;
position: relative;
z-index: 2;
}
.checkout-title {
font-size: 4rem;
font-weight: 800;
margin-bottom: 1.5rem;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.2;
}
.checkout-subtitle {
color: #a0aec0;
font-size: 1.8rem;
font-weight: 400;
max-width: 600px;
margin: 0 auto;
line-height: 1.5;
}
.checkout-content {
display: grid;
grid-template-columns: 1fr 500px;
gap: 4rem;
align-items: start;
position: relative;
z-index: 2;
}
@media (max-width: 1200px) {
.checkout-content {
grid-template-columns: 1fr;
gap: 3rem;
}
}
/* Order Summary Styles */
.order-summary {
background: linear-gradient(135deg, rgba(20, 20, 20, 0.95) 0%, rgba(30, 30, 30, 0.95) 100%);
border: 2px solid;
border-image: linear-gradient(135deg, #667eea, #764ba2) 1;
border-radius: 24px;
padding: 3rem;
backdrop-filter: blur(30px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 100%;
}
.order-summary::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
pointer-events: none;
border-radius: 24px;
}
.summary-title {
font-size: 2.4rem;
font-weight: 700;
margin-bottom: 2.5rem;
display: flex;
align-items: center;
gap: 1rem;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
position: relative;
z-index: 2;
}
.summary-title i {
font-size: 2.2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.cart-items {
margin-bottom: 3rem;
position: relative;
z-index: 2;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 2rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(102, 126, 234, 0.1);
border-radius: 16px;
margin-bottom: 1.5rem;
transition: all 0.3s ease;
gap: 1.5rem;
}
.cart-item:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(102, 126, 234, 0.2);
transform: translateY(-2px);
}
.cart-item:last-child {
margin-bottom: 0;
}
.item-details h4 {
color: white;
margin-bottom: 0.5rem;
font-size: 1.8rem;
font-weight: 600;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.item-details p {
color: #a0aec0;
font-size: 1.4rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.item-details p i {
color: #667eea;
font-size: 1.2rem;
}
.item-controls {
display: flex;
align-items: center;
gap: 1.5rem;
}
.quantity-controls {
display: flex;
align-items: center;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(102, 126, 234, 0.2);
border-radius: 12px;
padding: 0.5rem;
}
.qty-btn {
background: rgba(102, 126, 234, 0.2);
border: 1px solid rgba(102, 126, 234, 0.3);
color: #667eea;
width: 36px;
height: 36px;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
font-size: 1.2rem;
}
.qty-btn:hover {
background: rgba(102, 126, 234, 0.3);
border-color: #667eea;
transform: scale(1.1);
}
.qty-btn:active {
transform: scale(0.95);
}
.quantity-display {
color: white;
font-weight: 600;
font-size: 1.4rem;
min-width: 30px;
text-align: center;
}
.quantity-note,
.single-quantity-note {
font-size: 0.85rem;
color: #94a3b8;
white-space: nowrap;
}
.item-price {
color: white;
font-weight: 700;
font-size: 1.8rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.remove-item-btn {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #a0aec0;
width: 44px;
height: 44px;
border-radius: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
font-size: 1.2rem;
flex-shrink: 0;
}
.remove-item-btn i {
color: currentColor;
}
.remove-item-btn:hover {
background: rgba(102, 126, 234, 0.15);
border-color: rgba(102, 126, 234, 0.45);
color: white;
transform: translateY(-1px);
}
.total-section {
border-top: 2px solid rgba(102, 126, 234, 0.2);
padding-top: 2rem;
margin-top: 2rem;
position: relative;
z-index: 2;
}
.total-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
font-size: 1.6rem;
}
.total-row span:first-child {
color: #a0aec0;
font-weight: 500;
}
.total-row span:last-child {
color: white;
font-weight: 600;
}
.total-row.final {
font-size: 2.2rem;
font-weight: 700;
color: white;
border-top: 2px solid rgba(102, 126, 234, 0.2);
padding-top: 1.5rem;
margin-top: 1.5rem;
}
.total-row.final span:last-child {
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Coupon Section Styles */
.coupon-section {
margin: 2rem 0;
padding: 2rem;
background: rgba(102, 126, 234, 0.1);
border-radius: 16px;
border: 1px solid rgba(102, 126, 234, 0.3);
}
.coupon-input {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.coupon-input-field {
flex: 1;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 1.2rem 1.5rem;
color: white;
font-size: 1.4rem;
transition: all 0.3s ease;
}
.coupon-input-field::placeholder {
color: #a0aec0;
}
.coupon-input-field:focus {
outline: none;
border-color: #667eea;
background: rgba(255, 255, 255, 0.15);
}
.coupon-message {
font-size: 1.3rem;
font-weight: 600;
min-height: 2rem;
}
.coupon-message.success {
color: #48bb78;
}
.coupon-message.error {
color: #f56565;
}
/* Authentication Section Styles */
.checkout-auth-section {
background: linear-gradient(135deg, rgba(20, 20, 20, 0.95) 0%, rgba(30, 30, 30, 0.95) 100%);
border: 2px solid;
border-image: linear-gradient(135deg, #667eea, #764ba2) 1;
border-radius: 24px;
padding: 3rem;
backdrop-filter: blur(30px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
margin-bottom: 2rem;
box-sizing: border-box;
width: 100%;
max-width: 100%;
}
.auth-title {
font-size: 2.4rem;
font-weight: 700;
margin-bottom: 2.5rem;
display: flex;
align-items: center;
gap: 1rem;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.auth-tabs {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 1rem;
}
.auth-tab {
background: transparent;
border: 2px solid rgba(102, 126, 234, 0.3);
color: #a0aec0;
padding: 1rem 2rem;
border-radius: 12px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.8rem;
}
.auth-tab:hover {
border-color: #667eea;
color: #667eea;
transform: translateY(-2px);
}
.auth-tab.active {
background: linear-gradient(135deg, #667eea, #764ba2);
border-color: #667eea;
color: white;
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.auth-content {
display: none;
}
.auth-content.active {
display: block;
}
.auth-form {
margin-bottom: 2rem;
}
.auth-divider {
text-align: center;
margin: 2rem 0;
position: relative;
}
.auth-divider::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: rgba(255, 255, 255, 0.2);
}
.auth-divider span {
background: #1a1a1a;
padding: 0 2rem;
color: #a0aec0;
font-size: 1.4rem;
}
.social-login {
display: flex;
flex-direction: column;
gap: 1rem;
}
.btn-social {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 1.2rem 2rem;
border-radius: 12px;
font-size: 1.4rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.btn-social:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.btn-google:hover {
background: linear-gradient(135deg, #ea4335, #fbbc05);
}
.btn-facebook:hover {
background: linear-gradient(135deg, #1877f2, #42a5f5);
}
.guest-info {
text-align: center;
margin-bottom: 2rem;
padding: 2rem;
background: rgba(102, 126, 234, 0.1);
border-radius: 16px;
border: 1px solid rgba(102, 126, 234, 0.3);
}
.guest-info i {
font-size: 4rem;
color: #667eea;
margin-bottom: 1rem;
}
.guest-info h3 {
font-size: 2rem;
color: white;
margin-bottom: 1rem;
}
.guest-info p {
color: #a0aec0;
font-size: 1.4rem;
margin-bottom: 1.5rem;
line-height: 1.6;
}
.guest-info ul {
list-style: none;
padding: 0;
text-align: left;
max-width: 300px;
margin: 0 auto;
}
.guest-info li {
color: #a0aec0;
font-size: 1.3rem;
margin-bottom: 0.5rem;
padding-left: 1.5rem;
position: relative;
}
.guest-info li::before {
content: '✓';
position: absolute;
left: 0;
color: #48bb78;
font-weight: bold;
}
/* Billing Address Styles */
.billing-address {
background: linear-gradient(135deg, rgba(20, 20, 20, 0.95) 0%, rgba(30, 30, 30, 0.95) 100%);
border: 2px solid;
border-image: linear-gradient(135deg, #667eea, #764ba2) 1;
border-radius: 24px;
padding: 3rem;
backdrop-filter: blur(30px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
position: relative;
margin-bottom: 2rem;
box-sizing: border-box;
width: 100%;
max-width: 100%;
}
.billing-address::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
pointer-events: none;
border-radius: 24px;
}
.billing-title {
font-size: 2.4rem;
font-weight: 700;
margin-bottom: 2.5rem;
display: flex;
align-items: center;
gap: 1rem;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
position: relative;
z-index: 2;
}
.billing-title i {
font-size: 2.2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.address-form {
position: relative;
z-index: 2;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: #ffffff;
font-weight: 600;
font-size: 1rem;
}
.form-group input,
.form-group select {
width: 100%;
padding: 1rem 1.2rem;
background: rgba(255, 255, 255, 0.05);
border: 2px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: #ffffff;
font-size: 1rem;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
box-sizing: border-box;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
background: rgba(255, 255, 255, 0.08);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.form-group input::placeholder {
color: #a0aec0;
}
.form-group select option {
background: #1a1a1a;
color: #ffffff;
}
/* Mobile-specific improvements */
@media (max-width: 768px) {
/* Ensure all inputs are touch-friendly */
input[type="text"],
input[type="email"],
input[type="password"],
select,
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
border-radius: 12px;
}
/* Fix select dropdown on iOS */
select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23667eea' d='M6 9L1 4h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
background-size: 12px;
padding-right: 3rem;
}
/* Prevent text size adjustment on iOS */
input,
select,
textarea {
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
text-size-adjust: 100%;
}
/* Improve button touch targets */
button,
.btn,
.qty-btn,
.remove-item-btn {
-webkit-tap-highlight-color: rgba(102, 126, 234, 0.3);
touch-action: manipulation;
}
/* Prevent horizontal scroll */
* {
max-width: 100%;
}
html,
body {
overflow-x: hidden;
width: 100%;
position: relative;
}
.order-summary .cart-item {
flex-direction: column;
align-items: flex-start;
}
.item-controls {
width: 100%;
flex-wrap: wrap;
gap: 1rem;
}
.item-controls .quantity-controls {
order: 2;
width: 100%;
justify-content: space-between;
}
.item-controls .item-price {
order: 1;
width: 100%;
text-align: left;
}
.remove-item-btn {
order: 3;
}
}
/* Payment Methods Styles */
.payment-methods {
background: linear-gradient(135deg, rgba(20, 20, 20, 0.95) 0%, rgba(30, 30, 30, 0.95) 100%);
border: 2px solid;
border-image: linear-gradient(135deg, #667eea, #764ba2) 1;
border-radius: 24px;
padding: 3rem;
backdrop-filter: blur(30px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
position: relative;
box-sizing: border-box;
width: 100%;
max-width: 100%;
}
.payment-methods::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="10" height="10" patternUnits="userSpaceOnUse"><path d="M 10 0 L 0 0 0 10" fill="none" stroke="rgba(102,126,234,0.1)" stroke-width="0.5"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>');
opacity: 0.3;
pointer-events: none;
border-radius: 24px;
}
.payment-title {
font-size: 2.4rem;
font-weight: 700;
margin-bottom: 2.5rem;
display: flex;
align-items: center;
gap: 1rem;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
position: relative;
z-index: 2;
}
.payment-title i {
font-size: 2.2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.payment-options {
display: flex;
flex-direction: column;
gap: 1.5rem;
margin-bottom: 3rem;
position: relative;
z-index: 2;
}
.payment-option {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 1.5rem;
background: rgba(255, 255, 255, 0.02);
border: 2px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
}
.payment-option:hover {
border-color: rgba(102, 126, 234, 0.2);
background: rgba(255, 255, 255, 0.04);
transform: none;
}
.payment-option.selected {
border-color: rgba(102, 126, 234, 0.4);
background: rgba(102, 126, 234, 0.08);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
}
.payment-option input[type="radio"] {
position: absolute;
opacity: 0;
pointer-events: none;
}
.payment-option::before {
content: '';
width: 22px;
height: 22px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
background: rgba(255, 255, 255, 0.02);
transition: all 0.2s ease;
flex-shrink: 0;
order: -1;
}
.payment-option.selected::before {
border-color: #667eea;
background: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
}
.payment-option.selected::after {
content: '';
position: absolute;
left: 1.65rem;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 8px;
border-radius: 50%;
background: white;
z-index: 1;
}
.payment-icon {
font-size: 2.5rem;
color: #667eea;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(102, 126, 234, 0.1);
border-radius: 12px;
}
.payment-info h4 {
color: white;
margin-bottom: 0.5rem;
font-size: 1.8rem;
font-weight: 600;
}
.payment-info p {
color: #a0aec0;
font-size: 1.4rem;
margin: 0;
}
.payment-info p {
color: #a0aec0;
font-size: 0.9rem;
}
.pay-button {
width: 100%;
padding: 2.2rem;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 16px;
font-size: 1.8rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4), 0 0 20px rgba(102, 126, 234, 0.2);
position: relative;
z-index: 2;
margin-top: 1rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.pay-button:hover {
transform: translateY(-3px);
box-shadow: 0 15px 40px rgba(102, 126, 234, 0.6), 0 0 30px rgba(102, 126, 234, 0.3);
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.pay-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.back-to-cart {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: #a0aec0;
text-decoration: none;
margin-bottom: 2rem;
transition: color 0.3s ease;
}
.back-to-cart:hover {
color: white;
}
.stripe-form-container {
margin-top: 1rem;
}
.card-element {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
min-height: 60px;
}
.card-errors {
color: #ff6b6b;
font-size: 0.9rem;
margin-bottom: 1rem;
min-height: 20px;
}
.payment-summary {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.summary-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.8rem;
color: #a0aec0;
}
.summary-row.total {
color: white;
font-weight: 600;
font-size: 1.1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 0.8rem;
margin-top: 0.8rem;
}
.payment-actions {
display: flex;
gap: 1rem;
}
.pay-button.secondary {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.pay-button.secondary:hover {
background: rgba(255, 255, 255, 0.15);
color: white;
transform: none;
box-shadow: none;
}
/* Responsive Design */
@media (max-width: 1024px) {
.checkout-content {
grid-template-columns: 1fr;
gap: 3rem;
}
.checkout-title {
font-size: 3rem;
}
.checkout-subtitle {
font-size: 1.5rem;
}
.order-summary {
max-width: 100%;
}
}
@media (max-width: 768px) {
.checkout-container {
padding: 1.5rem 1rem;
overflow-x: hidden;
min-height: auto;
}
.checkout-content {
grid-template-columns: 1fr !important;
gap: 1.5rem;
}
.checkout-header {
margin-bottom: 1.5rem;
}
.checkout-title {
font-size: 2.2rem;
line-height: 1.2;
margin-bottom: 1rem;
}
.checkout-subtitle {
font-size: 1.2rem;
line-height: 1.4;
}
.order-summary,
.payment-methods,
.billing-address,
.checkout-auth-section {
padding: 1.5rem;
margin-bottom: 1.5rem;
border-radius: 16px;
}
.summary-title,
.payment-title,
.billing-title,
.auth-title {
font-size: 1.8rem;
margin-bottom: 1.5rem;
}
.summary-title i,
.payment-title i,
.billing-title i,
.auth-title i {
font-size: 1.6rem;
}
.cart-item {
padding: 1.2rem;
flex-direction: column;
gap: 1rem;
margin-bottom: 1rem;
}
.item-details {
width: 100%;
}
.item-details h4 {
font-size: 1.4rem;
margin-bottom: 0.5rem;
}
.item-details p {
font-size: 1.1rem;
}
.item-controls {
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 1rem;
width: 100%;
flex-wrap: wrap;
}
.quantity-controls {
order: 1;
flex-shrink: 0;
}
.item-price {
order: 2;
font-size: 1.4rem;
text-align: right;
flex: 1;
min-width: 80px;
}
.remove-item-btn {
order: 3;
flex-shrink: 0;
}
.qty-btn {
width: 32px;
height: 32px;
font-size: 1rem;
}
.quantity-display {
font-size: 1.2rem;
min-width: 25px;
}
.total-section {
padding-top: 1.5rem;
margin-top: 1.5rem;
}
.total-row {
font-size: 1.4rem;
margin-bottom: 1rem;
}
.total-row.final {
font-size: 1.8rem;
padding-top: 1rem;
margin-top: 1rem;
}
.coupon-section {
padding: 1.5rem;
margin: 1.5rem 0;
}
.coupon-input {
flex-direction: column;
gap: 1rem;
}
.coupon-input-field {
width: 100%;
font-size: 1.4rem;
padding: 1.2rem;
}
.coupon-input button {
width: 100%;
padding: 1.2rem;
font-size: 1.4rem;
}
.payment-actions {
flex-direction: column;
gap: 1rem;
}
.pay-button {
padding: 1.5rem;
font-size: 1.4rem;
width: 100%;
min-height: 52px;
}
.form-row {
grid-template-columns: 1fr !important;
gap: 1rem;
}
.form-group {
margin-bottom: 1.2rem;
}
.form-group label {
font-size: 1.3rem;
margin-bottom: 0.5rem;
}
.form-group input,
.form-group select {
font-size: 16px !important; /* Prevents iOS zoom */
min-height: 48px;
padding: 1.2rem;
width: 100%;
box-sizing: border-box;
}
.auth-tabs {
flex-direction: column;
gap: 0.75rem;
}
.auth-tab {
width: 100%;
padding: 1.2rem;
font-size: 1.3rem;
justify-content: center;
}
.auth-tab i {
font-size: 1.2rem;
}
.billing-address,
.checkout-auth-section {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.address-form {
width: 100%;
}
.guest-info {
padding: 1.5rem;
}
.guest-info i {
font-size: 3rem;
}
.guest-info h3 {
font-size: 1.8rem;
}
.guest-info p {
font-size: 1.3rem;
}
.guest-info ul {
max-width: 100%;
}
.guest-info li {
font-size: 1.2rem;
}
.btn-social {
padding: 1.2rem;
font-size: 1.3rem;
}
.payment-option {
padding: 1.5rem;
flex-wrap: wrap;
gap: 1rem;
}
.payment-option.selected::after {
left: 1.5rem;
}
.payment-icon {
width: 50px;
height: 50px;
font-size: 1.8rem;
flex-shrink: 0;
}
.payment-info {
flex: 1;
min-width: 200px;
}
.payment-info h4 {
font-size: 1.5rem;
}
.payment-info p {
font-size: 1.2rem;
}
.stripe-form-container {
margin-top: 1rem;
}
.card-element {
padding: 1.2rem;
min-height: 52px;
}
.payment-summary {
padding: 1.2rem;
}
.summary-row {
font-size: 1.3rem;
}
.summary-row.total {
font-size: 1.5rem;
}
.success-message,
.error-message,
.notification-message {
top: 10px;
right: 10px;
left: 10px;
padding: 1rem;
font-size: 1.3rem;
max-width: calc(100% - 20px);
min-width: auto;
}
.success-content,
.error-content,
.notification-content {
gap: 0.5rem;
}
.success-content i,
.error-content i,
.notification-content i {
font-size: 1.1rem;
}
.btn-primary,
.btn-secondary {
padding: 1.2rem 2rem;
font-size: 1.4rem;
min-height: 48px;
width: 100%;
}
}
@media (max-width: 480px) {
.checkout-container {
padding: 1rem 0.75rem;
}
.checkout-title {
font-size: 1.8rem;
margin-bottom: 0.75rem;
}
.checkout-subtitle {
font-size: 1.1rem;
}
.checkout-content {
gap: 1rem;
}
.order-summary,
.payment-methods,
.billing-address,
.checkout-auth-section {
padding: 1.2rem;
margin-bottom: 1rem;
border-radius: 12px;
}
.summary-title,
.payment-title,
.billing-title,
.auth-title {
font-size: 1.6rem;
margin-bottom: 1.2rem;
}
.summary-title i,
.payment-title i,
.billing-title i,
.auth-title i {
font-size: 1.4rem;
}
.cart-item {
padding: 1rem;
gap: 0.75rem;
}
.item-details h4 {
font-size: 1.3rem;
}
.item-details p {
font-size: 1rem;
}
.item-price {
font-size: 1.3rem;
}
.qty-btn {
width: 28px;
height: 28px;
font-size: 0.9rem;
}
.quantity-display {
font-size: 1.1rem;
}
.total-row {
font-size: 1.3rem;
}
.total-row.final {
font-size: 1.6rem;
}
.coupon-section {
padding: 1.2rem;
}
.coupon-input-field {
font-size: 1.3rem;
padding: 1rem;
}
.pay-button {
padding: 1.2rem;
font-size: 1.3rem;
}
.form-group label {
font-size: 1.2rem;
}
.auth-tab {
padding: 1rem;
font-size: 1.2rem;
}
.payment-option {
padding: 1.2rem;
}
.payment-option.selected::after {
left: 1.35rem;
}
.payment-icon {
width: 45px;
height: 45px;
font-size: 1.6rem;
}
.payment-info h4 {
font-size: 1.4rem;
}
.payment-info p {
font-size: 1.1rem;
}
.guest-info {
padding: 1.2rem;
}
.guest-info i {
font-size: 2.5rem;
}
.guest-info h3 {
font-size: 1.6rem;
}
.guest-info p {
font-size: 1.2rem;
}
.guest-info li {
font-size: 1.1rem;
}
.btn-social {
padding: 1rem;
font-size: 1.2rem;
}
}
@media (max-width: 360px) {
.checkout-container {
padding: 0.75rem 0.5rem;
}
.checkout-title {
font-size: 1.6rem;
}
.checkout-subtitle {
font-size: 1rem;
}
.order-summary,
.payment-methods,
.billing-address,
.checkout-auth-section {
padding: 1rem;
}
.summary-title,
.payment-title,
.billing-title,
.auth-title {
font-size: 1.4rem;
}
.cart-item {
padding: 0.75rem;
}
.item-details h4 {
font-size: 1.2rem;
}
.pay-button {
padding: 1rem;
font-size: 1.2rem;
}
}
.success-message,
.error-message,
.notification-message {
position: fixed;
top: 20px;
right: 20px;
color: white;
padding: 1rem 1.5rem;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
z-index: 10000;
animation: slideIn 0.3s ease-out;
max-width: 400px;
min-width: 300px;
display: flex;
align-items: center;
gap: 0.8rem;
}
.success-message {
background: linear-gradient(135deg, #667eea, #764ba2);
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
}
.error-message {
background: linear-gradient(135deg, #e74c3c, #c0392b);
box-shadow: 0 4px 20px rgba(231, 76, 60, 0.3);
}
.notification-message {
background: linear-gradient(135deg, #667eea, #764ba2);
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3);
}
.success-content,
.error-content,
.notification-content {
display: flex;
align-items: center;
gap: 0.8rem;
width: 100%;
}
.success-content i,
.error-content i,
.notification-content i {
font-size: 1.2rem;
flex-shrink: 0;
}
.success-content p,
.error-content p,
.notification-content p {
margin: 0;
flex: 1;
line-height: 1.4;
}
.notification-close {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 0.9rem;
transition: background 0.2s;
}
.notification-close:hover {
background: rgba(255, 255, 255, 0.3);
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
}
</style>
<div class="checkout-container">
<div class="checkout-header">
<h1 class="checkout-title"><?= t('checkout.complete_purchase') ?></h1>
<p class="checkout-subtitle"><?= t('checkout.secure_payment') ?></p>
</div>
<div class="checkout-content">
<!-- Order Summary -->
<div class="order-summary">
<h2 class="summary-title">
<i class="fas fa-shopping-cart"></i>
<?= t('checkout.order_summary') ?>
</h2>
<?php if ($has_credits && $is_logged_in && $has_active_subscription): ?>
<!-- Subscription requirement notice for credits -->
<div style="background: linear-gradient(135deg, rgba(72, 187, 120, 0.1), rgba(56, 161, 105, 0.1)); border: 1px solid rgba(72, 187, 120, 0.3); border-radius: 12px; padding: 1.5rem; margin-bottom: 2rem;">
<div style="display: flex; align-items: center; gap: 1rem;">
<i class="fas fa-check-circle" style="color: #48bb78; font-size: 1.5rem;"></i>
<div>
<strong style="color: #48bb78; display: block; margin-bottom: 0.5rem;"><?= t('checkout.subscription_verified_title') ?></strong>
<p style="color: #a0aec0; margin: 0; font-size: 1.3rem;"><?= t('checkout.subscription_verified_message') ?></p>
</div>
</div>
</div>
<?php endif; ?>
<div class="cart-items">
<?php if (empty($cart)): ?>
<div class="empty-cart">
<i class="fas fa-shopping-bag"></i>
<p><?= t('checkout.cart_empty') ?></p>
</div>
<?php else: ?>
<?php foreach ($cart as $index => $item): ?>
<?php
// Determine which cart this item belongs to and its identifier
$item_identifier = null;
$item_type = null;
if (isset($item['type']) && $item['type'] === 'ticket') {
$item_identifier = $item['event_id'];
$item_type = 'ticket';
$cart_key = 'ticket_cart';
} elseif (isset($item['type']) && $item['type'] === 'credit') {
$item_identifier = $item['package'];
$item_type = 'credit';
$cart_key = 'credit_cart';
} elseif (isset($item['type']) && $item['type'] === 'track') {
$item_identifier = $item['track_id'];
$item_type = 'track';
$cart_key = isset($item['track_id']) ? 'cart' : 'credit_cart';
} elseif (isset($item['track_id'])) {
$item_identifier = $item['track_id'];
$item_type = 'track';
$cart_key = 'cart';
} else {
$item_identifier = $item['package'] ?? 'unknown';
$item_type = 'credit';
$cart_key = 'credit_cart';
}
$ticket_limit_attr = '';
if ($item_type === 'ticket') {
$ticket_limit_attr = ' data-ticket-limit="' . $ticket_purchase_limit . '"';
}
$ticket_limit_reached = ($item_type === 'ticket' && ($item['quantity'] ?? 1) >= $ticket_purchase_limit);
?>
<div class="cart-item" data-index="<?= $index ?>" data-type="<?= $item_type ?>" data-identifier="<?= $item_identifier ?>" data-cart-key="<?= $cart_key ?>"<?= $ticket_limit_attr ?>>
<div class="item-details">
<?php if (isset($item['type']) && $item['type'] === 'ticket'): ?>
<h4><?= htmlspecialchars($item['event_title'] ?? t('checkout.event_ticket')) ?></h4>
<p>
<i class="fas fa-ticket-alt"></i>
<?= t('checkout.event_ticket') ?><?= $item['quantity'] > 1 ? 's' : '' ?>
<?php if ($item['is_free']): ?>
<span style="color: #48bb78; margin-left: 0.5rem;">(<?= t('common.free') ?>)</span>
<?php endif; ?>
</p>
<?php elseif (isset($item['type']) && $item['type'] === 'credit'): ?>
<h4><?= htmlspecialchars(ucfirst($item['package'])) ?> <?= t('checkout.package') ?></h4>
<p>
<i class="fas fa-coins"></i>
<?= $item['credits'] ?> <?= t('checkout.credits') ?>
</p>
<?php elseif (isset($item['type']) && $item['type'] === 'track'): ?>
<h4><?= htmlspecialchars($item['title']) ?></h4>
<p>
<i class="fas fa-music"></i>
<?= t('checkout.by') ?> <?= htmlspecialchars($item['artist_name'] ?? $item['artist'] ?? t('checkout.unknown_artist')) ?>
</p>
<?php elseif (isset($item['track_id'])): ?>
<!-- Music track from music cart -->
<h4><?= htmlspecialchars($item['title']) ?></h4>
<p>
<i class="fas fa-music"></i>
<?= t('checkout.by') ?> <?= htmlspecialchars($item['artist_name'] ?? $item['artist'] ?? t('checkout.unknown_artist')) ?>
</p>
<?php else: ?>
<!-- Fallback for items without type (legacy support) -->
<h4><?= htmlspecialchars(ucfirst($item['package'] ?? 'Unknown')) ?> <?= t('checkout.package') ?></h4>
<p>
<i class="fas fa-coins"></i>
<?= $item['credits'] ?? 0 ?> <?= t('checkout.credits') ?>
</p>
<?php endif; ?>
</div>
<div class="item-controls">
<div class="quantity-controls">
<?php if ($item_type === 'track'): ?>
<span class="quantity-display"><?= $item['quantity'] ?></span>
<span class="single-quantity-note">
<a href="/commercial_license.php" target="_blank" style="color: #667eea; text-decoration: none; border-bottom: 1px dotted #667eea;" title="<?= t('checkout.view_license_details') ?>">
<?= t('cart.single_quantity_note') ?>
</a>
</span>
<?php else: ?>
<button class="qty-btn qty-decrease" onclick="updateQuantity(<?= $index ?>, '<?= $item_type ?>', '<?= $item_identifier ?>', '<?= $cart_key ?>', -1)" title="<?= t('checkout.decrease_quantity') ?>">
<i class="fas fa-minus"></i>
</button>
<span class="quantity-display"><?= $item['quantity'] ?></span>
<button class="qty-btn qty-increase" onclick="updateQuantity(<?= $index ?>, '<?= $item_type ?>', '<?= $item_identifier ?>', '<?= $cart_key ?>', 1)" title="<?= $ticket_limit_reached ? htmlspecialchars(t('checkout.ticket_limit_reached', ['limit' => $ticket_purchase_limit])) : t('checkout.increase_quantity') ?>" <?= $ticket_limit_reached ? 'disabled' : '' ?>>
<i class="fas fa-plus"></i>
</button>
<?php endif; ?>
</div>
<div class="item-price">$<?= number_format($item['price'] * $item['quantity'], 2) ?></div>
<button class="remove-item-btn" onclick="removeItem(<?= $index ?>, '<?= $item_type ?>', '<?= $item_identifier ?>', '<?= $cart_key ?>')" title="<?= t('checkout.remove_item') ?>">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<!-- Coupon Code Section -->
<div class="coupon-section">
<div class="coupon-input">
<input type="text" id="couponCode" placeholder="<?= t('checkout.have_coupon') ?>" class="coupon-input-field">
<button type="button" onclick="applyCoupon()" class="btn btn-secondary">
<i class="fas fa-tag"></i>
<?= t('checkout.apply') ?>
</button>
</div>
<div id="couponMessage" class="coupon-message"></div>
</div>
<div class="total-section">
<?php if ($totalCredits > 0): ?>
<div class="total-row">
<span><?= t('checkout.credits_to_add') ?></span>
<span><?= $totalCredits ?> <?= t('checkout.credits') ?></span>
</div>
<?php endif; ?>
<?php if ($totalTracks > 0): ?>
<div class="total-row">
<span><?= t('checkout.tracks_to_purchase') ?></span>
<span><?= $totalTracks ?> <?= t('common.tracks') ?></span>
</div>
<?php endif; ?>
<?php if ($totalTickets > 0): ?>
<div class="total-row">
<span><?= t('checkout.event_ticket_total_label') ?></span>
<span><?= $totalTickets ?> <?= $totalTickets > 1 ? t('checkout.event_ticket_plural') : t('checkout.event_ticket') ?></span>
</div>
<?php endif; ?>
<div class="total-row">
<span><?= t('checkout.subtotal') ?></span>
<span>$<?= number_format($total, 2) ?></span>
</div>
<div class="total-row">
<span><?= t('checkout.tax') ?></span>
<span>$0.00</span>
</div>
<div class="total-row final">
<span><?= t('checkout.total') ?></span>
<span>$<?= number_format($total, 2) ?></span>
</div>
</div>
</div>
<?php if (!$is_logged_in): ?>
<!-- Login/Registration Section -->
<div class="checkout-auth-section" id="checkoutAuthSection">
<h2 class="auth-title">
<i class="fas fa-user"></i>
<?= t('checkout.account_options') ?>
</h2>
<div class="auth-options">
<div class="auth-tabs">
<button class="auth-tab active" onclick="switchAuthTab('login', event)">
<i class="fas fa-sign-in-alt"></i>
<?= t('checkout.returning_customer') ?>
</button>
<button class="auth-tab" onclick="switchAuthTab('register', event)">
<i class="fas fa-user-plus"></i>
<?= t('checkout.create_account') ?>
</button>
<button class="auth-tab" onclick="switchAuthTab('guest', event)">
<i class="fas fa-shopping-cart"></i>
<?= t('checkout.guest_checkout') ?>
</button>
</div>
<!-- Login Form -->
<div class="auth-content active" id="loginContent">
<form class="auth-form" id="loginForm">
<div class="form-group">
<label for="login_email"><?= t('checkout.email_address') ?></label>
<input type="email" id="login_email" name="login_email" required>
</div>
<div class="form-group">
<label for="login_password"><?= t('checkout.password') ?></label>
<input type="password" id="login_password" name="login_password" required>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i>
<?= t('checkout.login_continue') ?>
</button>
</form>
<div class="auth-divider">
<span><?= t('checkout.or') ?></span>
</div>
<div class="social-login">
<button class="btn btn-social btn-google">
<i class="fab fa-google"></i>
<?= t('checkout.continue_google') ?>
</button>
<button class="btn btn-social btn-facebook">
<i class="fab fa-facebook"></i>
<?= t('checkout.continue_facebook') ?>
</button>
</div>
</div>
<!-- Registration Form -->
<div class="auth-content" id="registerContent">
<form class="auth-form" id="registerForm">
<div class="form-row">
<div class="form-group">
<label for="register_first_name"><?= t('checkout.first_name') ?></label>
<input type="text" id="register_first_name" name="register_first_name" required>
</div>
<div class="form-group">
<label for="register_last_name"><?= t('checkout.last_name') ?></label>
<input type="text" id="register_last_name" name="register_last_name" required>
</div>
</div>
<div class="form-group">
<label for="register_email"><?= t('checkout.email_address') ?></label>
<input type="email" id="register_email" name="register_email" required>
</div>
<div class="form-group">
<label for="register_password"><?= t('checkout.password') ?></label>
<input type="password" id="register_password" name="register_password" required>
</div>
<div class="form-group">
<label for="register_confirm_password"><?= t('checkout.confirm_password') ?></label>
<input type="password" id="register_confirm_password" name="register_confirm_password" required>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-user-plus"></i>
<?= t('checkout.create_account_continue') ?>
</button>
</form>
</div>
<!-- Guest Checkout -->
<div class="auth-content" id="guestContent">
<div class="guest-info">
<i class="fas fa-info-circle"></i>
<h3><?= t('checkout.guest_checkout_title') ?></h3>
<p><?= t('checkout.guest_checkout_desc') ?></p>
<ul>
<li><?= t('checkout.quick_checkout') ?></li>
<li><?= t('checkout.no_account_required') ?></li>
<li><?= t('checkout.secure_payment_processing') ?></li>
<li><?= t('checkout.email_confirmation') ?></li>
</ul>
</div>
<button type="button" class="btn btn-primary" onclick="proceedAsGuest()">
<i class="fas fa-shopping-cart"></i>
<?= t('checkout.continue_as_guest') ?>
</button>
</div>
</div>
</div>
<?php endif; ?>
<!-- Billing Address -->
<div class="billing-address" id="billingAddressSection" <?= !$is_logged_in ? 'style="display: none;"' : '' ?>>
<h2 class="billing-title">
<i class="fas fa-map-marker-alt"></i>
<?= t('checkout.billing_address') ?>
</h2>
<div class="address-form">
<div class="form-row">
<div class="form-group">
<label for="billing_first_name"><?= t('checkout.first_name') ?> *</label>
<input type="text" id="billing_first_name" name="billing_first_name" required>
</div>
<div class="form-group">
<label for="billing_last_name"><?= t('checkout.last_name') ?> *</label>
<input type="text" id="billing_last_name" name="billing_last_name" required>
</div>
</div>
<div class="form-group">
<label for="billing_email"><?= t('checkout.email_address') ?> *</label>
<input type="email" id="billing_email" name="billing_email" value="<?= htmlspecialchars($user['email'] ?? '') ?>" required>
</div>
<div class="form-group">
<label for="billing_address"><?= t('checkout.street_address') ?> *</label>
<input type="text" id="billing_address" name="billing_address" required>
</div>
<div class="form-row">
<div class="form-group">
<label for="billing_city"><?= t('checkout.city') ?> *</label>
<input type="text" id="billing_city" name="billing_city" required>
</div>
<div class="form-group">
<label for="billing_state"><?= t('checkout.state_province') ?> *</label>
<input type="text" id="billing_state" name="billing_state" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="billing_zip"><?= t('checkout.zip_postal_code') ?> *</label>
<input type="text" id="billing_zip" name="billing_zip" required>
</div>
<div class="form-group">
<label for="billing_country"><?= t('checkout.country') ?> *</label>
<select id="billing_country" name="billing_country" required>
<option value=""><?= t('checkout.select_country') ?></option>
<option value="US" selected>United States</option>
<option value="CA">Canada</option>
<option value="GB">United Kingdom</option>
<option value="AU">Australia</option>
<option value="DE">Germany</option>
<option value="FR">France</option>
<option value="JP">Japan</option>
<option value="MX">Mexico</option>
<option value="BR">Brazil</option>
<option value="IN">India</option>
<option value="CN">China</option>
<option value="KR">South Korea</option>
<option value="IT">Italy</option>
<option value="ES">Spain</option>
<option value="NL">Netherlands</option>
<option value="SE">Sweden</option>
<option value="NO">Norway</option>
<option value="DK">Denmark</option>
<option value="FI">Finland</option>
<option value="CH">Switzerland</option>
<option value="AT">Austria</option>
<option value="BE">Belgium</option>
<option value="IE">Ireland</option>
<option value="NZ">New Zealand</option>
<option value="SG">Singapore</option>
<option value="HK">Hong Kong</option>
<option value="TW">Taiwan</option>
<option value="IL">Israel</option>
<option value="ZA">South Africa</option>
<option value="AR">Argentina</option>
<option value="CL">Chile</option>
<option value="CO">Colombia</option>
<option value="PE">Peru</option>
<option value="VE">Venezuela</option>
<option value="UY">Uruguay</option>
<option value="PY">Paraguay</option>
<option value="BO">Bolivia</option>
<option value="EC">Ecuador</option>
<option value="GY">Guyana</option>
<option value="SR">Suriname</option>
<option value="GF">French Guiana</option>
<option value="FK">Falkland Islands</option>
<option value="GS">South Georgia</option>
<option value="BV">Bouvet Island</option>
<option value="HM">Heard Island</option>
<option value="TF">French Southern Territories</option>
<option value="AQ">Antarctica</option>
<option value="IO">British Indian Ocean Territory</option>
<option value="CX">Christmas Island</option>
<option value="CC">Cocos Islands</option>
<option value="NF">Norfolk Island</option>
<option value="NC">New Caledonia</option>
<option value="PF">French Polynesia</option>
<option value="YT">Mayotte</option>
<option value="RE">Réunion</option>
<option value="BL">Saint Barthélemy</option>
<option value="MF">Saint Martin</option>
<option value="PM">Saint Pierre and Miquelon</option>
<option value="WF">Wallis and Futuna</option>
<option value="AW">Aruba</option>
<option value="CW">Curaçao</option>
<option value="SX">Sint Maarten</option>
<option value="BQ">Caribbean Netherlands</option>
<option value="AI">Anguilla</option>
<option value="BM">Bermuda</option>
<option value="IO">British Indian Ocean Territory</option>
<option value="KY">Cayman Islands</option>
<option value="FK">Falkland Islands</option>
<option value="GI">Gibraltar</option>
<option value="MS">Montserrat</option>
<option value="PN">Pitcairn Islands</option>
<option value="SH">Saint Helena</option>
<option value="TC">Turks and Caicos Islands</option>
<option value="VG">British Virgin Islands</option>
<option value="VI">U.S. Virgin Islands</option>
<option value="PR">Puerto Rico</option>
<option value="GU">Guam</option>
<option value="MP">Northern Mariana Islands</option>
<option value="AS">American Samoa</option>
<option value="UM">U.S. Minor Outlying Islands</option>
<option value="PS">Palestine</option>
<option value="EH">Western Sahara</option>
<option value="SS">South Sudan</option>
<option value="XK">Kosovo</option>
<option value="AX">Åland Islands</option>
<option value="FO">Faroe Islands</option>
<option value="GL">Greenland</option>
<option value="IS">Iceland</option>
<option value="LI">Liechtenstein</option>
<option value="MC">Monaco</option>
<option value="SM">San Marino</option>
<option value="VA">Vatican City</option>
<option value="AD">Andorra</option>
<option value="MT">Malta</option>
<option value="CY">Cyprus</option>
<option value="GR">Greece</option>
<option value="PT">Portugal</option>
<option value="LU">Luxembourg</option>
<option value="SI">Slovenia</option>
<option value="SK">Slovakia</option>
<option value="CZ">Czech Republic</option>
<option value="PL">Poland</option>
<option value="HU">Hungary</option>
<option value="RO">Romania</option>
<option value="BG">Bulgaria</option>
<option value="HR">Croatia</option>
<option value="RS">Serbia</option>
<option value="ME">Montenegro</option>
<option value="BA">Bosnia and Herzegovina</option>
<option value="MK">North Macedonia</option>
<option value="AL">Albania</option>
<option value="MD">Moldova</option>
<option value="UA">Ukraine</option>
<option value="BY">Belarus</option>
<option value="LT">Lithuania</option>
<option value="LV">Latvia</option>
<option value="EE">Estonia</option>
<option value="RU">Russia</option>
<option value="KZ">Kazakhstan</option>
<option value="UZ">Uzbekistan</option>
<option value="KG">Kyrgyzstan</option>
<option value="TJ">Tajikistan</option>
<option value="TM">Turkmenistan</option>
<option value="AF">Afghanistan</option>
<option value="PK">Pakistan</option>
<option value="BD">Bangladesh</option>
<option value="LK">Sri Lanka</option>
<option value="NP">Nepal</option>
<option value="BT">Bhutan</option>
<option value="MV">Maldives</option>
<option value="MM">Myanmar</option>
<option value="TH">Thailand</option>
<option value="LA">Laos</option>
<option value="KH">Cambodia</option>
<option value="VN">Vietnam</option>
<option value="MY">Malaysia</option>
<option value="ID">Indonesia</option>
<option value="PH">Philippines</option>
<option value="BN">Brunei</option>
<option value="TL">East Timor</option>
<option value="PG">Papua New Guinea</option>
<option value="FJ">Fiji</option>
<option value="VU">Vanuatu</option>
<option value="NC">New Caledonia</option>
<option value="PF">French Polynesia</option>
<option value="TO">Tonga</option>
<option value="WS">Samoa</option>
<option value="KI">Kiribati</option>
<option value="TV">Tuvalu</option>
<option value="NR">Nauru</option>
<option value="PW">Palau</option>
<option value="MH">Marshall Islands</option>
<option value="FM">Micronesia</option>
<option value="CK">Cook Islands</option>
<option value="NU">Niue</option>
<option value="TK">Tokelau</option>
<option value="AS">American Samoa</option>
<option value="GU">Guam</option>
<option value="MP">Northern Mariana Islands</option>
<option value="PW">Palau</option>
<option value="MH">Marshall Islands</option>
<option value="FM">Micronesia</option>
<option value="KI">Kiribati</option>
<option value="TV">Tuvalu</option>
<option value="NR">Nauru</option>
<option value="TO">Tonga</option>
<option value="WS">Samoa</option>
<option value="VU">Vanuatu</option>
<option value="FJ">Fiji</option>
<option value="PG">Papua New Guinea</option>
<option value="TL">East Timor</option>
<option value="BN">Brunei</option>
<option value="PH">Philippines</option>
<option value="ID">Indonesia</option>
<option value="MY">Malaysia</option>
<option value="VN">Vietnam</option>
<option value="KH">Cambodia</option>
<option value="LA">Laos</option>
<option value="TH">Thailand</option>
<option value="MM">Myanmar</option>
<option value="MV">Maldives</option>
<option value="BT">Bhutan</option>
<option value="NP">Nepal</option>
<option value="LK">Sri Lanka</option>
<option value="BD">Bangladesh</option>
<option value="PK">Pakistan</option>
<option value="AF">Afghanistan</option>
<option value="TM">Turkmenistan</option>
<option value="TJ">Tajikistan</option>
<option value="KG">Kyrgyzstan</option>
<option value="UZ">Uzbekistan</option>
<option value="KZ">Kazakhstan</option>
<option value="RU">Russia</option>
<option value="EE">Estonia</option>
<option value="LV">Latvia</option>
<option value="LT">Lithuania</option>
<option value="BY">Belarus</option>
<option value="UA">Ukraine</option>
<option value="MD">Moldova</option>
<option value="AL">Albania</option>
<option value="MK">North Macedonia</option>
<option value="BA">Bosnia and Herzegovina</option>
<option value="ME">Montenegro</option>
<option value="RS">Serbia</option>
<option value="HR">Croatia</option>
<option value="BG">Bulgaria</option>
<option value="RO">Romania</option>
<option value="HU">Hungary</option>
<option value="PL">Poland</option>
<option value="CZ">Czech Republic</option>
<option value="SK">Slovakia</option>
<option value="SI">Slovenia</option>
<option value="LU">Luxembourg</option>
<option value="PT">Portugal</option>
<option value="GR">Greece</option>
<option value="CY">Cyprus</option>
<option value="MT">Malta</option>
<option value="AD">Andorra</option>
<option value="VA">Vatican City</option>
<option value="SM">San Marino</option>
<option value="MC">Monaco</option>
<option value="LI">Liechtenstein</option>
<option value="IS">Iceland</option>
<option value="FO">Faroe Islands</option>
<option value="AX">Åland Islands</option>
<option value="XK">Kosovo</option>
<option value="SS">South Sudan</option>
<option value="EH">Western Sahara</option>
<option value="PS">Palestine</option>
<option value="UM">U.S. Minor Outlying Islands</option>
<option value="AS">American Samoa</option>
<option value="MP">Northern Mariana Islands</option>
<option value="GU">Guam</option>
<option value="FK">Falkland Islands</option>
<option value="IO">British Indian Ocean Territory</option>
<option value="GF">French Guiana</option>
<option value="SR">Suriname</option>
<option value="GY">Guyana</option>
<option value="EC">Ecuador</option>
<option value="BO">Bolivia</option>
<option value="PY">Paraguay</option>
<option value="UY">Uruguay</option>
<option value="VE">Venezuela</option>
<option value="PE">Peru</option>
<option value="CO">Colombia</option>
<option value="CL">Chile</option>
<option value="AR">Argentina</option>
<option value="ZA">South Africa</option>
<option value="IL">Israel</option>
<option value="TW">Taiwan</option>
<option value="HK">Hong Kong</option>
<option value="SG">Singapore</option>
<option value="NZ">New Zealand</option>
<option value="IE">Ireland</option>
<option value="BE">Belgium</option>
<option value="AT">Austria</option>
<option value="CH">Switzerland</option>
<option value="FI">Finland</option>
<option value="NO">Norway</option>
<option value="SE">Sweden</option>
<option value="NL">Netherlands</option>
<option value="ES">Spain</option>
<option value="IT">Italy</option>
<option value="KR">South Korea</option>
<option value="CN">China</option>
<option value="IN">India</option>
<option value="BR">Brazil</option>
<option value="MX">Mexico</option>
<option value="JP">Japan</option>
<option value="FR">France</option>
<option value="DE">Germany</option>
<option value="AU">Australia</option>
<option value="GB">United Kingdom</option>
<option value="CA">Canada</option>
</select>
</div>
</div>
</div>
</div>
<!-- Payment Methods -->
<div class="payment-methods" id="paymentMethodsSection">
<h2 class="payment-title">
<i class="fas fa-credit-card"></i>
<?= t('checkout.payment_method') ?>
</h2>
<div class="payment-options">
<?php
$stripe_enabled = isStripeEnabled();
$paypal_enabled = isPayPalEnabled();
?>
<?php if ($stripe_enabled): ?>
<label class="payment-option selected" onclick="selectPaymentMethod('stripe', event)">
<input type="radio" name="payment_method" value="stripe" checked>
<div class="payment-icon">
<i class="fas fa-credit-card"></i>
</div>
<div class="payment-info">
<h4><?= t('checkout.credit_card') ?></h4>
<p><?= t('checkout.credit_card_desc') ?></p>
</div>
</label>
<?php endif; ?>
<?php if ($paypal_enabled): ?>
<label class="payment-option <?= !$stripe_enabled ? 'selected' : '' ?>" onclick="selectPaymentMethod('paypal', event)">
<input type="radio" name="payment_method" value="paypal" <?= !$stripe_enabled ? 'checked' : '' ?>>
<div class="payment-icon">
<i class="fab fa-paypal"></i>
</div>
<div class="payment-info">
<h4><?= t('checkout.paypal') ?></h4>
<p><?= t('checkout.paypal_desc') ?></p>
</div>
</label>
<?php endif; ?>
<?php if (!$stripe_enabled && !$paypal_enabled): ?>
<div style="padding: 2rem; text-align: center; color: #e53e3e; background: rgba(229, 62, 62, 0.1); border-radius: 8px; border: 1px solid rgba(229, 62, 62, 0.3);">
<i class="fas fa-exclamation-triangle" style="font-size: 2rem; margin-bottom: 1rem;"></i>
<p style="font-size: 1.1rem; margin: 0;">No payment methods are currently enabled. Please contact support.</p>
</div>
<?php endif; ?>
</div>
<div style="text-align: center; margin: 2rem 0 1rem 0; color: #a0aec0; font-size: 1.2rem; position: relative; z-index: 2;">
Select a payment method above, then click below to pay
</div>
<button class="pay-button" onclick="processPayment()">
<i class="fas fa-shield-alt"></i>
<?= t('checkout.pay_securely') ?> $<?= number_format($total, 2) ?>
</button>
</div>
<!-- Stripe Payment Form (Hidden by default) -->
<div class="payment-methods" id="stripePaymentForm" style="display: none;">
<h2 class="payment-title">
<i class="fas fa-credit-card"></i>
<?= t('checkout.credit_card') ?>
</h2>
<div class="stripe-form-container">
<div id="card-element" class="card-element"></div>
<div id="card-errors" class="card-errors" role="alert"></div>
<div class="payment-summary">
<div class="summary-row">
<span>Subtotal:</span>
<span>$<?= number_format($total, 2) ?></span>
</div>
<div class="summary-row">
<span>Tax:</span>
<span>$0.00</span>
</div>
<div class="summary-row total">
<span>Total:</span>
<span>$<?= number_format($total, 2) ?></span>
</div>
</div>
<div class="payment-actions">
<button class="pay-button secondary" onclick="backToPaymentMethods()">
<i class="fas fa-arrow-left"></i>
<?= t('checkout.back') ?>
</button>
<button class="pay-button" id="stripe-pay-button" onclick="confirmStripePayment()">
<i class="fas fa-lock"></i>
<?= t('checkout.pay') ?> $<?= number_format($total, 2) ?>
</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('pk_live_51Rn8TtD0zXLMB4gHMCZ5OMunyo0YtN6hBR30BoXFEiQxPG9I6U2tko6Axxwl0yJS21DCCykhC9PxAMdZoEfwJI0p00KlrZUR3w');
const TICKET_PURCHASE_LIMIT = <?= $ticket_purchase_limit ?>;
let selectedPaymentMethod = '<?= isStripeEnabled() ? "stripe" : (isPayPalEnabled() ? "paypal" : "stripe") ?>';
let elements;
let paymentIntent;
// Translations object for JavaScript
const translations = {
processing: '<?= addslashes(t('checkout.processing')) ?>',
paymentFailed: '<?= addslashes(t('checkout.payment_failed')) ?>',
errorLoadingForm: '<?= addslashes(t('checkout.error_loading_form')) ?>',
formNotReady: '<?= addslashes(t('checkout.form_not_ready')) ?>',
removeItemConfirm: '<?= addslashes(t('checkout.remove_item_confirm')) ?>',
removeCreditConfirm: '<?= addslashes(t('checkout.remove_credit_confirm')) ?>',
ticketLimit: '<?= addslashes(t('checkout.ticket_limit', ['limit' => ':limit'])) ?>',
ticketSoldOut: '<?= addslashes(t('checkout.ticket_limit_sold_out')) ?>',
trackSingleQuantity: '<?= addslashes(t('cart.single_quantity_error')) ?>',
subscription_required: '<?= addslashes(t('checkout.subscription_required_message')) ?>'
};
const successMessages = {
generic: '<?= addslashes(t('checkout.order_processed')) ?>',
tracks: '<?= addslashes(t('checkout.order_processed_tracks')) ?>',
tickets: '<?= addslashes(t('checkout.order_processed_tickets')) ?>'
};
// Test Stripe loading
console.log('🎯 Stripe loaded:', !!stripe);
function selectPaymentMethod(method, event) {
selectedPaymentMethod = method;
// Update UI
document.querySelectorAll('.payment-option').forEach(option => {
option.classList.remove('selected');
});
if (event && event.currentTarget) {
event.currentTarget.classList.add('selected');
event.currentTarget.querySelector('input[type="radio"]').checked = true;
} else {
// Fallback: find the option by method
document.querySelectorAll('.payment-option').forEach(option => {
const radio = option.querySelector('input[type="radio"]');
if (radio && radio.value === method) {
option.classList.add('selected');
radio.checked = true;
}
});
}
}
async function processPayment() {
const payButton = document.querySelector('.pay-button');
const originalText = payButton.innerHTML;
console.log('🎯 Starting payment process...');
console.log('🎯 Selected payment method:', selectedPaymentMethod);
console.log('🎯 Pay button clicked - about to show Stripe form');
// Check if cart has credits - if so, validate subscription requirement
// IMPORTANT: Only check for credit items, not tracks or tickets
const cartData = <?= json_encode($cart) ?>;
const hasCredits = cartData && cartData.some(item => item.type === 'credit' || (item.package && !item.track_id && !item.event_id));
if (hasCredits) {
// Validate subscription before processing
try {
const subCheckResponse = await fetch('/api/check_subscription.php', {
method: 'GET',
credentials: 'same-origin'
});
const subCheck = await subCheckResponse.json();
if (!subCheck.has_active_subscription) {
showErrorMessage('<?= addslashes(t('checkout.subscription_required_message')) ?>');
window.location.href = '/account_settings.php?tab=subscription&require_subscription=1';
return;
}
} catch (error) {
console.error('Subscription check failed:', error);
// Continue - backend will validate
}
}
try {
payButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + translations.processing;
payButton.disabled = true;
if (selectedPaymentMethod === 'stripe') {
console.log('🎯 Processing with Stripe...');
// Show Stripe payment form
await showStripePaymentForm();
// Reset button since we're showing the form
payButton.innerHTML = originalText;
payButton.disabled = false;
} else if (selectedPaymentMethod === 'paypal') {
console.log('🎯 Processing with PayPal...');
// Process with PayPal
// Format cart data to match backend expectations
const formattedCart = <?= json_encode($cart) ?>.map(item => ({
package: item.package,
quantity: item.quantity
}));
const response = await fetch('process_credit_payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'process_paypal_payment',
cart: formattedCart
})
});
const result = await response.json();
if (!result.success) {
throw new Error(result.error);
}
// Redirect to PayPal
window.location.href = result.paypal_url;
}
} catch (error) {
console.error('Payment error:', error);
console.error('Error details:', error);
let errorMessage = translations.paymentFailed + ': ' + error.message;
let requiresSubscription = false;
let subscriptionUrl = '/account_settings.php?tab=subscription&require_subscription=1';
// Try to get more specific error details
if (error.response) {
try {
const errorData = await error.response.json();
errorMessage = errorData.message || errorData.error || error.message;
requiresSubscription = errorData.requires_subscription || false;
subscriptionUrl = errorData.subscription_url || subscriptionUrl;
} catch (e) {
errorMessage = translations.paymentFailed + ': HTTP ' + error.response.status;
}
}
// Check if error message indicates subscription requirement
if (error.message && (error.message.includes('subscription') || error.message.includes('Active subscription'))) {
requiresSubscription = true;
}
if (requiresSubscription) {
showErrorMessage(errorMessage || translations.subscription_required);
setTimeout(() => {
window.location.href = subscriptionUrl;
}, 2000);
} else {
showErrorMessage(errorMessage);
}
payButton.innerHTML = originalText;
payButton.disabled = false;
}
}
async function showStripePaymentForm() {
console.log('🎯 showStripePaymentForm() called');
try {
console.log('🎯 Validating billing address...');
// Check if billing address section is visible
const billingSection = document.getElementById('billingAddressSection');
const isBillingVisible = billingSection && billingSection.style.display !== 'none' &&
window.getComputedStyle(billingSection).display !== 'none';
// Validate billing address fields only if section is visible
const billingFields = [
'billing_first_name',
'billing_last_name',
'billing_email',
'billing_address',
'billing_city',
'billing_state',
'billing_zip',
'billing_country'
];
const billingData = {};
if (isBillingVisible) {
for (const field of billingFields) {
const element = document.getElementById(field);
if (!element) {
throw new Error(`Billing field not found: ${field}`);
}
if (!element.value.trim()) {
element.focus();
throw new Error(`Please fill in your ${field.replace('billing_', '').replace('_', ' ')}`);
}
billingData[field] = element.value.trim();
}
} else {
// If billing section is hidden, try to get user data or use defaults
console.log('🎯 Billing section not visible, using minimal data');
for (const field of billingFields) {
const element = document.getElementById(field);
if (element && element.value.trim()) {
billingData[field] = element.value.trim();
}
}
// If no billing data available, we'll let the backend handle it
if (Object.keys(billingData).length === 0) {
console.log('🎯 No billing data available, proceeding without it');
}
}
console.log('🎯 Billing address validated:', billingData);
console.log('🎯 Creating Stripe payment intent...');
// Create payment intent first
// Debug: Log the raw cart data
const rawCartData = <?= json_encode($cart) ?>;
console.log('🎯 Raw cart data:', rawCartData);
console.log('🎯 Raw cart data type:', typeof rawCartData);
console.log('🎯 Raw cart data length:', rawCartData ? rawCartData.length : 'null');
// Format cart data to match backend expectations
if (!rawCartData || !Array.isArray(rawCartData) || rawCartData.length === 0) {
throw new Error('Cart is empty or invalid');
}
// Separate credit, track, and ticket items
const creditItems = [];
const trackItems = [];
const ticketItems = [];
rawCartData.forEach(item => {
console.log('🎯 Processing cart item:', item);
if (item.type === 'credit' || !item.type) {
creditItems.push({
package: item.package,
quantity: item.quantity || 1
});
} else if (item.type === 'track') {
// Check if track_id exists
if (!item.track_id) {
console.error('❌ Track item missing track_id:', item);
throw new Error('Track item missing track_id: ' + JSON.stringify(item));
}
trackItems.push({
track_id: item.track_id,
title: item.title,
artist: item.artist_name || item.artist,
price: item.price,
quantity: item.quantity || 1
});
} else if (item.type === 'ticket') {
// Check if event_id exists
if (!item.event_id) {
console.error('❌ Ticket item missing event_id:', item);
throw new Error('Ticket item missing event_id: ' + JSON.stringify(item));
}
ticketItems.push({
event_id: item.event_id,
event_title: item.event_title,
ticket_price: item.ticket_price,
is_free: item.is_free || false,
quantity: item.quantity || 1
});
} else {
throw new Error('Unknown item type: ' + item.type);
}
});
console.log('🎯 Credit items:', creditItems);
console.log('🎯 Track items:', trackItems);
console.log('🎯 Ticket items:', ticketItems);
// Send the full mixed cart structure to the backend
const formattedCart = {
credits: creditItems,
tracks: trackItems,
tickets: ticketItems
};
console.log('🎯 Formatted cart (mixed):', formattedCart);
const response = await fetch('process_credit_payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'process_cart_payment',
cart: formattedCart,
billing_address: billingData
})
});
console.log('🎯 API response status:', response.status);
console.log('🎯 API response headers:', response.headers);
const responseText = await response.text();
console.log('🎯 API response text:', responseText);
// Check if response is empty
if (!responseText || responseText.trim() === '') {
throw new Error('Empty response from server - this indicates a PHP error');
}
let result;
try {
result = JSON.parse(responseText);
// Check if error is subscription requirement
if (!result.success && result.requires_subscription) {
showErrorMessage(result.message || translations.subscription_required);
setTimeout(() => {
window.location.href = result.subscription_url || '/account_settings.php?tab=subscription&require_subscription=1';
}, 2000);
payButton.innerHTML = originalText;
payButton.disabled = false;
return;
}
} catch (parseError) {
console.error('❌ JSON parse error:', parseError);
console.error('❌ Response text:', responseText);
throw new Error('Invalid response from server: ' + responseText);
}
if (!result.success) {
// Check if error is subscription requirement
if (result.requires_subscription) {
showErrorMessage(result.message || translations.subscription_required);
setTimeout(() => {
window.location.href = result.subscription_url || '/account_settings.php?tab=subscription&require_subscription=1';
}, 2000);
backToPaymentMethods();
return;
}
// Check if error is subscription requirement
if (result.requires_subscription) {
showErrorMessage(result.message || '<?= addslashes(t('checkout.subscription_required_message')) ?>');
setTimeout(() => {
window.location.href = result.subscription_url || '/account_settings.php?tab=subscription&require_subscription=1';
}, 2000);
backToPaymentMethods();
return;
}
throw new Error(result.error || result.message || 'Unknown error from backend');
}
// Check if this is a free order
if (result.is_free_order) {
console.log('🎯 Free order detected, redirecting to success page...');
// Determine appropriate success message
let successMessage = successMessages.generic;
const hasTickets = Array.isArray(result.tickets) && result.tickets.length > 0;
const hasTracks = Array.isArray(result.tracks) && result.tracks.length > 0;
if (hasTickets && !hasTracks) {
successMessage = successMessages.tickets;
} else if (hasTracks && !hasTickets) {
successMessage = successMessages.tracks;
}
showSuccessMessage(successMessage);
// Redirect to order success page with appropriate type
let purchaseType = 'mixed';
if (hasTickets && !hasTracks) {
purchaseType = 'tickets';
} else if (hasTracks && !hasTickets) {
purchaseType = 'tracks';
}
const redirectTarget = '/order_success.php?type=' + purchaseType + '&free=1';
setTimeout(() => {
window.location.href = redirectTarget;
}, 2000);
return; // Exit early, no Stripe form needed
}
paymentIntent = result.client_secret;
// Hide payment methods, show Stripe form
console.log('🎯 Switching to Stripe form...');
const paymentMethodsSection = document.getElementById('paymentMethodsSection');
const stripePaymentForm = document.getElementById('stripePaymentForm');
console.log('🎯 paymentMethodsSection found:', !!paymentMethodsSection);
console.log('🎯 stripePaymentForm found:', !!stripePaymentForm);
if (!paymentMethodsSection || !stripePaymentForm) {
throw new Error('Payment form elements not found');
}
paymentMethodsSection.style.display = 'none';
stripePaymentForm.style.display = 'block';
// Create Stripe Elements
console.log('🎯 Creating Stripe Elements...');
elements = stripe.elements({
clientSecret: paymentIntent,
});
// Create Payment Element instead of Card Element
const paymentElement = elements.create('payment');
console.log('🎯 Mounting payment element...');
paymentElement.mount('#card-element');
console.log('🎯 Payment element mounted successfully!');
console.log('🎯 Stripe form ready!');
// Store payment element reference
window.paymentElement = paymentElement;
} catch (error) {
console.error('Error showing Stripe form:', error);
console.error('Error details:', error);
let errorMessage = translations.errorLoadingForm + ': ' + error.message;
// Try to get more specific error details
if (error.response) {
try {
const errorData = await error.response.json();
errorMessage = translations.errorLoadingForm + ': ' + (errorData.error || error.message);
} catch (e) {
errorMessage = translations.errorLoadingForm + ': HTTP ' + error.response.status;
}
}
showErrorMessage(errorMessage);
throw error; // Re-throw so the calling function can handle it
}
}
function backToPaymentMethods() {
document.getElementById('stripePaymentForm').style.display = 'none';
document.getElementById('paymentMethodsSection').style.display = 'block';
// Clean up Stripe Elements
if (window.paymentElement) {
window.paymentElement.destroy();
window.paymentElement = null;
}
}
async function confirmStripePayment() {
if (!paymentIntent || !window.paymentElement) {
showErrorMessage(translations.formNotReady);
return;
}
const payButton = document.getElementById('stripe-pay-button');
const originalText = payButton.innerHTML;
try {
payButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + translations.processing;
payButton.disabled = true;
const { error } = await stripe.confirmPayment({
elements: elements,
confirmParams: {
return_url: window.location.origin + '/checkout.php?success=1',
},
redirect: 'if_required',
});
if (error) {
throw new Error(error.message);
}
// Payment successful - redirect will happen automatically
showSuccessMessage('Payment successful! Redirecting...');
} catch (error) {
console.error('Payment error:', error);
showErrorMessage(translations.paymentFailed + ': ' + error.message);
payButton.innerHTML = originalText;
payButton.disabled = false;
}
}
// Unified notification system
function showNotification(message, type = 'success', duration = 5000) {
// Remove any existing notifications
const existingNotifications = document.querySelectorAll('.success-message, .error-message, .notification-message');
existingNotifications.forEach(notif => {
notif.style.animation = 'slideOut 0.3s ease-out';
setTimeout(() => notif.remove(), 300);
});
const notificationDiv = document.createElement('div');
const isError = type === 'error';
const className = isError ? 'error-message' : 'success-message';
notificationDiv.className = className;
const icon = isError ? 'fa-exclamation-circle' : 'fa-check-circle';
notificationDiv.innerHTML = `
<div class="${isError ? 'error' : 'success'}-content">
<i class="fas ${icon}"></i>
<p>${message}</p>
<button class="notification-close" aria-label="Close">
<i class="fas fa-times"></i>
</button>
</div>
`;
document.body.appendChild(notificationDiv);
// Auto-dismiss after duration
const timeout = setTimeout(() => {
notificationDiv.style.animation = 'slideOut 0.3s ease-out';
setTimeout(() => notificationDiv.remove(), 300);
}, duration);
// Clear timeout if user manually closes
const closeBtn = notificationDiv.querySelector('.notification-close');
closeBtn.addEventListener('click', () => {
clearTimeout(timeout);
notificationDiv.style.animation = 'slideOut 0.3s ease-out';
setTimeout(() => notificationDiv.remove(), 300);
});
}
// Keep showSuccessMessage for backward compatibility
function showSuccessMessage(message) {
showNotification(message, 'success', 3000);
}
// Add showErrorMessage for convenience
function showErrorMessage(message) {
showNotification(message, 'error', 6000);
}
// Make notification functions globally available
window.showNotification = showNotification;
window.showSuccessMessage = showSuccessMessage;
window.showErrorMessage = showErrorMessage;
// Authentication Functions
function switchAuthTab(tab, event) {
// Update tab buttons
document.querySelectorAll('.auth-tab').forEach(btn => {
btn.classList.remove('active');
});
if (event && event.currentTarget) {
event.currentTarget.classList.add('active');
} else {
// Fallback: find button by tab
document.querySelectorAll('.auth-tab').forEach(btn => {
if (btn.getAttribute('onclick') && btn.getAttribute('onclick').includes(tab)) {
btn.classList.add('active');
}
});
}
// Update content
document.querySelectorAll('.auth-content').forEach(content => {
content.classList.remove('active');
});
const targetContent = document.getElementById(tab + 'Content');
if (targetContent) {
targetContent.classList.add('active');
}
}
function proceedAsGuest() {
// Guest checkout is not supported for tracks - they require a user account
// Show error message explaining that account is required
showErrorMessage('An account is required to purchase tracks. Tracks need to be saved to your library, which requires an account. Please login or create an account to continue.');
// Optionally, switch to register tab to encourage account creation
setTimeout(() => {
switchAuthTab('register', null);
}, 2000);
}
// Login form handler
document.addEventListener('DOMContentLoaded', function() {
const loginForm = document.getElementById('loginForm');
if (loginForm) {
loginForm.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const loginData = {
email: formData.get('login_email'),
password: formData.get('login_password')
};
try {
const response = await fetch('/auth/login.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(loginData)
});
const result = await response.json();
if (result.success) {
// Login successful - reload page to show logged-in state
window.location.reload();
} else {
showErrorMessage('Login failed: ' + (result.error || 'Invalid credentials'));
}
} catch (error) {
console.error('Login error:', error);
showErrorMessage('Login failed: ' + error.message);
}
});
}
// Registration form handler
const registerForm = document.getElementById('registerForm');
if (registerForm) {
registerForm.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const registerData = {
first_name: formData.get('register_first_name'),
last_name: formData.get('register_last_name'),
email: formData.get('register_email'),
password: formData.get('register_password'),
confirm_password: formData.get('register_confirm_password')
};
// Validate passwords match
if (registerData.password !== registerData.confirm_password) {
showErrorMessage('Passwords do not match');
return;
}
try {
const response = await fetch('/auth/register.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(registerData)
});
const result = await response.json();
if (result.success) {
// Registration successful - reload page to show logged-in state
window.location.reload();
} else {
showErrorMessage('Registration failed: ' + (result.error || 'Please try again'));
}
} catch (error) {
console.error('Registration error:', error);
showErrorMessage('Registration failed: ' + error.message);
}
});
}
// Social login handlers
const googleBtn = document.querySelector('.btn-google');
if (googleBtn) {
googleBtn.addEventListener('click', function(e) {
e.preventDefault();
showNotification('Google login integration coming soon!', 'success', 3000);
});
}
const facebookBtn = document.querySelector('.btn-facebook');
if (facebookBtn) {
facebookBtn.addEventListener('click', function(e) {
e.preventDefault();
showNotification('Facebook login integration coming soon!', 'success', 3000);
});
}
});
// Coupon functionality
async function applyCoupon() {
const couponCode = document.getElementById('couponCode').value.trim();
const couponMessage = document.getElementById('couponMessage');
if (!couponCode) {
couponMessage.textContent = 'Please enter a coupon code';
couponMessage.className = 'coupon-message error';
return;
}
try {
const response = await fetch('/api/apply_coupon.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
coupon_code: couponCode,
cart_total: <?= $total ?>
})
});
const result = await response.json();
if (result.success) {
couponMessage.textContent = `🎉 Coupon applied! ${result.discount_type === 'percentage' ? result.discount + '%' : '$' + result.discount} discount`;
couponMessage.className = 'coupon-message success';
// Update total display
const totalElement = document.querySelector('.total-row.final span:last-child');
if (totalElement) {
const newTotal = (<?= $total ?> - result.discount_amount).toFixed(2);
totalElement.textContent = `$${newTotal}`;
}
// Disable coupon input
document.getElementById('couponCode').disabled = true;
document.querySelector('.coupon-input button').disabled = true;
} else {
couponMessage.textContent = result.error || 'Invalid coupon code';
couponMessage.className = 'coupon-message error';
}
} catch (error) {
console.error('Coupon error:', error);
couponMessage.textContent = 'Error applying coupon. Please try again.';
couponMessage.className = 'coupon-message error';
}
}
// Allow Enter key to apply coupon
document.addEventListener('DOMContentLoaded', function() {
const couponInput = document.getElementById('couponCode');
if (couponInput) {
couponInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
applyCoupon();
}
});
}
});
// Update cart item quantity
async function updateQuantity(index, type, identifier, cartKey, change) {
try {
if (type === 'track') {
showErrorMessage(translations.trackSingleQuantity || 'This track can only be purchased once.');
return;
}
if (type === 'ticket' && change > 0) {
const itemCard = document.querySelector(`.cart-item[data-index="${index}"]`);
const limit = itemCard ? parseInt(itemCard.dataset.ticketLimit || TICKET_PURCHASE_LIMIT, 10) : TICKET_PURCHASE_LIMIT;
const currentQtyEl = itemCard ? itemCard.querySelector('.quantity-display') : null;
const currentQty = currentQtyEl ? parseInt(currentQtyEl.textContent, 10) : 1;
if (currentQty >= limit) {
showErrorMessage(translations.ticketLimit.replace(':limit', limit));
return;
}
}
const response = await fetch('/api/update_cart_quantity.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: type,
identifier: identifier,
cart_key: cartKey,
change: change
})
});
const result = await response.json();
if (result.success) {
// Reload page to show updated quantities and totals
window.location.reload();
} else {
showErrorMessage('Error updating quantity: ' + (result.error || 'Unknown error'));
}
} catch (error) {
console.error('Error updating quantity:', error);
showErrorMessage('Error updating quantity: ' + error.message);
}
}
// Remove item from cart
async function removeItem(index, type, identifier, cartKey) {
const confirmMessage = type === 'credit' ? translations.removeCreditConfirm : translations.removeItemConfirm;
if (!confirm(confirmMessage)) {
return;
}
try {
const response = await fetch('/api/update_cart_quantity.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: type,
identifier: identifier,
cart_key: cartKey,
remove: true
})
});
const result = await response.json();
if (result.success) {
// Reload page to show updated cart
window.location.reload();
} else {
showErrorMessage('Error removing item: ' + (result.error || 'Unknown error'));
}
} catch (error) {
console.error('Error removing item:', error);
showErrorMessage('Error removing item: ' + error.message);
}
}
</script>
<?php include 'includes/footer.php'; ?>