Commit 926949e2 authored by Your Name's avatar Your Name

Add admin payment settings UI with complete API endpoints

- Created admin_payment_settings.html template with system status dashboard
- Added route /dashboard/admin/payment-settings in main.py
- Implemented GET /api/admin/payment-system/status endpoint (master keys, balances, pending/failed payments)
- Implemented GET /api/admin/payment-system/config endpoint (all payment config)
- Implemented PUT /api/admin/payment-system/config/* endpoints:
  - /price-sources - Configure crypto price sources
  - /blockchain - Configure blockchain monitoring
  - /email - Configure email notifications
  - /consolidation - Configure wallet consolidation
- Added Payment Settings link to admin navigation menu in base.html
- Maintained backward compatibility with legacy POST endpoints
parent 6e250055
......@@ -6227,8 +6227,8 @@ async def get_price_sources(request: Request):
return JSONResponse({'price_sources': sources})
@app.post("/api/admin/config/price-sources")
async def update_price_sources(request: Request):
@app.put("/api/admin/payment-system/config/price-sources")
async def update_payment_price_sources(request: Request):
"""Update crypto price source configuration"""
auth_check = require_admin(request)
if auth_check:
......@@ -6265,6 +6265,11 @@ async def update_price_sources(request: Request):
logger.error(f"Error updating price sources: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.post("/api/admin/config/price-sources")
async def update_price_sources(request: Request):
"""Update crypto price source configuration (legacy endpoint)"""
return await update_payment_price_sources(request)
@app.get("/api/admin/config/consolidation")
async def get_consolidation_config(request: Request):
......@@ -6296,8 +6301,8 @@ async def get_consolidation_config(request: Request):
return JSONResponse({'consolidation_settings': settings})
@app.post("/api/admin/config/consolidation")
async def update_consolidation_config(request: Request):
@app.put("/api/admin/payment-system/config/consolidation")
async def update_payment_consolidation_config(request: Request):
"""Update wallet consolidation configuration"""
auth_check = require_admin(request)
if auth_check:
......@@ -6332,6 +6337,11 @@ async def update_consolidation_config(request: Request):
logger.error(f"Error updating consolidation settings: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.post("/api/admin/config/consolidation")
async def update_consolidation_config(request: Request):
"""Update wallet consolidation configuration (legacy endpoint)"""
return await update_payment_consolidation_config(request)
@app.get("/api/admin/config/email")
async def get_email_config(request: Request):
......@@ -6385,8 +6395,8 @@ async def get_email_config(request: Request):
})
@app.post("/api/admin/config/email")
async def update_email_config(request: Request):
@app.put("/api/admin/payment-system/config/email")
async def update_payment_email_config(request: Request):
"""Update email notification configuration"""
auth_check = require_admin(request)
if auth_check:
......@@ -6463,6 +6473,50 @@ async def update_email_config(request: Request):
logger.error(f"Error updating email configuration: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.post("/api/admin/config/email")
async def update_email_config(request: Request):
"""Update email notification configuration (legacy endpoint)"""
return await update_payment_email_config(request)
@app.put("/api/admin/payment-system/config/blockchain")
async def update_payment_blockchain_config(request: Request):
"""Update blockchain monitoring configuration"""
auth_check = require_admin(request)
if auth_check:
return auth_check
try:
body = await request.json()
db = DatabaseRegistry.get_config_database()
with db._get_connection() as conn:
cursor = conn.cursor()
placeholder = '?' if db.db_type == 'sqlite' else '%s'
for config in body.get('blockchain_config', []):
cursor.execute(f"""
UPDATE blockchain_monitoring_config
SET rpc_url = {placeholder},
confirmations_required = {placeholder},
scan_interval_seconds = {placeholder},
is_enabled = {placeholder}
WHERE crypto_type = {placeholder}
""", (
config['rpc_url'],
config['confirmations'],
config['scan_interval'],
config['enabled'],
config['crypto_type']
))
conn.commit()
return JSONResponse({'success': True, 'message': 'Blockchain monitoring configuration updated'})
except Exception as e:
logger.error(f"Error updating blockchain monitoring configuration: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.get("/api/admin/scheduler/status")
async def get_scheduler_status(request: Request):
......@@ -6516,6 +6570,195 @@ async def run_scheduler_job(request: Request):
return JSONResponse({'error': str(e)}, status_code=500)
@app.get("/api/admin/payment-system/status")
async def get_payment_system_status(request: Request):
"""Get payment system status including master keys, balances, and payment counts"""
auth_check = require_admin(request)
if auth_check:
return auth_check
try:
db = DatabaseRegistry.get_config_database()
with db._get_connection() as conn:
cursor = conn.cursor()
# Check master keys status
cursor.execute("SELECT COUNT(*) FROM crypto_master_keys")
master_keys_count = cursor.fetchone()[0]
# Get total crypto balances (would need actual blockchain queries in production)
cursor.execute("""
SELECT crypto_type, SUM(balance) as total
FROM crypto_addresses
GROUP BY crypto_type
""")
balances = {row[0]: float(row[1]) for row in cursor.fetchall()}
# Get pending payments count
cursor.execute("""
SELECT COUNT(*) FROM payments
WHERE status = 'pending'
""")
pending_count = cursor.fetchone()[0]
# Get failed payments count
cursor.execute("""
SELECT COUNT(*) FROM payments
WHERE status = 'failed'
""")
failed_count = cursor.fetchone()[0]
# Get recent payment activity (last 24 hours)
cursor.execute("""
SELECT COUNT(*), SUM(amount) FROM payments
WHERE created_at >= datetime('now', '-1 day')
AND status = 'completed'
""")
recent = cursor.fetchone()
recent_count = recent[0] if recent[0] else 0
recent_amount = float(recent[1]) if recent[1] else 0.0
return JSONResponse({
'master_keys': {
'initialized': master_keys_count > 0,
'count': master_keys_count
},
'balances': balances,
'pending_payments': pending_count,
'failed_payments': failed_count,
'recent_activity': {
'count': recent_count,
'amount': recent_amount
}
})
except Exception as e:
logger.error(f"Error getting payment system status: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.get("/api/admin/payment-system/config")
async def get_payment_system_config(request: Request):
"""Get all payment system configuration"""
auth_check = require_admin(request)
if auth_check:
return auth_check
try:
db = DatabaseRegistry.get_config_database()
with db._get_connection() as conn:
cursor = conn.cursor()
# Get price sources
cursor.execute("""
SELECT crypto_type, price_source, api_key, update_interval_seconds, is_enabled
FROM crypto_price_sources
""")
price_sources = [
{
'crypto_type': row[0],
'price_source': row[1],
'api_key': row[2],
'update_interval': row[3],
'enabled': bool(row[4])
}
for row in cursor.fetchall()
]
# Get blockchain monitoring config
cursor.execute("""
SELECT crypto_type, rpc_url, confirmations_required, scan_interval_seconds, is_enabled
FROM blockchain_monitoring_config
""")
blockchain_config = [
{
'crypto_type': row[0],
'rpc_url': row[1],
'confirmations': row[2],
'scan_interval': row[3],
'enabled': bool(row[4])
}
for row in cursor.fetchall()
]
# Get consolidation settings
cursor.execute("""
SELECT crypto_type, threshold_amount, admin_address, is_enabled
FROM crypto_consolidation_settings
""")
consolidation = [
{
'crypto_type': row[0],
'threshold': float(row[1]),
'admin_address': row[2],
'enabled': bool(row[3])
}
for row in cursor.fetchall()
]
# Get email config
cursor.execute("""
SELECT smtp_host, smtp_port, smtp_username, from_email, from_name, use_tls
FROM email_config
LIMIT 1
""")
smtp_row = cursor.fetchone()
smtp_config = None
if smtp_row:
smtp_config = {
'smtp_host': smtp_row[0],
'smtp_port': smtp_row[1],
'smtp_username': smtp_row[2],
'from_email': smtp_row[3],
'from_name': smtp_row[4],
'use_tls': bool(smtp_row[5])
}
# Get notification settings
cursor.execute("""
SELECT notification_type, is_enabled, subject_template
FROM email_notification_settings
""")
notifications = [
{
'type': row[0],
'enabled': bool(row[1]),
'subject': row[2]
}
for row in cursor.fetchall()
]
return JSONResponse({
'price_sources': price_sources,
'blockchain_config': blockchain_config,
'consolidation': consolidation,
'email': {
'smtp_config': smtp_config,
'notifications': notifications
}
})
except Exception as e:
logger.error(f"Error getting payment system config: {e}")
return JSONResponse({'error': str(e)}, status_code=500)
@app.get("/dashboard/admin/payment-settings")
async def dashboard_admin_payment_settings(request: Request):
"""Admin payment system settings page"""
auth_check = require_admin(request)
if auth_check:
return auth_check
return templates.TemplateResponse(
request=request,
name="dashboard/admin_payment_settings.html",
context={
"request": request,
"session": request.session
}
)
@app.get("/dashboard/pricing")
async def dashboard_pricing(request: Request):
......
......@@ -507,6 +507,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<a href="{{ url_for(request, '/dashboard/users') }}" {% if '/users' in request.path %}class="active"{% endif %}>Users</a>
<a href="{{ url_for(request, '/dashboard/settings') }}" {% if '/settings' in request.path %}class="active"{% endif %}>Settings</a>
<a href="{{ url_for(request, '/dashboard/admin/tiers') }}" {% if '/admin/tiers' in request.path %}class="active"{% endif %}>Tiers</a>
<a href="{{ url_for(request, '/dashboard/admin/payment-settings') }}" {% if '/admin/payment-settings' in request.path %}class="active"{% endif %}>Payment Settings</a>
{% endif %}
{% if show_upgrade_button %}
<a href="{{ url_for(request, '/dashboard/pricing') }}" class="upgrade-button rainbow-text">✨ Upgrade! ✨</a>
......
{% extends "base.html" %}
{% block title %}Payment System Settings - Admin{% endblock %}
{% block content %}
<div class="container-fluid" style="padding: 20px; background: #0f3460; min-height: 100vh;">
<div style="max-width: 1400px; margin: 0 auto;">
<h2 style="color: #4a9eff; margin-bottom: 30px;">
<i class="fas fa-cog me-2"></i>Payment System Settings
</h2>
<!-- System Status -->
<div style="background: #16213e; border: 2px solid #4a9eff; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin: 0 0 20px 0; color: #4a9eff;">
<i class="fas fa-heartbeat me-2"></i>System Status
</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;">
<div style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px;">
<div style="color: #888; font-size: 14px; margin-bottom: 5px;">Master Keys Status</div>
<div id="masterKeysStatus" style="font-size: 24px; font-weight: bold; color: #4a9eff;">
<i class="fas fa-spinner fa-spin"></i> Loading...
</div>
</div>
<div style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px;">
<div style="color: #888; font-size: 14px; margin-bottom: 5px;">Total Crypto Balance</div>
<div id="totalBalance" style="font-size: 24px; font-weight: bold; color: #4a9eff;">
<i class="fas fa-spinner fa-spin"></i> Loading...
</div>
</div>
<div style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px;">
<div style="color: #888; font-size: 14px; margin-bottom: 5px;">Pending Payments</div>
<div id="pendingPayments" style="font-size: 24px; font-weight: bold; color: #f39c12;">
<i class="fas fa-spinner fa-spin"></i> Loading...
</div>
</div>
<div style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px;">
<div style="color: #888; font-size: 14px; margin-bottom: 5px;">Failed Payments</div>
<div id="failedPayments" style="font-size: 24px; font-weight: bold; color: #e74c3c;">
<i class="fas fa-spinner fa-spin"></i> Loading...
</div>
</div>
</div>
<div style="margin-top: 15px; padding: 15px; background: #1a1a2e; border-radius: 8px; border-left: 4px solid #4a9eff;">
<div style="color: #4a9eff; font-weight: bold; margin-bottom: 5px;">
<i class="fas fa-info-circle me-2"></i>About Master Keys
</div>
<div style="color: #e0e0e0; font-size: 14px;">
Master keys are automatically generated on first startup and encrypted with your ENCRYPTION_KEY.
They are used to derive unique payment addresses for each user. The actual keys are never displayed for security.
</div>
</div>
</div>
<!-- Price Sources Configuration -->
<div style="background: #16213e; border: 2px solid #f39c12; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin: 0 0 20px 0; color: #f39c12;">
<i class="fas fa-chart-line me-2"></i>Crypto Price Sources
</h3>
<div style="margin-bottom: 15px; color: #e0e0e0;">
Select which exchanges to use for cryptocurrency price aggregation:
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px;">
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center;">
<input type="checkbox" id="priceCoinbase" checked style="margin-right: 10px; width: 20px; height: 20px;">
<span style="color: #e0e0e0; font-weight: 500;">Coinbase</span>
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center;">
<input type="checkbox" id="priceBinance" checked style="margin-right: 10px; width: 20px; height: 20px;">
<span style="color: #e0e0e0; font-weight: 500;">Binance</span>
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center;">
<input type="checkbox" id="priceKraken" checked style="margin-right: 10px; width: 20px; height: 20px;">
<span style="color: #e0e0e0; font-weight: 500;">Kraken</span>
</label>
</div>
<button type="button" class="btn" onclick="savePriceSources()" style="background: #f39c12; color: white;">
<i class="fas fa-save me-2"></i>Save Price Sources
</button>
</div>
<!-- Blockchain Monitoring -->
<div style="background: #16213e; border: 2px solid #9b59b6; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin: 0 0 20px 0; color: #9b59b6;">
<i class="fas fa-cube me-2"></i>Blockchain Monitoring
</h3>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 10px; color: #e0e0e0; font-weight: 500;">Monitoring Mode</label>
<select id="blockchainMode" style="width: 100%; max-width: 400px; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
<option value="api">API Polling (Recommended)</option>
<option value="webhook">Webhook (Requires public URL)</option>
</select>
<div style="margin-top: 10px; color: #888; font-size: 14px;">
API Polling: Checks blockchain APIs every minute for new transactions<br>
Webhook: Receives instant notifications (requires webhook setup)
</div>
</div>
<div style="margin-bottom: 20px;">
<label style="display: block; margin-bottom: 10px; color: #e0e0e0; font-weight: 500;">Polling Interval (seconds)</label>
<input type="number" id="pollingInterval" value="60" min="30" max="300" style="width: 100%; max-width: 400px; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
</div>
<button type="button" class="btn" onclick="saveBlockchainConfig()" style="background: #9b59b6; color: white;">
<i class="fas fa-save me-2"></i>Save Blockchain Configuration
</button>
</div>
<!-- Email Notifications -->
<div style="background: #16213e; border: 2px solid #27ae60; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin: 0 0 20px 0; color: #27ae60;">
<i class="fas fa-envelope me-2"></i>Email Notifications
</h3>
<div style="margin-bottom: 15px; color: #e0e0e0;">
Enable or disable email notifications for payment events:
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; margin-bottom: 20px;">
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Payment Success</span>
<input type="checkbox" id="emailPaymentSuccess" checked style="width: 20px; height: 20px;">
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Payment Failed</span>
<input type="checkbox" id="emailPaymentFailed" checked style="width: 20px; height: 20px;">
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Subscription Upgraded</span>
<input type="checkbox" id="emailSubscriptionUpgraded" checked style="width: 20px; height: 20px;">
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Subscription Downgraded</span>
<input type="checkbox" id="emailSubscriptionDowngraded" checked style="width: 20px; height: 20px;">
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Subscription Cancelled</span>
<input type="checkbox" id="emailSubscriptionCancelled" checked style="width: 20px; height: 20px;">
</label>
<label style="background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px; padding: 15px; cursor: pointer; display: flex; align-items: center; justify-content: space-between;">
<span style="color: #e0e0e0;">Payment Retry</span>
<input type="checkbox" id="emailPaymentRetry" checked style="width: 20px; height: 20px;">
</label>
</div>
<button type="button" class="btn" onclick="saveEmailConfig()" style="background: #27ae60; color: white;">
<i class="fas fa-save me-2"></i>Save Email Configuration
</button>
</div>
<!-- Consolidation Settings -->
<div style="background: #16213e; border: 2px solid #e67e22; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
<h3 style="margin: 0 0 20px 0; color: #e67e22;">
<i class="fas fa-compress-arrows-alt me-2"></i>Wallet Consolidation
</h3>
<div style="margin-bottom: 15px; color: #e0e0e0;">
Set minimum balance thresholds for automatic consolidation to admin addresses:
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-bottom: 20px;">
<div>
<label style="display: block; margin-bottom: 5px; color: #e0e0e0; font-weight: 500;">
<i class="fab fa-bitcoin me-2"></i>Bitcoin (BTC)
</label>
<input type="number" id="consolidationBTC" value="0.01" step="0.001" min="0" style="width: 100%; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
</div>
<div>
<label style="display: block; margin-bottom: 5px; color: #e0e0e0; font-weight: 500;">
<i class="fab fa-ethereum me-2"></i>Ethereum (ETH)
</label>
<input type="number" id="consolidationETH" value="0.1" step="0.01" min="0" style="width: 100%; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
</div>
<div>
<label style="display: block; margin-bottom: 5px; color: #e0e0e0; font-weight: 500;">
<i class="fas fa-dollar-sign me-2"></i>USDT
</label>
<input type="number" id="consolidationUSDT" value="100" step="10" min="0" style="width: 100%; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
</div>
<div>
<label style="display: block; margin-bottom: 5px; color: #e0e0e0; font-weight: 500;">
<i class="fas fa-coins me-2"></i>USDC
</label>
<input type="number" id="consolidationUSDC" value="100" step="10" min="0" style="width: 100%; padding: 10px; border: 1px solid #0f3460; border-radius: 5px; background: #1a1a2e; color: #e0e0e0;">
</div>
</div>
<button type="button" class="btn" onclick="saveConsolidationConfig()" style="background: #e67e22; color: white;">
<i class="fas fa-save me-2"></i>Save Consolidation Settings
</button>
</div>
<!-- Back Button -->
<div style="margin-top: 30px;">
<a href="{{ url_for(request, '/dashboard/admin/tiers') }}" class="btn" style="background: #0f3460; color: #4a9eff; border: 1px solid #4a9eff;">
<i class="fas fa-arrow-left me-2"></i>Back to Tiers
</a>
</div>
</div>
</div>
<script>
// Load system status
async function loadSystemStatus() {
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/status") }}');
const data = await response.json();
// Update master keys status
const masterKeysEl = document.getElementById('masterKeysStatus');
if (data.master_keys_initialized) {
masterKeysEl.innerHTML = '<i class="fas fa-check-circle" style="color: #27ae60;"></i> ' + data.master_keys_count + ' Keys Active';
} else {
masterKeysEl.innerHTML = '<i class="fas fa-exclamation-triangle" style="color: #e74c3c;"></i> Not Initialized';
}
// Update balances
document.getElementById('totalBalance').textContent = '$' + (data.total_balance_usd || 0).toFixed(2);
document.getElementById('pendingPayments').textContent = data.pending_payments || 0;
document.getElementById('failedPayments').textContent = data.failed_payments || 0;
} catch (error) {
console.error('Error loading system status:', error);
document.getElementById('masterKeysStatus').innerHTML = '<i class="fas fa-times-circle" style="color: #e74c3c;"></i> Error';
}
}
// Load configuration
async function loadConfiguration() {
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/config") }}');
const data = await response.json();
// Load price sources
if (data.price_sources) {
document.getElementById('priceCoinbase').checked = data.price_sources.coinbase || false;
document.getElementById('priceBinance').checked = data.price_sources.binance || false;
document.getElementById('priceKraken').checked = data.price_sources.kraken || false;
}
// Load blockchain config
if (data.blockchain) {
document.getElementById('blockchainMode').value = data.blockchain.mode || 'api';
document.getElementById('pollingInterval').value = data.blockchain.polling_interval || 60;
}
// Load email config
if (data.email_notifications) {
document.getElementById('emailPaymentSuccess').checked = data.email_notifications.payment_success !== false;
document.getElementById('emailPaymentFailed').checked = data.email_notifications.payment_failed !== false;
document.getElementById('emailSubscriptionUpgraded').checked = data.email_notifications.subscription_upgraded !== false;
document.getElementById('emailSubscriptionDowngraded').checked = data.email_notifications.subscription_downgraded !== false;
document.getElementById('emailSubscriptionCancelled').checked = data.email_notifications.subscription_cancelled !== false;
document.getElementById('emailPaymentRetry').checked = data.email_notifications.payment_retry !== false;
}
// Load consolidation config
if (data.consolidation) {
document.getElementById('consolidationBTC').value = data.consolidation.btc || 0.01;
document.getElementById('consolidationETH').value = data.consolidation.eth || 0.1;
document.getElementById('consolidationUSDT').value = data.consolidation.usdt || 100;
document.getElementById('consolidationUSDC').value = data.consolidation.usdc || 100;
}
} catch (error) {
console.error('Error loading configuration:', error);
}
}
// Save functions
async function savePriceSources() {
const config = {
coinbase: document.getElementById('priceCoinbase').checked,
binance: document.getElementById('priceBinance').checked,
kraken: document.getElementById('priceKraken').checked
};
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/config/price-sources") }}', {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(config)
});
if (response.ok) {
showToast('Price sources configuration saved', 'success');
} else {
showToast('Failed to save price sources', 'danger');
}
} catch (error) {
showToast('Error saving price sources: ' + error.message, 'danger');
}
}
async function saveBlockchainConfig() {
const config = {
mode: document.getElementById('blockchainMode').value,
polling_interval: parseInt(document.getElementById('pollingInterval').value)
};
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/config/blockchain") }}', {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(config)
});
if (response.ok) {
showToast('Blockchain configuration saved', 'success');
} else {
showToast('Failed to save blockchain configuration', 'danger');
}
} catch (error) {
showToast('Error saving blockchain configuration: ' + error.message, 'danger');
}
}
async function saveEmailConfig() {
const config = {
payment_success: document.getElementById('emailPaymentSuccess').checked,
payment_failed: document.getElementById('emailPaymentFailed').checked,
subscription_upgraded: document.getElementById('emailSubscriptionUpgraded').checked,
subscription_downgraded: document.getElementById('emailSubscriptionDowngraded').checked,
subscription_cancelled: document.getElementById('emailSubscriptionCancelled').checked,
payment_retry: document.getElementById('emailPaymentRetry').checked
};
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/config/email") }}', {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(config)
});
if (response.ok) {
showToast('Email configuration saved', 'success');
} else {
showToast('Failed to save email configuration', 'danger');
}
} catch (error) {
showToast('Error saving email configuration: ' + error.message, 'danger');
}
}
async function saveConsolidationConfig() {
const config = {
btc: parseFloat(document.getElementById('consolidationBTC').value),
eth: parseFloat(document.getElementById('consolidationETH').value),
usdt: parseFloat(document.getElementById('consolidationUSDT').value),
usdc: parseFloat(document.getElementById('consolidationUSDC').value)
};
try {
const response = await fetch('{{ url_for(request, "/api/admin/payment-system/config/consolidation") }}', {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(config)
});
if (response.ok) {
showToast('Consolidation settings saved', 'success');
} else {
showToast('Failed to save consolidation settings', 'danger');
}
} catch (error) {
showToast('Error saving consolidation settings: ' + error.message, 'danger');
}
}
function showToast(message, type) {
const toastContainer = document.createElement('div');
toastContainer.className = 'position-fixed top-0 end-0 p-3';
toastContainer.style.zIndex = '9999';
toastContainer.innerHTML = `
<div class="toast show" role="alert" style="min-width: 300px;">
<div class="toast-header bg-${type} text-white">
<strong class="me-auto">Notification</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast"></button>
</div>
<div class="toast-body">
${message}
</div>
</div>
`;
document.body.appendChild(toastContainer);
setTimeout(() => {
if (toastContainer.parentNode) {
toastContainer.parentNode.removeChild(toastContainer);
}
}, 5000);
}
// Load data on page load
document.addEventListener('DOMContentLoaded', function() {
loadSystemStatus();
loadConfiguration();
});
</script>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment