const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook secret key (keep secure)
const WEBHOOK_SECRET = 'your-webhook-secret-key';
// Verify webhook signature
const verifyWebhookSignature = (req, res, next) => {
const signature = req.headers['x-tournament-signature'];
const body = JSON.stringify(req.body);
if (!signature) {
return res.status(401).json({ error: 'Missing signature' });
}
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(body)
.digest('hex');
const receivedSignature = signature.replace('sha256=', '');
if (!crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
)) {
return res.status(401).json({ error: 'Invalid signature' });
}
next();
};
// Webhook handler
app.post('/webhooks/tournaments', verifyWebhookSignature, (req, res) => {
const event = req.body;
console.log('Received webhook:', event.eventType, event.eventId);
try {
// Handle the event based on type
switch (event.eventType) {
case 'tournament.started':
handleTournamentStarted(event);
break;
case 'tournament.completed':
handleTournamentCompleted(event);
break;
case 'player.eliminated':
handlePlayerEliminated(event);
break;
case 'player.registered':
handlePlayerRegistered(event);
break;
case 'player.rebought':
handlePlayerRebought(event);
break;
case 'tournament.cancelled':
handleTournamentCancelled(event);
break;
default:
console.log('Unknown event type:', event.eventType);
}
// Return success response
res.status(200).json({
received: true,
eventId: event.eventId,
processedAt: new Date().toISOString()
});
} catch (error) {
console.error('Webhook processing failed:', error);
// Return error response to trigger retry
res.status(500).json({
error: 'Processing failed',
eventId: event.eventId
});
}
});
// Event handlers
const handleTournamentStarted = (event) => {
const { tournamentId, data } = event;
console.log(`Tournament ${tournamentId} started with ${data.initialPlayerCount} players`);
// Update tournament status in your database
updateTournamentStatus(tournamentId, 'in_progress', {
actualStartTime: data.actualStartTime,
playerCount: data.initialPlayerCount,
prizePool: data.prizePool
});
// Send notifications to players
notifyTournamentStart(tournamentId, data);
// Update analytics
trackEvent('tournament_started', {
tournamentId: tournamentId,
playerCount: data.initialPlayerCount,
prizePool: data.prizePool.totalAmount
});
};
const handleTournamentCompleted = (event) => {
const { tournamentId, data } = event;
console.log(`Tournament ${tournamentId} completed with ${data.winners.length} winners`);
// Update tournament status
updateTournamentStatus(tournamentId, 'completed', {
completedAt: data.completedAt,
duration: data.duration,
totalPrizeDistributed: data.prizePool.distributed
});
// Process prize distributions
processPrizeDistributions(tournamentId, data.winners);
// Update player statistics
updatePlayerStats(data.winners);
// Send result notifications
notifyTournamentResults(tournamentId, data.winners);
// Track completion metrics
trackEvent('tournament_completed', {
tournamentId: tournamentId,
duration: data.duration,
playerCount: data.totalPlayers,
totalPrizes: data.prizePool.distributed
});
};
const handlePlayerEliminated = (event) => {
const { tournamentId, playerId, data } = event;
console.log(`Player ${playerId} eliminated from tournament ${tournamentId} at position ${data.eliminationDetails.finalPosition}`);
// Update player status
updatePlayerTournamentStatus(tournamentId, playerId, 'eliminated', {
finalPosition: data.eliminationDetails.finalPosition,
finalScore: data.eliminationDetails.finalScore,
eliminatedAt: data.eliminationDetails.eliminatedAt
});
// Send elimination notification
notifyPlayerElimination(playerId, tournamentId, data.eliminationDetails);
// Update player statistics
updatePlayerStats(playerId, {
tournamentPlayed: 1,
finalPosition: data.eliminationDetails.finalPosition,
amountSpent: data.participation.totalPaid
});
};
const handlePlayerRebought = (event) => {
const { tournamentId, playerId, data } = event;
console.log(`Player ${playerId} rebought in tournament ${tournamentId} for $${data.rebuyDetails.rebuyAmount}`);
// Process rebuy payment
processRebuyPayment(playerId, data.rebuyDetails.rebuyAmount);
// Update player entry count
updatePlayerEntries(tournamentId, playerId, data.rebuyDetails.newEntryCount);
// Track rebuy analytics
trackEvent('player_rebuy', {
tournamentId: tournamentId,
playerId: playerId,
rebuyAmount: data.rebuyDetails.rebuyAmount,
totalEntries: data.rebuyDetails.newEntryCount
});
};
const handleTournamentCancelled = (event) => {
const { tournamentId, data } = event;
console.log(`Tournament ${tournamentId} cancelled: ${data.reason}`);
// Update tournament status
updateTournamentStatus(tournamentId, 'cancelled', {
cancelledAt: data.cancelledAt,
reason: data.reason
});
// Process refunds
processRefunds(tournamentId, data.refunds);
// Notify affected players
notifyTournamentCancellation(tournamentId, data);
};
// Helper functions
const updateTournamentStatus = async (tournamentId, status, data) => {
// Update tournament in your database
console.log(`Updating tournament ${tournamentId} status to ${status}`);
};
const processPrizeDistributions = async (tournamentId, winners) => {
for (const winner of winners) {
// Credit prize amount to player account
console.log(`Crediting $${winner.prizeAmount} to player ${winner.playerId}`);
}
};
const notifyTournamentStart = async (tournamentId, data) => {
// Send push notifications, emails, etc.
console.log(`Sending tournament start notifications for ${tournamentId}`);
};
const trackEvent = (eventType, data) => {
// Send to analytics service
console.log(`Tracking event: ${eventType}`, data);
};
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});