您可以设置webhook以接收实时通知,而不是手动监控游戏中的所有事件和用户请求。您可以将这些通知发送到第三方消息工具或能够接收HTTP请求的自定义端点。这有助于您自动化通知管理工作流程,从而减少手动处理通知的工作量。
Webhook工作流程
Webhook在两个不同应用程序或服务之间发送实时通知或数据,例如Roblox和第三方消息工具。与传统API不同,后者需要您设置客户端应用程序向服务器发送请求以接收数据,webhook在事件发生时立即将数据发送到您的客户端端点。它们有助于自动化Roblox与您使用的第三方应用程序之间的工作流程,便于实时数据共享和处理。
一旦您设置了webhook,每当目标事件发生时,Roblox就会向您提供的webhook URL发送请求。然后,webhook URL将请求重定向到您的接收应用程序或自定义端点,该端点可以根据webhook有效负载中包含的数据采取行动。这可能包括根据GDPR删除数据、向用户发送确认或触发另一个事件。
支持的触发器
Roblox目前支持以下事件触发器。
订阅
- 订阅续订 - 当用户重新订阅时,发送一条包含订阅和订阅者的消息。
- 订阅续费 - 当用户续费订阅时,发送一条包含订阅和订阅者的消息。
- 订阅退款 - 当用户收到订阅退款时,发送一条包含订阅和订阅者的消息。
- 订阅购买 - 当用户购买订阅时,发送一条包含订阅和订阅者的消息。
- 订阅取消 - 当用户取消订阅时,发送一条包含订阅和订阅者的消息,以及给出的取消理由。
有关订阅事件及其字段的更多信息,请参见订阅参考。
合规性
- 删除请求 - 当用户根据通用数据保护条例(GDPR)提交数据删除请求时。
商务
- 商务产品订单退款 - 当用户收到他们的商务产品订单退款或订单被取消时。
- 商务产品订单已支付 - 当用户支付其商务产品订单时。请注意,可能会出现重复的webhook事件,因此您应该使用唯一的商务订单ID去重事件。
在创作者仪表板上配置webhook
要通过webhook接收通知,您需要配置一个订阅特定事件以触发通知的webhook。对于团体拥有的游戏,只有团体所有者可以配置和接收webhook通知。
要设置webhook:
在创作者中心上选择您的体验。
在配置下,选择Webhook并点击添加Webhook。
webhook URL来自您的提供程序。例如,Slack URL的格式可能如下所示:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX输入您的webhook URL和名称。
(可选)包括一个密钥,帮助确保您收到的通知来自Roblox。有关更多信息,请参见验证webhook安全性。
从您要接收通知的支持触发器事件列表中选择一个或多个选项。
(可选)使用测试响应按钮检查您的服务是否可以接收示例请求。
点击保存更改。
设置webhook URL
您可以将自定义HTTP服务端点设置为您的webhook URL,前提是它满足以下要求:
- 它必须是公共可访问的,以处理请求。
- 它可以处理POST请求。
- 它可以在5秒内用2XX回应请求。
- 它可以处理HTTPS请求。
当您的端点接收到POST请求时,它必须能够:
- 从POST消息的正文中提取所需的通知详细信息。
- 读取POST消息的主体,包含关于通知的一般详细信息以及与通知事件类型相关的具体详细信息。
有关要处理的POST请求的模式的更多信息,请参见有效负载模式。
发送失败重试策略
当webhook通知由于端点不可用等错误未能到达您指定的URL时,Roblox会使用固定的时间窗口尝试将消息发送到配置的URL 5次。如果在5次尝试后通知仍然未能送达,Roblox将停止尝试发送通知,并认为该URL不再有效。在这种情况下,您需要使用可访问并能够接收通知的新URL更新您的webhook配置。要排除故障并确认您的webhook URL可以成功接收通知,请参见测试webhook。
第三方要求
第三方工具通常有自己对webhook的要求,您在设置webhook URL时需要遵循这些要求。您可以通过在目标工具的支持或文档网站上搜索“webhook”关键字找到这些要求。有关支持的第三方工具,请参见以下内容:
测试webhook
您可以在创作者仪表板上测试您配置的webhook是否能够成功接收通知:
- 转到Webhook配置页面。
- 从配置的webhook列表中选择您想要测试的webhook。
- 点击目标webhook旁边的铅笔图标。
- 点击测试响应按钮。
系统将发送一个SampleNotification事件,其中包括触发通知的用户的用户ID,如下所示:
SampleNotification schema
{
"NotificationId": "string",
"EventType": "SampleNotification",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1
}
}
如果您将webhook与第三方服务集成,您可以使用第三方URL进行测试,以确认该服务可以成功接收来自您的webhook的通知。如果您在配置webhook时提供了密钥,它还会生成一个roblox-signature,您可以使用它来测试roblox-signature逻辑。
验证webhook安全性
在您配置服务器以接收有效负载后,它开始侦听发送到该端点的任何有效负载。如果您在配置webhook时设置了密钥,Roblox会在每个webhook通知中发送一个roblox-signature,以确保请求确实来自Roblox。该签名在自定义端点的有效负载头中,并在第三方服务器的页脚中。
具有自定义端点密钥的签名格式t=<timestamp>,v1=<signature>
如果您没有为webhook设置密钥,则签名仅包含发送通知时的时间戳:
没有自定义端点密钥的签名格式
t=<timestamp>
要验证签名:
提取时间戳和签名值。所有带密钥的webhook签名共享相同格式,作为包含这两个值的CSV字符串,并加上前缀:
- t:发送通知的时间戳。
- v1:使用创作者仪表板配置提供的密钥生成的签名值。
通过连接以下部分重新创建roblox-signature的基本字符串:
- 将时间戳作为字符串。
- 句点字符 .。
- 请求正文的JSON字符串。
使用您在配置期间定义的密钥作为密钥,使用SHA256哈希函数计算基于哈希的消息认证代码(HMAC),并将您在步骤2中生成的基本字符串作为消息。将结果转换为Base64格式以获取预期签名。
将提取的签名值与预期签名进行比较。如果您正确生成了签名,值应该是相同的。
(可选)为了防止重放攻击,这是一种网络攻击类型,在这种攻击中,攻击者截取并重新发送数据以获得未授权访问或执行恶意操作,比较提取的时间戳值与当前时间戳,并确保它在合理时间范围内。例如,10分钟的窗口通常是一个合理的时间限制。
有效负载模式
当webhook的目标事件被触发时,它会向您的webhook URL发送请求,并在有效负载中包含有关该事件的信息。所有请求的有效负载共享相同的模式,包括固定字段和可变字段。这确保了在有效负载中传输的数据结构化且一致,使接收应用程序能够更轻松地处理和使用数据。
固定有效负载模式字段可以帮助在所有webhook请求之间保持一致性,以下字段可用:
- NotificationId (字符串):每个发送的通知的唯一标识符。如果收到相同的NotificationId两次,则将其视为重复。
- EventType (字符串):指示触发通知的事件类型。
- EventTime (字符串):事件触发的时间戳。
可变有效负载模式字段为webhook提供灵活性,以适应各种类型的事件,包括:
- EventPayload (对象):包含特定于触发webhook的EventType的信息。EventPayload模式的结构根据事件类型的不同而变化。
以下示例显示了删除请求事件的有效负载模式:
删除请求的示例模式
{
"NotificationId": "string",
"EventType": "RightToErasureRequest",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1,
"GameIds": [
1234, 2345
]
}
}
处理通知
如果您存储有关用户的任何个人可识别信息(PII),例如他们的用户ID,当用户提交此类请求时,您必须删除此信息,以遵守GDPR删除权合规性要求。您可以创建一个机器人来处理webhook通知,并帮助自动化数据删除,前提是您在数据存储中存储PII。请参见自动化删除删除请求,了解如何在Discord中创建一个使用开放云API数据存储来删除PII数据的机器人作为自动化解决方案。该示例可以调整以处理其他通知,例如订阅事件。
如果您使用自定义端点作为webhook服务器,而不是第三方工具,您可以从webhook有效负载中提取要删除的数据,并构建自己的自动化解决方案。以下代码示例是一个服务器的例子,该服务器通过验证时间戳和请求来自Roblox,来防止重放攻击:
从有效负载中提取PII
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('服务器已启动');
});