Skip to content

Döviz Kuru API (Exchange Rates)

open.er-api.com, Cloudflare KV önbelleği, cron ile 30 dakikada bir güncelleme; eğitmen bakiyesi ve kazançların yerel para biriminde gösterimi.

Achidemy’de eğitmen bakiye ve kazançlar çok para birimli (USD, TRY, EUR vb.) saklanır. Kullanıcıya yerel para biriminde (TR → ₺, Euro bölgesi → €, diğer → $) göstermek ve tutarları birbirine çevirmek için döviz kuru servisi kullanılır. Bu sayfa servisin nasıl çalıştığını, API, KV önbelleği, cron ve loader/UI kullanımını açıklar.

  1. Harici API: open.er-api.comGET https://open.er-api.com/v6/latest/usd ile USD bazlı güncel kurlar alınır.
  2. Önbellek: Cloudflare KV (EXCHANGE_RATES_KV) ile kurlar saklanır; loader’lar KV’den okur, 25 dakikadan eski veri varsa API’den çekip KV’ye yazar.
  3. Cron: 30 dakikada bir (*/30 * * * *) Worker scheduled handler’ı API’den kurları çeker ve KV’ye yazar; böylece taze veri sürekli hazır kalır.
  4. Kullanım: Eğitmen ödemeler sayfası (instructor.payouts) loader’da getCachedRates(env) çağrılır; bakiye hesaplanırken convertMinorToUsd ile tüm kazançlar USD’ye çevrilir; UI’da convertUsdToCurrency / convertAmountToCurrency ile yerel para birimine çevrilip gösterilir. Account layout (app/routes/account.tsx) loader’da da getCachedRates(env) çağrılır; affiliate profil sayfası (account.profile.tsx) bakiye ve ödeme talebini yerel para biriminde gösterir. Affiliate bakiye hesaplanırken GraphQL tarafında her kazanç satırı satış anı kuru (earnings.rateAtSale) ile USD’ye çevrilip toplanır; profil sayfasında getDisplayCurrencyFromCountry(user?.country) ve convertUsdToCurrency ile kullanıcının para biriminde (₺/€/$) gösterilir (konum bazlı dinamik döviz). Detay için Döviz Gösterimi ve İade Akışları ve Affiliate Programı sayfalarına bakın.

Endpoint: https://open.er-api.com/v6/latest/usd

Yanıt örneği:

{
"result": "success",
"base_code": "USD",
"rates": {
"USD": 1,
"TRY": 43.72,
"EUR": 0.92,
"GBP": 0.79
},
"time_last_update_unix": 1708123456,
"time_last_update_utc": "2024-02-17 12:00:00+00"
}

Anlam: 1 USD = rates[currency] birim. Örneğin rates.TRY = 43.72 → 1 USD = 43,72 TRY.

Dosya: app/lib/exchange-rates.ts

  • fetchRatesFromApi(): API’ye istek atar; result === "success" ve rates kontrolü yapar; dönen objeye updatedAt: new Date().toISOString() ekleyerek CachedRatesPayload döner.
  • getCachedRates(env): Önce KV’den exchange_rates anahtarıyla okur. Veri yoksa veya 25 dakikadan eskiyse (CACHE_MAX_AGE_MS = 25 * 60 * 1000) API’den çeker, KV’ye yazar (TTL 3600 saniye) ve payload döner. KV yoksa (yerel geliştirme) her seferinde API’ye gider.
  • updateCachedRates(env): Cron tarafından çağrılır; API’den çeker, KV’ye yazar. Loader’lar bu fonksiyonu çağırmaz.

wrangler.toml:

[[kv_namespaces]]
binding = "EXCHANGE_RATES_KV"
id = "<KV_NAMESPACE_ID>"
  • Anahtar: exchange_rates (sabit).
  • Değer: CachedRatesPayload JSON (içinde rates, updatedAt, base_code vb.).
  • TTL: Yazarken expirationTtl: 3600 (1 saat). Cron 30 dakikada bir yazdığı için veri sürekli yenilenir.

Dosya: workers/app.ts

export default {
async scheduled(_event, env: { EXCHANGE_RATES_KV?: KVNamespace }, ctx) {
ctx.waitUntil(updateCachedRates(env));
},
async fetch(request, env, ctx) { ... }
};

wrangler.toml:

crons = ["*/30 * * * *"]
  • Aralık: Her 30 dakikada bir (dakika 0 ve 30’da) Cloudflare otomatik olarak scheduled handler’ı tetikler.
  • updateCachedRates(env): fetchRatesFromApi() ile kurları alır; env.EXCHANGE_RATES_KV varsa put(KV_KEY, JSON.stringify(payload), { expirationTtl: 3600 }) ile yazar.

Böylece API’ye yük sadece cron’da olur; sayfa isteklerinde yalnızca KV okunur (ve gerekiyorsa fallback olarak tek seferlik API çağrısı).

Tüm fonksiyonlar app/lib/exchange-rates.ts içinde; rates her zaman 1 USD = rates[code] formatında (API’den gelen ham veri).

FonksiyonAçıklama
toApiCurrency(currency)Para birimi kodunu büyük harfe çevirir (örn. tryTRY).
convertMinorToUsd(amountMinor, currency, rates)Belirli para birimindeki minor tutarı (cent/kuruş) USD’ye çevirir. Sonuç dolar cinsinden (cent değil). rates[currency] yoksa veya 0 ise tutarı 100’e bölerek USD kabul eder.
convertUsdToMinor(usdAmount, toCurrency, rates)USD tutarını hedef para biriminde minor (cent/kuruş) cinsinden döner. Math.round(usdAmount * rate * 100).
convertUsdToCurrency(usdAmount, toCurrency, rates)USD’yi hedef para biriminde birim başına (gösterim için) çevirir; örn. 10 USD → TRY için 437,21.
convertAmountToCurrency(amount, fromCurrency, toCurrency, rates)Herhangi bir para birimindeki tutarı (birim başına) hedef para birimine çevirir: önce USD’ye, sonra hedefe.
getDisplayCurrencyFromCountry(country)Eğitmenin ülke koduna göre gösterim para birimini döner: TR / TURTRY, Euro bölgesi ülkeleri → EUR, diğer → USD. Euro bölgesi listesi sabit (AT, BE, DE, FR, …).

