mcpField
R
roblox-monetization

roblox-monetization

github.com/flatbedj開発055 回インストール6 週間前に更新
リポジトリ

roblox-monetization について

Developer ProductやGamepassを扱うRoblox開発者向けの収益化実装スキルです。購入の消失や重複付与を防ぐため、レシートの冪等処理、原子的な保存、ロールバックを重視します。

主な機能

  1. 01ProcessReceiptの安全な処理
  2. 02報酬とレシートの原子的な保存
  3. 03保存失敗時の付与ロールバック
  4. 04Gamepassの現在の所有状況に基づく付与
  5. 05購入データの保存・読み込み整合性確認

活用シーン

  1. 01再試行に耐えるDeveloper Product購入の実装
  2. 02保存失敗後の報酬消失防止
  3. 03参加時の永続特典の再確認
  4. 04購入データの保存・読み込み整合性検証

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/roblox-monetizationを入力して読み込まれたか確認します。

詳細

Roblox の収益化:安全な ProcessReceipt とゲームパス

Robux の扱いに手加減はありません。ここでわずかなバグがあると、プレイヤーは課金されたのに何も受け取れなかったり、無料アイテムを無限に受け取れたりします。正しく処理するためのパターンを紹介します。

Developer Products:MarketplaceService.ProcessReceipt

Robux による DevProduct の購入が完了すると、このコールバックが実行されます。PurchaseGranted を返すまで Roblox が再配信するため、冪等でなければならず、付与処理と重複排除の記録は同じ永続セーブに含める必要があるため、アトミックでなければなりません。

コミットとロールバックの構成(すべての分岐で使用):

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

よくあるバグを防ぐルール:

  • ProcessReceipt の中では代替可能なものだけを付与してください(通貨、またはクライアントが引き換える永続的なトークン数)。ProcessReceipt の中でランダムな非代替アイテム(特定のカード)を抽選して付与しないでください。セーブの失敗後にロールバックされ、再配信されると、データの消失や「大当たり」の誤った通知につながります。特定のアイテムを付与する必要がある場合は、アイテムを付与するとともに正確な差分をスナップショットに記録し、ロールバックですべてを元に戻せるようにしてください(個数を減らし、0 になったらエントリーを削除します)。
  • 報酬を永続化する同じ save() の中で PurchaseId を記録し、セーブに失敗した場合は両方をロールバックしてください。save() は成功を表す bool を返す必要があります。
  • 設定テーブルをテーブルキーとして絶対に使用しないでください。 よくある BLOCKER バグです。{ id = "x", count = 2 } のような設定エントリーをそのまま inventory[def] = ... として使用すると、DataStore はテーブルキーをシリアライズできないため、セーブが失敗し、付与全体がロールバックされ、NotProcessedYet が永久に再配信されて、プレイヤーは課金されたのに何も受け取れません。必ず文字列を取り出してください:local id, n = def.id, def.count
  • ProcessReceipt は書き込み専用のコールバックです。テストのために読み出したり呼び出したりすることはできません(get は利用できません)。付与ロジックは静的に読み、純粋なチェック(例:対象プールが空でないことの確認)で検証し、呼び出してテストしないでください。
  • 既知の軽微な競合期間:receipts[key] = true は save() が yield する前に設定されるため、同時に再呼び出しされると、セーブ中のフラグが見える可能性があります。標準的で発生確率は低いため、記録しておき、過度に複雑化しないでください。

ゲームパス(永続特典)

  • 保存済みフラグだけでなく、リアルタイムの所有状況から所有権を判定します。参加時に MarketplaceService:UserOwnsGamePassAsync(userId, passId) を呼び出し、PromptGamePassPurchaseFinished で即座に付与します。state.<pass> の bool はキャッシュとして保存しますが、参加時に再検証してください。
  • すべてのゲームパス(現在および将来)を付与する「全アクセス/メガ」バンドルは、永続的な state.mega フラグで実装し、参加するたびにすべてのゲームパスフラグを再適用するのが最適です。これにより、後から追加されたゲームパスも自動的に利用可能になります。

設定とダッシュボードの実情

  • productId == 0 / gamePassId == 0 は無効なプレースホルダーです。ショップは安全に使えるよう、購入を促す代わりに「まだ設定されていません」という注記を表示してください。
  • 設定の robux の数値は表示専用です。実際の請求額は、Creator Dashboard で DevProduct/ゲームパスに設定されている価格です。実際の価格はそこで設定してください。ゲーム内のラベルは見た目だけのものです。
  • 地域別価格設定により、ゲーム内にハードコードされた価格ラベルは、基準地域以外のプレイヤーへの請求額とずれます(購入プロンプトには常に正しい価格が表示されます)。正確な価格を表示するには、GetProductInfo/GetGamePassProductInfo で最新価格を取得してください。
  • 開発者が実施するリリースチェックリスト(こちらでは実施できません):ダッシュボードで各 DevProduct/ゲームパスを作成し、実際の ID を設定に貼り付け、ゲームを保存/公開してください。

購入フィールドの永続化

すべての購入フィールド(receipts、通貨、ゲームパスフラグ、トークン、invitedBy)について、保存と読み込みの整合性を完全に保つ必要があります。読み込まれてもシリアライズされないフィールド(またはその逆)があると、一度限りのギフトを再び受け取れたり、購入が失われたりします(ここでは漏れが実際のお金に影響するため、明示的に確認する価値があります)。一般的な保存/読み込み/リセットのチェックリストは roblox-studio-workflow にあります。

コメント