ゲーム内のすべてのイベントやユーザーからのリクエストを手動で監視する代わりに、ウェブフックを設定して、サードパーティのメッセージングツールまたはHTTPリクエストを受け取れるカスタムエンドポイントにリアルタイムの通知を受け取ることができます。これにより、通知管理のワークフローが自動化され、通知処理における手動の手間が減ります。
ウェブフックワークフロー
ウェブフックは、Robloxとサードパーティのメッセージングツールなど、2つの異なるアプリケーションやサービス間でリアルタイムの通知またはデータを送信します。従来のAPIとは異なり、ウェブフックはイベントが発生するとすぐにデータをクライアントエンドポイントに送信し、サーバーにリクエストを送信するためのクライアントアプリケーションを設定する必要はありません。これは、チームとのコラボレーションに使用するサードパーティアプリケーションとRoblox間のワークフローを自動化するのに便利で、リアルタイムデータの共有と処理を可能にします。
ウェブフックを設定すると、ターゲットイベントが発生するたびに、Robloxは指定したウェブフックURLにリクエストを送信します。ウェブフックURLは、そのリクエストを受信アプリケーションまたはカスタムエンドポイントにリダイレクトし、ウェブフックペイロードに含まれるデータに基づいてアクションを実行できます。これには、RTBFコンプライアンスのためのデータの消去、ユーザーへの確認の送信、または他のイベントのトリガーが含まれる可能性があります。
サポートされているトリガー
Robloxは現在、以下のイベントトリガーをサポートしています。
サブスクリプション
- サブスクリプションの再購読 - ユーザーがサブスクリプションを再購読したときに、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプションの更新 - ユーザーがサブスクリプションを更新したときに、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプションの払い戻し - ユーザーがサブスクリプションの払い戻しを受けたときに、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプションの購入 - ユーザーがサブスクリプションを購入したときに、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプションのキャンセル - ユーザーがサブスクリプションをキャンセルしたときに、サブスクリプションと購読者、ならびにキャンセルの理由を含むメッセージが送信されます。
サブスクリプションイベントとそのフィールドについての詳細は、Subscriptionリファレンスを参照してください。
コンプライアンス
- 消去の権利 / 削除リクエスト - ユーザーが適用されるグローバルなデータ保護およびプライバシー規制に基づいて、個人情報を永久に削除する権利を行使したとき。詳細については、RTBFとクリエイターを参照してください。
商取引
- 商取引製品注文の払い戻し - ユーザーが商取引製品注文の払い戻しを受けたとき、または注文がキャンセルされたとき。
- 商取引製品注文の支払い - ユーザーが商取引製品注文の支払いを行ったとき。重複するウェブフックイベントが発生する可能性があるため、ユニークな商取引注文IDを使用してイベントを重複削除する必要があります。
Creator Dashboardでのウェブフックの設定
ウェブフックを通じて通知を受け取るには、通知をトリガーする特定のイベントにサブスクライブするウェブフックを設定する必要があります。グループ所有のゲームの場合、グループの所有者のみがウェブフック通知を設定し、受け取ることができます。
ウェブフックを設定するには:
Creator Hubでエクスペリエンスを選択します。
設定の下で、ウェブフックを選択し、ウェブフックの追加をクリックします。
ウェブフックURLは、あなたのプロバイダーから取得します。例えば、SlackのURLは以下のようになります:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXXウェブフックURLと名前を入力します。
(オプション)秘密を含めて、受信する通知がRobloxから来ていることを確認するのに役立ちます。詳細については、ウェブフックのセキュリティを確認するを参照してください。
通知を受け取りたいイベントのサポートされているトリガーリストから1つ以上のオプションを選択します。
(オプション)テストレスポンスボタンを使用して、サービスがサンプルリクエストを受け取れるかどうか確認します。
変更を保存をクリックします。
ウェブフックURLの設定
カスタムHTTPサービスエンドポイントをウェブフックURLとして設定できます。ただし、以下の要件を満たす必要があります:
- リクエストを処理するために公開アクセス可能である必要があります。
- POSTリクエストを処理できる必要があります。
- リクエストに対して5秒以内に2XXレスポンスを返す必要があります。
- HTTPSリクエストを処理できる必要があります。
エンドポイントがPOSTリクエストを受け取ると、次のことができなければなりません:
- POSTメッセージの本文から通知に必要な詳細を抽出します。
- 通知に関する一般的な詳細と、通知のイベントタイプに関連する特定の詳細を持つPOSTメッセージの本文を読み取ります。
POSTリクエストを扱うためのスキーマについての詳細は、ペイロードスキーマを参照してください。
配信失敗の再試行ポリシー
ウェブフック通知が指定したURLに到達できない場合、Robloxは固定ウィンドウサイズを使用して5回そのメッセージの送信を再試行します。5回の試行後にまだ通知の配信が失敗した場合、Robloxは通知の送信を停止し、そのURLがもはや有効ではないと仮定します。この場合、通知を受け取れる新しいURLでウェブフック設定を更新する必要があります。ウェブフックURLが正常に通知を受け取れるかどうかを確認し、トラブルシューティングするには、ウェブフックをテストするを参照してください。
サードパーティの要件
サードパーティツールには、ウェブフックの設定時に従う必要がある独自の要件があります。これらの要件は、対象ツールのサポートまたはドキュメントサイトで「ウェブフック」キーワードを検索することで見つけることができます。サポートされているサードパーティツールについては、次を参照してください:
ウェブフックのテスト
設定したウェブフックがCreator Dashboardで正常に通知を受け取れるかどうかをテストできます:
- ウェブフック設定ページに移動します。
- 設定されたウェブフックのリストからテストしたいウェブフックを選択します。
- 対象のウェブフックの隣のペンのアイコンをクリックします。
- テストレスポンスボタンをクリックします。
システムは次に、通知をトリガーしたユーザーのユーザーIDを含むSampleNotificationイベントを送信します。これは以下のようになります:
{
"NotificationId": "string",
"EventType": "SampleNotification",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1
}
}サードパーティサービスにウェブフックを統合している場合は、サードパーティURLを使用して、サービスがウェブフックから正常に通知を受け取れるかどうかをテストできます。ウェブフックを設定する際に秘密を提供した場合、roblox-signatureが生成され、そのロジックをテストするために使用できます。
ウェブフックのセキュリティを確認する
サーバーをペイロードを受け取るように設定した後、それはエンドポイントに送信されるペイロードをリスニングし始めます。ウェブフックを設定する際に秘密を設定している場合、Robloxは各ウェブフック通知にroblox-signatureを送信し、リクエストが実際にRobloxから来ていることを確認します。この署名は、カスタムエンドポイントのペイロードヘッダーに含まれ、サードパーティサーバーのフッターに含まれます。
t=<timestamp>,v1=<signature>ウェブフックの秘密を設定しなかった場合、署名には通知が送信された時刻のタイムスタンプのみが含まれます:
t=<timestamp>署名を検証するには:
タイムスタンプと署名の値を抽出します。秘密を持つウェブフックのすべての署名は、これらの2つの値を接頭辞に持つCSV文字列の形式を共有します:
- t: 通知が送信された時刻。
- v1:クリエイターダッシュボードの設定によって提供された秘密を使用して生成された署名値。
roblox-signatureのベース文字列を再作成するには:
- タイムスタンプを文字列として。
- ピリオド文字.を。
- リクエスト本文のJSON文字列を。
設定時に定義した秘密をキーとして、生成したベース文字列をメッセージとしてSHA256ハッシュ関数を使用してハッシュベースのメッセージ認証コード(HMAC)を計算します。結果をBase64形式に変換して期待される署名を取得します。
抽出した署名値と期待される署名を比較します。署名を正しく生成していれば、その値は同じである必要があります。
(オプション)リプレイ攻撃を防ぐために、攻撃者がデータを傍受して再送信し、不正アクセスや悪意のある行動を取るサイバー攻撃の一種です。抽出したタイムスタンプ値を現在のタイムスタンプと比較し、それが妥当な時間制限内に収まっていることを確認するのが役立ちます。例えば、10分のウィンドウは通常、妥当な時間制限として良好です。
ペイロードスキーマ
ウェブフックのターゲットイベントがトリガーされると、イベントに関する情報を含むリクエストがウェブフックURLに送信されます。リクエストのすべてのペイロードは、固定フィールドと変数フィールドで構成される同じスキーマを共有します。これにより、ペイロード内で送信されるデータの構造が整備され、一貫性が保たれ、受信アプリケーションがデータを処理して使用するのが容易になります。
固定ペイロードスキーマフィールドは、ウェブフックリクエスト全体で一貫性を維持するのに役立ち、以下のフィールドが利用できます:
- NotificationId(文字列):送信された各通知の一意の識別子。同じNotificationIdが2回受信された場合、それは重複と見なされます。
- EventType(文字列):通知がトリガーされたイベントのタイプを示します。
- EventTime(文字列):イベントがトリガーされた日時。
可変ペイロードスキーマフィールドは、ウェブフックがさまざまなタイプのイベントに対応できる柔軟性を提供し、以下を含みます:
- EventPayload(オブジェクト):ウェブフックをトリガーしたEventTypeに特有の情報を含みます。EventPayloadスキーマの構造は、イベントタイプに基づいて異なります。
次の例は、消去の権利リクエストイベントのペイロードスキーマを示しています:
{
"NotificationId": "string",
"EventType": "RightToErasureRequest",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1,
"GameIds": [
1234, 2345
]
}
}通知の処理
ユーザーの**個人を特定できる情報(PII)**を保存している場合、例えばユーザーIDなど、法的義務に照らしてリクエストを評価する必要があります。詳細については、RTBFとクリエイターを参照してください。ウェブフック通知を処理し、データ削除を自動化するためにボットを作成できます。ただし、PIIをデータストアに保存している場合に限ります。消去の権利リクエストの削除を自動化するを参照すると、PIIデータを削除するためにデータストア用のOpen Cloud APIを使用してDiscord内でボットを作成する方法の例を見ることができます。この例は、サブスクリプションイベントなどの他の通知を処理するために適応可能です。
サードパーティツールの代わりにカスタムエンドポイントをウェブフックサーバーとして使用する場合、ウェブフックペイロードから削除対象のデータを抽出し、自分の自動化ソリューションを構築できます。次のコードサンプルは、タイムスタンプを検証し、リクエストがRobloxから来ていることを確認することでリプレイ攻撃を防ぐサーバーの例です:
const crypto = require('crypto');
const express = require('express');
const secret = '<your_secret>' // これは環境変数として設定できます。
let app = express();
app.use(express.json());
app.all('/*', function (req, res) {
console.log('新しいリクエストが受信されました');
// ヘッダーからタイムスタンプと署名を抽出します。
const signatureHeader = req.headers['roblox-signature'].split(',');
const timestamp = signatureHeader.find(e => e.startsWith('t=')).substring(2);
const signature = signatureHeader.find(e => e.startsWith('v1=')).substring(3);
// リプレイ攻撃を防ぐために300秒のウィンドウ内でリクエストが来たことを確認します。
const requestTimestampMs = timestamp * 1000;
const windowTimeMs = 300 * 1000;
const oldestTimestampAllowed = Date.now() - windowTimeMs;
if (requestTimestampMs < oldestTimestampAllowed) {
return res.status(403).send('期限切れのリクエスト');
}
// 署名の検証
const message = `${timestamp}.${JSON.stringify(req.body)}`;
const hmac = crypto.createHmac('sha256', secret);
const calculatedSignature = hmac.update(message).digest('base64');
if (signature !== calculatedSignature) {
return res.status(401).send('未許可のリクエスト');
}
// ペイロードを処理するためのロジック
const payloadBody = req.body;
const eventType = payloadBody['EventType'];
if (eventType === 'RightToErasureRequest'){
const userId = payloadBody['EventPayload']['UserId'];
const gameIds = payloadBody['EventPayload']['GameIds'];
console.log(`ペイロードデータ: UserId=${userId} と GameIds=${gameIds}`);
// データストアにPIIを保存している場合、UserIdとGameIdsを使用してデータストアから情報を削除します。
}
return res.json({ message: 'メッセージを正常に処理しました' });
});
app.listen(8080, function () {
console.log('サーバーが起動しました');
});