Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191

This commit is contained in:
southseact-3d
2026-02-07 20:32:41 +00:00
commit ed67b7741b
252 changed files with 99814 additions and 0 deletions

View File

@@ -0,0 +1,375 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subscription Activated | Plugin Compass</title>
<link rel="icon" type="image/png" href="/assets/Plugin.png">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
}
}
}
}
</script>
<style>
.glass-panel {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(0, 66, 37, 0.1);
}
</style>
<!-- PostHog Analytics -->
<script src="/posthog.js"></script>
</head>
<body class="bg-amber-50 text-gray-900 font-sans antialiased min-h-screen flex flex-col">
<!-- Navigation -->
<nav class="w-full z-50 transition-all duration-300">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-20">
<!-- Logo -->
<a href="/" class="flex-shrink-0 flex items-center gap-2 cursor-pointer">
<img src="/assets/Plugin.png" alt="Plugin Compass" class="w-8 h-8">
<span class="font-bold text-xl tracking-tight text-gray-800">Plugin<span
class="text-green-700">Compass</span></span>
</a>
<div class="text-sm text-gray-600">
Welcome to Plugin Compass
</div>
</div>
</div>
</nav>
<main class="flex-grow flex items-center justify-center px-4 py-12">
<div class="w-full max-w-2xl">
<!-- Success Message -->
<div class="text-center mb-8">
<div class="inline-flex items-center justify-center w-16 h-16 bg-green-100 rounded-full mb-4">
<i class="fas fa-check text-green-600 text-2xl"></i>
</div>
<h1 class="text-3xl font-bold text-gray-900 mb-4">Subscription Activated!</h1>
<p class="text-lg text-gray-700 mb-6">Your subscription has been successfully activated and you're ready to start building.</p>
</div>
<!-- Subscription Details -->
<div class="bg-white rounded-2xl p-8 shadow-xl border border-gray-100 mb-8" id="subscription-details">
<div class="text-center">
<div class="inline-flex items-center px-4 py-2 bg-green-100 text-green-800 rounded-full text-sm font-medium mb-4">
<i class="fas fa-credit-card mr-2"></i>
<span id="plan-display">Starter Plan</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-6">
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">Billing Cycle</div>
<div class="font-semibold text-gray-900" id="billing-cycle">Monthly</div>
</div>
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">Currency</div>
<div class="font-semibold text-gray-900" id="currency-display">USD</div>
</div>
<div class="text-center">
<div class="text-sm text-gray-500 mb-1">Next Billing</div>
<div class="font-semibold text-gray-900" id="next-billing">Loading...</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="text-center space-y-4">
<a href="/apps"
class="inline-flex items-center px-8 py-3 bg-green-700 text-white font-bold rounded-xl hover:bg-green-600 transition-all shadow-lg shadow-green-700/20 hover:shadow-green-700/40 transform hover:-translate-y-0.5">
<i class="fas fa-rocket mr-2"></i>
Start Building Apps
</a>
<div class="text-sm text-gray-600">
<a href="/settings" class="text-green-700 hover:text-green-600 font-medium">
Manage your subscription
</a>
</div>
</div>
<!-- Getting Started Tips -->
<div class="mt-12 bg-green-50 rounded-2xl p-6 border border-green-200">
<h3 class="font-semibold text-gray-900 mb-3 flex items-center">
<i class="fas fa-lightbulb text-green-600 mr-2"></i>
What's Next?
</h3>
<ul class="space-y-2 text-sm text-gray-700">
<li class="flex items-start">
<i class="fas fa-check text-green-600 mr-2 mt-0.5"></i>
<span>Create your first WordPress plugin using our AI builder</span>
</li>
<li class="flex items-start">
<i class="fas fa-check text-green-600 mr-2 mt-0.5"></i>
<span>Access premium templates and advanced features</span>
</li>
<li class="flex items-start">
<i class="fas fa-check text-green-600 mr-2 mt-0.5"></i>
<span>Export and deploy your plugins anytime</span>
</li>
</ul>
</div>
</div>
</main>
<footer class="py-8 text-center text-gray-500 text-xs">
<p>© 2026 Plugin Compass. All rights reserved.</p>
<div style="margin-top: 12px; display: flex; justify-content: center; gap: 16px;">
<a href="/terms" style="color: #008060; text-decoration: none;">Terms</a>
<a href="/privacy" style="color: #008060; text-decoration: none;">Privacy</a>
<a href="/contact" style="color: #008060; text-decoration: none;">Contact Us</a>
</div>
</footer>
</body>
</html>
<script>
// Get session ID from URL parameters
const urlParams = new URLSearchParams(window.location.search);
const sessionId = urlParams.get('session_id') || urlParams.get('checkout_id') || urlParams.get('session');
async function confirmSubscription() {
if (!sessionId) {
// No session id present - attempt to verify status in the background then redirect to /apps
showPending('Checking subscription status. This may take a few seconds...');
let attempts = 0;
const maxAttempts = 8;
const intervalMs = 2000;
async function checkStatusOnce() {
attempts++;
try {
const resp = await fetch('/api/subscription/status', {
method: 'GET',
credentials: 'include'
});
if (resp.ok) {
const statusData = await resp.json();
if (statusData.hasActiveSubscription) {
// Subscription is active - go to apps page
window.location.href = '/apps';
return true;
}
}
} catch (err) {
console.warn('Subscription status check failed', err);
}
if (attempts >= maxAttempts) {
showError('Unable to confirm subscription automatically. Visit Settings to check or go to Apps.');
return true;
}
return false;
}
const intervalId = setInterval(async () => {
const done = await checkStatusOnce();
if (done) clearInterval(intervalId);
}, intervalMs);
// Run an immediate check
await checkStatusOnce();
return;
}
try {
const authHeaders = getAuthHeaders();
const response = await fetch(`/api/subscription/confirm?session_id=${encodeURIComponent(sessionId)}`, {
method: 'GET',
headers: {
...authHeaders
},
credentials: 'include'
});
const data = await response.json();
if (response.ok && data.ok) {
updateLocalStorageUser(data);
updateSubscriptionDisplay(data);
} else if (data.pending) {
showPending('Payment is still processing. Please wait...');
} else {
showError(data.error || 'Failed to activate subscription');
}
} catch (error) {
console.error('Error confirming subscription:', error);
showError('Network error. Please try again.');
}
}
// Helper to get auth headers from localStorage
function getAuthHeaders() {
try {
const userStr = localStorage.getItem('wordpress_plugin_ai_user') || localStorage.getItem('shopify_ai_user');
if (userStr) {
const user = JSON.parse(userStr);
if (user && user.sessionToken) {
return { 'Authorization': `Bearer ${user.sessionToken}` };
}
}
} catch (err) {
console.warn('Failed to get auth headers from localStorage', err);
}
return {};
}
function updateSubscriptionDisplay(data) {
// Update plan display
const planDisplay = document.getElementById('plan-display');
if (planDisplay) {
planDisplay.textContent = getPlanDisplayName(data.plan);
}
// Update billing cycle
const billingCycle = document.getElementById('billing-cycle');
if (billingCycle) {
billingCycle.textContent = data.billingCycle ?
data.billingCycle.charAt(0).toUpperCase() + data.billingCycle.slice(1) : 'Monthly';
}
// Update currency
const currencyDisplay = document.getElementById('currency-display');
if (currencyDisplay) {
currencyDisplay.textContent = data.currency ?
data.currency.toUpperCase() : 'USD';
}
// Calculate next billing date
const nextBilling = document.getElementById('next-billing');
if (nextBilling && data.billingCycle) {
const nextDate = calculateNextBillingDate(data.billingCycle);
nextBilling.textContent = nextDate.toLocaleDateString();
}
// Display payment method if available
const paymentMethod = data.account?.paymentMethod;
if (paymentMethod && paymentMethod.last4) {
const planBadge = document.getElementById('plan-display')?.parentElement;
if (planBadge) {
const brand = paymentMethod.brand || 'Card';
const last4 = paymentMethod.last4;
planBadge.innerHTML = `
<i class="fas fa-credit-card mr-2"></i>
<span id="plan-display">${getPlanDisplayName(data.plan)}</span>
<span class="ml-3 text-gray-500 text-sm">(${brand} ••••• ${last4})</span>
`;
}
}
// Track successful subscription
if (window.posthog) {
window.posthog.capture('subscription_activated', {
plan: data.plan,
billing_cycle: data.billingCycle,
currency: data.currency
});
}
}
function updateLocalStorageUser(data) {
try {
const userStr = localStorage.getItem('wordpress_plugin_ai_user') || localStorage.getItem('shopify_ai_user');
if (userStr) {
const user = JSON.parse(userStr);
if (user && data.account) {
user.plan = data.account.plan;
user.billingStatus = data.account.billingStatus;
user.billingCycle = data.account.billingCycle;
user.subscriptionRenewsAt = data.account.subscriptionRenewsAt;
user.billingEmail = data.account.billingEmail;
localStorage.setItem('wordpress_plugin_ai_user', JSON.stringify(user));
localStorage.setItem('shopify_ai_user', JSON.stringify(user));
}
}
} catch (err) {
console.warn('Failed to update localStorage user', err);
}
}
function showPending(message) {
const subscriptionDetails = document.getElementById('subscription-details');
if (subscriptionDetails) {
subscriptionDetails.innerHTML = `
<div class="text-center">
<div class="inline-flex items-center justify-center w-16 h-16 bg-yellow-100 rounded-full mb-4">
<i class="fas fa-clock text-yellow-600 text-2xl"></i>
</div>
<h2 class="text-xl font-semibold text-gray-900 mb-4">Processing Payment</h2>
<p class="text-gray-600 mb-4">${message}</p>
<button onclick="location.reload()" class="px-6 py-2 bg-green-700 text-white rounded-lg hover:bg-green-600 transition-colors">
Refresh Status
</button>
</div>
`;
}
}
function showError(message) {
const subscriptionDetails = document.getElementById('subscription-details');
if (subscriptionDetails) {
subscriptionDetails.innerHTML = `
<div class="text-center">
<div class="inline-flex items-center justify-center w-16 h-16 bg-red-100 rounded-full mb-4">
<i class="fas fa-exclamation-triangle text-red-600 text-2xl"></i>
</div>
<h2 class="text-xl font-semibold text-gray-900 mb-4">Subscription Error</h2>
<p class="text-gray-600 mb-4">${message}</p>
<div class="space-y-2">
<a href="/select-plan" class="block px-6 py-2 bg-green-700 text-white rounded-lg hover:bg-green-600 transition-colors">
Try Again
</a>
<a href="/contact" class="block px-6 py-2 bg-gray-200 text-gray-800 rounded-lg hover:bg-gray-300 transition-colors">
Contact Support
</a>
</div>
</div>
`;
}
}
function getPlanDisplayName(plan) {
const planNames = {
'hobby': 'Hobby Plan',
'starter': 'Starter Plan',
'professional': 'Professional Plan',
'business': 'Professional Plan',
'enterprise': 'Enterprise Plan'
};
return planNames[plan] || `${plan.charAt(0).toUpperCase() + plan.slice(1)} Plan`;
}
function calculateNextBillingDate(billingCycle) {
const now = new Date();
if (billingCycle === 'yearly') {
return new Date(now.getFullYear() + 1, now.getMonth(), now.getDate());
} else {
return new Date(now.getFullYear(), now.getMonth() + 1, now.getDate());
}
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
confirmSubscription();
});
</script>