ゲーム内のすべてのイベントやユーザーからのリクエストを手動で監視する代わりに、ウェブフックを設定して、サードパーティのメッセージングツールやHTTPリクエストを受け取るカスタムエンドポイントでリアルタイム通知を受け取ることができます。これにより、通知管理ワークフローを自動化し、通知の処理にかかる手動の手間を減らすことができます。
ウェブフックワークフロー
ウェブフックは、Robloxとサードパーティのメッセージングツールなど、2つの異なるアプリケーションやサービス間でリアルタイムの通知やデータを送信します。従来のAPIとは異なり、クライアントアプリケーションを設定してサーバーにリクエストを送信してデータを受け取る必要がなく、ウェブフックはイベントが発生するとすぐにクライアントエンドポイントにデータを送信します。これは、チームとのコラボレーションに使用するサードパーティアプリケーションとの間でワークフローを自動化するのに役立ち、リアルタイムのデータ共有と処理を可能にします。
ウェブフックを設定すると、ターゲットイベントが発生するたびに、Robloxは提供したウェブフックURLにリクエストを送信します。ウェブフックURLは、そのリクエストを受信アプリケーションまたはカスタムエンドポイントにリダイレクトし、ウェブフックペイロードに含まれるデータに基づいてアクションを実行できます。これには、RTBFコンプライアンスのためのデータの削除、ユーザーへの確認の送信、または別のイベントのトリガーが含まれる可能性があります。
サポートされているトリガー
Robloxは現在、以下のイベントトリガーをサポートしています。
サブスクリプション
- サブスクリプション再購読 - ユーザーがサブスクリプションを再購読すると、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプション更新 - ユーザーがサブスクリプションを更新すると、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプション返金 - ユーザーがサブスクリプションの返金を受けると、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプション購入 - ユーザーがサブスクリプションを購入すると、サブスクリプションと購読者を含むメッセージが送信されます。
- サブスクリプションキャンセル - ユーザーがサブスクリプションをキャンセルすると、サブスクリプションと購読者、キャンセルの理由を含むメッセージが送信されます。
サブスクリプションイベントとそのフィールドに関する詳細は、サブスクリプションリファレンスを参照してください。
コンプライアンス
- 削除の権利 / 削除リクエスト - ユーザーが適用されるグローバルなデータ保護およびプライバシー規制に基づいて、個人情報を永久に削除する権利を行使する場合。詳細はRTBFとクリエイターを参照してください。
コマース
- コマース製品注文の返金 - ユーザーがコマース製品注文の返金を受けた場合、または注文がキャンセルされた場合。
- コマース製品注文の支払い - ユーザーがコマース製品注文の支払いを行った場合。重複したウェブフックイベントが発生する可能性があるため、ユニークなコマース注文IDを使用してイベントを重複排除する必要があります。
Creator Dashboardでのウェブフックの設定
ウェブフックを通じて通知を受け取るには、通知をトリガーする特定のイベントにサブスクライブするウェブフックを設定する必要があります。グループ所有のゲームの場合、グループの所有者のみがウェブフック通知を設定および受信できます。
ウェブフックを設定するには:
Creator Hubであなたの体験を選択します。
設定の下で、ウェブフックを選択し、ウェブフックを追加をクリックします。
ウェブフックURLは、プロバイダーから取得します。たとえば、SlackのURLは次のようになります:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXXウェブフックURLと名前を入力します。
- OPTIONALシークレットを含めると、受信する通知がRobloxからのものであることを確認できます。詳細はウェブフックのセキュリティを確認するを参照してください。
通知を受け取りたいイベントのサポートされているトリガーのリストから1つ以上のオプションを選択します。
- OPTIONALテストレスポンスボタンを使用して、サービスがサンプルリクエストを受信できるかどうかを確認します。
変更を保存をクリックします。
ウェブフックURLの設定
ウェブフックURLとしてカスタムHTTPサービスエンドポイントを設定できますが、以下の要件を満たす必要があります:
- リクエストを処理するために公開アクセス可能である必要があります。
- POSTリクエストを処理できる必要があります。
- リクエストに対して5秒以内に2XXレスポンスで応答できる必要があります。
- HTTPSリクエストを処理できる必要があります。
エンドポイントがPOSTリクエストを受信すると、次のことができる必要があります:
- POSTメッセージの本文から通知に関する必要な詳細を抽出します。
- 通知に関する一般的な詳細とイベントタイプに関連する特定の詳細を含むPOSTメッセージの本文を読み取ります。
POSTリクエストを処理するためのスキーマの詳細については、ペイロードスキーマを参照してください。
配信失敗の再試行ポリシー
ウェブフック通知が指定されたURLに到達できない場合、Robloxは固定ウィンドウサイズを使用して、構成されたURLにメッセージを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-signatureロジックをテストするために使用できます。
ウェブフックのセキュリティを確認する
ペイロードを受信するようにサーバーを設定した後、エンドポイントに送信されたペイロードをリッスンし始めます。ウェブフックを設定する際にシークレットを設定した場合、Robloxは各ウェブフック通知にroblox-signatureを送信し、リクエストが実際にRobloxから送信されたことを確認します。シグネチャは、カスタムエンドポイントのペイロードヘッダーにあり、サードパーティサーバーのフッターにあります。
t=<timestamp>,v1=<signature>ウェブフックにシークレットを設定しなかった場合、シグネチャには通知が送信された時刻のタイムスタンプのみが含まれます:
t=<timestamp>シグネチャを確認するには:
タイムスタンプとシグネチャの値を抽出します。シークレットを持つウェブフックのすべてのシグネチャは、これらの2つの値が次のプレフィックスに続くCSV文字列として同じ形式を共有します:
- t: 通知が送信された時刻のタイムスタンプ。
- v1: Creator Dashboard設定で提供されたシークレットを使用して生成されたシグネチャ値。
roblox-signatureのベース文字列を再作成します。次のように連結します:
- タイムスタンプを文字列として。
- ピリオド文字 .。
- リクエスト本文のJSON文字列。
設定中に定義したシークレットをキーとして使用し、ステップ2で生成したベース文字列をメッセージとして使用して、SHA256ハッシュ関数を使用してハッシュベースのメッセージ認証コード(HMAC)を計算します。結果をBase64形式に変換して、期待されるシグネチャを取得します。
抽出したシグネチャ値を期待されるシグネチャと比較します。シグネチャを正しく生成した場合、値は同じであるはずです。
- OPTIONALリプレイ攻撃を防ぐために、攻撃者がデータを傍受して再送信し、不正アクセスや悪意のある行動を行うサイバー攻撃の一種であるため、抽出したタイムスタンプ値を現在のタイムスタンプと比較し、合理的な時間制限内に収まることを確認することが役立ちます。たとえば、10分のウィンドウは通常、合理的な時間制限として良いとされています。
ペイロードスキーマ
ウェブフックのターゲットイベントがトリガーされると、イベントに関する情報をペイロードに含めてウェブフックURLにリクエストを送信します。すべてのリクエストのペイロードは、固定フィールドと可変フィールドで構成される同じスキーマを共有します。これにより、ペイロード内で送信されるデータが構造化され、一貫性が保たれ、受信アプリケーションがデータを処理しやすくなります。
固定ペイロードスキーマフィールドは、すべてのウェブフックリクエストで一貫性を維持するのに役立ち、以下のフィールドが利用可能です:
- NotificationId (string): 送信された各通知の一意の識別子。同じNotificationIdが2回受信された場合、それは重複と見なされます。
- EventType (string): 通知がトリガーされたイベントの種類を示します。
- EventTime (string): イベントがトリガーされた時刻のタイムスタンプ。
可変ペイロードスキーマフィールドは、さまざまなタイプのイベントに対応するための柔軟性を提供し、以下を含みます:
- EventPayload (object): ウェブフックをトリガーしたEventTypeに特有の情報を含みます。EventPayloadスキーマの構造は、イベントの種類に基づいて異なります。
以下の例は、削除の権利リクエストイベントのペイロードスキーマを示しています:
{
"NotificationId": "string",
"EventType": "RightToErasureRequest",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1,
"GameIds": [
1234, 2345
]
}
}通知の処理
ユーザーの**個人を特定できる情報(PII)**を保存している場合、たとえばユーザーIDなど、法的義務に照らしてリクエストを評価する必要があります。詳細はRTBFとクリエイターを参照してください。ウェブフック通知を処理し、データ削除を自動化するボットを作成できます。これは、PIIをデータストアに保存している場合に可能です。Discord内でボットを作成し、データストア用のOpen Cloud APIを使用してPIIデータを削除する自動化ソリューションの例については、削除の権利リクエストの自動化を参照してください。この例は、サブスクリプションイベントなど、他の通知を処理するために適応できます。
サードパーティツールの代わりにカスタムエンドポイントをウェブフックサーバーとして使用する場合、ウェブフックペイロードから削除対象のデータを抽出し、自分自身の自動化ソリューションを構築できます。以下のコードサンプルは、タイムスタンプを検証し、リクエストが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('サーバーが起動しました');
});