MarketSync

Choose your plan

Your free trial has ended — pick a plan to keep going. All prices shown in CAD. Cancel anytime.

Loading plans…

Already have an account? Sign in

; try { const r = await fetch(`${API}/billing/packages`); const d = await r.json(); packages = d.packages || []; currency = d.currency || 'USD'; symbol = d.symbol || '$'; } catch {} // Order Starter → Growth → Pro; fall back to fixed prices if the API has none. const order = ['starter', 'growth', 'pro']; const byKey = Object.fromEntries(packages.map(p => [p.key, p])); const pkgCards = order.filter(k => byKey[k] || PKG_META[k]).map(k => { const p = byKey[k] || {}; const meta = PKG_META[k] || { features: [], tagline: '' }; const amt = p.amount != null ? p.amount : ({ starter: 999, growth: 1499, pro: 1999 })[k]; return card({ key: k, label: (p.label || k), price: `${p.symbol || symbol}${amt}`, tagline: meta.tagline, features: meta.features, popular: !!meta.popular, trialDays: 30 }); }); const chatCard = card({ key: CHATBOT.key, label: CHATBOT.label, price: `${symbol}${CHATBOT.amount}`, tagline: CHATBOT.tagline, features: CHATBOT.features, accent: 'emerald', trialDays: 7 }); grid.innerHTML = pkgCards.join('') + chatCard; grid.querySelectorAll('button[data-plan]').forEach(b => b.addEventListener('click', () => startCheckout(b.dataset.plan, b))); } async function startCheckout(plan, btn) { errBox().classList.add('hidden'); const original = btn.textContent; btn.disabled = true; btn.textContent = 'Connecting to billing...'; const token = localStorage.getItem('token'); if (!token) { window.location.href = `register.html?plan=${plan}`; return; } const isChatbot = plan === 'ai_chatbot'; const url = isChatbot ? `${API}/billing/subscribe-ai-chatbot` : `${API}/billing/subscribe-package`; const body = isChatbot ? {} : { package: plan }; try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(body) }); const data = await res.json(); if (data.url) { window.location.href = data.url; return; } throw new Error(data.error || 'Could not start checkout'); } catch (err) { showErr(err.message); btn.disabled = false; btn.textContent = original; } } renderPlans();
; } catch {} // Order Starter → Growth → Pro; fall back to fixed prices if the API has none. const order = ['starter', 'growth', 'pro']; const byKey = Object.fromEntries(packages.map(p => [p.key, p])); const pkgCards = order.filter(k => byKey[k] || PKG_META[k]).map(k => { const p = byKey[k] || {}; const meta = PKG_META[k] || { features: [], tagline: '' }; const amt = p.amount != null ? p.amount : ({ starter: 999, growth: 1499, pro: 1999 })[k]; return card({ key: k, label: (p.label || k), price: `${p.symbol || symbol}${amt}`, tagline: meta.tagline, features: meta.features, popular: !!meta.popular, trialDays: 30 }); }); const chatCard = card({ key: CHATBOT.key, label: CHATBOT.label, price: `${symbol}${CHATBOT.amount}`, tagline: CHATBOT.tagline, features: CHATBOT.features, accent: 'emerald', trialDays: 7 }); grid.innerHTML = pkgCards.join('') + chatCard; grid.querySelectorAll('button[data-plan]').forEach(b => b.addEventListener('click', () => startCheckout(b.dataset.plan, b))); } async function startCheckout(plan, btn) { errBox().classList.add('hidden'); const original = btn.textContent; btn.disabled = true; btn.textContent = 'Connecting to billing...'; const token = localStorage.getItem('token'); if (!token) { window.location.href = `register.html?plan=${plan}`; return; } const isChatbot = plan === 'ai_chatbot'; const url = isChatbot ? `${API}/billing/subscribe-ai-chatbot` : `${API}/billing/subscribe-package`; const body = isChatbot ? {} : { package: plan }; try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(body) }); const data = await res.json(); if (data.url) { window.location.href = data.url; return; } throw new Error(data.error || 'Could not start checkout'); } catch (err) { showErr(err.message); btn.disabled = false; btn.textContent = original; } } renderPlans();
; try { const r = await fetch(`${API}/billing/packages`); const d = await r.json(); packages = d.packages || []; currency = d.currency || 'USD'; symbol = d.symbol || '$'; } catch {} // Order Starter → Growth → Pro; fall back to fixed prices if the API has none. const order = ['starter', 'growth', 'pro']; const byKey = Object.fromEntries(packages.map(p => [p.key, p])); const pkgCards = order.filter(k => byKey[k] || PKG_META[k]).map(k => { const p = byKey[k] || {}; const meta = PKG_META[k] || { features: [], tagline: '' }; const amt = p.amount != null ? p.amount : ({ starter: 999, growth: 1499, pro: 1999 })[k]; return card({ key: k, label: (p.label || k), price: `${p.symbol || symbol}${amt}`, tagline: meta.tagline, features: meta.features, popular: !!meta.popular, trialDays: 30 }); }); const chatCard = card({ key: CHATBOT.key, label: CHATBOT.label, price: `${symbol}${CHATBOT.amount}`, tagline: CHATBOT.tagline, features: CHATBOT.features, accent: 'emerald', trialDays: 7 }); grid.innerHTML = pkgCards.join('') + chatCard; grid.querySelectorAll('button[data-plan]').forEach(b => b.addEventListener('click', () => startCheckout(b.dataset.plan, b))); } async function startCheckout(plan, btn) { errBox().classList.add('hidden'); const original = btn.textContent; btn.disabled = true; btn.textContent = 'Connecting to billing...'; const token = localStorage.getItem('token'); if (!token) { window.location.href = `register.html?plan=${plan}`; return; } const isChatbot = plan === 'ai_chatbot'; const url = isChatbot ? `${API}/billing/subscribe-ai-chatbot` : `${API}/billing/subscribe-package`; const body = isChatbot ? {} : { package: plan }; try { const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(body) }); const data = await res.json(); if (data.url) { window.location.href = data.url; return; } throw new Error(data.error || 'Could not start checkout'); } catch (err) { showErr(err.message); btn.disabled = false; btn.textContent = original; } } renderPlans();