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.
Genel Akış
Section titled “Genel Akış”- Harici API: open.er-api.com —
GET https://open.er-api.com/v6/latest/usdile USD bazlı güncel kurlar alınır. - Ö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. - Cron: 30 dakikada bir (
*/30 * * * *) Workerscheduledhandler’ı API’den kurları çeker ve KV’ye yazar; böylece taze veri sürekli hazır kalır. - Kullanım: Eğitmen ödemeler sayfası (
instructor.payouts) loader’dagetCachedRates(env)çağrılır; bakiye hesaplanırkenconvertMinorToUsdile tüm kazançlar USD’ye çevrilir; UI’daconvertUsdToCurrency/convertAmountToCurrencyile yerel para birimine çevrilip gösterilir. Account layout (app/routes/account.tsx) loader’da dagetCachedRates(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ındagetDisplayCurrencyFromCountry(user?.country)veconvertUsdToCurrencyile 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.
API ve Veri Formatı
Section titled “API ve Veri Formatı”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"verateskontrolü yapar; dönen objeyeupdatedAt: new Date().toISOString()ekleyerekCachedRatesPayloaddöner. - getCachedRates(env): Önce KV’den
exchange_ratesanahtarı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.
Cloudflare KV ve Cron
Section titled “Cloudflare KV ve Cron”KV Binding
Section titled “KV Binding”wrangler.toml:
[[kv_namespaces]]binding = "EXCHANGE_RATES_KV"id = "<KV_NAMESPACE_ID>"- Anahtar:
exchange_rates(sabit). - Değer:
CachedRatesPayloadJSON (içinderates,updatedAt,base_codevb.). - TTL: Yazarken
expirationTtl: 3600(1 saat). Cron 30 dakikada bir yazdığı için veri sürekli yenilenir.
Cron (Scheduled) Handler
Section titled “Cron (Scheduled) Handler”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
scheduledhandler’ı tetikler. - updateCachedRates(env):
fetchRatesFromApi()ile kurları alır;env.EXCHANGE_RATES_KVvarsaput(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ı).
Çevrim Fonksiyonları
Section titled “Çevrim Fonksiyonları”Tüm fonksiyonlar app/lib/exchange-rates.ts içinde; rates her zaman 1 USD = rates[code] formatında (API’den gelen ham veri).
| Fonksiyon | Açıklama |
|---|---|
| toApiCurrency(currency) | Para birimi kodunu büyük harfe çevirir (örn. try → TRY). |
| 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 / TUR → TRY, Euro bölgesi ülkeleri → EUR, diğer → USD. Euro bölgesi listesi sabit (AT, BE, DE, FR, …). |
Eğitmen Bakiye ve Kazançlarda Kullanım
Section titled “Eğitmen Bakiye ve Kazançlarda Kullanım”Dosya: app/routes/instructor.payouts.tsx
Loader
Section titled “Loader”- getCachedRates(env) ile kurlar alınır (KV’den veya API fallback). Hata olursa
`{ rates: {}, updatedAt: null }`kullanılır. getInstructorBalance(db, userId, { rates })çağrılır.app/lib/db-queries.tsiçindegetInstructorBalance:- Tüm kazanç satırlarını (
earnings) okur; her satırdainstructorShare(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.rateAtSaleyoksa veya 0 ise convertMinorToUsd(minorAmount, currency, rates) (güncel kur) kullanılır; rates de yoksaminor/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.
- Tüm kazanç satırlarını (
- 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.updatedAtgö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.
Yerel Geliştirme
Section titled “Yerel Geliştirme”- 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 manuelupdateCachedRatesç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 Değişkenleri / Binding
Section titled “Ortam Değişkenleri / Binding”| Ortam / Binding | Açıklama |
|---|---|
| EXCHANGE_RATES_KV | Cloudflare 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.
Earnings ve rateAtSale
Section titled “Earnings ve rateAtSale”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üncelratesile 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.
| Konu | Açıklama |
|---|---|
| API | open.er-api.com/v6/latest/usd — USD bazlı kurlar. |
| Önbellek | Cloudflare KV, anahtar exchange_rates, TTL 1 saat. |
| Cron | 30 dakikada bir updateCachedRates ile KV güncellenir. |
| Loader | getCachedRates(env) — KV’den oku; 25 dk’dan eskiyse API’den çek, KV’ye yaz. |
| Earnings | currency + rateAtSale ile saklanır; raporlamada minorToUsdWithRate kullanılır. |
| Bakiye | getInstructorBalance — rateAtSale öncelikli, yoksa convertMinorToUsd. |
| UI | Yerel para birimi getDisplayCurrencyFromCountry; tutarlar convertUsdToCurrency / convertAmountToCurrency ile gösterilir. |
İlgili Dosyalar
Section titled “İlgili Dosyalar”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ı).