Skip to content

Bölgesel Fiyatlandırma (PPP - Matrix)

Cloudflare request.cf ile ülke tespiti, Tier 1–Tier 50 mantığı, tier_prices ve pricing-engine.ts.

Achidemy, öğrencinin bulunduğu ülkeye göre dinamik fiyatlandırma yapar. Bu sayfa, Cloudflare request.cf verisiyle ülke tespiti, Tier 1 – Tier 50 mantığı, tier_prices tablosunun işleyişi ve pricing-engine.ts içindeki para birimi çevrim mantığını açıklar.

Edge’de gelen isteğin ülke kodu, Cloudflare tarafından sağlanır:

  • Production (Cloudflare Workers): request.cf.country — ISO 3166-1 alpha-2 (örn. TR, US, DE).
  • Context üzerinden: React Router Cloudflare adapter’da context.cloudflare?.request?.cf?.country kullanılır.

app/lib/pricing.ts — getCountryFromRequest

Section titled “app/lib/pricing.ts — getCountryFromRequest”
export function getCountryFromRequest(request, env?, context?): string {
// Öncelik: URL ?country= → env DEV_COUNTRY → context.cloudflare.request.cf.country → "US"
const cfCountry = context?.cloudflare?.request?.cf?.country ?? request?.cf?.country;
return cfCountry ?? "US";
}
  • Yerel test: ?country=TR veya DEV_COUNTRY=TR ile ülke taklidi.
  • Varsayılan: Ülke bilinmiyorsa US kullanılır.

Tier 1 – Tier 50 Mantığı ve tier_prices Tablosu

Section titled “Tier 1 – Tier 50 Mantığı ve tier_prices Tablosu”
  • Kurslar courses.price_tier_id ile tek bir fiyat katmanına bağlanır.
  • Paketler: course_bundles tablosunda ayrı bir price_tier_id yoktur; bölgesel paket fiyatı getBundleRegionalPrice ile paketteki her kursun price_tier_id + tier_prices değerleri toplanıp paket discountPercentage uygulanarak hesaplanır.
  • Katmanlar Tier 1, Tier 2, … Tier 50 gibi isimlendirilebilir; veritabanında price_tiers tablosu ile yönetilir.

tier_prices tablosu, katman + para birimi bazında sabit fiyat tutar:

AlanAçıklama
tier_idHangi fiyat katmanı (FK → price_tiers)
currencyPara birimi: usd, try, eur, gbp, aud vb.
amountFiyat (decimal; örn. 19.99, 499.00)

İşleyiş:

  1. Kullanıcı konumu → request.cf.country (örn. TR).
  2. Ülke kodu → para birimi: TRtry, USusd, DEeur (aşağıda CURRENCY_MAP).
  3. Kursun price_tier_id + para birimi ile tier_prices tablosundan tek satır çekilir.
  4. Eşleşme yoksa USD fiyatı fallback olarak kullanılır.

pricing-engine.ts — Para Birimi Çevrim Mantığı

Section titled “pricing-engine.ts — Para Birimi Çevrim Mantığı”

Ülke → para birimi eşlemesi (CURRENCY_MAP):

export const CURRENCY_MAP: Record<string, string> = {
TR: "try", DE: "eur", FR: "eur", US: "usd", GB: "gbp",
CA: "usd", AU: "aud", JP: "jpy", KR: "krw", CN: "cny", IN: "inr", BR: "brl", MX: "mxn", ...
};

Ana fonksiyonlar:

  • getRegionalPrice(db, priceTierId, countryCode)
    Ülke kodunu büyük harfe normalize eder, CURRENCY_MAP ile currency’ye çevirir; tier_prices tablosundan tier_id + currency ile fiyat satırını arar. Bulunamazsa aynı tier için usd satırına düşer; o da yoksa varsayılan { amount: "0.00", currency: "usd" } döner.

  • getRegionalPriceFromRequest(db, priceTierId, request)
    request.cf.country (veya context üzerinden cf) ile ülke kodunu alır; getRegionalPrice çağırır.

  • getBundleRegionalPrice(db, bundleId, countryCode)
    Bir pakete bağlı tüm kursları bulur, her kurs için getRegionalPrice ile PPP fiyatları toplar, ardından paketin discountPercentage alanını uygular. Sonuç olarak originalAmount (toplam tekil kurs fiyatı), discountedAmount (indirimli paket fiyatı), currency ve courseCount döner. bundle.$slug.tsx, course.$id.tsx, cart.tsx ve payment.checkout.tsx route’larında sepet/checkout ve vitrin tarafında paket fiyatı bu fonksiyonla hesaplanır.

Para birimi çevrimi: Sistem döviz kuru çevrimi yapmaz; her tier için her para biriminde ayrı sabit fiyat tanımlanır. Böylece PPP (satın alma gücü) ve yerel fiyatlandırma manuel kontrol edilir.

import { getCountryFromRequest } from "~/lib/pricing";
import { getRegionalPriceFromRequest } from "~/lib/pricing-engine";
// Loader içinde
const countryCode = getCountryFromRequest(request, env, context?.cloudflare);
const { amount, currency } = await getRegionalPriceFromRequest(db, course.price_tier_id, request);

Sepet, checkout ve liste sayfalarında fiyat gösterimi bu amount ve currency ile yapılır. Sembol için app/lib/pricing.ts içindeki getCurrencySymbol(currency) kullanılır.

Dosya: app/routes/account.purchase-history.tsx

Öğrenci hangi para birimi ile ödediyse (€, ₺, $) satın alma kartında aynı para birimi ve tutar gösterilir. Veri kaynağı: enrollments.paid_amount_minor ve enrollments.paid_currency (migration 0032_enrollments_paid_amount_currency.sql). Webhook ve GraphQL buyWithSavedCard enrollment oluştururken bu alanlar Stripe session.amount_total / session.currency veya paymentIntent.amount / paymentIntent.currency ile doldurulur. UI’da formatPriceWithCurrency(amountMinor, currency) (app/lib/pricing.ts) kullanılır; bölgesel fiyatlandırma ile tutarlı deneyim sağlanır. Detay için Döviz Gösterimi ve İade Akışları sayfasına bakın.

AdımAçıklama
1Cloudflare request.cf.country (veya context) ile ülke kodu alınır.
2CURRENCY_MAP ile ülke → para birimi (try, usd, eur vb.) belirlenir.
3Kurs/paketin price_tier_id + bu currency ile tier_prices sorgulanır.
4Sonuç yoksa aynı tier için usd satırı, o da yoksa varsayılan fiyat kullanılır.

Detaylı tablo yapısı için Veritabanı Şeması ve kısa özet için Bölgesel Fiyat Matrix sayfalarına bakın.

Paket vitrin ve kurs sayfasındaki paket fiyatı: getBundleRegionalPrice paketteki kursların price_tier_id değerleri üzerinden PPP toplamı ve paket indirim yüzdesini birleştirir; UI akışı için Kurs ve Paket Vitrin Sayfaları sayfasına bakın.

  • app/lib/pricing-engine.ts — Bölgesel fiyat hesaplama; getCountryFromRequest, getRegionalPriceFromRequest.
  • app/lib/pricing.ts — getCountryFromRequest, para birimi ve tier_prices yardımcıları.
  • app/routes/payment.checkout.tsx — Checkout’ta bölgesel fiyat kullanımı.
  • app/db/schema.ts — price_tiers, tier_prices tabloları.