GTA FiveM
Overview
Let your FiveM community earn in-game rewards for voting. This Lua resource uses the topgservers vote-check API to verify votes and grant money, items, or custom rewards through your server's framework (ESX, QBCore, or standalone).
Prerequisites
- A FiveM server with a framework (ESX, QBCore, or standalone)
- topgservers API key — generate one in My Servers → API
- Webhook secret (optional, for real-time rewards)
- Server artifact build 5848+ recommended
Installation
1 Create the resource folder
Create a `topg-vote` folder inside your `resources/` directory with the standard FiveM layout.
resources/
└── topg-vote/
├── fxmanifest.lua
├── config.lua
└── server.lua 2 Create the manifest
Register the resource with FiveM.
fx_version 'cerulean'
game 'gta5'
name 'topg-vote'
description 'topgservers vote reward integration'
version '1.0.0'
server_scripts {
'config.lua',
'server.lua'
} 3 Start the resource
Add `ensure topg-vote` to your `server.cfg`, then restart the server or run `refresh` + `start topg-vote` in the console.
Configuration
Config = {}
Config.ApiKey = "tgs_your_api_key_here"
Config.WebhookSecret = "whsec_your_secret_here"
-- How often to poll the vote-check API (seconds)
Config.PollInterval = 60
-- Reward settings
Config.RewardMoney = 5000 -- In-game currency to give
Config.RewardMessage = "Thanks for voting on topgservers! You received $5,000."
-- Framework: "esx", "qbcore", or "standalone"
Config.Framework = "esx" Vote Check
Call the /api/v1/vote-check endpoint to determine if a player has voted today.
local rewarded = {}
function CheckPlayerVote(playerId, playerName)
if rewarded[playerId] then return end
PerformHttpRequest(
"https://topgservers.net/api/v1/vote-check?username=" ..
playerName,
function(statusCode, body, headers)
if statusCode ~= 200 then return end
local data = json.decode(body)
if data and data.voted then
rewarded[playerId] = true
GrantReward(playerId)
end
end,
"GET",
"",
{ ["Authorization"] = "Bearer " .. Config.ApiKey }
)
end
-- Poll all connected players
CreateThread(function()
while true do
Wait(Config.PollInterval * 1000)
for _, playerId in ipairs(GetPlayers()) do
local name = GetPlayerName(playerId)
if name then
CheckPlayerVote(playerId, name)
end
end
end
end) Webhook Receiver
Verify the X-TopG-Signature header using HMAC-SHA256 to ensure webhook payloads are authentic.
-- FiveM does not natively support running an HTTP server,
-- so webhook handling requires an external proxy or middleware.
--
-- Option A: Use a lightweight Node.js sidecar that receives
-- webhooks and triggers a server event via direct connection.
--
-- Option B: Use the polling approach above (recommended for FiveM).
--
-- If using a sidecar, verify the HMAC signature in Node.js:
--[[
Node.js sidecar example:
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const sig = req.headers['x-topg-signature'];
const parts = sig.split(',');
const timestamp = parts.find(p => p.startsWith('t=')).slice(2);
const hash = parts.find(p => p.startsWith('v1=')).slice(3);
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(timestamp + '.' + req.body)
.digest('hex');
if (expected !== hash) return res.status(401).end();
const data = JSON.parse(req.body);
// Forward to FiveM via HTTP endpoint or direct TCP
res.status(200).end();
});
app.listen(8090);
]] Reward Examples
function GrantReward(playerId)
if Config.Framework == "esx" then
-- ESX money
local xPlayer = ESX.GetPlayerFromId(playerId)
if xPlayer then
xPlayer.addMoney(Config.RewardMoney)
end
elseif Config.Framework == "qbcore" then
-- QBCore money
local Player = QBCore.Functions.GetPlayer(playerId)
if Player then
Player.Functions.AddMoney('cash', Config.RewardMoney)
end
else
-- Standalone: trigger a custom event
TriggerClientEvent('topg:rewardGranted', playerId)
end
TriggerClientEvent('chat:addMessage', playerId, {
args = { "topgservers", Config.RewardMessage }
})
end Notes & Tips
FiveM's `PerformHttpRequest` runs on a separate thread and won't block the game loop. The `rewarded` table resets on resource restart. For persistence, save rewarded player IDs to a database (MySQL/oxmysql) keyed by the current date.