Dosya: app/routes/instructor.payouts.tsx

  1. getCachedRates(env) ile kurlar alınır (KV’den veya API fallback). Hata olursa `{ rates: {}, updatedAt: null }` kullanılır.
  2. getInstructorBalance(db, userId, { rates }) çağrılır. app/lib/db-queries.ts içinde getInstructorBalance:
    • Tüm kazanç satırlarını (earnings) okur; her satırda instructorShare (minor), currency, rateAtSale (satış anındaki kur: 1 USD = X birim) vardır.
    • USD’ye çevrim: Önce rateAtSale kullanılır: minor / 100 / rateAtSale → USD. rateAtSale yoksa veya 0 ise convertMinorToUsd(minorAmount, currency, rates) (güncel kur) kullanılır; rates de yoksa minor/100 (USD kabul). Böylece TRY/EUR/USD ile yapılan satışlar doğru USD bakiye verir.
    • Para birimi bazlı toplamlar (byCurrency) minor cinsinden saklanır; birleşik bakiye USD’dir.
  3. Loader, exchangeRates: { rates, updatedAt } ile sayfaya kurları ve son güncelleme zamanını verir.
  • Yerel para birimi: getDisplayCurrencyFromCountry(user?.country) ile TR → TRY, EU ülkeleri → EUR, diğer → USD.
  • Bakiye kartı: USD’deki toplam bakiye convertUsdToCurrency(value, displayCurrency, rates) ile yerel para birimine çevrilip sembol ile formatlanır (örn. ₺1.234,56).
  • Kazanç tablosu: Her satırda orijinal tutar ve para birimi (örn. $19,99 veya €18,50) gösterilir; ayrıca yerel karşılık convertAmountToCurrency(amount, fromCurrency, displayCurrency, rates) ile hesaplanıp gösterilir. İsteğe bağlı kur metni (örn. “1 EUR = 43,72 TRY”) da eklenebilir.
  • Kur bilgisi: Sayfada “Kurlar son güncellenme: …” şeklinde exchangeRates.updatedAt gösterilir; kullanıcı verinin ne zaman tazelendiğini görür.

Özet: Backend’de bakiye tek para birimi (USD) olarak toplanır; UI’da eğitmenin ülkesine göre yerel para birimine çevrilerek sunulur. Böylece TR eğitmeni ₺, Alman eğitmeni €, ABD eğitmeni $ görür.

  • KV binding yoksa: getCachedRates(env) her çağrıda API’ye gider (KV’ye yazılamaz, sadece dönen payload kullanılır). Cron yerelde çalışmaz; gerekirse manuel updateCachedRates çağrılmaz.
  • API erişimi: open.er-api.com ücretsiz katmanda rate limit’e tabi olabilir; yerelde sayfa yenileme sıklığına dikkat edin. Production’da cron ile KV güncellendiği için sayfa isteklerinde API’ye gidilmez (KV’deki veri 25 dakikadan yeni ise).
Ortam / BindingAçıklama
EXCHANGE_RATES_KVCloudflare KV namespace binding. Production’da tanımlı olmalı; yoksa önbellek kullanılmaz, her loader isteğinde API’ye gidilir.

Harici API için ayrı bir API key gerekmez; open.er-api.com v6/latest/usd endpoint’i ücretsiz kullanılabilir.

Webhook’ta her satış için earnings kaydına currency (try/eur/usd) ve rateAtSale (1 USD = X birim) yazılır. Raporlama ve bakiye hesaplarında:

  • minorToUsdWithRate(minor, currency, rateAtSale): rateAtSale varsa ve > 0 ise minor/100/rateAtSale → USD; yoksa güncel rates ile convertMinorToUsd veya fallback.
  • getAdminStats, getInstructorBalance ve admin “Döviz Dağılımı” bu mantıkla tüm kazançları USD’ye çevirir; Brüt/Platform sütunlarında orijinal para birimi (minor/100) gösterilir. Detay: Checkout & Webhooks, Döviz Gösterimi ve İade.
KonuAçıklama
APIopen.er-api.com/v6/latest/usd — USD bazlı kurlar.
ÖnbellekCloudflare KV, anahtar exchange_rates, TTL 1 saat.
Cron30 dakikada bir updateCachedRates ile KV güncellenir.
LoadergetCachedRates(env) — KV’den oku; 25 dk’dan eskiyse API’den çek, KV’ye yaz.
Earningscurrency + rateAtSale ile saklanır; raporlamada minorToUsdWithRate kullanılır.
BakiyegetInstructorBalance — rateAtSale öncelikli, yoksa convertMinorToUsd.
UIYerel para birimi getDisplayCurrencyFromCountry; tutarlar convertUsdToCurrency / convertAmountToCurrency ile gösterilir.
  • app/lib/exchange-rates.ts — API, KV, çevrim fonksiyonları, getDisplayCurrencyFromCountry.
  • workers/app.ts — scheduled handler, updateCachedRates.
  • wrangler.toml — crons, kv_namespaces (EXCHANGE_RATES_KV).
  • app/routes/instructor.payouts.tsx — getCachedRates loader, yerel para birimi gösterimi.
  • app/lib/db-queries.ts — getInstructorBalance (convertMinorToUsd kullanımı).