mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 12:01:40 +00:00
165 lines
5.6 KiB
Lua
165 lines
5.6 KiB
Lua
Bridge.Database.AfterMigration("sky_phone", function()
|
|
|
|
local function valid_amount(value)
|
|
local amount = tonumber(value)
|
|
if not amount or amount ~= math.floor(amount) then
|
|
return nil
|
|
end
|
|
if amount < Config.Banking.MinimumAmount or amount > Config.Banking.MaximumAmount then
|
|
return nil
|
|
end
|
|
return amount
|
|
end
|
|
|
|
local function player_name(source)
|
|
local firstname = Bridge.Framework.GetFirstname(source)
|
|
local lastname = Bridge.Framework.GetLastname(source)
|
|
local name = ((firstname or "") .. " " .. (lastname or "")):match("^%s*(.-)%s*$")
|
|
if name == "" then
|
|
name = GetPlayerName(source) or ("Player %s"):format(source)
|
|
end
|
|
return name
|
|
end
|
|
|
|
local function require_banking_session(source)
|
|
local session, error_response = SkyPhone.RequireSession(source)
|
|
if not session then
|
|
return nil, error_response
|
|
end
|
|
local identifier = Bridge.Framework.GetIdentifier(source)
|
|
if type(identifier) ~= "string" or identifier == "" then
|
|
return nil, { success = false, error = "banking_unavailable" }
|
|
end
|
|
return identifier
|
|
end
|
|
|
|
local function transaction_dto(row)
|
|
return {
|
|
id = tonumber(row.id),
|
|
kind = row.kind,
|
|
amount = tonumber(row.amount) or 0,
|
|
label = row.label or "",
|
|
reference = row.reference or "",
|
|
createdAt = (tonumber(row.created_at_unix) or 0) * 1000,
|
|
}
|
|
end
|
|
|
|
local function transactions(identifier)
|
|
local rows = Bridge.Database.Query([[
|
|
SELECT `id`, `kind`, `amount`, `label`, `reference`,
|
|
UNIX_TIMESTAMP(`created_at`) AS `created_at_unix`
|
|
FROM `sky_phone_bank_transactions`
|
|
WHERE `owner_identifier` = ?
|
|
ORDER BY `id` DESC
|
|
LIMIT ?
|
|
]], { identifier, Config.Banking.HistoryLimit })
|
|
local result = {}
|
|
for _, row in ipairs(rows) do
|
|
result[#result + 1] = transaction_dto(row)
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function overview(source, identifier)
|
|
return {
|
|
bank = math.max(0, tonumber(Bridge.Framework.GetMoney(source, "bank")) or 0),
|
|
cash = math.max(0, tonumber(Bridge.Framework.GetMoney(source, "cash")) or 0),
|
|
currency = Config.Banking.Currency,
|
|
playerId = source,
|
|
playerName = player_name(source),
|
|
transactions = transactions(identifier),
|
|
}
|
|
end
|
|
|
|
local function record_transaction(identifier, kind, amount, label, reference)
|
|
Bridge.Database.Query([[
|
|
INSERT INTO `sky_phone_bank_transactions`
|
|
(`owner_identifier`, `kind`, `amount`, `label`, `reference`)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
]], { identifier, kind, amount, label or "", reference or "" })
|
|
end
|
|
|
|
local function notify_changed(source, data)
|
|
TriggerClientEvent("sky_phone:banking:changed", source, data)
|
|
end
|
|
|
|
local function online_source_for_phone(number)
|
|
local rows = Bridge.Database.Query([[
|
|
SELECT d.`imei`
|
|
FROM `sky_phone_sims` s
|
|
INNER JOIN `sky_phone_devices` d ON d.`sim_id` = s.`id`
|
|
WHERE s.`phone_number` = ?
|
|
LIMIT 1
|
|
]], { number })
|
|
local device = rows[1]
|
|
if not device or type(device.imei) ~= "string" then
|
|
return nil
|
|
end
|
|
for _, player_source in ipairs(Bridge.Framework.GetPlayers()) do
|
|
local target = tonumber(player_source) or player_source
|
|
if SkyPhone.FindDeviceSlots(target, device.imei)[1] then
|
|
return target
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
Bridge.Callbacks.Register("sky_phone:banking:overview", function(source)
|
|
local identifier, error_response = require_banking_session(source)
|
|
if not identifier then
|
|
return error_response
|
|
end
|
|
return { success = true, data = overview(source, identifier) }
|
|
end)
|
|
|
|
Bridge.Callbacks.Register("sky_phone:banking:transfer", function(source, data)
|
|
if not SkyPhone.AllowOperation(source, "banking_transaction", Config.Banking.ActionsPerMinute, 60) then
|
|
return { success = false, error = "rate_limited" }
|
|
end
|
|
local identifier, error_response = require_banking_session(source)
|
|
if not identifier then
|
|
return error_response
|
|
end
|
|
local amount = valid_amount(data and data.amount)
|
|
local number = SkyPhoneSimNumber.Normalize(
|
|
data and data.phoneNumber,
|
|
Config.Sim.NumberLength,
|
|
Config.Sim.NumberPrefix
|
|
)
|
|
if not amount or not number then
|
|
return { success = false, error = "invalid_request" }
|
|
end
|
|
local target = online_source_for_phone(number)
|
|
if not target then
|
|
return { success = false, error = "target_not_found" }
|
|
end
|
|
if target == source then
|
|
return { success = false, error = "self_transfer" }
|
|
end
|
|
local target_identifier = Bridge.Framework.GetIdentifier(target)
|
|
if type(target_identifier) ~= "string" or target_identifier == "" then
|
|
return { success = false, error = "target_not_found" }
|
|
end
|
|
if not Bridge.Framework.RemoveMoney(source, "bank", amount) then
|
|
return { success = false, error = "insufficient_funds" }
|
|
end
|
|
if not Bridge.Framework.AddMoney(target, "bank", amount) then
|
|
Bridge.Framework.AddMoney(source, "bank", amount)
|
|
return { success = false, error = "transfer_failed" }
|
|
end
|
|
|
|
local reference = ("phone-transfer-%s-%s"):format(os.time(), source)
|
|
local sender_name = player_name(source)
|
|
record_transaction(identifier, "transfer_out", amount, player_name(target), reference)
|
|
record_transaction(target_identifier, "transfer_in", amount, sender_name, reference)
|
|
notify_changed(target, {
|
|
kind = "transfer_in",
|
|
amount = amount,
|
|
currency = Config.Banking.Currency,
|
|
sender = sender_name,
|
|
})
|
|
return { success = true, data = overview(source, identifier) }
|
|
end)
|
|
|
|
end)
|