245 lines
10 KiB
Lua
245 lines
10 KiB
Lua
local nk = require("nakama")
|
|
local utils = require("lua.utils")
|
|
|
|
local economy = {}
|
|
|
|
local SHOP_CATALOG_DEFS = {
|
|
{ id = "oldpop-blue-hat", name = "Oldpop Blue Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-green-hat", name = "Oldpop Green Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-red-hat", name = "Oldpop Red Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-yellow-hat", name = "Oldpop Yellow Hat", category = "head", gold = 100, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-og-pant", name = "Copper OG Pant", category = "costume", gold = 0, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-grey-pant", name = "Copper Grey Pant", category = "costume", gold = 150, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-red-pant", name = "Copper Red Pant", category = "costume", gold = 150, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-yellow-pant", name = "Copper Yellow Pant", category = "costume", gold = 150, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-blue-gloves", name = "Oldpop Blue Gloves", category = "glove", gold = 75, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-green-gloves", name = "Oldpop Green Gloves", category = "glove", gold = 75, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-red-gloves", name = "Oldpop Red Gloves", category = "glove", gold = 75, star = 0, rarity = "Common", character = "Oldpop" },
|
|
{ id = "oldpop-yellow-gloves", name = "Oldpop Yellow Gloves", category = "glove", gold = 75, star = 0, rarity = "Common", character = "Oldpop" }
|
|
}
|
|
|
|
local function build_shop_catalog()
|
|
local catalog = {}
|
|
for _, def in ipairs(SHOP_CATALOG_DEFS) do
|
|
local cat = def.category
|
|
if not catalog[cat] then catalog[cat] = {} end
|
|
local entry = {
|
|
id = def.id,
|
|
name = def.name,
|
|
gold = def.gold or 0,
|
|
star = def.star or 0,
|
|
rarity = def.rarity or "Common",
|
|
character = def.character
|
|
}
|
|
table.insert(catalog[cat], entry)
|
|
end
|
|
return catalog
|
|
end
|
|
|
|
function economy.rpc_get_shop_catalog(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
|
|
local result = { catalog = build_shop_catalog(), featured_banners = {} }
|
|
|
|
local status, objs = pcall(nk.storage_read, {{ collection = "shop_config", key = "featured_banners", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
if status and objs and #objs > 0 then
|
|
local val = objs[1].value
|
|
if val.banners then result.featured_banners = val.banners end
|
|
end
|
|
|
|
return nk.json_encode(result)
|
|
end
|
|
|
|
function economy.rpc_buy_currency(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
|
|
local request = nk.json_decode(payload)
|
|
local packageId = request.package_id
|
|
local receipt = request.receipt
|
|
local idempotencyKey = request.idempotency_key
|
|
|
|
if not packageId or packageId == "" then error("Package ID required") end
|
|
if not idempotencyKey or idempotencyKey == "" then error("Idempotency key required") end
|
|
|
|
local status, existing = pcall(nk.storage_read, {{ collection = "receipts", key = idempotencyKey, user_id = context.user_id }})
|
|
if status and existing and #existing > 0 then
|
|
return nk.json_encode({ success = true, package_id = packageId, duplicate = true, status = existing[1].value.status })
|
|
end
|
|
|
|
local changeset = { gold = 0, star = 0 }
|
|
local requiresVerification = false
|
|
|
|
if packageId == "gold_100" then changeset.gold = 100; requiresVerification = true
|
|
elseif packageId == "gold_500" then changeset.gold = 550; requiresVerification = true
|
|
elseif packageId == "gold_1000" then changeset.gold = 1150; requiresVerification = true
|
|
elseif packageId == "gold_2000" then changeset.gold = 2400; requiresVerification = true
|
|
elseif packageId == "gold_5000" then changeset.gold = 6250; requiresVerification = true
|
|
elseif packageId == "gold_10000" then changeset.gold = 13000; requiresVerification = true
|
|
elseif packageId == "star_100" then changeset.star = 100; changeset.gold = -500
|
|
elseif packageId == "star_250" then changeset.star = 250; changeset.gold = -1100
|
|
elseif packageId == "star_600" then changeset.star = 600; changeset.gold = -2500
|
|
else error("Invalid package ID") end
|
|
|
|
if requiresVerification and not receipt then
|
|
nk.storage_write({{
|
|
collection = "receipts",
|
|
key = idempotencyKey,
|
|
user_id = context.user_id,
|
|
value = { type = "currency", package_id = packageId, status = "pending", created_at = os.date("!%Y-%m-%dT%H:%M:%S.000Z") },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
return nk.json_encode({ success = true, status = "pending", package_id = packageId })
|
|
end
|
|
|
|
local s, err = pcall(function()
|
|
if changeset.gold ~= 0 or changeset.star ~= 0 then
|
|
nk.wallet_update(context.user_id, changeset, {}, true)
|
|
end
|
|
nk.storage_write({{
|
|
collection = "receipts",
|
|
key = idempotencyKey,
|
|
user_id = context.user_id,
|
|
value = { type = "currency", package_id = packageId, changeset = changeset, receipt = receipt or nk.json_null(), status = "verified", processed_at = os.date("!%Y-%m-%dT%H:%M:%S.000Z") },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}})
|
|
end)
|
|
|
|
if not s then
|
|
nk.logger_error("Currency purchase failed: " .. tostring(err))
|
|
error("NotEnoughFunds")
|
|
end
|
|
|
|
nk.logger_info("User " .. context.user_id .. " bought currency package " .. packageId)
|
|
return nk.json_encode({ success = true, status = "verified", package_id = packageId })
|
|
end
|
|
|
|
function economy.rpc_purchase_item(context, payload)
|
|
if not context.user_id then error("Not authenticated") end
|
|
|
|
local request = nk.json_decode(payload)
|
|
local itemId = request.item_id
|
|
local quantity = request.quantity or 1
|
|
local idempotencyKey = request.idempotency_key
|
|
|
|
if not itemId or itemId == "" then error("Item ID required") end
|
|
if quantity < 1 then error("Invalid quantity") end
|
|
if not idempotencyKey or idempotencyKey == "" then error("Idempotency key required") end
|
|
|
|
local status, existing = pcall(nk.storage_read, {{ collection = "receipts", key = idempotencyKey, user_id = context.user_id }})
|
|
if status and existing and #existing > 0 then
|
|
return nk.json_encode({ success = true, item = itemId, duplicate = true })
|
|
end
|
|
|
|
local itemDef = nil
|
|
for _, def in ipairs(SHOP_CATALOG_DEFS) do
|
|
if def.id == itemId then
|
|
itemDef = def
|
|
break
|
|
end
|
|
end
|
|
|
|
if not itemDef then error("ItemNotFound") end
|
|
|
|
local priceGold = (itemDef.gold or 0) * quantity
|
|
local priceStar = (itemDef.star or 0) * quantity
|
|
local category = itemDef.category or "accessory"
|
|
|
|
local s, err = pcall(function()
|
|
local changeset = {}
|
|
if priceGold > 0 then changeset.gold = -priceGold end
|
|
if priceStar > 0 then changeset.star = -priceStar end
|
|
|
|
if priceGold > 0 or priceStar > 0 then
|
|
nk.wallet_update(context.user_id, changeset, {}, true)
|
|
end
|
|
end)
|
|
if not s then
|
|
nk.logger_error("Wallet update failed: " .. tostring(err))
|
|
error("NotEnoughFunds")
|
|
end
|
|
|
|
local s2, err2 = pcall(function()
|
|
local writes = {
|
|
{
|
|
collection = "inventory",
|
|
key = itemId,
|
|
user_id = context.user_id,
|
|
value = { category = category, purchased_at = os.date("!%Y-%m-%dT%H:%M:%S.000Z"), quantity = quantity },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
},
|
|
{
|
|
collection = "receipts",
|
|
key = idempotencyKey,
|
|
user_id = context.user_id,
|
|
value = { type = "item", item_id = itemId, quantity = quantity, cost = { gold = priceGold, star = priceStar }, processed_at = os.date("!%Y-%m-%dT%H:%M:%S.000Z") },
|
|
permission_read = 1,
|
|
permission_write = 0
|
|
}
|
|
}
|
|
nk.storage_write(writes)
|
|
end)
|
|
if not s2 then
|
|
nk.logger_error("Purchase failed: " .. tostring(err2))
|
|
error("PurchaseFailed")
|
|
end
|
|
|
|
nk.logger_info("User " .. context.user_id .. " purchased " .. itemId)
|
|
return nk.json_encode({ success = true, item = itemId })
|
|
end
|
|
|
|
function economy.rpc_admin_set_featured_banners(context, payload)
|
|
utils.require_admin(context)
|
|
local req = nk.json_decode(payload or "{}")
|
|
local banners = req.banners or {}
|
|
|
|
local finalBanners = {}
|
|
for i = 1, math.min(#banners, 3) do
|
|
table.insert(finalBanners, banners[i])
|
|
end
|
|
|
|
for _, b in ipairs(finalBanners) do
|
|
local itemId = b.item_id or ""
|
|
if itemId ~= "" then
|
|
local found = false
|
|
for _, def in ipairs(SHOP_CATALOG_DEFS) do
|
|
if def.id == itemId then found = true; break end
|
|
end
|
|
if not found then error("Item not found in catalog: " .. itemId) end
|
|
end
|
|
end
|
|
|
|
nk.storage_write({{
|
|
collection = "shop_config",
|
|
key = "featured_banners",
|
|
user_id = "00000000-0000-0000-0000-000000000000",
|
|
value = { banners = finalBanners },
|
|
permission_read = 2,
|
|
permission_write = 0
|
|
}})
|
|
|
|
nk.logger_info("Featured banners updated by admin " .. context.user_id)
|
|
return nk.json_encode({ success = true, banners = finalBanners })
|
|
end
|
|
|
|
function economy.rpc_admin_get_featured_banners(context, payload)
|
|
utils.require_admin(context)
|
|
local status, objs = pcall(nk.storage_read, {{ collection = "shop_config", key = "featured_banners", user_id = "00000000-0000-0000-0000-000000000000" }})
|
|
if status and objs and #objs > 0 then
|
|
return nk.json_encode({ banners = objs[1].value.banners or {} })
|
|
end
|
|
return nk.json_encode({ banners = {} })
|
|
end
|
|
|
|
nk.register_rpc(economy.rpc_get_shop_catalog, "get_shop_catalog")
|
|
nk.register_rpc(economy.rpc_buy_currency, "buy_currency")
|
|
nk.register_rpc(economy.rpc_purchase_item, "purchase_item")
|
|
nk.register_rpc(economy.rpc_admin_set_featured_banners, "admin_set_featured_banners")
|
|
nk.register_rpc(economy.rpc_admin_get_featured_banners, "admin_get_featured_banners")
|
|
|
|
nk.logger_info("LUA TEST: economy module loaded successfully")
|
|
|
|
return economy
|