Refactor backend and add registry build workflow
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
This commit is contained in:
commit
c177d7981d
43 files changed
+18124
No files matched your search
@@ -0,0 +1,107 @@
|
||||
"""Exchange-rate retrieval and Stripe currency conversion."""
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
import requests
|
||||
from runtime_config import (
|
||||
CACHE_DURATION,
|
||||
EXCHANGE_RATE_CACHE,
|
||||
EXCHANGE_RATE_LOCK,
|
||||
logger,
|
||||
)
|
||||
|
||||
__all__ = ['calculate_stripe_amount', 'fetch_exchange_rates', 'get_cached_exchange_rates']
|
||||
|
||||
|
||||
|
||||
def calculate_stripe_amount(credits: int, currency: str) -> int:
|
||||
"""
|
||||
Calculate amount in smallest currency unit (cents, pence, etc.)
|
||||
Base rate: 100 credits = 1.00 USD
|
||||
"""
|
||||
# Get USD price
|
||||
usd_price = credits / 100 # $1 per 100 credits
|
||||
|
||||
# Get exchange rate
|
||||
rates = get_cached_exchange_rates()
|
||||
rate = rates.get(currency, 1.0)
|
||||
|
||||
# Convert to target currency
|
||||
currency_price = usd_price * rate
|
||||
|
||||
# Convert to smallest unit (cents)
|
||||
# Most currencies use 2 decimal places, but some don't
|
||||
if currency in ['JPY', 'KRW']: # Zero decimal currencies
|
||||
return int(currency_price)
|
||||
else:
|
||||
return int(currency_price * 100)
|
||||
|
||||
def fetch_exchange_rates():
|
||||
"""
|
||||
Fetch exchange rates from the free currency API
|
||||
Uses EUR as base currency
|
||||
"""
|
||||
try:
|
||||
# Using the fawazahmed0 currency API - EUR as base
|
||||
url = 'https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/eur.json'
|
||||
|
||||
logger.info("Fetching exchange rates from API...")
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
|
||||
if 'eur' in data:
|
||||
rates_data = data['eur']
|
||||
|
||||
# Extract rates for currencies we support - all relative to EUR
|
||||
new_rates = {
|
||||
'EUR': 1.00, # Base currency
|
||||
'USD': rates_data.get('usd', 1.5), # EUR->USD rate
|
||||
'GBP': rates_data.get('gbp', 0.85), # EUR->GBP rate
|
||||
'PLN': rates_data.get('pln', 4.32), # EUR->PLN rate
|
||||
'PHP': rates_data.get('php', 60.50), # EUR->PHP rate
|
||||
'CZK': rates_data.get('czk', 24.65) # EUR->CZK rate
|
||||
}
|
||||
|
||||
with EXCHANGE_RATE_LOCK:
|
||||
EXCHANGE_RATE_CACHE['rates'] = new_rates
|
||||
EXCHANGE_RATE_CACHE['last_updated'] = datetime.now().isoformat()
|
||||
|
||||
logger.info(f"✅ Exchange rates updated successfully: {new_rates}")
|
||||
return True
|
||||
else:
|
||||
logger.error("Invalid response format from currency API")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error("❌ Timeout fetching exchange rates")
|
||||
return False
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"❌ Error fetching exchange rates: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Unexpected error fetching exchange rates: {e}")
|
||||
return False
|
||||
|
||||
def get_cached_exchange_rates():
|
||||
"""Get cached exchange rates, fetch new ones if cache is stale"""
|
||||
with EXCHANGE_RATE_LOCK:
|
||||
last_updated = EXCHANGE_RATE_CACHE.get('last_updated')
|
||||
|
||||
# If never updated or cache is stale, fetch new rates
|
||||
if last_updated is None:
|
||||
logger.info("Exchange rates cache is empty, fetching...")
|
||||
# Release lock before fetching to avoid deadlock
|
||||
elif (datetime.now() - datetime.fromisoformat(last_updated)).total_seconds() > CACHE_DURATION:
|
||||
logger.info("Exchange rates cache is stale, fetching...")
|
||||
else:
|
||||
# Cache is fresh, return it
|
||||
return EXCHANGE_RATE_CACHE['rates']
|
||||
|
||||
# Fetch new rates (outside lock to avoid blocking)
|
||||
fetch_exchange_rates()
|
||||
|
||||
# Return rates (either newly fetched or fallback)
|
||||
with EXCHANGE_RATE_LOCK:
|
||||
return EXCHANGE_RATE_CACHE['rates']
|
||||
Reference in new issue
Block a user