362 lines
14 KiB
Lua
362 lines
14 KiB
Lua
|
|
-- svdden_banking (server): Banking data management, transactions, and callbacks
|
||
|
|
-- Responsibilities:
|
||
|
|
-- - Wait for RegisterCallback to be available, then register server callbacks
|
||
|
|
-- - Debug logging when Config.debug is true
|
||
|
|
-- - Fetch/create user banking records (credit score, savings, transactions)
|
||
|
|
-- - Provide account snapshots for UI
|
||
|
|
-- - Handle transactions: deposit, withdraw, transfer (personal/savings)
|
||
|
|
-- - Update credit score via exported functions (addCredit/removeCredit)
|
||
|
|
-- NOTE: Functionality preserved exactly; comments added for clarity only.
|
||
|
|
while true do
|
||
|
|
if RegisterCallback then break end
|
||
|
|
Wait(0)
|
||
|
|
end
|
||
|
|
-- Debug logger (respects Config.debug)
|
||
|
|
function logDebug(message)
|
||
|
|
local debugEnabled = Config.debug
|
||
|
|
if debugEnabled then
|
||
|
|
local fmt = "^5[Svdden Banking Debug]^0 %s"
|
||
|
|
print(fmt:format(message))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
-- Fetch or create user's banking row; returns row table
|
||
|
|
function getOrCreateBankingRow(sourceId)
|
||
|
|
local uuid = fetchUsersUUID(sourceId)
|
||
|
|
if not uuid then return end
|
||
|
|
|
||
|
|
logDebug("Attempting to fetch user banking data for ID: " .. sourceId)
|
||
|
|
|
||
|
|
local rows = MySQL.Sync.fetchAll(
|
||
|
|
"SELECT * FROM sv_banking_data WHERE uuid = @uuid",
|
||
|
|
{ ["@uuid"] = uuid }
|
||
|
|
)
|
||
|
|
|
||
|
|
if not rows[1] then
|
||
|
|
-- Create initial record if missing
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"INSERT INTO sv_banking_data (uuid, credit_score, savings_balance, transactions) VALUES (@uuid, @credit_score, @savings_balance, @transactions)",
|
||
|
|
{
|
||
|
|
["@uuid"] = uuid,
|
||
|
|
["@credit_score"] = Config.creditScore.startingCredit,
|
||
|
|
["@savings_balance"] = 0,
|
||
|
|
["@transactions"] = json.encode({}),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
rows = { {
|
||
|
|
uuid = uuid,
|
||
|
|
credit_score = Config.creditScore.startingCredit,
|
||
|
|
savings_balance = 0,
|
||
|
|
transactions = json.encode({}),
|
||
|
|
} }
|
||
|
|
logDebug("User ID: " .. sourceId .. " did not have any banking data, creating new data")
|
||
|
|
end
|
||
|
|
|
||
|
|
logDebug("User ID: " .. sourceId .. " banking data has been successfully fetched")
|
||
|
|
return rows[1]
|
||
|
|
end
|
||
|
|
-- Fetch user's credit score (or starting value if missing)
|
||
|
|
function getCreditScore(sourceId)
|
||
|
|
if not sourceId then return end
|
||
|
|
local uuid = fetchUsersUUID(sourceId)
|
||
|
|
if not uuid then return end
|
||
|
|
|
||
|
|
local rows = MySQL.Sync.fetchAll(
|
||
|
|
"SELECT credit_score FROM sv_banking_data WHERE uuid = @uuid",
|
||
|
|
{ ["@uuid"] = uuid }
|
||
|
|
)
|
||
|
|
logDebug("Attempting to fetch user credit score for ID: " .. sourceId)
|
||
|
|
|
||
|
|
local score = rows[1] and rows[1].credit_score or Config.creditScore.startingCredit
|
||
|
|
return score
|
||
|
|
end
|
||
|
|
function fetchAccountData(sourceId)
|
||
|
|
-- Ensure player exists
|
||
|
|
local user = fetchUserBySource(sourceId)
|
||
|
|
if not user then return end
|
||
|
|
|
||
|
|
-- Fetch or create row for savings/transactions
|
||
|
|
local row = getOrCreateBankingRow(sourceId)
|
||
|
|
|
||
|
|
local payload = {}
|
||
|
|
payload.CHARACTER_NAME = fetchUserNameBySource(sourceId)
|
||
|
|
|
||
|
|
-- Credit score (if enabled)
|
||
|
|
local creditScoreValue = 0
|
||
|
|
if Config.creditScore.enabled then
|
||
|
|
local fetched = getCreditScore(sourceId)
|
||
|
|
creditScoreValue = fetched or 0
|
||
|
|
end
|
||
|
|
payload.CHARACTER_CREDIT_SCORE = creditScoreValue
|
||
|
|
|
||
|
|
-- Primary balance
|
||
|
|
payload.PRIMARY_BANK_BALANCE = getUserPrimaryAccount(sourceId)
|
||
|
|
|
||
|
|
-- Savings balance
|
||
|
|
local savingsBalance = 0
|
||
|
|
if row then
|
||
|
|
local parsed = tonumber(row.savings_balance)
|
||
|
|
if parsed then savingsBalance = parsed end
|
||
|
|
end
|
||
|
|
payload.SAVINGS_BANK_BALANCE = savingsBalance
|
||
|
|
|
||
|
|
-- Cash balance
|
||
|
|
payload.CASH_BALANCE = getUserCashAmount(sourceId)
|
||
|
|
|
||
|
|
-- Transactions
|
||
|
|
local transactions = {}
|
||
|
|
if row then
|
||
|
|
local decoded = json.decode(row.transactions)
|
||
|
|
if decoded then transactions = decoded end
|
||
|
|
end
|
||
|
|
payload.TRANSACTIONS = transactions
|
||
|
|
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully fetched their account data")
|
||
|
|
return payload
|
||
|
|
end
|
||
|
|
RegisterCallback("fetchAccountData", fetchAccountData)
|
||
|
|
|
||
|
|
function handleTransaction(A0_2, A1_2, A2_2, A3_2, A4_2, A5_2)
|
||
|
|
-- Readable locals
|
||
|
|
local sourceId, account, action, amount, dateStr, targetId = A0_2, A1_2, A2_2, A3_2, A4_2, A5_2
|
||
|
|
if not (sourceId and account and action and amount) or not dateStr then
|
||
|
|
return { type = "error", response = "invalid_data" }
|
||
|
|
end
|
||
|
|
|
||
|
|
logDebug(string.format("Attempting to create transaction for ID:%s with action %s and amount %s", sourceId, action, amount))
|
||
|
|
|
||
|
|
if amount <= 0 then
|
||
|
|
logDebug("User ID: " .. sourceId .. " attempted to perform an action with less than the minimum amount")
|
||
|
|
return { type = "error", response = "min_amount" }
|
||
|
|
end
|
||
|
|
|
||
|
|
local me = fetchUserBySource(sourceId)
|
||
|
|
if not me then
|
||
|
|
return { type = "error", response = "myself_not_found" }
|
||
|
|
end
|
||
|
|
local sourceUuid = fetchUsersUUID(sourceId)
|
||
|
|
if not sourceUuid then
|
||
|
|
return { type = "error", response = "myself_not_found" }
|
||
|
|
end
|
||
|
|
|
||
|
|
local resultType, resultResponse = "", ""
|
||
|
|
local row = getOrCreateBankingRow(sourceId)
|
||
|
|
local actionLower = string.lower(action)
|
||
|
|
|
||
|
|
if actionLower == "transfer" then
|
||
|
|
if not targetId then return end
|
||
|
|
if targetId == sourceId then
|
||
|
|
logDebug("User ID: " .. sourceId .. " attempted to transfer money to themselves")
|
||
|
|
return { type = "error", response = "cant_transfer_to_self" }
|
||
|
|
end
|
||
|
|
if not fetchUserBySource(targetId) or not fetchUserBySource(targetId) then
|
||
|
|
logDebug("User ID: " .. sourceId .. " attempted to transfer money to a non-existent player")
|
||
|
|
return { type = "error", response = "no_target" }
|
||
|
|
end
|
||
|
|
|
||
|
|
if string.lower(account) == "personal" then
|
||
|
|
logDebug("User ID: " .. sourceId .. " is transferring money to " .. targetId .. " from their personal account")
|
||
|
|
local current = getUserPrimaryAccount(sourceId)
|
||
|
|
if amount > current then
|
||
|
|
resultType, resultResponse = "error", "no_money"
|
||
|
|
logDebug("User ID: " .. sourceId .. " does not have enough money to transfer")
|
||
|
|
else
|
||
|
|
removeAcountMoney(sourceId, amount)
|
||
|
|
addAccountMoney(targetId, amount)
|
||
|
|
resultType, resultResponse = "success", "transfer_success"
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully transferred money to " .. targetId)
|
||
|
|
end
|
||
|
|
|
||
|
|
local transactions = json.decode(row.transactions) or {}
|
||
|
|
table.insert(transactions, {
|
||
|
|
account = "Personal",
|
||
|
|
type = "Transfer",
|
||
|
|
recipient = fetchUserNameBySource(targetId),
|
||
|
|
amount = amount,
|
||
|
|
status = (resultType == "error" and "Failed" or "Completed"),
|
||
|
|
date = dateStr,
|
||
|
|
})
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET transactions = @transactions WHERE uuid = @uuid",
|
||
|
|
{ ["@transactions"] = json.encode(transactions), ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their transaction history")
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif actionLower == "deposit" then
|
||
|
|
local accountLower = string.lower(account)
|
||
|
|
if accountLower == "personal" then
|
||
|
|
logDebug("User ID: " .. sourceId .. " is depositing money into their personal account")
|
||
|
|
local cash = getUserCashAmount(sourceId)
|
||
|
|
if amount > cash then
|
||
|
|
resultType, resultResponse = "error", "no_money"
|
||
|
|
logDebug("User ID: " .. sourceId .. " does not have enough money to deposit")
|
||
|
|
else
|
||
|
|
removeCashAmount(sourceId, amount)
|
||
|
|
addAccountMoney(sourceId, amount)
|
||
|
|
resultType, resultResponse = "success", "deposit_success"
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully deposited money into their personal account")
|
||
|
|
end
|
||
|
|
|
||
|
|
local transactions = json.decode(row.transactions) or {}
|
||
|
|
table.insert(transactions, {
|
||
|
|
account = "Personal",
|
||
|
|
type = "Deposit",
|
||
|
|
recipient = "Personal",
|
||
|
|
amount = amount,
|
||
|
|
status = (resultType == "error" and "Failed" or "Completed"),
|
||
|
|
date = dateStr,
|
||
|
|
})
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET transactions = @transactions WHERE uuid = @uuid",
|
||
|
|
{ ["@transactions"] = json.encode(transactions), ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their transaction history")
|
||
|
|
|
||
|
|
elseif accountLower == "savings" then
|
||
|
|
logDebug("User ID: " .. sourceId .. " is depositing money into their savings account")
|
||
|
|
local primary = getUserPrimaryAccount(sourceId)
|
||
|
|
if amount > primary then
|
||
|
|
resultType, resultResponse = "error", "no_money"
|
||
|
|
logDebug("User ID: " .. sourceId .. " does not have enough money to deposit")
|
||
|
|
else
|
||
|
|
removeAcountMoney(sourceId, amount)
|
||
|
|
local currentSavings = tonumber(row.savings_balance)
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET savings_balance = @savings_balance WHERE uuid = @uuid",
|
||
|
|
{ ["@savings_balance"] = currentSavings + amount, ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
resultType, resultResponse = "success", "deposit_success"
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully deposited money into their savings account")
|
||
|
|
end
|
||
|
|
|
||
|
|
local transactions = json.decode(row.transactions) or {}
|
||
|
|
table.insert(transactions, {
|
||
|
|
account = "Savings",
|
||
|
|
type = "Deposit",
|
||
|
|
recipient = "Savings",
|
||
|
|
amount = amount,
|
||
|
|
status = (resultType == "error" and "Failed" or "Completed"),
|
||
|
|
date = dateStr,
|
||
|
|
})
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET transactions = @transactions WHERE uuid = @uuid",
|
||
|
|
{ ["@transactions"] = json.encode(transactions), ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their transaction history")
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif actionLower == "withdraw" then
|
||
|
|
local accountLower = string.lower(account)
|
||
|
|
if accountLower == "personal" then
|
||
|
|
logDebug("User ID: " .. sourceId .. " is withdrawing money from their personal account")
|
||
|
|
local primary = getUserPrimaryAccount(sourceId)
|
||
|
|
if amount > primary then
|
||
|
|
resultType, resultResponse = "error", "no_money"
|
||
|
|
logDebug("User ID: " .. sourceId .. " does not have enough money to withdraw")
|
||
|
|
else
|
||
|
|
addCashAmount(sourceId, amount)
|
||
|
|
removeAcountMoney(sourceId, amount)
|
||
|
|
resultType, resultResponse = "success", "withdraw_success"
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully withdrawn money from their personal account")
|
||
|
|
end
|
||
|
|
|
||
|
|
local transactions = json.decode(row.transactions) or {}
|
||
|
|
table.insert(transactions, {
|
||
|
|
account = "Personal",
|
||
|
|
type = "Withdraw",
|
||
|
|
recipient = "Personal",
|
||
|
|
amount = amount,
|
||
|
|
status = (resultType == "error" and "Failed" or "Completed"),
|
||
|
|
date = dateStr,
|
||
|
|
})
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET transactions = @transactions WHERE uuid = @uuid",
|
||
|
|
{ ["@transactions"] = json.encode(transactions), ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their transaction history")
|
||
|
|
|
||
|
|
elseif accountLower == "savings" then
|
||
|
|
logDebug("User ID: " .. sourceId .. " is withdrawing money from their savings account")
|
||
|
|
local currentSavings = tonumber(row.savings_balance)
|
||
|
|
if amount > currentSavings then
|
||
|
|
resultType, resultResponse = "error", "no_money"
|
||
|
|
logDebug("User ID: " .. sourceId .. " does not have enough money to withdraw")
|
||
|
|
else
|
||
|
|
addAccountMoney(sourceId, amount)
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET savings_balance = @savings_balance WHERE uuid = @uuid",
|
||
|
|
{ ["@savings_balance"] = currentSavings - amount, ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
resultType, resultResponse = "success", "withdraw_success"
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully withdrawn money from their savings account")
|
||
|
|
end
|
||
|
|
|
||
|
|
local transactions = json.decode(row.transactions) or {}
|
||
|
|
table.insert(transactions, {
|
||
|
|
account = "Savings",
|
||
|
|
type = "Withdraw",
|
||
|
|
recipient = "Savings",
|
||
|
|
amount = amount,
|
||
|
|
status = (resultType == "error" and "Failed" or "Completed"),
|
||
|
|
date = dateStr,
|
||
|
|
})
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET transactions = @transactions WHERE uuid = @uuid",
|
||
|
|
{ ["@transactions"] = json.encode(transactions), ["@uuid"] = sourceUuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their transaction history")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
logDebug("User ID: " .. sourceId .. " transaction has been fully created")
|
||
|
|
return { type = resultType, response = resultResponse }
|
||
|
|
end
|
||
|
|
function manageAccountCallback(sourceId, data)
|
||
|
|
if not sourceId then return end
|
||
|
|
local user = fetchUserBySource(sourceId)
|
||
|
|
if not user then
|
||
|
|
logDebug("User ID: " .. sourceId .. " attempted to perform an action without being found")
|
||
|
|
return { type = "error", response = "myself_not_found" }
|
||
|
|
end
|
||
|
|
|
||
|
|
if not (data and data.action and data.amount) then
|
||
|
|
logDebug("User ID: " .. sourceId .. " attempted to perform an action with invalid data")
|
||
|
|
return { type = "error", response = "uncaught_error" }
|
||
|
|
end
|
||
|
|
|
||
|
|
local amount = tonumber(data.amount)
|
||
|
|
local dateStr = os.date("%m/%d/%Y")
|
||
|
|
local recipientNum = tonumber(data.recipient)
|
||
|
|
return handleTransaction(sourceId, data.account, data.action, amount, dateStr, recipientNum)
|
||
|
|
end
|
||
|
|
RegisterCallback("manageAccount", manageAccountCallback)
|
||
|
|
|
||
|
|
function addCredit(sourceId, amount)
|
||
|
|
if not sourceId or not amount then return end
|
||
|
|
local user = fetchUserBySource(sourceId); if not user then return end
|
||
|
|
local uuid = fetchUsersUUID(sourceId); if not uuid then return end
|
||
|
|
getOrCreateBankingRow(sourceId) -- ensure row exists
|
||
|
|
local current = getCreditScore(sourceId)
|
||
|
|
local newScore = current + amount
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET credit_score = @credit_score WHERE uuid = @uuid",
|
||
|
|
{ ["@credit_score"] = newScore, ["@uuid"] = uuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their credit score")
|
||
|
|
end
|
||
|
|
function removeCredit(sourceId, amount)
|
||
|
|
if not sourceId or not amount then return end
|
||
|
|
local user = fetchUserBySource(sourceId); if not user then return end
|
||
|
|
local uuid = fetchUsersUUID(sourceId); if not uuid then return end
|
||
|
|
getOrCreateBankingRow(sourceId) -- ensure row exists
|
||
|
|
local current = getCreditScore(sourceId)
|
||
|
|
local newScore = current - amount
|
||
|
|
MySQL.Sync.execute(
|
||
|
|
"UPDATE sv_banking_data SET credit_score = @credit_score WHERE uuid = @uuid",
|
||
|
|
{ ["@credit_score"] = newScore, ["@uuid"] = uuid }
|
||
|
|
)
|
||
|
|
logDebug("User ID: " .. sourceId .. " has successfully updated their credit score")
|
||
|
|
end
|
||
|
|
exports("addCredit", addCredit)
|
||
|
|
exports("removeCredit", removeCredit)
|