![]() 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/private_html/ |
<?php
/**
* Subscription Management Page
* View and manage active subscriptions
*/
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Start output buffering BEFORE session_start to prevent any output
if (ob_get_level() === 0) {
ob_start();
}
session_start();
require_once 'config/database.php';
require_once __DIR__ . '/includes/translations.php';
if (!isset($_SESSION['user_id'])) {
// Clean output buffer before redirect
while (ob_get_level() > 0) {
ob_end_clean();
}
header('Location: /auth/login.php');
exit;
}
try {
$pdo = getDBConnection();
// Check if subscription tables exist
$tables_check = $pdo->query("SHOW TABLES LIKE 'user_subscriptions'");
if ($tables_check->rowCount() == 0) {
throw new Exception("Subscription tables not set up. Please run create_subscription_tables.php first.");
}
require_once __DIR__ . '/utils/subscription_helpers.php';
// Get user info for debugging
$user_stmt = $pdo->prepare("SELECT id, name, email, stripe_customer_id, plan FROM users WHERE id = ?");
$user_stmt->execute([$_SESSION['user_id']]);
$current_user = $user_stmt->fetch(PDO::FETCH_ASSOC);
// FIX: Force refresh subscription data from database to ensure we have latest plan
// Check for active subscription directly from database (most reliable)
$active_sub_stmt = $pdo->prepare("
SELECT id, plan_name, status, current_period_start, current_period_end, stripe_subscription_id
FROM user_subscriptions
WHERE user_id = ? AND status IN ('active', 'trialing') AND current_period_end > NOW()
ORDER BY created_at DESC
LIMIT 1
");
$active_sub_stmt->execute([$_SESSION['user_id']]);
$active_sub_db = $active_sub_stmt->fetch(PDO::FETCH_ASSOC);
// Use unified function to get effective plan and subscription
$effective_plan = getEffectivePlan($_SESSION['user_id']);
$subscription = getEffectiveSubscription($_SESSION['user_id']);
// CRITICAL FIX: If we found an active subscription in DB but effective_plan is 'free', force update
if ($active_sub_db && $effective_plan === 'free') {
error_log("FIX: User {$_SESSION['user_id']} has active subscription in DB (plan: {$active_sub_db['plan_name']}, status: {$active_sub_db['status']}) but getEffectivePlan returned 'free'. Forcing correct plan.");
$effective_plan = $active_sub_db['plan_name'];
// Also update users.plan to match
$fix_plan_stmt = $pdo->prepare("UPDATE users SET plan = ? WHERE id = ?");
$fix_plan_stmt->execute([$active_sub_db['plan_name'], $_SESSION['user_id']]);
// Refresh subscription object
if (!$subscription) {
$subscription = getSubscriptionInfo($_SESSION['user_id']);
}
if ($subscription) {
$subscription['plan_name'] = $active_sub_db['plan_name'];
$subscription['effective_plan'] = $active_sub_db['plan_name'];
}
}
$usage = null;
// Debug: Check if subscription exists in database
if (!$subscription && $current_user) {
$check_stmt = $pdo->prepare("SELECT COUNT(*) FROM user_subscriptions WHERE user_id = ?");
$check_stmt->execute([$_SESSION['user_id']]);
$sub_count = $check_stmt->fetchColumn();
if ($sub_count == 0 && !empty($current_user['stripe_customer_id'])) {
// User has Stripe customer ID but no subscription in database
// This suggests the subscription needs to be synced
error_log("User {$_SESSION['user_id']} ({$current_user['email']}) has Stripe customer ID ({$current_user['stripe_customer_id']}) but no subscription in database. May need to sync from Stripe.");
}
}
// Determine effective plan for usage tracking - use active subscription plan if available
$plan_for_usage = $effective_plan;
if ($active_sub_db && in_array($active_sub_db['status'], ['active', 'trialing'])) {
$plan_for_usage = $active_sub_db['plan_name'];
} elseif ($subscription && isset($subscription['effective_plan'])) {
$plan_for_usage = $subscription['effective_plan'];
}
if ($subscription && $plan_for_usage !== 'free' && in_array($plan_for_usage, ['essential', 'starter', 'pro', 'premium', 'enterprise'])) {
try {
$usage = getMonthlyTrackUsage($_SESSION['user_id'], $plan_for_usage);
} catch (Exception $e) {
error_log("Error getting monthly usage: " . $e->getMessage());
$usage = ['tracks_created' => 0, 'track_limit' => 5];
}
} else {
// User has no subscription or is on free plan, create empty usage record for display
$usage = ['tracks_created' => 0, 'track_limit' => 0];
}
} catch (Exception $e) {
error_log("Error in manage_subscription.php: " . $e->getMessage());
error_log("Stack trace: " . $e->getTraceAsString());
$subscription = null;
$usage = ['tracks_created' => 0, 'track_limit' => 0];
$error_message = t('subscription.load_error') . " " . htmlspecialchars($e->getMessage());
}
// Handle cancellation
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['cancel_subscription'])) {
if ($subscription && $subscription['stripe_subscription_id']) {
$stripe_secret = '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';
// Cancel subscription at period end
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.stripe.com/v1/subscriptions/' . urlencode($subscription['stripe_subscription_id']));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $stripe_secret]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'cancel_at_period_end' => 'true'
]));
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 200) {
$success_message = t('subscription.cancel_success');
// Refresh subscription data
$subscription = getSubscriptionInfo($_SESSION['user_id']);
} else {
$error_message = t('subscription.cancel_failed');
}
}
}
// Handle reactivation
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['reactivate_subscription'])) {
if ($subscription && $subscription['stripe_subscription_id']) {
$stripe_secret = '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';
// Reactivate subscription (remove cancel_at_period_end)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.stripe.com/v1/subscriptions/' . urlencode($subscription['stripe_subscription_id']));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $stripe_secret]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'cancel_at_period_end' => 'false'
]));
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code === 200) {
$success_message = t('subscription.reactivate_success');
// Refresh subscription data
$subscription = getSubscriptionInfo($_SESSION['user_id']);
} else {
$error_message = t('subscription.reactivate_failed');
}
}
}
// Check for switch success message
if (isset($_GET['switched']) && $_GET['switched'] == '1') {
$success_message = t('subscription.switch_success');
}
// Check for canceled to free message
if (isset($_GET['canceled_to_free']) && $_GET['canceled_to_free'] == '1') {
$success_message = t('subscribe.canceled_to_free_success', ['default' => 'Your subscription has been canceled and you have been switched to the Free plan.']);
$warning_message = t('subscribe.rights_forfeit_notice', ['default' => 'IMPORTANT: By switching to Free, you have forfeited all commercial track rights as per our Terms of Service.']);
}
// Handle admin manual plan change
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['admin_change_plan']) && isset($_SESSION['is_admin']) && $_SESSION['is_admin']) {
// SECURITY: Verify admin is only changing their own plan OR has explicit permission
$user_id_to_update = (int)($_POST['user_id'] ?? $_SESSION['user_id']);
// SECURITY: Prevent admin from changing other users' plans unless explicitly authorized
// For now, only allow admins to change their own plan via this interface
// If you need to change other users, use a dedicated admin tool with proper authorization
if ($user_id_to_update !== $_SESSION['user_id']) {
error_log("SECURITY WARNING: Admin {$_SESSION['user_id']} attempted to change plan for user {$user_id_to_update} via manage_subscription.php. Blocked.");
$error_message = t('subscription.error_updating_plan', ['error' => 'Unauthorized: Cannot change other users\' plans via this interface.']);
} else {
$new_plan = $_POST['new_plan'] ?? '';
$valid_plans = ['free', 'essential', 'starter', 'pro', 'premium', 'enterprise'];
if (in_array($new_plan, $valid_plans)) {
try {
$pdo->beginTransaction();
// Get current subscription (refresh it)
$current_sub = getSubscriptionInfo($user_id_to_update);
// Update user_subscriptions table if subscription exists
if ($current_sub && isset($current_sub['id'])) {
$update_stmt = $pdo->prepare("
UPDATE user_subscriptions
SET plan_name = ?,
updated_at = NOW()
WHERE id = ?
");
$update_stmt->execute([$new_plan, $current_sub['id']]);
}
// Always update users table
$user_update_stmt = $pdo->prepare("UPDATE users SET plan = ? WHERE id = ?");
$user_update_stmt->execute([$new_plan, $user_id_to_update]);
// Update track limit if subscription exists
if ($current_sub && isset($current_sub['id'])) {
$plans_config = require __DIR__ . '/config/subscription_plans.php';
$track_limit = $plans_config[$new_plan]['tracks_per_month'] ?? 5;
$period_start = $current_sub['current_period_start'] ?? null;
if ($period_start) {
if (is_numeric($period_start)) {
$period_start = date('Y-m-d H:i:s', $period_start);
}
// Get current usage to check if we need to cap tracks_created
$current_usage_stmt = $pdo->prepare("
SELECT tracks_created, track_limit FROM monthly_track_usage
WHERE user_id = ? AND subscription_period_start = ?
");
$current_usage_stmt->execute([$user_id_to_update, $period_start]);
$current_usage = $current_usage_stmt->fetch(PDO::FETCH_ASSOC);
if ($current_usage) {
$current_tracks_created = (int)($current_usage['tracks_created'] ?? 0);
$old_track_limit = (int)($current_usage['track_limit'] ?? 0);
$is_downgrade = $track_limit < $old_track_limit;
// CRITICAL: If downgrading and tracks_created exceeds new limit, cap it
$new_tracks_created = $current_tracks_created;
if ($is_downgrade && $current_tracks_created > $track_limit) {
$new_tracks_created = $track_limit;
error_log("SECURITY (Admin): User {$user_id_to_update} manually downgraded from {$old_track_limit} to {$track_limit} tracks. Capping tracks_created from {$current_tracks_created} to {$track_limit}");
}
$usage_update_stmt = $pdo->prepare("
UPDATE monthly_track_usage
SET track_limit = ?,
tracks_created = ?,
updated_at = NOW()
WHERE user_id = ? AND subscription_period_start = ?
");
$usage_update_stmt->execute([$track_limit, $new_tracks_created, $user_id_to_update, $period_start]);
} else {
// No usage record exists, just update limit
$usage_update_stmt = $pdo->prepare("
UPDATE monthly_track_usage
SET track_limit = ?,
updated_at = NOW()
WHERE user_id = ? AND subscription_period_start = ?
");
$usage_update_stmt->execute([$track_limit, $user_id_to_update, $period_start]);
}
}
}
$pdo->commit();
$plans_config = require __DIR__ . '/config/subscription_plans.php';
require_once __DIR__ . '/includes/translations.php';
$plan_display_name = getPlanLabel($new_plan, $plans_config[$new_plan]['name'] ?? ucfirst($new_plan));
$success_message = t('subscription.plan_manually_changed', ['plan' => $plan_display_name]);
// Refresh subscription data
$subscription = getSubscriptionInfo($user_id_to_update);
if ($subscription) {
try {
$usage = getMonthlyTrackUsage($user_id_to_update, $subscription['plan_name'] ?? null);
} catch (Exception $e) {
error_log("Error getting monthly usage: " . $e->getMessage());
$usage = ['tracks_created' => 0, 'track_limit' => 5];
}
}
// Refresh current_user data
$user_stmt = $pdo->prepare("SELECT id, name, email, stripe_customer_id, plan FROM users WHERE id = ?");
$user_stmt->execute([$user_id_to_update]);
$current_user = $user_stmt->fetch(PDO::FETCH_ASSOC);
} catch (Exception $e) {
$pdo->rollBack();
$error_message = t('subscription.error_updating_plan', ['error' => htmlspecialchars($e->getMessage())]);
error_log("Admin plan change error: " . $e->getMessage());
}
} else {
$error_message = t('subscription.invalid_plan_selected');
}
}
}
$page_title = t('subscription.manage_subscription');
/**
* Translate a plan feature string
*/
function translateFeature($feature) {
// Extract number from "X tracks per month" format
if (preg_match('/^(\d+)\s+tracks\s+per\s+month$/i', $feature, $matches)) {
return t('plan.feature.tracks_per_month', ['count' => $matches[1]]);
}
// Map exact feature strings to translation keys
$featureMap = [
'Monthly reset' => 'plan.feature.monthly_reset',
'Basic AI models' => 'plan.feature.basic_ai_models',
'Advanced AI models' => 'plan.feature.advanced_ai_models',
'Standard generation speed' => 'plan.feature.standard_speed',
'High-speed generation' => 'plan.feature.high_speed',
'Priority queue access' => 'plan.feature.priority_queue',
'Highest priority queue' => 'plan.feature.highest_priority',
'Personal use license' => 'plan.feature.personal_license',
'Commercial use license' => 'plan.feature.commercial_license',
'Unlimited downloads' => 'plan.feature.unlimited_downloads',
'API access' => 'plan.feature.api_access',
'Full API access' => 'plan.feature.full_api_access',
'Dedicated support' => 'plan.feature.dedicated_support',
'White-label options' => 'plan.feature.white_label',
'Dedicated account manager' => 'plan.feature.account_manager',
'Custom integrations' => 'plan.feature.custom_integrations',
'SLA guarantee' => 'plan.feature.sla_guarantee',
'Cancel anytime' => 'plan.feature.cancel_anytime',
];
// Return translated feature if mapping exists, otherwise return original
if (isset($featureMap[$feature])) {
return t($featureMap[$feature]);
}
// Fallback to original if no translation found
return $feature;
}
// Check if header file exists
if (!file_exists(__DIR__ . '/includes/header.php')) {
die("Error: Header file not found. Please check file paths.");
}
include 'includes/header.php';
?>
<style>
.subscription-page {
max-width: 1000px;
margin: 60px auto;
padding: 40px 20px;
}
.subscription-page h1 {
color: white;
font-size: 3.5rem;
font-weight: 800;
margin-bottom: 50px;
background: linear-gradient(135deg, #ffffff, #667eea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
}
.subscription-card {
background: linear-gradient(135deg, rgba(30, 30, 30, 0.95), rgba(20, 20, 20, 0.95));
border-radius: 20px;
padding: 50px;
margin-bottom: 30px;
border: 1px solid rgba(102, 126, 234, 0.2);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.subscription-card:hover {
border-color: rgba(102, 126, 234, 0.4);
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.6);
}
.subscription-card h2 {
color: white;
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 40px;
display: flex;
align-items: center;
gap: 15px;
}
.subscription-card h2 i {
color: #667eea;
font-size: 2.2rem;
}
.subscription-info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin-bottom: 40px;
}
.info-item {
background: rgba(102, 126, 234, 0.1);
padding: 25px;
border-radius: 12px;
border: 1px solid rgba(102, 126, 234, 0.2);
}
.info-label {
color: #a0aec0;
font-size: 1.4rem;
margin-bottom: 12px;
font-weight: 500;
}
.info-value {
color: white;
font-size: 2rem;
font-weight: 700;
}
.info-value.renewal-date {
color: #48bb78;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.renewal-date-badge {
background: rgba(72, 187, 120, 0.2);
border: 1px solid rgba(72, 187, 120, 0.4);
padding: 6px 12px;
border-radius: 6px;
font-size: 1.2rem;
font-weight: 600;
color: #48bb78;
}
.renewal-date-countdown {
font-size: 1.4rem;
color: #a0aec0;
font-weight: 500;
}
.status-active {
color: #48bb78 !important;
}
.status-inactive {
color: #ffc107 !important;
}
.usage-card {
background: rgba(15, 15, 15, 0.8);
padding: 35px;
border-radius: 16px;
margin-bottom: 30px;
border: 1px solid rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
}
.usage-card.usage-limit-reached {
border-color: rgba(229, 62, 62, 0.5);
background: rgba(30, 15, 15, 0.9);
box-shadow: 0 0 30px rgba(229, 62, 62, 0.2);
}
.usage-card h3 {
color: white;
font-size: 2rem;
margin-bottom: 25px;
display: flex;
align-items: center;
gap: 12px;
}
.usage-card h3 i {
color: #667eea;
}
.usage-display-modern {
margin-bottom: 25px;
}
.usage-number-display {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 25px;
}
.usage-current, .usage-remaining {
text-align: center;
padding: 20px;
background: rgba(42, 42, 42, 0.5);
border-radius: 12px;
border: 1px solid rgba(102, 126, 234, 0.2);
}
.usage-label, .remaining-label {
display: block;
color: #a0aec0;
font-size: 1.2rem;
margin-bottom: 8px;
font-weight: 500;
}
.usage-value {
display: block;
color: white;
font-size: 3rem;
font-weight: 800;
line-height: 1;
}
.usage-value.usage-full {
color: #e53e3e;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.remaining-value {
display: block;
color: #48bb78;
font-size: 3rem;
font-weight: 800;
line-height: 1;
}
.usage-limit-badge {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 15px;
background: rgba(229, 62, 62, 0.2);
border: 2px solid #e53e3e;
border-radius: 12px;
color: #e53e3e;
font-size: 1.4rem;
font-weight: 700;
}
.usage-limit-badge i {
font-size: 1.6rem;
}
.progress-bar-modern {
position: relative;
background: rgba(42, 42, 42, 0.8);
height: 32px;
border-radius: 16px;
overflow: hidden;
margin-bottom: 20px;
border: 2px solid rgba(102, 126, 234, 0.2);
}
.progress-bar-fill-modern {
height: 100%;
transition: width 0.5s ease;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
position: relative;
}
.progress-bar-fill-modern.progress-full {
box-shadow: 0 0 30px rgba(229, 62, 62, 0.6);
animation: glow 2s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 30px rgba(229, 62, 62, 0.6); }
50% { box-shadow: 0 0 40px rgba(229, 62, 62, 0.9); }
}
.progress-pulse {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
animation: pulse-bar 2s ease-in-out infinite;
}
@keyframes pulse-bar {
0%, 100% { opacity: 0; }
50% { opacity: 0.3; }
}
.reset-info {
color: #a0aec0;
font-size: 1.4rem;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.reset-info i {
color: #667eea;
}
.reset-info strong {
color: #667eea;
font-weight: 600;
}
.upgrade-suggestion {
margin-top: 30px;
padding: 30px;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.15), rgba(118, 75, 162, 0.15));
border: 2px solid rgba(102, 126, 234, 0.4);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 25px;
animation: slideIn 0.5s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.upgrade-content {
display: flex;
align-items: center;
gap: 20px;
flex: 1;
}
.upgrade-icon {
font-size: 3rem;
color: #667eea;
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.upgrade-text h4 {
color: white;
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 8px;
}
.upgrade-text p {
color: #a0aec0;
font-size: 1.4rem;
margin: 0;
line-height: 1.5;
}
.upgrade-button {
padding: 16px 32px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-radius: 12px;
font-size: 1.5rem;
font-weight: 700;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 10px;
transition: all 0.3s ease;
white-space: nowrap;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.upgrade-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.upgrade-button i {
font-size: 1.3rem;
}
.action-buttons {
margin-top: 40px;
display: flex;
gap: 20px;
flex-wrap: wrap;
padding-top: 30px;
border-top: 1px solid rgba(102, 126, 234, 0.2);
}
.btn {
padding: 16px 40px;
border-radius: 12px;
font-size: 1.6rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: none;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 10px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn-primary:hover {
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.4);
}
.btn-danger {
background: linear-gradient(135deg, #e53e3e, #c53030);
color: white;
position: relative;
overflow: hidden;
}
.btn-danger::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
.btn-danger:hover::before {
left: 100%;
}
.btn-danger:hover {
box-shadow: 0 10px 30px rgba(229, 62, 62, 0.4);
transform: translateY(-2px);
}
.btn-secondary {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
border: 1px solid rgba(102, 126, 234, 0.4);
}
.btn-secondary:hover {
background: rgba(102, 126, 234, 0.3);
color: white;
}
.btn-success {
background: linear-gradient(135deg, #48bb78, #38a169);
color: white;
}
.btn-success:hover {
box-shadow: 0 10px 30px rgba(72, 187, 120, 0.4);
}
.no-subscription-card {
background: linear-gradient(135deg, rgba(30, 30, 30, 0.95), rgba(20, 20, 20, 0.95));
border-radius: 20px;
padding: 80px 50px;
text-align: center;
border: 1px solid rgba(102, 126, 234, 0.2);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.no-subscription-card p {
color: #a0aec0;
font-size: 2rem;
margin-bottom: 40px;
font-weight: 500;
}
.warning-box {
background: rgba(90, 26, 26, 0.5);
padding: 25px;
border-radius: 12px;
border: 1px solid rgba(255, 193, 7, 0.3);
margin-bottom: 20px;
}
.warning-box p {
color: #ffc107;
font-size: 1.6rem;
margin: 0 0 15px 0;
font-weight: 500;
}
.alert {
padding: 20px 25px;
border-radius: 12px;
margin-top: 30px;
font-size: 1.5rem;
font-weight: 500;
}
.alert-success {
background: rgba(45, 80, 22, 0.5);
border: 1px solid rgba(72, 187, 120, 0.3);
color: #48bb78;
}
.alert-error {
background: rgba(90, 26, 26, 0.5);
border: 1px solid rgba(229, 62, 62, 0.3);
color: #e53e3e;
}
.back-link {
margin-top: 50px;
text-align: center;
}
.back-link a {
color: #667eea;
text-decoration: none;
font-size: 1.6rem;
font-weight: 500;
transition: color 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.back-link a:hover {
color: #764ba2;
}
@media (max-width: 768px) {
.subscription-page {
margin: 20px auto;
padding: 20px 15px;
}
.subscription-page h1 {
font-size: 2rem;
margin-bottom: 30px;
}
.subscription-card {
padding: 25px 15px;
border-radius: 16px;
}
.subscription-card h2 {
font-size: 1.6rem;
margin-bottom: 25px;
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.subscription-card h2 i {
font-size: 1.8rem;
}
.subscription-info-grid {
grid-template-columns: 1fr;
gap: 15px;
margin-bottom: 25px;
}
.info-item {
padding: 20px 15px;
}
.info-label {
font-size: 1.2rem;
margin-bottom: 8px;
}
.info-value {
font-size: 1.5rem;
}
.usage-card {
padding: 20px 15px;
margin-bottom: 20px;
}
.usage-card h3 {
font-size: 1.5rem;
margin-bottom: 15px;
flex-wrap: wrap;
}
.usage-stats {
flex-direction: column;
align-items: flex-start;
gap: 8px;
margin-bottom: 15px;
}
.usage-stats span:first-child {
font-size: 1.3rem;
}
.usage-stats span:last-child {
font-size: 1.6rem;
}
.progress-bar-container {
height: 24px;
margin-bottom: 15px;
}
.reset-info {
font-size: 1.2rem;
}
.action-buttons {
margin-top: 25px;
flex-direction: column;
gap: 15px;
}
.btn {
padding: 14px 24px;
font-size: 1.4rem;
width: 100%;
justify-content: center;
min-height: 44px;
}
.no-subscription-card {
padding: 40px 20px;
}
.no-subscription-card p {
font-size: 1.5rem;
margin-bottom: 25px;
}
.warning-box {
padding: 20px 15px;
margin-bottom: 15px;
}
.warning-box p {
font-size: 1.3rem;
margin-bottom: 12px;
}
.alert {
padding: 15px 18px;
font-size: 1.3rem;
margin-top: 20px;
}
.back-link {
margin-top: 30px;
}
.back-link a {
font-size: 1.4rem;
padding: 10px;
}
}
@media (max-width: 480px) {
.subscription-page {
padding: 15px 10px;
}
.subscription-page h1 {
font-size: 1.75rem;
margin-bottom: 20px;
}
.subscription-card {
padding: 20px 12px;
}
.subscription-card h2 {
font-size: 1.4rem;
}
.info-label {
font-size: 1.1rem;
}
.info-value {
font-size: 1.3rem;
}
.usage-card h3 {
font-size: 1.3rem;
}
.usage-stats span:first-child {
font-size: 1.2rem;
}
.usage-stats span:last-child {
font-size: 1.4rem;
}
.btn {
padding: 12px 20px;
font-size: 1.3rem;
min-height: 44px;
}
.no-subscription-card p {
font-size: 1.3rem;
}
.warning-box p {
font-size: 1.2rem;
}
.usage-number-display {
grid-template-columns: 1fr;
gap: 15px;
}
.usage-value, .remaining-value {
font-size: 2.5rem;
}
.upgrade-suggestion {
flex-direction: column;
text-align: center;
padding: 25px 20px;
}
.upgrade-content {
flex-direction: column;
text-align: center;
}
.upgrade-button {
width: 100%;
justify-content: center;
}
}
/* Professional Confirmation Modal */
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(8px);
z-index: 10000;
align-items: center;
justify-content: center;
animation: fadeIn 0.3s ease;
}
.modal-overlay.active {
display: flex;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-container {
background: linear-gradient(135deg, rgba(30, 30, 30, 0.98), rgba(20, 20, 20, 0.98));
border-radius: 20px;
padding: 0;
max-width: 550px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
border: 2px solid rgba(102, 126, 234, 0.3);
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.8);
animation: slideUp 0.3s ease;
position: relative;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modal-header {
padding: 30px 35px 25px;
border-bottom: 1px solid rgba(102, 126, 234, 0.2);
display: flex;
align-items: center;
gap: 15px;
}
.modal-header i {
font-size: 2.5rem;
color: #e53e3e;
}
.modal-header h3 {
color: white;
font-size: 2.2rem;
font-weight: 700;
margin: 0;
flex: 1;
}
.modal-close {
background: transparent;
border: none;
color: #a0aec0;
font-size: 2rem;
cursor: pointer;
padding: 5px;
width: 35px;
height: 35px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
transition: all 0.2s ease;
}
.modal-close:hover {
background: rgba(255, 255, 255, 0.1);
color: white;
}
.modal-body {
padding: 30px 35px;
}
.modal-body p {
color: #a0aec0;
font-size: 1.5rem;
line-height: 1.7;
margin-bottom: 20px;
}
.modal-body .warning-highlight {
background: rgba(229, 62, 62, 0.15);
border-left: 4px solid #e53e3e;
padding: 20px;
border-radius: 8px;
margin: 25px 0;
}
.modal-body .warning-highlight p {
color: #ffc107;
font-size: 1.4rem;
margin: 0;
font-weight: 600;
}
.modal-body .info-box {
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.3);
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.modal-body .info-box p {
color: #a0aec0;
font-size: 1.3rem;
margin: 0;
}
.modal-body .info-box strong {
color: #667eea;
}
.modal-footer {
padding: 25px 35px 30px;
border-top: 1px solid rgba(102, 126, 234, 0.2);
display: flex;
gap: 15px;
justify-content: flex-end;
}
.modal-btn {
padding: 14px 28px;
border-radius: 10px;
font-size: 1.5rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
border: none;
display: inline-flex;
align-items: center;
gap: 10px;
min-width: 140px;
justify-content: center;
}
.modal-btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: #a0aec0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.modal-btn-secondary:hover {
background: rgba(255, 255, 255, 0.15);
color: white;
}
.modal-btn-danger {
background: linear-gradient(135deg, #e53e3e, #c53030);
color: white;
box-shadow: 0 4px 15px rgba(229, 62, 62, 0.4);
}
.modal-btn-danger:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(229, 62, 62, 0.6);
}
@media (max-width: 768px) {
.modal-container {
width: 95%;
max-width: none;
}
.modal-header {
padding: 25px 20px 20px;
}
.modal-header h3 {
font-size: 1.8rem;
}
.modal-body {
padding: 25px 20px;
}
.modal-footer {
padding: 20px;
flex-direction: column;
}
.modal-btn {
width: 100%;
}
}
</style>
<main class="subscription-page">
<h1><i class="fas fa-sync-alt"></i> <?= t('subscription.manage_subscription') ?></h1>
<?php if ($subscription): ?>
<div class="subscription-card">
<h2>
<i class="fas fa-crown"></i>
<?= t('subscription.current_subscription') ?>
</h2>
<?php
// FIX: Use active subscription plan directly from database (most reliable)
// Check for active subscription first
$active_sub_check = $pdo->prepare("
SELECT plan_name, status
FROM user_subscriptions
WHERE user_id = ? AND status IN ('active', 'trialing') AND current_period_end > NOW()
ORDER BY created_at DESC
LIMIT 1
");
$active_sub_check->execute([$_SESSION['user_id']]);
$active_sub_plan = $active_sub_check->fetch(PDO::FETCH_ASSOC);
// Use active subscription plan if found, otherwise use effective_plan
if ($active_sub_plan && in_array($active_sub_plan['status'], ['active', 'trialing'])) {
$display_plan = $active_sub_plan['plan_name'];
} else {
$display_plan = $effective_plan;
if ($subscription && isset($subscription['effective_plan'])) {
$display_plan = $subscription['effective_plan'];
}
}
$is_canceled_or_refunded = in_array($subscription['status'] ?? '', ['canceled', 'past_due', 'unpaid']);
?>
<?php if ($is_canceled_or_refunded): ?>
<div class="warning-box" style="margin-bottom: 30px;">
<p>
<i class="fas fa-exclamation-triangle"></i>
<strong><?= t('subscription.subscription_canceled') ?></strong>
</p>
<p style="font-size: 1.4rem; margin-top: 10px; color: #a0aec0;">
<?= t('subscription.canceled_message') ?>
</p>
</div>
<?php endif; ?>
<div class="subscription-info-grid">
<div class="info-item">
<div class="info-label"><?= t('subscription.plan') ?></div>
<div class="info-value">
<?php
// Get proper plan label using translation function
$plans_config = require __DIR__ . '/config/subscription_plans.php';
$plan_key = $display_plan; // Use effective plan
$old_plan_key = (is_array($subscription) && isset($subscription['plan_name'])) ? $subscription['plan_name'] : 'free'; // Original plan for display if canceled
// Safety check: if plan_name looks corrupted (contains numbers/slashes), default to 'free'
if (preg_match('/[0-9\/]/', $plan_key) || !isset($plans_config[$plan_key])) {
error_log("Invalid plan_name detected in manage_subscription.php for user {$_SESSION['user_id']}: '{$plan_key}'");
$plan_key = 'free';
}
$plan_display_name = getPlanLabel($plan_key, $plans_config[$plan_key]['name'] ?? ucfirst($plan_key));
?>
<?php if ($is_canceled_or_refunded && $old_plan_key !== 'free'): ?>
<?php
$old_plan_display = getPlanLabel($old_plan_key, $plans_config[$old_plan_key]['name'] ?? ucfirst($old_plan_key));
?>
<span style="text-decoration: line-through; opacity: 0.5; margin-right: 10px;"><?= htmlspecialchars($old_plan_display) ?></span>
<span style="color: #ffc107;"><?= htmlspecialchars($plan_display_name) ?></span>
<?php else: ?>
<?= htmlspecialchars($plan_display_name) ?>
<?php endif; ?>
</div>
</div>
<div class="info-item">
<div class="info-label"><?= t('subscription.status') ?></div>
<div class="info-value <?= (is_array($subscription) && isset($subscription['status']) && $subscription['status'] === 'active') ? 'status-active' : 'status-inactive' ?>">
<?php
$subscription_status = (is_array($subscription) && isset($subscription['status'])) ? $subscription['status'] : 'unknown';
$status_key = 'subscription.status_' . $subscription_status;
$status_translation = t($status_key);
// If translation key doesn't exist, fall back to ucfirst
echo ($status_translation !== $status_key) ? $status_translation : ucfirst($subscription_status);
?>
</div>
</div>
<div class="info-item">
<div class="info-label"><?= $is_canceled_or_refunded ? t('subscription.ended') : t('subscription.renews') ?></div>
<div class="info-value">
<?= (is_array($subscription) && isset($subscription['current_period_end']) && $subscription['current_period_end']) ? date('M j, Y', strtotime($subscription['current_period_end'])) : t('subscription.na') ?>
</div>
</div>
</div>
<?php
// Only show usage if subscription is active (not canceled/refunded) and effective plan is valid
$is_canceled_or_refunded_check = false;
if (is_array($subscription) && isset($subscription['status'])) {
$is_canceled_or_refunded_check = in_array($subscription['status'], ['canceled', 'past_due', 'unpaid']);
}
if ($subscription && !$is_canceled_or_refunded_check && in_array($display_plan, ['essential', 'starter', 'pro', 'premium', 'enterprise']) && $usage):
?>
<?php
$tracks_used = $usage['tracks_created'] ?? 0;
$track_limit = $usage['track_limit'] ?? 0;
$usage_percentage = min(100, ($tracks_used / max(1, $track_limit)) * 100);
$is_at_limit = $tracks_used >= $track_limit;
$tracks_remaining = max(0, $track_limit - $tracks_used);
// Get next tier for upgrade suggestion
$plan_order = ['essential' => 1, 'starter' => 2, 'pro' => 3, 'premium' => 4, 'enterprise' => 5];
$current_plan_level = $plan_order[$display_plan] ?? 0;
$next_plan = null;
if ($current_plan_level < 5) {
$plans_config = require __DIR__ . '/config/subscription_plans.php';
foreach ($plan_order as $plan_key => $level) {
if ($level > $current_plan_level && isset($plans_config[$plan_key])) {
$next_plan = ['key' => $plan_key, 'data' => $plans_config[$plan_key]];
break;
}
}
}
require_once __DIR__ . '/includes/translations.php';
?>
<div class="usage-card <?= $is_at_limit ? 'usage-limit-reached' : '' ?>">
<h3>
<i class="fas fa-chart-line"></i>
<?= t('subscription.monthly_track_usage') ?>
</h3>
<div class="usage-display-modern">
<div class="usage-number-display">
<div class="usage-current">
<span class="usage-label"><?= t('subscription.tracks_used') ?></span>
<span class="usage-value <?= $is_at_limit ? 'usage-full' : '' ?>">
<?= $tracks_used ?> / <?= $track_limit ?>
</span>
</div>
<?php if (!$is_at_limit): ?>
<div class="usage-remaining">
<span class="remaining-label"><?= t('subscription.tracks_left_this_month') ?></span>
<span class="remaining-value"><?= $tracks_remaining ?></span>
</div>
<?php else: ?>
<div class="usage-limit-badge">
<i class="fas fa-exclamation-triangle"></i>
<span><?= t('subscription.limit_reached') ?></span>
</div>
<?php endif; ?>
</div>
<div class="progress-bar-modern">
<div class="progress-bar-fill-modern <?= $is_at_limit ? 'progress-full' : '' ?>"
style="width: <?= $usage_percentage ?>%;
background: <?= $is_at_limit ? 'linear-gradient(90deg, #e53e3e, #c53030)' : 'linear-gradient(90deg, #667eea, #764ba2)' ?>;">
</div>
<?php if ($is_at_limit): ?>
<div class="progress-pulse"></div>
<?php endif; ?>
</div>
<p class="reset-info">
<i class="fas fa-calendar-alt"></i>
<?= t('subscription.resets_on') ?>
<strong style="color: #48bb78;">
<?php
if (is_array($subscription) && isset($subscription['current_period_end']) && $subscription['current_period_end']) {
$period_end = strtotime($subscription['current_period_end']);
$now = time();
$days_remaining = max(0, floor(($period_end - $now) / 86400));
$formatted_date = date('F j, Y', $period_end);
echo $formatted_date;
if ($days_remaining > 0) {
echo ' (' . $days_remaining . ' ' . ($days_remaining === 1 ? t('subscription.day_remaining') : t('subscription.days_remaining')) . ')';
}
} else {
echo t('subscription.your_next_billing_date');
}
?>
</strong>
</p>
</div>
<?php if ($is_at_limit && $next_plan): ?>
<div class="upgrade-suggestion">
<div class="upgrade-content">
<div class="upgrade-icon">
<i class="fas fa-rocket"></i>
</div>
<div class="upgrade-text">
<h4><?= t('subscription.upgrade_suggestion_title') ?></h4>
<p><?= t('subscription.upgrade_suggestion_text', [
'plan' => getPlanLabel($next_plan['key'], $next_plan['data']['name']),
'tracks' => $next_plan['data']['tracks_per_month']
]) ?></p>
</div>
</div>
<form method="POST" action="/subscribe.php" style="margin: 0; display: inline;">
<input type="hidden" name="switch_plan" value="1">
<input type="hidden" name="plan" value="<?= $next_plan['key'] ?>">
<button type="submit" class="upgrade-button" style="border: none; cursor: pointer; font-family: inherit;">
<i class="fas fa-arrow-up"></i>
<?= t('subscription.upgrade_now') ?>
</button>
</form>
</div>
<?php elseif ($is_at_limit): ?>
<div class="upgrade-suggestion">
<div class="upgrade-content">
<div class="upgrade-icon">
<i class="fas fa-info-circle"></i>
</div>
<div class="upgrade-text">
<h4><?= t('subscription.max_plan_reached') ?></h4>
<p><?= t('subscription.max_plan_text') ?></p>
</div>
</div>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php
// Always show upgrade options for higher-tier plans (upsell section)
if ($subscription && isset($display_plan)) {
$is_canceled_for_upgrade = in_array($subscription['status'] ?? '', ['canceled', 'past_due', 'unpaid']);
if (!$is_canceled_for_upgrade && in_array($display_plan, ['essential', 'starter', 'pro', 'premium'])) {
$plans_config = require __DIR__ . '/config/subscription_plans.php';
$plan_order = ['essential' => 1, 'starter' => 2, 'pro' => 3, 'premium' => 4, 'enterprise' => 5];
$current_plan_level = $plan_order[$display_plan] ?? 0;
$upgrade_plans = [];
// Get all higher-tier plans
foreach ($plan_order as $plan_key => $level) {
if ($level > $current_plan_level && isset($plans_config[$plan_key])) {
$upgrade_plans[] = ['key' => $plan_key, 'data' => $plans_config[$plan_key], 'level' => $level];
}
}
if (!empty($upgrade_plans)):
?>
<div class="upgrade-options-section" style="margin-top: 40px; padding: 30px; background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); border-radius: 16px; border: 2px solid rgba(102, 126, 234, 0.3);">
<h3 style="color: white; font-size: 2rem; margin-bottom: 20px; display: flex; align-items: center; gap: 10px;">
<i class="fas fa-arrow-up" style="color: #667eea;"></i>
<?= t('subscription.upgrade_options') ?>
</h3>
<p style="color: #a0aec0; font-size: 1.3rem; margin-bottom: 30px;">
<?= t('subscription.upgrade_options_desc') ?>
</p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;">
<?php foreach ($upgrade_plans as $upgrade_plan): ?>
<div style="background: rgba(30, 30, 30, 0.8); padding: 25px; border-radius: 12px; border: 1px solid rgba(102, 126, 234, 0.3); transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-5px)'; this.style.borderColor='rgba(102, 126, 234, 0.6)';"
onmouseout="this.style.transform='translateY(0)'; this.style.borderColor='rgba(102, 126, 234, 0.3)';">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 15px;">
<i class="fas <?= $upgrade_plan['data']['icon'] ?>" style="font-size: 2rem; color: <?= $upgrade_plan['data']['color'] ?>;"></i>
<h4 style="color: white; font-size: 1.6rem; margin: 0;">
<?= htmlspecialchars(getPlanLabel($upgrade_plan['key'], $upgrade_plan['data']['name'])) ?>
</h4>
</div>
<div style="margin-bottom: 15px;">
<div style="color: #667eea; font-size: 2.5rem; font-weight: 700; margin-bottom: 5px;">
$<?= number_format($upgrade_plan['data']['price'], 2) ?><span style="font-size: 1.2rem; color: #a0aec0;"><?= t('subscription.per_month') ?></span>
</div>
<div style="color: #48bb78; font-size: 1.4rem; font-weight: 600;">
<?= $upgrade_plan['data']['tracks_per_month'] ?> <?= t('subscription.tracks_per_month') ?>
</div>
</div>
<ul style="list-style: none; padding: 0; margin-bottom: 20px;">
<?php
$feature_count = 0;
foreach ($upgrade_plan['data']['features'] as $feature):
if ($feature_count++ >= 3) break; // Show first 3 features
?>
<li style="color: #a0aec0; font-size: 1.1rem; padding: 5px 0; display: flex; align-items: center; gap: 8px;">
<i class="fas fa-check" style="color: #48bb78; font-size: 0.9rem;"></i>
<?= htmlspecialchars(translateFeature($feature)) ?>
</li>
<?php endforeach; ?>
</ul>
<form method="POST" action="/subscribe.php" style="margin: 0;">
<input type="hidden" name="switch_plan" value="1">
<input type="hidden" name="plan" value="<?= $upgrade_plan['key'] ?>">
<button type="submit"
style="display: block; width: 100%; padding: 12px 20px; background: linear-gradient(135deg, <?= $upgrade_plan['data']['color'] ?>, <?= $upgrade_plan['data']['color'] ?>dd); color: white; text-align: center; border-radius: 8px; border: none; font-weight: 600; font-size: 1.2rem; transition: all 0.3s ease; cursor: pointer; font-family: inherit;"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<i class="fas fa-arrow-up" style="margin-right: 8px;"></i>
<?= t('subscription.upgrade_to_plan', ['plan' => htmlspecialchars(getPlanLabel($upgrade_plan['key'], $upgrade_plan['data']['name']))]) ?>
</button>
</form>
</div>
<?php endforeach; ?>
</div>
<div style="margin-top: 25px; text-align: center;">
<a href="/pricing.php" style="color: #667eea; text-decoration: none; font-size: 1.2rem; display: inline-flex; align-items: center; gap: 8px;">
<i class="fas fa-eye"></i>
<?= t('subscription.view_all_plans') ?>
</a>
</div>
</div>
<?php endif; // End if (!empty($upgrade_plans)) ?>
<?php } // End if (!$is_canceled_for_upgrade...) ?>
<?php } // End if ($subscription && isset($display_plan)) ?>
<?php
// Show credit packages section if user has active subscription
$has_active_subscription = false;
if (is_array($subscription) && isset($subscription['status'])) {
$has_active_subscription = in_array($subscription['status'], ['active', 'trialing']);
}
if ($has_active_subscription):
?>
<!-- Extra Credits Section -->
<div id="extra-credits" style="margin-top: 60px; padding-top: 60px; border-top: 2px solid rgba(255, 255, 255, 0.1);">
<div style="text-align: center; margin-bottom: 40px;">
<h2 style="color: white; font-size: 2.5rem; margin-bottom: 15px;">
<i class="fas fa-coins"></i>
<?= t('credits.extra_credits') ?>
</h2>
<p style="color: #a0aec0; font-size: 1.2rem;">
<?= t('credits.extra_credits_desc') ?>
</p>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; max-width: 120rem; margin: 0 auto;">
<!-- Starter Credits Package -->
<div style="background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%); border: 2px solid rgba(102, 126, 234, 0.3); border-radius: 20px; padding: 2.5rem; text-align: center; position: relative; transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-8px)'; this.style.boxShadow='0 20px 60px rgba(102, 126, 234, 0.3)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<div style="font-size: 3rem; color: #667eea; margin-bottom: 1.5rem;">
<i class="fas fa-star"></i>
</div>
<h3 style="font-size: 2rem; font-weight: 700; color: white; margin-bottom: 0.8rem;"><?= t('credits.starter_credits') ?></h3>
<div style="font-size: 2.4rem; font-weight: 800; color: #667eea; margin-bottom: 0.8rem;">30 <?= t('user.credits') ?></div>
<div style="margin-bottom: 1rem;">
<span style="font-size: 1.6rem; color: #ff6b6b; text-decoration: line-through; opacity: 0.8;">$29.99</span>
<span style="font-size: 2.6rem; font-weight: 800; color: #667eea; margin: 0 0.5rem;">$19.99</span>
<div style="font-size: 1.2rem; color: #a0aec0; margin-top: 0.5rem;">$0.67 <?= t('common.per_track') ?></div>
</div>
<div style="background: linear-gradient(135deg, #ff6b6b, #ee5a52); color: white; padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.9rem; font-weight: 500; margin-bottom: 1.5rem; display: inline-block;">
<?= t('credits.one_time_purchase') ?>
</div>
<button onclick="purchaseCredits('starter', 30, 19.99, event)"
style="width: 100%; padding: 12px 20px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 1.2rem; cursor: pointer; transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<i class="fas fa-shopping-cart"></i> <?= t('credits.purchase_now') ?>
</button>
</div>
<!-- Pro Credits Package -->
<div style="background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%); border: 2px solid rgba(102, 126, 234, 0.5); border-radius: 20px; padding: 2.5rem; text-align: center; position: relative; transform: scale(1.05); transition: all 0.3s ease;"
onmouseover="this.style.transform='scale(1.08) translateY(-8px)'; this.style.boxShadow='0 20px 60px rgba(102, 126, 234, 0.4)';"
onmouseout="this.style.transform='scale(1.05) translateY(0)'; this.style.boxShadow='none';">
<div style="position: absolute; top: -10px; right: 15px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 0.4rem 0.8rem; border-radius: 8px; font-size: 0.8rem; font-weight: 500; z-index: 3;">
<?= t('credits.best_value') ?>
</div>
<div style="font-size: 3rem; color: #667eea; margin-bottom: 1.5rem;">
<i class="fas fa-fire"></i>
</div>
<h3 style="font-size: 2rem; font-weight: 700; color: white; margin-bottom: 0.8rem;"><?= t('credits.pro_credits') ?></h3>
<div style="font-size: 2.4rem; font-weight: 800; color: #667eea; margin-bottom: 0.8rem;">150 <?= t('user.credits') ?></div>
<div style="margin-bottom: 1rem;">
<span style="font-size: 1.6rem; color: #ff6b6b; text-decoration: line-through; opacity: 0.8;">$59</span>
<span style="font-size: 2.6rem; font-weight: 800; color: #667eea; margin: 0 0.5rem;">$49</span>
<div style="font-size: 1.2rem; color: #a0aec0; margin-top: 0.5rem;">$0.33 <?= t('common.per_track') ?></div>
</div>
<div style="background: linear-gradient(135deg, #ff6b6b, #ee5a52); color: white; padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.9rem; font-weight: 500; margin-bottom: 1.5rem; display: inline-block;">
<?= t('credits.one_time_purchase') ?>
</div>
<button onclick="purchaseCredits('pro', 150, 49, event)"
style="width: 100%; padding: 12px 20px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 1.2rem; cursor: pointer; transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<i class="fas fa-shopping-cart"></i> <?= t('credits.purchase_now') ?>
</button>
</div>
<!-- Premium Credits Package -->
<div style="background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%); border: 2px solid rgba(102, 126, 234, 0.3); border-radius: 20px; padding: 2.5rem; text-align: center; position: relative; transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-8px)'; this.style.boxShadow='0 20px 60px rgba(102, 126, 234, 0.3)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<div style="font-size: 3rem; color: #667eea; margin-bottom: 1.5rem;">
<i class="fas fa-crown"></i>
</div>
<h3 style="font-size: 2rem; font-weight: 700; color: white; margin-bottom: 0.8rem;"><?= t('credits.premium_credits') ?></h3>
<div style="font-size: 2.4rem; font-weight: 800; color: #667eea; margin-bottom: 0.8rem;">500 <?= t('user.credits') ?></div>
<div style="margin-bottom: 1rem;">
<span style="font-size: 1.6rem; color: #ff6b6b; text-decoration: line-through; opacity: 0.8;">$179</span>
<span style="font-size: 2.6rem; font-weight: 800; color: #667eea; margin: 0 0.5rem;">$129</span>
<div style="font-size: 1.2rem; color: #a0aec0; margin-top: 0.5rem;">$0.26 <?= t('common.per_track') ?></div>
</div>
<div style="background: linear-gradient(135deg, #ff6b6b, #ee5a52); color: white; padding: 0.5rem 1rem; border-radius: 8px; font-size: 0.9rem; font-weight: 500; margin-bottom: 1.5rem; display: inline-block;">
<?= t('credits.one_time_purchase') ?>
</div>
<button onclick="purchaseCredits('premium', 500, 129, event)"
style="width: 100%; padding: 12px 20px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; border-radius: 8px; font-weight: 600; font-size: 1.2rem; cursor: pointer; transition: all 0.3s ease;"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<i class="fas fa-shopping-cart"></i> <?= t('credits.purchase_now') ?>
</button>
</div>
</div>
<div style="text-align: center; margin-top: 3rem;">
<a href="/credits.php" style="color: #667eea; text-decoration: none; font-size: 1.2rem; display: inline-flex; align-items: center; gap: 8px;">
<i class="fas fa-eye"></i>
View All Credit Packages
</a>
</div>
</div>
<?php endif; // End if ($has_active_subscription) ?>
<div class="action-buttons">
<?php if (isset($subscription['status']) && $subscription['status'] === 'active' && !($subscription['cancel_at_period_end'] ?? false)): ?>
<button type="button" onclick="showCancelConfirmation()" class="btn btn-danger">
<i class="fas fa-times-circle"></i>
<?= t('subscription.cancel_subscription') ?>
</button>
<?php elseif (isset($subscription['cancel_at_period_end']) && $subscription['cancel_at_period_end']): ?>
<div class="warning-box" style="flex: 1; min-width: 100%;">
<p>
<i class="fas fa-exclamation-triangle"></i>
<?= t('subscription.cancel_scheduled', ['date' => $subscription['current_period_end'] ? date('M j, Y', strtotime($subscription['current_period_end'])) : t('subscription.end_of_period')]) ?>
</p>
<form method="POST" style="margin: 0;">
<button type="submit" name="reactivate_subscription" value="1" class="btn btn-success">
<i class="fas fa-check-circle"></i>
<?= t('subscription.reactivate_subscription') ?>
</button>
</form>
</div>
<?php endif; ?>
<?php if (isset($subscription['status']) && $subscription['status'] === 'active'): ?>
<a href="/pricing.php" class="btn btn-primary">
<i class="fas fa-eye"></i>
<?= t('subscription.view_all_plans') ?>
</a>
<?php endif; ?>
<a href="/invoices.php" class="btn btn-secondary" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<i class="fas fa-file-invoice"></i>
<?= t('invoice.view_invoices') ?>
</a>
</div>
<?php if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<?php
$plans_config = require __DIR__ . '/config/subscription_plans.php';
require_once __DIR__ . '/includes/translations.php';
?>
<div style="margin-top: 40px; padding: 25px; background: rgba(255, 193, 7, 0.1); border-radius: 12px; border: 2px solid rgba(255, 193, 7, 0.3);">
<h3 style="color: #ffc107; margin-bottom: 20px; font-size: 1.8rem;">
<i class="fas fa-shield-alt"></i> <?= t('subscription.admin_manual_plan_change') ?>
</h3>
<form method="POST" onsubmit="return confirm('<?= htmlspecialchars(t('subscription.admin_confirm_plan_change'), ENT_QUOTES) ?>');">
<input type="hidden" name="user_id" value="<?= $_SESSION['user_id'] ?>">
<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
<div style="flex: 1; min-width: 200px;">
<label style="color: #a0aec0; display: block; margin-bottom: 8px; font-size: 1.2rem;"><?= t('subscription.change_plan_to') ?></label>
<select name="new_plan" required style="width: 100%; padding: 12px; background: #1a1a1a; border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; color: white; font-size: 1.2rem;">
<?php foreach ($plans_config as $p_key => $p_data): ?>
<option value="<?= $p_key ?>" <?= $display_plan === $p_key ? 'selected' : '' ?>>
<?= htmlspecialchars(getPlanLabel($p_key, $p_data['name'])) ?> (<?= $p_data['tracks_per_month'] ?> <?= t('subscription.tracks_per_month') ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<button type="submit" name="admin_change_plan" value="1" style="padding: 12px 30px; background: linear-gradient(135deg, #ffc107, #ff9800); color: #000; border: none; border-radius: 8px; font-size: 1.2rem; font-weight: 700; cursor: pointer; white-space: nowrap;">
<i class="fas fa-edit"></i> <?= t('subscription.update_plan') ?>
</button>
</div>
</div>
<p style="color: #ffc107; margin-top: 15px; font-size: 1.1rem; margin-bottom: 0;">
<i class="fas fa-exclamation-triangle"></i>
<strong><?= t('subscription.admin_warning_db_update') ?></strong>
</p>
</form>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="subscription-card no-subscription-card">
<i class="fas fa-sync-alt" style="font-size: 5rem; color: #667eea; margin-bottom: 30px; opacity: 0.5;"></i>
<p><?= t('subscription.no_active_subscription') ?></p>
<a href="/pricing.php" class="btn btn-primary">
<i class="fas fa-eye"></i>
<?= t('subscription.view_all_plans') ?>
</a>
</div>
<?php if (isset($_SESSION['is_admin']) && $_SESSION['is_admin']): ?>
<?php
$plans_config = require __DIR__ . '/config/subscription_plans.php';
require_once __DIR__ . '/includes/translations.php';
?>
<div style="margin-top: 40px; padding: 25px; background: rgba(255, 193, 7, 0.1); border-radius: 12px; border: 2px solid rgba(255, 193, 7, 0.3);">
<h3 style="color: #ffc107; margin-bottom: 20px; font-size: 1.8rem;">
<i class="fas fa-shield-alt"></i> <?= t('subscription.admin_set_user_plan') ?>
</h3>
<form method="POST" onsubmit="return confirm('<?= htmlspecialchars(t('subscription.admin_confirm_set_plan'), ENT_QUOTES) ?>');">
<input type="hidden" name="user_id" value="<?= $_SESSION['user_id'] ?>">
<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
<div style="flex: 1; min-width: 200px;">
<label style="color: #a0aec0; display: block; margin-bottom: 8px; font-size: 1.2rem;"><?= t('subscription.set_plan_to') ?></label>
<select name="new_plan" required style="width: 100%; padding: 12px; background: #1a1a1a; border: 1px solid rgba(255, 193, 7, 0.3); border-radius: 8px; color: white; font-size: 1.2rem;">
<?php foreach ($plans_config as $p_key => $p_data): ?>
<option value="<?= $p_key ?>" <?= ($current_user['plan'] ?? 'free') === $p_key ? 'selected' : '' ?>>
<?= htmlspecialchars(getPlanLabel($p_key, $p_data['name'])) ?> (<?= $p_data['tracks_per_month'] ?> <?= t('subscription.tracks_per_month') ?>)
</option>
<?php endforeach; ?>
</select>
</div>
<div>
<button type="submit" name="admin_change_plan" value="1" style="padding: 12px 30px; background: linear-gradient(135deg, #ffc107, #ff9800); color: #000; border: none; border-radius: 8px; font-size: 1.2rem; font-weight: 700; cursor: pointer; white-space: nowrap;">
<i class="fas fa-edit"></i> <?= t('subscription.set_plan') ?>
</button>
</div>
</div>
<p style="color: #ffc107; margin-top: 15px; font-size: 1.1rem; margin-bottom: 0;">
<i class="fas fa-exclamation-triangle"></i>
<strong><?= t('subscription.admin_warning_users_plan') ?></strong>
</p>
</form>
</div>
<?php endif; ?>
<?php endif; ?>
<?php if (isset($success_message)): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> <?= htmlspecialchars($success_message) ?>
</div>
<?php endif; ?>
<?php if (isset($warning_message)): ?>
<div class="alert" style="background: rgba(255, 193, 7, 0.15); border: 2px solid rgba(255, 193, 7, 0.4); color: #ffc107; padding: 25px; border-radius: 12px; margin-top: 30px; font-size: 1.5rem; font-weight: 500;">
<i class="fas fa-exclamation-triangle"></i> <?= htmlspecialchars($warning_message) ?>
</div>
<?php endif; ?>
<?php if (isset($error_message)): ?>
<div class="alert alert-error">
<i class="fas fa-exclamation-circle"></i> <?= htmlspecialchars($error_message) ?>
</div>
<?php endif; ?>
<div class="back-link">
<a href="/account_settings.php">
<i class="fas fa-arrow-left"></i>
<?= t('subscription.back_to_account_settings') ?>
</a>
</div>
</main>
<!-- Professional Cancellation Confirmation Modal -->
<div id="cancelModal" class="modal-overlay" onclick="if(event.target === this) closeCancelModal()">
<div class="modal-container">
<div class="modal-header">
<i class="fas fa-exclamation-triangle"></i>
<h3><?= t('subscription.cancel_subscription') ?></h3>
<button type="button" class="modal-close" onclick="closeCancelModal()" aria-label="Close">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<p><?= t('subscription.cancel_confirm') ?></p>
<?php
// Check if user is on a paid plan
$current_plan = $display_plan ?? ($subscription['plan_name'] ?? 'free');
$is_paid_plan = in_array($current_plan, ['essential', 'starter', 'pro', 'premium', 'enterprise']);
$can_downgrade_to_essential = in_array($current_plan, ['starter', 'pro', 'premium', 'enterprise']);
?>
<?php if ($is_paid_plan): ?>
<div class="warning-highlight" style="background: rgba(220, 53, 69, 0.2); border-left: 4px solid #dc3545; margin-bottom: 20px;">
<p style="color: #ffc107; font-weight: 700; font-size: 1.5rem;">
<i class="fas fa-exclamation-triangle"></i>
<?= t('subscription.cancel_rights_warning_title') ?>
</p>
<p style="color: #ffc107; margin-top: 10px; font-size: 1.4rem;">
<?= t('subscription.cancel_rights_warning') ?>
</p>
<?php if ($can_downgrade_to_essential): ?>
<p style="color: #a0aec0; margin-top: 15px; font-size: 1.3rem; line-height: 1.6;">
<strong><?= t('subscription.consider_downgrade') ?></strong> <?= t('subscription.downgrade_to_essential') ?>
</p>
<div style="margin-top: 20px;">
<form method="POST" action="/subscribe.php" style="margin: 0; display: inline;">
<input type="hidden" name="switch_plan" value="1">
<input type="hidden" name="plan" value="essential">
<button type="submit" style="display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; text-decoration: none; border-radius: 8px; border: none; font-weight: 600; font-size: 1.3rem; transition: all 0.3s ease; cursor: pointer; font-family: inherit;" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 5px 15px rgba(102, 126, 234, 0.4)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none';">
<i class="fas fa-arrow-down"></i>
<?= t('subscription.downgrade_to_essential_btn') ?>
</button>
</form>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="warning-highlight">
<p>
<i class="fas fa-info-circle"></i>
<?= t('subscription.cancel_warning') ?>
</p>
</div>
<div class="info-box">
<p>
<strong><?= t('subscription.cancel_details_title') ?></strong>
</p>
<p style="margin-top: 10px;">
• <?= t('subscription.cancel_detail_1') ?> <strong><?= $subscription['current_period_end'] ? date('F j, Y', strtotime($subscription['current_period_end'])) : t('subscription.end_of_period') ?></strong><br>
• <?= t('subscription.cancel_detail_2') ?><br>
• <?= t('subscription.cancel_detail_3') ?>
</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="modal-btn modal-btn-secondary" onclick="closeCancelModal()">
<i class="fas fa-arrow-left"></i>
<?= t('subscription.keep_subscription') ?>
</button>
<form method="POST" style="margin: 0;" id="cancelForm">
<input type="hidden" name="cancel_subscription" value="1">
<button type="submit" class="modal-btn modal-btn-danger">
<i class="fas fa-times-circle"></i>
<?= t('subscription.confirm_cancel') ?>
</button>
</form>
</div>
</div>
</div>
<script>
function showCancelConfirmation() {
const modal = document.getElementById('cancelModal');
if (modal) {
modal.classList.add('active');
document.body.style.overflow = 'hidden';
}
}
function closeCancelModal() {
const modal = document.getElementById('cancelModal');
if (modal) {
modal.classList.remove('active');
document.body.style.overflow = '';
}
}
// Close modal on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeCancelModal();
}
});
// Prevent form submission if user clicks outside or closes modal
document.getElementById('cancelForm')?.addEventListener('submit', function(e) {
const modal = document.getElementById('cancelModal');
if (!modal || !modal.classList.contains('active')) {
e.preventDefault();
}
});
// Purchase credits function - redirect to checkout page
function purchaseCredits(package, credits, price, event) {
// Show loading state
let button = null;
let originalText = '';
if (event && event.target) {
button = event.target.closest('button') || event.target;
originalText = button.innerHTML;
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Loading...';
button.disabled = true;
}
// Add to existing cart instead of replacing it
fetch('add_to_cart.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'credit',
package: package,
credits: credits,
price: price,
quantity: 1
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Use AJAX navigation to preserve global player
if (window.ajaxNavigation) {
window.ajaxNavigation.navigateToPage('/checkout.php');
} else {
window.location.href = '/checkout.php';
}
} else {
// Show error message
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
let errorMsg = data.message || data.error || 'An error occurred';
// Check if subscription is required
if (data.requires_subscription) {
const subscriptionMsg = '<?= addslashes(t('checkout.subscription_required_message')) ?>';
if (confirm(errorMsg + '\n\n' + subscriptionMsg)) {
window.location.href = data.subscription_url || '/account_settings.php?tab=subscription';
}
} else {
alert(errorMsg);
}
}
})
.catch(error => {
console.error('Error adding to cart:', error);
if (button) {
button.innerHTML = originalText;
button.disabled = false;
}
alert('An error occurred. Please try again.');
});
}
</script>
<?php include 'includes/footer.php'; ?>