From 951caa3c93ab41e193a654a0af7fb7820e3f3420 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Tue, 11 Aug 2026 11:42:47 +0300 Subject: [PATCH] refactor(odeme): update environment variable usage and documentation Replaced `NEXT_PUBLIC_APP_URL` with `APP_URL` in the payment processing logic to ensure proper runtime access. Updated related documentation to clarify the distinction between build-time and runtime environment variables, emphasizing the importance of using `APP_URL` for callback and webhook URLs. Adjusted references in the codebase to reflect this change, enhancing clarity and functionality in the payment flow. --- .gitea/workflows/ci.yaml | 5 ++++- docs/odeme/iyzico.md | 10 ++++++++-- src/app/api/odeme/callback/route.ts | 3 ++- src/features/odeme/odeme-actions.ts | 3 ++- src/lib/app-url.ts | 27 +++++++++++++++++++++++++++ src/lib/seo.tsx | 8 ++++---- 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 src/lib/app-url.ts diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index f945d8a..01f4ab5 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -18,7 +18,10 @@ jobs: cat > .env.production <<'EOF' BETTER_AUTH_SECRET=${{ secrets.BETTER_AUTH_SECRET }} BETTER_AUTH_URL=${{ secrets.BETTER_AUTH_URL }} - NEXT_PUBLIC_APP_URL=${{ secrets.NEXT_PUBLIC_APP_URL }} + # Ödeme callback/yönlendirme kökü. Public önekli DEĞİL: build'e değil + # runtime'a geçmesi gerekiyor (bkz. src/lib/app-url.ts). Secret adı + # geçmişten NEXT_PUBLIC_APP_URL, değeri aynı https kök adres. + APP_URL=${{ secrets.NEXT_PUBLIC_APP_URL }} GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }} GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }} RESEND_API_KEY=${{ secrets.RESEND_API_KEY }} diff --git a/docs/odeme/iyzico.md b/docs/odeme/iyzico.md index 82f38ee..6b4d903 100644 --- a/docs/odeme/iyzico.md +++ b/docs/odeme/iyzico.md @@ -10,14 +10,20 @@ zaman bize ulaşmaz, kullanıcı iyzico'nun barındırdığı ödeme sayfasına | `IYZICO_API_KEY` | Merchant Portal → Ayarlar → API anahtarları | | `IYZICO_SECRET_KEY` | Aynı ekran. İmza doğrulamasında da kullanılır | | `IYZICO_BASE_URL` | Sandbox: `https://sandbox-api.iyzipay.com` · Canlı: `https://api.iyzipay.com` | -| `NEXT_PUBLIC_APP_URL` | callback ve webhook URL'lerinin kökü; **https ve geçerli SSL şart** | +| `APP_URL` | callback ve yönlendirme URL'lerinin kökü; **https ve geçerli SSL şart**. Verilmezse `BETTER_AUTH_URL`'e düşer | `IYZICO_BASE_URL` verilmezse sandbox'a düşülür. Prod'da bu durum log'a uyarı basar — canlıya çıkarken bu satır mutlaka ayarlanmalı. +Kök adres `NEXT_PUBLIC_APP_URL` ile **verilemez**: `NEXT_PUBLIC_*` değişkenleri +build sırasında koda gömülür, Docker build'i ise `.env.production`'ı görmez +(`.dockerignore` `.env*`'i dışlar). Sonuç, env doğru girilmiş olsa bile imaja +`http://localhost:3000` çakılması ve iyzico'nun initialize'ı reddetmesiydi. +Ayrıntı: `src/lib/app-url.ts`. + > Lokal `http://localhost:3000` ile uçtan uca test edilemez: iyzico callback > adresinden geçerli SSL ister. Sandbox testinde tünel (cloudflared/ngrok) açıp -> `NEXT_PUBLIC_APP_URL`'i o https adrese ayarla. +> `APP_URL`'i o https adrese ayarla. ## Akış diff --git a/src/app/api/odeme/callback/route.ts b/src/app/api/odeme/callback/route.ts index a4b2dca..717aabd 100644 --- a/src/app/api/odeme/callback/route.ts +++ b/src/app/api/odeme/callback/route.ts @@ -1,5 +1,6 @@ import { eq } from "drizzle-orm"; import { NextResponse, type NextRequest } from "next/server"; +import { appUrl as siteKok } from "@/lib/app-url"; import { appDb, schema } from "@/lib/appdb"; import { odemeyiSonuclandir } from "@/lib/odeme"; @@ -11,7 +12,7 @@ import { odemeyiSonuclandir } from "@/lib/odeme"; // SameSite=Lax nedeniyle session çerezi GELMEZ — kullanıcı token'dan çözülür, // verifySession ÇAĞRILMAZ. Kredi tanımlama odemeyiSonuclandir içinde idempotenttir. export async function POST(request: NextRequest) { - const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000"; + const appUrl = siteKok(); let token: string | undefined; try { diff --git a/src/features/odeme/odeme-actions.ts b/src/features/odeme/odeme-actions.ts index 92f32fb..ccc09e1 100644 --- a/src/features/odeme/odeme-actions.ts +++ b/src/features/odeme/odeme-actions.ts @@ -4,6 +4,7 @@ import { eq } from "drizzle-orm"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; import { verifySession } from "@/lib/session"; +import { appUrl as siteKok } from "@/lib/app-url"; import { appDb, schema } from "@/lib/appdb"; import { URUNLER } from "@/lib/credits"; import { initializeCheckoutForm, initImzaDurumu } from "@/lib/iyzico"; @@ -47,7 +48,7 @@ export async function baslatOdeme( 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"; + const appUrl = siteKok(); const [ad, soyad] = adSoyadAyir(session.user.name ?? ""); let init; diff --git a/src/lib/app-url.ts b/src/lib/app-url.ts new file mode 100644 index 0000000..ff0125c --- /dev/null +++ b/src/lib/app-url.ts @@ -0,0 +1,27 @@ +import "server-only"; + +/** + * Uygulamanın dışarıya görünen kök adresi — iyzico callbackUrl'i ve ödeme + * yönlendirmeleri buradan okunur. + * + * NEXT_PUBLIC_APP_URL BİLİNÇLİ OLARAK KULLANILMAZ: `NEXT_PUBLIC_*` değişkenleri + * Next tarafından build sırasında koda sabit string olarak gömülür, runtime'da + * okunmaz. Docker imajı build edilirken .env.production build katmanına hiç + * geçmediği için (.dockerignore `.env*` dosyalarını dışlıyor) o değişken build + * anında `undefined` olur ve fallback ("http://localhost:3000") imaja çakılırdı; + * compose `env_file` ile değeri container'a verse bile kod artık onu okumazdı. + * Sonuç: iyzico'ya localhost callback'i gidiyor ve initialize reddediliyordu. + * + * Buradaki değişkenler public önekli olmadığı için runtime'da okunur — env + * değişince yeniden build gerekmez. + */ +export function appUrl(): string { + const ham = + process.env.APP_URL ?? + // Aynı origin'i zaten gösteriyor ve prod .env'inde mevcut; ayrı bir secret + // eklemeden doğru değere düşmek için ikinci sırada. + process.env.BETTER_AUTH_URL ?? + "http://localhost:3000"; + // Sondaki "/" ile birleştirmelerde "//paket" üretmemek için kırpılır. + return ham.replace(/\/+$/, ""); +} diff --git a/src/lib/seo.tsx b/src/lib/seo.tsx index 28de4cd..caa7c18 100644 --- a/src/lib/seo.tsx +++ b/src/lib/seo.tsx @@ -1,10 +1,10 @@ import { URUNLER } from "./credits"; /** - * Kanonik site adresi. NEXT_PUBLIC_APP_URL bilinçli olarak KULLANILMAZ — - * o değişken ödeme callback akışına ait ve .env.local'de localhost'a işaret - * ediyor. .dockerignore .env* dosyalarını dışladığı için prod build burada - * deterministik olarak kolaytercih.com'a düşer. + * Kanonik site adresi. Ödeme akışının kök adresiyle (`appUrl()`, + * src/lib/app-url.ts) karıştırılmamalı: o runtime'da okunur, bu ise metadata + * için build anında sabitlenir. .dockerignore .env* dosyalarını dışladığı için + * prod build burada deterministik olarak kolaytercih.com'a düşer. */ export const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ??