feat: yıllık abonelik (varsayılan) — Stripe yıllık fiyatlar + periyot checkout'a geçiyor

This commit is contained in:
2026-08-29 11:58:46 +03:00
parent f04401031c
commit 8bb3d6ee48
3 changed files with 36 additions and 14 deletions
+13 -4
View File
@@ -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 || '',
});
+1 -1
View File
@@ -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 });