refactor: odeme feature'ı + son süpürme — geçiş tamamlandı (Faz 10)
Some checks failed
Deploy / deploy (push) Failing after 3m46s

- paket/actions.ts → odeme-actions.ts; satin-al-form + rapor-yok-cta
  features/odeme/components/ altına; odeme-queries.ts (siparisGetir)
- Yeni async OdemeSonucKarti: verifySession + sahiplik kontrolü + self-healing
  odemeyiSonuclandir çağrısı akıştaki yerinde (artık Suspense altındaki feature
  bileşeninde, aynı render semantiği); başarı/hata kartları içeride
- Yeni async PaketSatinAl: paket + top-up kartları, getCurrentUser okuması
  içeride; statik hero/dipnot sayfada kaldı
- İki sayfa senkron searchParams.then(); paket hata uyarısı sayfada çözülmüş
  JSX (hata yokken hiçbir şey çizilmediği için fallback bilerek null);
  paket/loading.tsx + odeme/sonuc/loading.tsx silindi
- Süpürme doğrulandı: app/ altında route-dışı bileşen dosyası kalmadı; eski
  yol grep'leri sıfır; 7 *-queries server-only, 3 *-actions 'use server';
  async sayfa sıfır; /api/odeme/callback el değmedi
- Davranış değişikliği yok

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-08-08 01:46:23 +03:00
parent 22e2fde058
commit d2732828cc
10 changed files with 373 additions and 345 deletions

View File

@@ -0,0 +1,75 @@
"use server";
import { eq } from "drizzle-orm";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { verifySession } from "@/lib/session";
import { appDb, schema } from "@/lib/appdb";
import { URUNLER } from "@/lib/credits";
import { initializeCheckoutForm } from "@/lib/iyzico";
export type OdemeBaslatDurum = { error?: string } | undefined;
export async function baslatOdeme(
_prev: OdemeBaslatDurum,
formData: FormData,
): Promise<OdemeBaslatDurum> {
const session = await verifySession("/paket");
const product = formData.get("urun");
if (product !== "paket" && product !== "topup") {
return { error: "Geçersiz ürün." };
}
const urun = URUNLER[product];
const orderId = crypto.randomUUID();
await appDb.insert(schema.orders).values({
id: orderId,
userId: session.user.id,
product,
amountKurus: urun.amountKurus,
credits: urun.credits,
status: "pending",
createdAt: new Date(),
});
const h = await headers();
const buyerIp =
h.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "85.34.78.112";
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
let init;
try {
init = await initializeCheckoutForm({
orderId,
fiyatKurus: urun.amountKurus,
urunAdi: `KolayTercih — ${urun.label}`,
email: session.user.email,
userId: session.user.id,
callbackUrl: `${appUrl}/api/odeme/callback`,
buyerIp,
});
} catch (err) {
if (err instanceof Error && err.message === "IYZICO_KEYS_MISSING") {
// Yapılandırma detayı log'a; kullanıcıya altyapı sızdırmayan mesaj
console.error("[odeme] iyzico anahtarları eksik — ödeme başlatılamadı");
return {
error:
"Ödeme şu anda başlatılamıyor; kartından çekim yapılmadı. Birazdan tekrar dener misin?",
};
}
throw err;
}
if (init.status !== "success" || !init.paymentPageUrl || !init.token) {
return {
error: init.errorMessage ?? "Ödeme başlatılamadı. Tekrar dener misin?",
};
}
await appDb
.update(schema.orders)
.set({ iyzicoToken: init.token })
.where(eq(schema.orders.id, orderId));
redirect(init.paymentPageUrl);
}