mcpField
R
roblox-monetization

roblox-monetization

github.com/flatbedj开发055 次安装6 周前更新
代码仓库

关于 roblox-monetization

面向 Roblox 开发者的变现实现指南,涵盖开发者产品收据与 Gamepass 权益。重点是通过幂等处理、原子持久化和回滚,避免购买丢失或重复发放。

主要功能

  1. 01安全处理 ProcessReceipt 收据
  2. 02奖励与收据原子保存
  3. 03保存失败时回滚奖励
  4. 04基于实时所有权发放 Gamepass
  5. 05校验购买字段的存档与加载

适用场景

  1. 01实现可重试的开发者产品购买
  2. 02避免保存失败导致奖励丢失
  3. 03加入时重新验证永久权益
  4. 04检查购买数据的保存与加载一致性

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/flatbedj/roblox-skills --skill roblox-monetization

安装后在 Claude Code 中输入/roblox-monetization确认已加载。

详细介绍

Roblox 变现:安全的 ProcessReceipt + Gamepasses

Robux 交易容不得差错。这里一个细微的 bug,就可能导致玩家付了钱却什么也没拿到(或者无限获得免费物品)。以下模式可确保逻辑正确。

Developer Products:MarketplaceService.ProcessReceipt

Robux DevProduct 购买完成时会运行此回调。它 MUST 是幂等的(Roblox 会重复投递,直到你返回 PurchaseGranted),并且必须是原子的(发放奖励和记录去重信息必须保存在同一次持久化存档中)。

提交并回滚的模式(每个分支都要使用):

local key = tostring(receipt.PurchaseId)
if state.receipts[key] then return Enum.ProductPurchaseDecision.PurchaseGranted end -- already granted
-- snapshot what you'll change, apply the grant, THEN record the receipt:
state.cash += amount
state.receipts[key] = true
if not PlayerData.save(player) then          -- durable save failed → undo everything
    state.cash -= amount
    state.receipts[key] = nil
    return Enum.ProductPurchaseDecision.NotProcessedYet   -- Roblox will re-deliver later
end
PlayerData.sync(player)
return Enum.ProductPurchaseDecision.PurchaseGranted

避免经典 bug 的规则:

  • 只在 ProcessReceipt 中发放可替代物品(货币,或客户端可兑换的持久化 令牌 数量)。不要在 ProcessReceipt 中随机发放非同质化奖励(例如特定卡牌)——存档失败、回滚并重新投递时,会导致数据丢失,并产生虚假的“大获胜”事件。如果必须发放特定物品,则要同时发放该物品并记录精确的变更快照,以便回滚时彻底撤销(减少该物品副本数,如果减至 0 则删除条目)。
  • 在保存奖励的同一次 save() 中记录 PurchaseId,并在存档失败时同时回滚两者。save() 必须返回成功布尔值。
  • 绝不要将配置 TABLE 用作表键。 常见的 BLOCKER bug:像 { id = "x", count = 2 } 这样的配置条目被直接用作 inventory[def] = ...。DataStore 无法序列化表键 → 存档报错 → 整个奖励回滚 → NotProcessedYet 被无限重新投递 → 玩家付了钱却什么也得不到。始终提取字符串:local id, n = def.id, def.count。
  • ProcessReceipt 是只写回调——你无法读取或调用它来测试(没有 get 可用)。应通过静态阅读和纯检查(例如确认符合条件的奖池非空)验证奖励逻辑,而不是调用它。
  • 已知的一个小窗口:receipts[key] = true 会在 save() 让出执行权之前设置;并发的再次调用可能在保存过程中看到该标记。这种情况常见但概率较低;记录这一点即可,不要过度设计。

Gamepasses(永久特权)

  • 根据实时所有权判断是否拥有,而不是只看存档标记:玩家加入时调用 MarketplaceService:UserOwnsGamePassAsync(userId, passId);在 PromptGamePassPurchaseFinished 时立即发放。将 state.<pass> 布尔值持久化为缓存,但玩家加入时要重新验证。
  • 对于授予 EVERY pass(现在及未来)的“全权限 / mega”礼包,最好使用持久化的 state.mega 标记,并在每次玩家加入时重新应用所有 pass 标记——这样之后添加的 pass 也会自动解锁。

配置与控制面板的实际情况

  • productId == 0 / gamePassId == 0 = 无效占位值:显示“尚未设置”提示,而不是弹出购买提示,这样在尚未设置 ID 时,商店也能安全使用。
  • 配置中的 robux 数值仅用于显示——实际扣款金额取决于 Creator Dashboard 中 DevProduct/Gamepass 设置的价格。应在那里设置实际价格;游戏内标签仅用于展示。
  • 区域定价意味着,游戏内硬编码的价格标签会与非基础区域玩家实际支付的价格不一致(购买 PROMPT 显示的价格始终正确)。若要准确显示价格,请通过 GetProductInfo / GetGamePassProductInfo 获取实时价格。
  • 开发者必须完成的发布清单(你无法代劳):在控制面板中创建每个 DevProduct/Gamepass,将真实 ID 填入配置,并保存 / 发布游戏。

购买字段的持久化

每个购买字段(receipts、货币、pass 标记、令牌、invitedBy)都需要完整的存档 / 加载对应关系——某字段已加载却未序列化(或反过来),意味着一次性礼物可重复领取或购买记录丢失(这里的漏洞会造成真实金钱损失,因此值得明确检查)。常规的存档 / 加载 / 重置清单见 roblox-studio-workflow。

评论

支持 Markdown