feat: kimlik doğrulama, ödeme, kredi ve AI rapor sihirbazı ekle
- better-auth ile giriş/oturum yönetimi (src/lib/auth, giris sayfası) - iyzico ödeme entegrasyonu ve paket satın alma akışı - kredi sistemi ve uygulama veritabanı şeması (drizzle) - AI destekli rapor üretimi ve tercih sihirbazı - rapor yazdırma sayfası ve site header/kullanıcı menüsü Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
63
src/lib/odeme.ts
Normal file
63
src/lib/odeme.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { appDb, schema } from "./appdb";
|
||||
import { grantCredits, URUNLER } from "./credits";
|
||||
import { retrieveCheckoutForm } from "./iyzico";
|
||||
|
||||
const { orders } = schema;
|
||||
|
||||
/**
|
||||
* Token'la iyzico'dan sonucu çeker ve başarılıysa krediyi İDEMPOTENT tanımlar.
|
||||
* Hem callback route'u hem /odeme/sonuc self-healing fallback'i bunu kullanır.
|
||||
* Dönen değer: siparişin son durumu.
|
||||
*/
|
||||
export async function odemeyiSonuclandir(
|
||||
orderId: string,
|
||||
): Promise<"paid" | "pending" | "failed" | "not_found"> {
|
||||
const order = await appDb.query.orders.findFirst({
|
||||
where: eq(orders.id, orderId),
|
||||
});
|
||||
if (!order) return "not_found";
|
||||
if (order.status === "paid") return "paid";
|
||||
if (!order.iyzicoToken) return order.status;
|
||||
|
||||
let sonuc;
|
||||
try {
|
||||
sonuc = await retrieveCheckoutForm(order.iyzicoToken);
|
||||
} catch {
|
||||
return order.status; // iyzico'ya ulaşılamadı; durumu değiştirme
|
||||
}
|
||||
|
||||
if (sonuc.status !== "success" || sonuc.paymentStatus !== "SUCCESS") {
|
||||
await appDb
|
||||
.update(orders)
|
||||
.set({ status: "failed" })
|
||||
.where(and(eq(orders.id, orderId), eq(orders.status, "pending")));
|
||||
return "failed";
|
||||
}
|
||||
if (sonuc.conversationId && sonuc.conversationId !== order.id) {
|
||||
return order.status; // conversationId uyuşmazlığı: işleme
|
||||
}
|
||||
|
||||
// pending -> paid koşullu geçiş: 0 satır = başka istek zaten işledi
|
||||
const res = await appDb
|
||||
.update(orders)
|
||||
.set({
|
||||
status: "paid",
|
||||
iyzicoPaymentId: sonuc.paymentId ?? null,
|
||||
paidAt: new Date(),
|
||||
})
|
||||
.where(and(eq(orders.id, orderId), eq(orders.status, "pending")));
|
||||
if (res.rowsAffected === 0) return "paid";
|
||||
|
||||
// UNIQUE(reason, refId) ikinci katman güvence
|
||||
await grantCredits({
|
||||
userId: order.userId,
|
||||
delta: order.credits,
|
||||
reason: order.product === "paket" ? "purchase" : "topup",
|
||||
refId: order.id,
|
||||
setHasPaket: order.product === "paket",
|
||||
});
|
||||
return "paid";
|
||||
}
|
||||
|
||||
export { URUNLER };
|
||||
Reference in New Issue
Block a user