diff --git a/server/billing.mjs b/server/billing.mjs index 9ddd281..02a1162 100644 --- a/server/billing.mjs +++ b/server/billing.mjs @@ -51,6 +51,8 @@ export const PLANS = [ ], sinirlar: { takip: null, ekip: 5 }, priceEnv: 'STRIPE_PRICE_PRO', + priceEnvYillik: 'STRIPE_PRICE_PRO_YILLIK', + fiyatYillik: 8954, }, { id: 'kurumsal', @@ -69,6 +71,8 @@ export const PLANS = [ ], sinirlar: { takip: null, ekip: null }, priceEnv: 'STRIPE_PRICE_KURUMSAL', + priceEnvYillik: 'STRIPE_PRICE_KURUMSAL_YILLIK', + fiyatYillik: 24800, }, ]; @@ -118,6 +122,7 @@ export function planForPrice(priceId) { if (!priceId) return null; for (const p of PLANS) { if (p.priceEnv && process.env[p.priceEnv] === priceId) return p.id; + if (p.priceEnvYillik && process.env[p.priceEnvYillik] === priceId) return p.id; } return null; } @@ -126,6 +131,7 @@ export function planCatalog() { return PLANS.map((p) => ({ ...p, satinAlinabilir: Boolean(p.priceEnv && process.env[p.priceEnv] && billingConfigured()), + satinAlinabilirYillik: Boolean(p.priceEnvYillik && process.env[p.priceEnvYillik] && billingConfigured()), })); } @@ -141,25 +147,28 @@ function formEncode(obj) { return parts.join('&'); } -export async function createCheckoutSession({ planId, email }) { +export async function createCheckoutSession({ planId, email, periyot = 'yillik' }) { const key = process.env.STRIPE_SECRET_KEY; if (!key) throw new Error('Ödeme yapılandırılmamış (STRIPE_SECRET_KEY yok)'); const plan = PLANS.find((p) => p.id === planId); if (!plan || !plan.priceEnv) throw new Error('Geçersiz plan'); - const price = process.env[plan.priceEnv]; - if (!price) throw new Error(`${plan.ad} planı için ${plan.priceEnv} tanımlı değil`); + const yillik = periyot === 'yillik'; + const env = yillik && plan.priceEnvYillik ? plan.priceEnvYillik : plan.priceEnv; + const price = process.env[env]; + if (!price) throw new Error(`${plan.ad} planı için ${env} tanımlı değil`); const appUrl = process.env.APP_URL || 'https://ihaleist.seymata.com'; const body = formEncode({ mode: 'subscription', 'line_items[0][price]': price, 'line_items[0][quantity]': 1, - success_url: `${appUrl}/?odeme=basarili&plan=${planId}`, + success_url: `${appUrl}/?odeme=basarili&plan=${planId}&periyot=${periyot}`, cancel_url: `${appUrl}/?odeme=iptal`, customer_email: email, allow_promotion_codes: 'true', client_reference_id: email || '', 'metadata[planId]': planId, + 'metadata[periyot]': periyot, 'metadata[email]': email || '', }); diff --git a/server/index.js b/server/index.js index 1bad2b6..b7d11ff 100644 --- a/server/index.js +++ b/server/index.js @@ -757,7 +757,7 @@ const server = http.createServer(async (req, res) => { if (!user) return json(res, 401, { success: false, error: 'oturum yok' }); try { const body = await readBody(req); - const out = await createCheckoutSession({ planId: body.planId, email: user.email }); + const out = await createCheckoutSession({ planId: body.planId, email: user.email, periyot: body.periyot === 'aylik' ? 'aylik' : 'yillik' }); return json(res, 200, { success: true, ...out }); } catch (e) { return json(res, billingConfigured() ? 400 : 503, { success: false, error: e.message }); diff --git a/src/components/tools/Pricing.jsx b/src/components/tools/Pricing.jsx index 992e091..32b631d 100644 --- a/src/components/tools/Pricing.jsx +++ b/src/components/tools/Pricing.jsx @@ -8,7 +8,7 @@ export const Pricing = () => { const [data, setData] = useState(null); const [busy, setBusy] = useState(null); const [error, setError] = useState(null); - const [yillik, setYillik] = useState(false); + const [yillik, setYillik] = useState(true); // yıllık varsayılan (indirimli) useEffect(() => { fetch('/api/billing/plans') @@ -24,7 +24,7 @@ export const Pricing = () => { const res = await fetch('/api/billing/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getToken()}` }, - body: JSON.stringify({ planId }), + body: JSON.stringify({ planId, periyot: yillik ? 'yillik' : 'aylik' }), }); const d = await res.json(); if (d.success && d.url) window.location.href = d.url; @@ -37,7 +37,9 @@ export const Pricing = () => { }; const plans = data?.plans || []; - const fiyatla = (p) => (yillik ? Math.round(p.fiyat * 12 * 0.83) : p.fiyat); + // Yıllık tutar Stripe'taki fiyatla birebir aynı olsun diye plandan gelir + const fiyatla = (p) => (yillik ? (p.fiyatYillik ?? Math.round(p.fiyat * 12 * 0.83)) : p.fiyat); + const aylikKarsiligi = (p) => (p.fiyatYillik ? Math.round(p.fiyatYillik / 12) : p.fiyat); return (
KDV hariç
} + {p.fiyat > 0 && ( ++ KDV hariç + {yillik && ( + <> · ayda ₺{aylikKarsiligi(p).toLocaleString('tr-TR')} · yılda{' '} + + ₺{(p.fiyat * 12 - fiyatla(p)).toLocaleString('tr-TR')} tasarruf + + > + )} +
+ )}