Add promo code functionality to deposits and enhance wallet routes
This commit is contained in:
29
schema.sql
29
schema.sql
@@ -8,15 +8,42 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS promo_codes (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
code VARCHAR(32) NOT NULL UNIQUE,
|
||||
bonus_type VARCHAR(16) NOT NULL DEFAULT 'fixed',
|
||||
bonus_value INT NOT NULL DEFAULT 0,
|
||||
max_uses INT NULL,
|
||||
expires_at DATETIME NULL,
|
||||
active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS deposits (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
amount INT NOT NULL,
|
||||
bonus_amount INT NOT NULL DEFAULT 0,
|
||||
promo_code_id BIGINT UNSIGNED NULL,
|
||||
stripe_payment_intent_id VARCHAR(128) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'created',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_deposits_user_id FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
CONSTRAINT fk_deposits_user_id FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
CONSTRAINT fk_deposits_promo_code_id FOREIGN KEY (promo_code_id) REFERENCES promo_codes(id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_deposits_stripe ON deposits (stripe_payment_intent_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS promo_redemptions (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
promo_code_id BIGINT UNSIGNED NOT NULL,
|
||||
deposit_id BIGINT UNSIGNED NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_redemptions_user_id FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
CONSTRAINT fk_redemptions_promo_code_id FOREIGN KEY (promo_code_id) REFERENCES promo_codes(id),
|
||||
CONSTRAINT fk_redemptions_deposit_id FOREIGN KEY (deposit_id) REFERENCES deposits(id),
|
||||
UNIQUE KEY uniq_user_promo (user_id, promo_code_id)
|
||||
);
|
||||
|
||||
@@ -19,6 +19,27 @@ function ensureStripeConfigured(res) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getPromoCode(code) {
|
||||
const rows = await query(
|
||||
'SELECT id, code, bonus_type, bonus_value, max_uses, expires_at, active FROM promo_codes WHERE code = ? AND active = 1 AND (expires_at IS NULL OR expires_at > NOW())',
|
||||
[code]
|
||||
);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
function calcBonusAmount(amount, promo) {
|
||||
if (!promo) {
|
||||
return 0;
|
||||
}
|
||||
if (promo.bonus_type === 'percent') {
|
||||
return Math.max(0, Math.floor(amount * Number(promo.bonus_value) / 100));
|
||||
}
|
||||
if (promo.bonus_type === 'fixed') {
|
||||
return Math.max(0, Number(promo.bonus_value));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
router.post('/api/wallet/deposit-intent', authMiddleware, async (req, res) => {
|
||||
try {
|
||||
if (!ensureStripeConfigured(res)) {
|
||||
@@ -36,6 +57,39 @@ router.post('/api/wallet/deposit-intent', authMiddleware, async (req, res) => {
|
||||
return res.status(400).json({ error: `A feltoltes ${MIN_DEPOSIT} es ${MAX_DEPOSIT} Ft kozott lehet.` });
|
||||
}
|
||||
|
||||
const promoInput = req.body.promoCode?.toString().trim().toUpperCase();
|
||||
let promo = null;
|
||||
let bonusAmount = 0;
|
||||
let promoCodeId = null;
|
||||
|
||||
if (promoInput) {
|
||||
promo = await getPromoCode(promoInput);
|
||||
if (!promo) {
|
||||
return res.status(400).json({ error: 'Ervenytelen promo kod.' });
|
||||
}
|
||||
|
||||
const redeemed = await query(
|
||||
'SELECT id FROM promo_redemptions WHERE user_id = ? AND promo_code_id = ? LIMIT 1',
|
||||
[req.userId, promo.id]
|
||||
);
|
||||
if (redeemed.length > 0) {
|
||||
return res.status(400).json({ error: 'A promo kod mar felhasznalva.' });
|
||||
}
|
||||
|
||||
if (promo.max_uses) {
|
||||
const usage = await query(
|
||||
'SELECT COUNT(*) AS count FROM promo_redemptions WHERE promo_code_id = ?',
|
||||
[promo.id]
|
||||
);
|
||||
if ((usage[0]?.count ?? 0) >= promo.max_uses) {
|
||||
return res.status(400).json({ error: 'A promo kod mar elfogyott.' });
|
||||
}
|
||||
}
|
||||
|
||||
bonusAmount = calcBonusAmount(amount, promo);
|
||||
promoCodeId = promo.id;
|
||||
}
|
||||
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount: stripeAmount,
|
||||
currency: 'huf',
|
||||
@@ -46,8 +100,8 @@ router.post('/api/wallet/deposit-intent', authMiddleware, async (req, res) => {
|
||||
});
|
||||
|
||||
await query(
|
||||
'INSERT INTO deposits (user_id, amount, stripe_payment_intent_id, status) VALUES (?, ?, ?, ?)',
|
||||
[req.userId, amount, paymentIntent.id, 'created']
|
||||
'INSERT INTO deposits (user_id, amount, bonus_amount, promo_code_id, stripe_payment_intent_id, status) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[req.userId, amount, bonusAmount, promoCodeId, paymentIntent.id, 'created']
|
||||
);
|
||||
|
||||
return res.json({ clientSecret: paymentIntent.client_secret, paymentIntentId: paymentIntent.id });
|
||||
@@ -72,7 +126,7 @@ router.post('/api/wallet/confirm', authMiddleware, async (req, res) => {
|
||||
}
|
||||
|
||||
const rows = await query(
|
||||
'SELECT id, status, amount FROM deposits WHERE user_id = ? AND stripe_payment_intent_id = ?',
|
||||
'SELECT id, status, amount, bonus_amount, promo_code_id FROM deposits WHERE user_id = ? AND stripe_payment_intent_id = ?',
|
||||
[req.userId, paymentIntentId]
|
||||
);
|
||||
const deposit = rows[0];
|
||||
@@ -90,7 +144,21 @@ router.post('/api/wallet/confirm', authMiddleware, async (req, res) => {
|
||||
'UPDATE deposits SET status = ? WHERE id = ?',
|
||||
['succeeded', deposit.id]
|
||||
);
|
||||
await query('UPDATE users SET balance = balance + ? WHERE id = ?', [deposit.amount, req.userId]);
|
||||
const totalCredit = Number(deposit.amount) + Number(deposit.bonus_amount || 0);
|
||||
await query('UPDATE users SET balance = balance + ? WHERE id = ?', [totalCredit, req.userId]);
|
||||
|
||||
if (deposit.promo_code_id) {
|
||||
const already = await query(
|
||||
'SELECT id FROM promo_redemptions WHERE user_id = ? AND promo_code_id = ? LIMIT 1',
|
||||
[req.userId, deposit.promo_code_id]
|
||||
);
|
||||
if (already.length === 0) {
|
||||
await query(
|
||||
'INSERT INTO promo_redemptions (user_id, promo_code_id, deposit_id) VALUES (?, ?, ?)',
|
||||
[req.userId, deposit.promo_code_id, deposit.id]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const balanceRows = await query('SELECT balance FROM users WHERE id = ?', [req.userId]);
|
||||
|
||||
Reference in New Issue
Block a user