mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-06-25 17:24:06 +08:00
feat(payment): 实现动态国家货币配置加载功能
- 移除硬编码国家货币映射,改为从API动态加载 - 添加 /api/payment/countries 接口获取支持的国家/货币列表 - 实现国家列表缓存机制(1小时有效期) - 在支付页面添加国家选择下拉框动态渲染 - 添加网络请求失败时的备用数据方案 - 保留批量模式状态不被重置的注释说明
This commit is contained in:
@@ -178,4 +178,52 @@ def mark_subscription(account_id: int, request: MarkSubscriptionRequest):
|
||||
|
||||
return {"success": True, "subscription_type": request.subscription_type}
|
||||
|
||||
# ============== 国家/货币配置 ==============
|
||||
|
||||
_countries_cache: dict = {} # {"data": [...], "expires_at": float}
|
||||
|
||||
|
||||
@router.get("/countries")
|
||||
def get_checkout_countries():
|
||||
"""从 ChatGPT checkout 接口获取支持的国家/货币列表(缓存 1 小时)"""
|
||||
import time
|
||||
import curl_cffi.requests as cffi_requests
|
||||
|
||||
now = time.time()
|
||||
if _countries_cache.get("expires_at", 0) > now:
|
||||
return {"success": True, "countries": _countries_cache["data"]}
|
||||
|
||||
with get_db() as db:
|
||||
proxy = get_settings().get_proxy_url(db=db)
|
||||
|
||||
try:
|
||||
resp = cffi_requests.get(
|
||||
"https://chatgpt.com/backend-api/checkout_pricing_config/countries",
|
||||
proxies={"http": proxy, "https": proxy} if proxy else None,
|
||||
timeout=15,
|
||||
impersonate="chrome110",
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
countries = data if isinstance(data, list) else data.get("countries", [])
|
||||
_countries_cache["data"] = countries
|
||||
_countries_cache["expires_at"] = now + 3600
|
||||
return {"success": True, "countries": countries}
|
||||
except Exception as e:
|
||||
logger.warning(f"获取国家列表失败: {e}")
|
||||
fallback = [
|
||||
{"country_code": "SG", "currency": "SGD", "country_name": "Singapore"},
|
||||
{"country_code": "US", "currency": "USD", "country_name": "United States"},
|
||||
{"country_code": "TR", "currency": "TRY", "country_name": "Turkey"},
|
||||
{"country_code": "JP", "currency": "JPY", "country_name": "Japan"},
|
||||
{"country_code": "HK", "currency": "HKD", "country_name": "Hong Kong"},
|
||||
{"country_code": "GB", "currency": "GBP", "country_name": "United Kingdom"},
|
||||
{"country_code": "AU", "currency": "AUD", "country_name": "Australia"},
|
||||
{"country_code": "CA", "currency": "CAD", "country_name": "Canada"},
|
||||
{"country_code": "IN", "currency": "INR", "country_name": "India"},
|
||||
{"country_code": "BR", "currency": "BRL", "country_name": "Brazil"},
|
||||
{"country_code": "MX", "currency": "MXN", "country_name": "Mexico"},
|
||||
]
|
||||
return {"success": False, "countries": fallback, "error": str(e)}
|
||||
|
||||
|
||||
|
||||
@@ -1061,7 +1061,7 @@ function resetButtons() {
|
||||
elements.cancelBtn.disabled = true;
|
||||
currentTask = null;
|
||||
currentBatch = null;
|
||||
isBatchMode = false;
|
||||
// 注意:不重置 isBatchMode,因为用户可能想继续使用批量模式
|
||||
// 重置完成标志
|
||||
taskCompleted = false;
|
||||
batchCompleted = false;
|
||||
|
||||
@@ -2,20 +2,53 @@
|
||||
* 支付页面 JavaScript
|
||||
*/
|
||||
|
||||
const COUNTRY_CURRENCY_MAP = {
|
||||
SG: 'SGD', US: 'USD', TR: 'TRY', JP: 'JPY',
|
||||
HK: 'HKD', GB: 'GBP', EU: 'EUR', AU: 'AUD',
|
||||
CA: 'CAD', IN: 'INR', BR: 'BRL', MX: 'MXN',
|
||||
};
|
||||
|
||||
let selectedPlan = 'plus';
|
||||
let generatedLink = '';
|
||||
let countryCurrencyMap = {}; // 动态从接口加载
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadAccounts();
|
||||
loadCountries();
|
||||
});
|
||||
|
||||
// 加载国家/货币列表
|
||||
async function loadCountries() {
|
||||
const sel = document.getElementById('country-select');
|
||||
try {
|
||||
const resp = await fetch('/api/payment/countries');
|
||||
const data = await resp.json();
|
||||
const countries = data.countries || [];
|
||||
|
||||
// 重建映射表
|
||||
countryCurrencyMap = {};
|
||||
countries.forEach(c => {
|
||||
countryCurrencyMap[c.country_code] = c.currency;
|
||||
});
|
||||
|
||||
// 记住当前选中值
|
||||
const current = sel.value;
|
||||
|
||||
// 渲染选项
|
||||
sel.innerHTML = countries.map(c =>
|
||||
`<option value="${c.country_code}">${c.country_name} (${c.currency})</option>`
|
||||
).join('');
|
||||
|
||||
// 恢复选中或默认 SG
|
||||
sel.value = current && countryCurrencyMap[current] ? current : 'SG';
|
||||
onCountryChange();
|
||||
|
||||
if (!data.success) {
|
||||
console.warn('国家列表使用内置 fallback:', data.error);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载国家列表失败:', e);
|
||||
sel.innerHTML = '<option value="SG">Singapore (SGD)</option>';
|
||||
countryCurrencyMap = { SG: 'SGD' };
|
||||
onCountryChange();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载账号列表
|
||||
async function loadAccounts() {
|
||||
try {
|
||||
@@ -37,7 +70,7 @@ async function loadAccounts() {
|
||||
// 国家切换
|
||||
function onCountryChange() {
|
||||
const country = document.getElementById('country-select').value;
|
||||
const currency = COUNTRY_CURRENCY_MAP[country] || 'USD';
|
||||
const currency = countryCurrencyMap[country] || '';
|
||||
document.getElementById('currency-display').value = currency;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,22 +106,12 @@
|
||||
<div class="form-group">
|
||||
<label for="country-select">计费国家</label>
|
||||
<select id="country-select" onchange="onCountryChange()">
|
||||
<option value="SG">新加坡 (SGD)</option>
|
||||
<option value="US">美国 (USD)</option>
|
||||
<option value="TR">土耳其 (TRY)</option>
|
||||
<option value="JP">日本 (JPY)</option>
|
||||
<option value="HK">香港 (HKD)</option>
|
||||
<option value="GB">英国 (GBP)</option>
|
||||
<option value="AU">澳大利亚 (AUD)</option>
|
||||
<option value="CA">加拿大 (CAD)</option>
|
||||
<option value="IN">印度 (INR)</option>
|
||||
<option value="BR">巴西 (BRL)</option>
|
||||
<option value="MX">墨西哥 (MXN)</option>
|
||||
<option value="">-- 加载中... --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>对应货币</label>
|
||||
<input type="text" id="currency-display" value="SGD" readonly style="background:var(--surface-hover);cursor:default">
|
||||
<input type="text" id="currency-display" value="" readonly style="background:var(--surface-hover);cursor:default">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user