# Webhook # 概览 Webhook是系统中发生的事件的通知。当特定事件发生时,艾克索拉会向您的应用程序发送HTTP请求,其中会传输事件数据。它通常是JSON格式的POST请求。 事件示例: - 用户与商品目录交互 - 付款或取消订单 当发生设定事件时,艾克索拉会通过Webhook通知您的系统。然后,您可以执行以下操作: - 补充用户余额 - 进行退款 - 向用户帐户发放或减除新商品 - 开始提供订阅 - 怀疑欺诈行为时封禁用户 支付处理Webhook工作流示例: ![支付处理Webhook](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks-general.svg)

注:

根据所使用的解决方案及其集成类型,Webhook集和交互顺序可能与示例中的不同。

艾克索拉Webhook集成视频指南:
使用艾克索拉产品和解决方案时的Webhook设置:
产品/解决方案 必需/可选 Webhook的用途是什么
付款 必需
  • 用户验证。
  • 付款成功或退款时接收交易详情的信息。
  • 将购买的商品记入用户帐户,及在订单取消时将商品减除。
商店 必需
  • 用户验证。
  • 付款成功或退款时接收交易详情的信息。
  • 将购买的商品记入用户帐户,及在订单取消时将商品减除。
游戏销售 可选 对于游戏密钥销售,用户验证和商品记入不是必需。如果想接收有关事件的信息(例如付款或订单取消),可以连接webhook。
如果连接Webhook,则必须处理所有传入的必需Webhook
订阅 可选 接收有关创建、更新或取消订阅的信息。您也可以通过API请求信息
网页商城 必需
  • 用户验证。
  • 付款成功或退款时接收交易详情的信息。
  • 将购买的商品记入用户帐户,及在订单取消时将商品减除。
  • 用户认证(如果使用通过用户ID进行身份认证)。您也可以使用通过艾克索拉登录管理器进行用户认证
Digital Distribution Hub 必需
  • 用户验证。
  • 将艾克索拉侧的交易ID与您系统中的交易ID关联。
  • 在订单中传输额外交易参数。
  • 将购买的商品记入用户帐户,及在订单取消时将商品减除。

请参阅文档,了解如何为Digital Distribution Hub设置Webhook。

登录管理器 可选

接收事件信息:

  • 用户注册/授权
  • 用户邮箱地址确认
  • 关联用户的社交媒体帐户

有关设置Webhook的详细信息,请参阅登录管理器文档

# 必需Webhook列表 如果使用需要与Webhook交互的产品和解决方案,请在您的发布商帐户中启用并测试Webhook设置Webhook处理。当特定事件发生时,Webhook会按顺序发送。因此,如果您不处理其中一个Webhook,则不会发送后续Web hook。下面列出了必需Webhook的列表。 ## 商店和支付 在艾克索拉侧已设置了2种Webhook发送选项,用于处理网站上的商品购买和退货——支付和交易数据信息以及已购商品信息可以分开发送,也可以合并为一个Webhook 发送。 在合并Webhook中接收信息: 如果您在2025年1月22日之后在发布商帐户注册,您将在订单成功支付(`order_paid`) 和订单取消(`order_canceled`) Webhook中收到所有信息。在这种情况下,您无需处理支付(`payment`)和退款(`refund`) Webhook。 在单独Webhook中接收信息: 如果您在2025年1月22日或之前在发布商帐户注册,您将收到以下Webhook: - 支付(`payment`)和退款(`refund`) Webhook,包含支付数据和交易详细信息。 - 订单成功支付(`order_paid`)和订单取消(`order_canceled`) Webhook,包含已购商品信息。 您需要处理所有收到的Webhook。如需切换到新的合并Webhook接收方式,请联系您的客户成功经理或发送邮件至csm@xsolla.com。 为确保游戏内商店和支付管理功能正常运行,必须实现主要Webhook的处理。 如果接收合并Webhook:
Webhook名称和类型 描述
用户验证 > 用户验证 (user_validation) 在支付流程的不同阶段发送,用于确保用户已在游戏中注册。
游戏服务 > 合并Webhook > 订单成功支付(order_paid) 包含支付数据、交易详情和已购商品信息。请使用Webhook中的数据为用户添加商品。
游戏服务 > 合并Webhook > 订单取消(order_canceled) 包含已取消支付的数据、交易详情和已购商品信息。请使用Webhook中的数据移除已购商品。
如果接收单独Webhook
Webhook名称和类型 描述
用户验证 > 用户验证 (user_validation) 在支付流程的不同阶段发送,用于确保用户已在游戏中注册。
付款 > 支付(payment) 包含支付数据和交易详细信息。
游戏服务 > 单独Webhook > 订单成功支付(order_paid) 包含已购商品信息。请使用Webhook中的数据为用户添加商品。
付款 > 退款 (refund) 包含支付数据和交易详细信息。
游戏服务 > 单独Webhook > 订单取消(order_canceled) 包含已购商品信息和已取消交易的ID。请使用Webhook中的数据移除已购商品。
如果您的应用程序侧实现了商品目录个性化,请设置对合作伙伴侧的目录个性化Webhook的处理。

注意

要接收真实支付,您只需签署许可协议并实现以下Webhook的处理:

## 订阅 要自动管理订阅计划,需要实现主要Webhook的处理: - 用户验证(`user_validation`) — 在支付过程的不同阶段发送,以确保用户已在游戏中注册。 - 支付(`payment`) — 在支付订单后发送,包含付款数据和交易详细信息。 - 创建了订阅(`create_subscription`) — 支付Webhook已成功处理或用户购买了具有试用期的订阅时发送。它包含所购买的订阅 的详细信息和用户数据。使用该Webhook数据向用户添加订阅。 - 更新了订阅(`update_subscription`) — 续订或更改订阅以及支付Webhook已成功 处理后发送。它包含所购买的订阅的详细信息和用户数据。使用该Webhook数据来延长用户的订阅或更改订阅参数。 - 退款(`refund`) — 订单被取消后发送,包含取消的付款数据和交易详细信息。 - 取消了订阅(`cancel_subscription`) — 退款Webhook已成功处理或订阅因其他原因被取消时发送。它包含有关订阅和用户数据的 信息。使用该Webhook数据扣除用户购买的订阅。 # 在发布商帐户中设置Webhook ## 常规设置 要启用接收Webhook: 1. 在发布商帐户的项目中,前往项目设置 > Webhook部分。 2. 在Webhook服务器字段中,指定要接收Webhook的服务器的URL,格式为`https://example.com`。您还可以指定在测试Web hook的工具中找到的URL。

注:

请使用HTTPS协议传输数据,不支持HTTP协议。

3. 生成密钥:
    1. 密钥部分,点击添加密钥
    2. 在弹出的窗口中,输入密钥名称,便于您在列表中识别该密钥。
    3. 点击 创建密钥
    4. 点击复制密钥,并在己侧保存创建的密钥。
    5. 点击完成
    6. 确认您已保存密钥,然后点击是,关闭
![添加密钥](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/add-key.svg)

注意

密钥建议:

4. 点击**启用Webhook**。

注:

要测试Webhook,可以选择任何专用网站(例如webhook.site)或平台(例如ngrok)。

注:

无法同时将Webhook发送到不同的URL。您可以在发布商帐户中执行的操作是先指定一个用于测试的URL,然后将其替换为真实的URL。

要禁用接收Webhook: 1. 在发布商帐户的项目中,前往项目设置 > Webhook部分。 2. 单击禁用Webhook。 ## 密钥轮换 定期更新密钥可以提升集成安全性。您最多可以在项目中创建5个密钥,用于密钥轮换。操作步骤如下: 1. 在项目设置 > Webhook部分,点击**添加密钥**。 ![添加密钥](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/add-new-key.svg) 2. 在弹出的窗口中,输入密钥名称,便于您在列表中识别该密钥。 3. 点击**创建密钥**。 4. 点击**复制密钥**,并在己侧保存创建的密钥。 5. 点击**完成**。 6. 确认您已保存密钥,然后点击**是,关闭**。

注意

密钥建议:

每个项目只能有一个有效密钥。如需更换,请在另一个密钥所在行点击**设为有效**,并确认操作。成功迁移到新密钥后,建议删除已停用的密钥。 ![更改有效密钥](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/activate-key.svg) ## 高级设置 付款和商店部分的Webhook提供高级设置。单击获取Webhook按钮后,这些设置将自动显示在常规设置区块下方。

注意

如果未显示高级设置,请确保已在常规设置中连接Webhook接收,且您位于测试 > 付款和商店选项卡中。

在此部分,您可以设置在Webhook中接收额外信息。要实现此目的,请将相应开关设为启用状态。每个权限的行都会标明设置变更将影响哪些Webhook。
开关 描述
显示已保存支付帐户的信息(仅当您在2025年1月22日或之前注册发布商帐户并接收单独Webhook时显示)。 有关保存的支付方式的信息在payment_account自定义对象中传递。
显示通过已保存支付方式进行的交易信息。

信息在Webhook的以下自定义参数中传递:

  • saved_payment_method:
    • 0 — 未使用保存的支付方式
    • 1 — 进行当前付款时保存了支付方式
    • 2 — 使用了之前保存的支付方式
  • payment_type:
    • 1 — 一次性支付
    • 2 — 定期支付
向Webhook添加order对象(仅当您在2025年1月22日或之前注册发布商帐户并接收单独Webhook时显示)。 有关订单的信息在支付Webhook的order对象中传递。
仅发送必要的用户参数,不包含敏感数据。

Webhook中仅传递用户的以下信息:

  • ID
  • 国家/地区
发送自定义参数。 自定义令牌参数的信息在webhook中传递。
显示银行卡BIN和后缀码。

Webhook中传递以下银行卡号的信息:

  • card_bin参数中的前6位数字
  • card_suffix中的后4位数字
显示银行卡品牌。 用于付款的银行卡的品牌。例如,Mastercard或Visa。
显示退款原因信息。 退款原因的详细信息。
显示国家/地区预扣税和用户获取费。 payment_details.​country_whtpayment_details.​user_acquisition_fee对象将通过Webhook发送。此开关默认开启。
发送3DS信息。 包含3-D Secure验证数据的cards对象将通过Webhook发送。
![高级设置](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/advanced-settings.png) # 在发布商帐户中测试Webhook 测试Webhook有助于确保己侧和艾克索拉侧的项目设置都正确。 如果Webhook设置成功,Webhook 设置部分下方会显示一个Webhook测试部分。 ![Webhook测试部分](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/testing-section.svg) 发布商帐户中的测试部分会根据Webhook接收方式显示不同内容。 如果您在2025年1月22日之后注册发布商帐户,将接收合并Webhook:
Webhook测试的选项卡名称 Webhook名称和类型
付款和商店 用户验证 > 用户验证 (user_validation)
游戏服务 > 合并Webhook > 订单成功支付(order_paid)
游戏服务 > 合并Webhook > 订单取消(order_canceled)
订阅 用户验证 > 用户验证 (user_validation)
付款 > 支付(payment)
如果您在2025年1月22日或之前注册发布商帐户,将接收单独Webhook:
Webhook测试的选项卡名称 Webhook名称和类型
商店 游戏服务 > 单独Webhook > 订单成功支付(order_paid)
游戏服务 > 单独Webhook > 订单取消(order_canceled)
付款 用户验证 > 用户验证 (user_validation)
付款 > 支付(payment)
订阅 用户验证 > 用户验证 (user_validation)
付款 > 支付(payment)

注:

如果测试部分出现测试未通过的警告,请在您的Webhook侦听器中检查Webhook响应设置。测试结果中指出了测试错误的原因。

示例:

您使用专门的网站webhook.site来进行测试。

测试对无效签名的响应部分显示了一个错误。

发生这种情况是因为艾克索拉发送了带有错误签名的Webhook,并期望您的处理程序用一个指出INVALID_SIGNATURE错误代码的4xx HTTP代码进行响应。

webhook.site对所有Webhook的响应中都发送一个200 HTTP代码,包括签名不正确的Webhook。由于无法获取预期的4xxHTTP代码,因此测试结果报错。

下文将介绍合并Webhook使用场景的测试流程。 ## 付款和商店 在付款和商店选项卡中,您可以测试以下Webhook: - 用户验证(`user_validation`) - 订单成功支付(`order_paid`) - 订单取消(`order_canceled`) 要测试webhooks: 1. 在Webhook测试部分,前往付款和商店选项卡。 2. 在下拉列表中选择商品类型。如果您尚未在发布商帐户中设置该商品类型,请点击按钮进行配置。创建商品后,返回Webhook测试部分并继续下一步。 3. 填写必填字段: * **用户ID** — 测试时,可使用任意字母和数字组合。 * 在**艾克索拉订单ID**字段中输入任意值。 * **艾克索拉发票IDID** — 艾克索拉侧的交易ID。测试时,可使用任意数字值。 * **发票ID** — 您游戏侧的交易ID。测试时,可使用任意字母和数字组合。该参数不是成功支付的必填参数,但您可以传递该参数,将己侧的交易ID与艾克索拉侧的交易ID关联。 * **金额** — 支付金额。测试时,可使用任意数字值。 * **货币** — 从下拉列表中选择货币。 * 从下拉列表中选择商品SKU并指定金额。如需选择多个同类型商品,请点击**+**,并在新行中添加。 4. 点击**测试Webhook**。 系统会将包含指定数据的用户验证订单成功支付订单取消Webhook发送到提供的URL。每种Webhook类型的测试结果将显示在测试Webhook按钮下方。 如果在项目设置 > 集成设置部分勾选了使用公共用户ID复选框,用户搜索Webhook也会发送到您的Webhook服务器URL,并显示测试结果。 对于每个Webhook,您需要配置处理两种情况:成功的情况和出现错误的情况。 ![支付测试部分](https://cdn.xsolla.net/developers/current/images/api_docs/webhooks/testing-results.svg) ## 订阅

注意

要测试Webhook,您需要先在发布商帐户的商品目录 > 订阅部分至少创建一个订阅计划

订阅选项卡中,您可以测试以下Webhook: - 用户验证(`user_validation`) - 支付(`payment`)

注意

您可以在集成指南中查看其他订阅管理场景的测试详情。

要测试webhooks: 1. 在测试部分,前往**订阅**选项卡。 2. 填写必填字段: * **用户ID** — 测试时,可使用任意字母和数字组合。 * **艾克索拉发票IDID** — 艾克索拉侧的交易ID。测试时,可使用任意数字值。 * **公共用户ID** — 用户可识别的ID,例如电子邮件地址或昵称。如果您在[项目设置 > 集成设置](https://publisher.xsolla.com/0/projects/0/edit/advanced)部分勾选了使用公共用户ID复选框,则 会显示此字段。 * **金额** — 支付金额。测试时,可使用任意数字值。 * **货币** — 从下拉列表中选择货币。 * **计划ID** — 订阅计划。从下拉列表中选择一个计划。 * **订阅产品** — 从下拉列表中选择一个产品(可选)。如果您的项目中已设置[产品](/zh/sell-subscriptions/integration-guide/get-started/#guides_subscriptions_glossary_product),则会显示该列表。 * **发票ID** — 您游戏侧的交易ID。测试时,可使用任意字母和数字组合。该参数不是成功支付的必填参数,但您可以传递该参数,将己侧的交易ID与艾克索拉侧的交易ID关联。 * **试用期**。如需测试[购买无试用期的订阅](/zh/sell-subscriptions/integration-guide/get-subscription-information/#guides_subscriptions_get_subscription_set_up_webhooks_sandbox),或测试[ 订阅续订](/zh/sell-subscriptions/integration-guide/get-subscription-information/#guides_subscriptions_get_subscription_set_up_webhooks_test_renewal) ,请指定值0。 3. 点击**测试**。 您将在指定URL接收已填充数据的Webhook。每个Webhook在成功场景和错误场景下的测试结果都会显示在测试按钮下方。 # Webhook侦听器 Webhook侦听器是一个程序代码,允许在指定URL地址接收传入的Webhook、生成签名以及发送响应到艾克索拉Webhook服务器。

注:

您可以使用Pay Station PHP SDK 库,它包含用于处理Webhook的现成类。

在您的应用程序侧,实现从以下IP地址接收Webhook: - `185.30.20.0/24` - `185.30.21.0/24` - `185.30.22.0/24` - `185.30.23.0/24` - `34.102.38.178` - `34.94.43.207` - `35.236.73.234` - `34.94.69.44` - `34.102.22.197` 如集成了登录管理器 产品,请另外添加对来自以下IP地址的Webhook的处理: - `34.94.0.85` - `34.94.14.95` - `34.94.25.33` - `34.94.115.185` - `34.94.154.26` - `34.94.173.132` - `34.102.48.30` - `35.235.99.248` - `35.236.32.131` - `35.236.35.100` - `35.236.117.164` 限制: - 应用程序的数据库中不应存在具有相同ID的多个成功交易。 - 如果Webhook侦听器收到的Webhook的ID已经存在于数据库中,则需返回之前处理该交易的结果。不建议向用户发放重复购买及在数据库中创建重复记录。 ## 生成签名 为确保数据传输安全,您必须验证Webhook确实来自艾克索拉服务器,且在传输过程中未被篡改。为此,需要基于请求正文负载生成您自己的签名,并将其与传入请求的`authorization`标头中提供的签名进行比较。如果签名匹配,则Webhook是真实的,可以安全处理。 验证步骤: 1. 从Webhook请求的`authorization`标头中检索签名。标头格式为`Signature `。 2. 检索JSON格式的Webhook请求正文。

注意

请完全按照接收到的JSON负载使用。不要解析或重新编码负载,因为这会改变 格式并导致签名验证失败。

3. 生成您自己的签名进行比较:
  1. 通过将密钥附加到字符串末尾,将JSON负载与您项目的密钥连接起来。
  2. 对结果字符串应用SHA-1加密哈希函数。结果将是小写十六进制字符串。
4. 将您生成的签名与`authorization`标头中的签名进行比较。如果匹配,则Webhook是真实的。 以下是C#、C++、Go、PHP和Node.js语言的签名生成实现示例。 ### Webhook示例(HTTP): ```http POST /your_uri HTTP/1.1 host: your.host accept: application/json content-type: application/json content-length: 165 authorization: Signature 52eac2713985e212351610d008e7e14fae46f902 { "notification_type":"user_validation", "user":{ "ip":"127.0.0.1", "phone":"18777976552", "email":"email@example.com", "id":1234567, "name":"Xsolla User", "country":"US" } } ``` ### Webhook示例(curl): ```bash curl -v 'https://your.hostname/your/uri' \ -X POST \ -H 'authorization: Signature 52eac2713985e212351610d008e7e14fae46f902' \ -d '{ "notification_type": "user_validation", "user": { "ip": "127.0.0.1", "phone": "18777976552", "email": "email@example.com", "id": 1234567, "name": "Xsolla User", "country": "US" } }' ``` ### C#签名生成实现示例(一般示例):

注意

此代码示例兼容.NET Framework 4.0及更高版本,同时支持.NET Core和其他现代.NET版本。签名验证通过ConstantTimeEquals方法实现恒定时间比较,有效防止时序攻击。

```csharp using System; using System.Security.Cryptography; using System.Text; public static class XsollaWebhookSignature { public static string ComputeSha1(string jsonBody, string secretKey) { // Concatenation of the JSON from the request body and the project's secret key string dataToSign = jsonBody + secretKey; using (SHA1 sha1 = SHA1.Create()) { byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(dataToSign)); // Convert hash bytes to lowercase hexadecimal string var hexString = new StringBuilder(hashBytes.Length * 2); foreach (byte b in hashBytes) { hexString.Append(b.ToString("x2")); } return hexString.ToString(); } } public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature) { string computedSignature = ComputeSha1(jsonBody, secretKey); string receivedSignatureLower = receivedSignature.ToLower(); // Use constant-time comparison to prevent timing attacks return ConstantTimeEquals(computedSignature, receivedSignatureLower); } private static bool ConstantTimeEquals(string a, string b) { if (a.Length != b.Length) { return false; } int result = 0; for (int i = 0; i < a.Length; i++) { result |= a[i] ^ b[i]; } return result == 0; } } ``` ### C#签名生成实现示例(适用于.NET 5.0及更高版本):

注意

使用 Convert.ToHexString方法需要.NET 5.0或更高版本。

若您使用.NET 7.0及更高版本,可选择CryptographicOperations.FixedTimeEquals方法替代ConstantTimeEquals

```csharp // For .NET 5.0 and later, you can use the more concise Convert.ToHexString method: using System; using System.Security.Cryptography; using System.Text; public static class XsollaWebhookSignature { public static string ComputeSha1(string jsonBody, string secretKey) { string dataToSign = jsonBody + secretKey; using var sha1 = SHA1.Create(); byte[] hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(dataToSign)); return Convert.ToHexString(hashBytes).ToLower(); } public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature) { string computedSignature = ComputeSha1(jsonBody, secretKey); string receivedSignatureLower = receivedSignature.ToLower(); // Use constant-time comparison to prevent timing attacks return ConstantTimeEquals(computedSignature, receivedSignatureLower); } private static bool ConstantTimeEquals(string a, string b) { if (a.Length != b.Length) { return false; } int result = 0; for (int i = 0; i < a.Length; i++) { result |= a[i] ^ b[i]; } return result == 0; } } ``` ### C#签名生成实现示例(适用于.NET 7.0及更高版本):

注意

若您使用.NET 7.0及更高版本,可选择使用CryptographicOperations.FixedTimeEquals方法。

```csharp // For .NET 7.0+, you can use the built-in CryptographicOperations.FixedTimeEquals: using System.Security.Cryptography; public static bool VerifySignature(string jsonBody, string secretKey, string receivedSignature) { string computedSignature = ComputeSha1(jsonBody, secretKey); byte[] computedBytes = Encoding.UTF8.GetBytes(computedSignature); byte[] receivedBytes = Encoding.UTF8.GetBytes(receivedSignature.ToLower()); return CryptographicOperations.FixedTimeEquals(computedBytes, receivedBytes); } ``` ### C++签名生成实现示例: ```c++ #include #include #include #include class XsollaWebhookSignature { public: static std::string computeSha1(const std::string& jsonBody, const std::string& secretKey) { // Concatenation of the JSON from the request body and the project's secret key std::string dataToSign = jsonBody + secretKey; unsigned char digest[SHA_DIGEST_LENGTH]; // Create SHA1 hash SHA1(reinterpret_cast(dataToSign.c_str()), dataToSign.length(), digest); // Convert to lowercase hexadecimal string std::ostringstream hexStream; hexStream << std::hex << std::setfill('0'); for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) { hexStream << std::setw(2) << static_cast(digest[i]); } return hexStream.str(); } static bool verifySignature(const std::string& jsonBody, const std::string& secretKey, const std::string& receivedSignature) { std::string computedSignature = computeSha1(jsonBody, secretKey); // Timing-safe comparison if (computedSignature.length() != receivedSignature.length()) { return false; } volatile unsigned char result = 0; for (size_t i = 0; i < computedSignature.length(); ++i) { result |= (computedSignature[i] ^ receivedSignature[i]); } return result == 0; } }; ``` ### Go签名生成实现示例: ```go package main import ( "crypto/sha1" "crypto/subtle" "encoding/hex" "strings" ) type XsollaWebhookSignature struct{} func (x *XsollaWebhookSignature) ComputeSha1(jsonBody, secretKey string) string { // Concatenation of the JSON from the request body and the project's secret key dataToSign := jsonBody + secretKey // Create SHA1 hash h := sha1.New() h.Write([]byte(dataToSign)) signature := h.Sum(nil) // Convert to lowercase hexadecimal string return strings.ToLower(hex.EncodeToString(signature)) } func (x *XsollaWebhookSignature) VerifySignature(jsonBody, secretKey, receivedSignature string) bool { computedSignature := x.ComputeSha1(jsonBody, secretKey) receivedSignatureLower := strings.ToLower(receivedSignature) // Use constant time comparison to prevent timing attacks return subtle.ConstantTimeCompare([]byte(computedSignature), []byte(receivedSignatureLower)) == 1 } ``` ### PHP签名生成实现示例: ```php ``` ### Node.js签名生成实现示例: ```js const crypto = require('crypto'); class XsollaWebhookSignature { // IMPORTANT: jsonBody must be the raw JSON string exactly as received from Xsolla static computeSha1(jsonBody, secretKey) { // Concatenation of the JSON from the request body and the project's secret key const dataToSign = jsonBody + secretKey; // Create SHA1 hash const hash = crypto.createHash('sha1'); hash.update(dataToSign, 'utf8'); // Convert to lowercase hexadecimal string return hash.digest('hex').toLowerCase(); } static verifySignature(jsonBody, secretKey, receivedSignature) { const computedSignature = this.computeSha1(jsonBody, secretKey); const cleanReceivedSignature = receivedSignature.toLowerCase(); // Check if signatures have the same length before using timingSafeEqual if (computedSignature.length !== cleanReceivedSignature.length) { return false; } try { return crypto.timingSafeEqual( Buffer.from(computedSignature, 'hex'), Buffer.from(cleanReceivedSignature, 'hex') ); } catch (error) { // Return false if there's any error (e.g., invalid hex characters) return false; } } } ``` ## 向Webhook发送响应 要确认收到Webhook,您的服务器必须返回: * 如果响应成功,返回`200`、`201`或`204`HTTP代码。 * 如果未找到指定的用户或传递了无效的签名,返回`400` HTTP代码和问题描述。如果您的服务器出现临时问题,您的Webhook处理程序还可 以返回`5xx`HTTP 代码。 如果艾克索拉服务器未收到订单成功支付订单取消Webhook的响应,或收到`5xx`代码的响应,系统将按以下计划重新发送Webhook: * 尝试2次,间隔5分钟 * 尝试7次,间隔15分钟 * 尝试10次,间隔60分钟 在首次尝试后的12小时内最多尝试发送20次Webhook。 支付退款Webhook的重试逻辑说明见相应的Webhook页面。

注意

如满足以下所有条件,款项仍将退还给用户:

  • 退款由艾克索拉发起。
  • Webhook响应返回4xx状态码,或在所有重试后未收到响应,或返回5xx状态码。

如果艾克索拉服务器未收到用户验证Webhook的响应,或收到`400`或`5xx`代码的响应,则不会重新发送用户验证Webhook。在这种情况下,用户会看到错误提示,且系统不会发送支付订单成功支付Webhook。 # 错误 HTTP代码400的错误代码:
代码 消息
INVALID_USER 无效用户
INVALID_PARAMETER 无效参数
INVALID_SIGNATURE 无效签名
INCORRECT_AMOUNT 金额不正确
INCORRECT_INVOICE 发票不正确
``` HTTP/1.1 400 Bad Request { "error":{ "code":"INVALID_USER", "message":"Invalid user" } } ``` # 最佳实践 ## 安全性 请遵循以下准则: * 仅使用HTTPS,并配置有效证书。 * 始终针对原始请求正文验证签名,不要解析或重新编码数据。 * 不要在URL中传递敏感数据,避免在错误消息中暴露技术细节。 * 将Webhook端点从[CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery)中间件中排除,因为来自艾克索拉的传入请求不包含CSRF令牌,如果不进行此设置将被拒绝。 * 将[艾克索拉IP地址](/zh/webhooks/section/webhook-listener)加入许可名单。 ## Webhook处理程序架构 请遵循以下准则: 1. 接受`POST`请求时保持正文和标头原样,**不做任何修改**。 2. [验证Webhook签名](/zh/webhooks/section/webhook-listener/generation-of-signature)并返回相应的状态码: * `4xx` — 签名不匹配时返回; * `2xx` — 成功时返回。我们建议在执行主要业务逻辑**之前**返回`204 No Content`,也可以使用`200 OK`。 3. 将有效负载传递给异步作业或队列以进行后续处理。 4. 实现[幂等性](https://en.wikipedia.org/wiki/Idempotence#Computer_science_meaning)。您必须确 保系统能够处理[多次接收同一Webhook](/zh/webhooks/section/webhook-listener/sending-responses-to-webhook)的情况。 **流程示例:** ```http HTTP POST /webhooks/xsolla read raw_body, headers if !verify_signature(raw_body, headers['authorization']): return 400 {"error":{"code":"INVALID_SIGNATURE","message":"Invalid signature"}} enqueue(raw_body) return 204 # or 200 ``` ## 幂等性和重复处理 请遵循以下准则: * 使用交易ID和/或[外部ID](/zh/dev-resources/faq/payments/#faq_payments_q_new_transaction_external_id)、订单ID作为幂等性键。 * 存储已处理的ID,如果收到重复请求则返回先前的结果。 * 避免重复发放商品、重复数据库条目和重复扣费。 * 请注意:在顺序交付模式下,较早事件处理失败会阻塞所有后续事件的处理。 ## 系统韧性 请遵循以下准则: * 对资源密集型操作使用队列和异步处理,例如第三方API调用、计费和商品发放。 * 为Webhook处理程序设置超时时间(1–3秒)。对于瞬时故障,依赖[艾克索拉重试机制](/zh/webhooks/section/webhook-listener/sending-responses-to-webhook)即可。 * 不要在Webhook处理程序中实现重试逻辑,重新交付由艾克索拉负责处理。 * 记录Webhook交付时间戳和处理状态;为`5xx`错误和重新交付的激增设置告警。 * 将关联ID从Webhook传播到您的日志和监控系统(APM)中。 * 设置错误日志记录和监控。对于不可恢复的故障,将作业移至死信队列(DLQ)。开发一个受幂等性机制保护的安全工具用于重放事件。 ## 实现示例 **成功购买 — 首次尝试即发放商品:** ![购买](https://cdn.xsolla.net/developers/current/images/api_docs/webhook-schemes/purchase-v2.svg) **重复交付(首次尝试时合作伙伴超时):** ![超时](https://cdn.xsolla.net/developers/current/images/api_docs/webhook-schemes/timeout-v2.svg) **退款:** ![退款](https://cdn.xsolla.net/developers/current/images/api_docs/webhook-schemes/refund-v2.svg) **合作伙伴服务中断**: ![合作伙伴服务中断](https://cdn.xsolla.net/developers/current/images/api_docs/webhook-schemes/server-error.svg) # 常见问答 ## Webhook协议是否必须使用HTTPS? 是的,必须使用。 ## 我能否在多个URL接收支付Webhook? 不能。支付Webhook使用服务器到服务器协议,仅发送到[项目设置](/zh/webhooks/section/set-up-webhooks-in-publisher-account)中指定的单个URL。如果您希望在游戏、网站或移动应用中接收通知,需要在您的服务器上设置Webhook转发,以便在艾克索拉和您的游戏之间传递数据。 您也可以从开发者控制台测试Webhook。

注意

如果您在本地测试集成,来自艾克索拉的`POST`请求无法到达类似http://localhost:3000/my-webhook-endpoint这样的URL。建议使用诸如Ngrok之类的服务创建外部访问隧道,以便在本地接收来自艾克索拉的请求。您可以在ngrok文档中了解更多相关信息。

## 为什么艾克索拉通知未发送到Webhook URL? 请确保您的Webhook服务器支持`POST`和`GET`类型的HTTP请求。 ## 如何在处理过程中防止重复的交易ID? 使用外部ID,即您游戏中的交易ID,它会分配给您系统中的订单。在艾克索拉侧,外部ID与交易ID关联,可以防止同一交易的重复支付。有关配置详情,请参阅[相关文档 ](/zh/dev-resources/faq/payments/#faq_payments_q_new_transaction_external_id)。 ## 使用Webhook时有哪些最佳实践? 我们建议: * 在签名验证后立即返回`204`或`200`。 * 针对原始请求正文验证Webhook签名,不做任何修改。 * 为所有操作实现幂等性。 * 记录所有事件并设置错误监控。 * 避免在URL中传递敏感数据,不在错误消息中暴露技术细节。 有关详细信息,请参阅[最佳实践](/zh/webhooks/section/best-practices)部分。 # Webhook集成检查清单 为确保Webhook正常工作,请在上线前确认以下事项: * 已使用HTTPS。 * 已针对原始请求正文实现Webhook[签名验证](/zh/webhooks/section/webhook-listener/generation-of-signature),不做任何修改。 * 签名确认后立即返回`204/200`响应。 * 已为所有操作实现幂等性。 * 已配置错误日志记录和监控。 * 不在URL中传递敏感数据,不在错误消息中暴露技术细节。 * 已根据[艾克索拉重试逻辑](/zh/webhooks/section/webhook-listener/sending-responses-to-webhook)支持Webhook重试。 * 已完整记录整个集成过程。 # Webhook列表

注:

通知类型在notification_type参数中发送。

Webhook 通知类型 描述
用户验证 user_validation 发送以检查用户是否存在于游戏中。
用户搜索 user_search 发送以根据公共用户ID获取用户信息。
支付 payment 用户完成支付流程时发送。
退款 refund 出于某些原因需要取消支付时发送。
部分退款 partial_refund 出于某些原因需要部分取消支付时发送。
付款被拒 ps_declined 当付款被支付系统拒绝时发送。
AFS拒绝交易 afs_reject 交易在AFS检查过程中被拒绝时发送。
AFS更新的拦截列表 afs_black_list AFS拦截列表发生更新时发送。
创建了订阅 create_subscription 用户创建订阅时发送。
更新了订阅 update_subscription 订阅发生续订或更改时发送。
取消了订阅 cancel_subscription 取消订阅时发送。
非续订订阅 non_renewal_subscription 状态设置为非续订时发送。
添加支付账户 payment_account_add 当用户添加或保存支付帐户时发送。
删除支付账户 payment_account_remove 用户从已保存的帐户中删除了支付帐户时发送。
Web商店中的用户验证 - 从Web商店网站发送以检查游戏中是否存在该用户。
合作伙伴侧目录个性化 partner_side_catalog 用户与商店交互时发送。
订单成功支付 order_paid 订单付款后发送。
订单取消 order_canceled 订单取消时发送。
争议 dispute 当提出新争议时发送。
Version: 1.0 ## Servers ``` https://api.xsolla.com/merchant/v2 ``` ## Download OpenAPI description [Webhook](https://developers.xsolla.com/_bundle/@l10n/zh/webhooks/index.yaml) ## 用户验证 ### 用户搜索 - [POST user-search](https://developers.xsolla.com/zh/webhooks/user-validation/user-search.md): 与User ID不同,Public User ID是唯一标识用户的参数且对用户可知(Public User ID可以是电子邮件、屏幕名称等)。在游戏商店外(例如通过自助终端机)进行购买时,艾克索拉会发送一个user_search类型的Webhook。 ### 用户验证 - [POST user-validation](https://developers.xsolla.com/zh/webhooks/user-validation/user-validation.md): 艾克索拉向Webhook URL发送一个user_validation类型的Webhook以验证用户是否已在游戏中注册。该请求作为付款流程的一部分会被多次发送: * 用户在支付UI中选择支付方式时 * 用户在支付表单中输入数据时,如通过PayPal支付时的银行卡数据或邮政编码 * 用户点击立即支付进行付款时 * 支付过程完成且交易状态变为done时 使用任何支付方式付款时都会发送该请求。 在发布商帐户中保存 Webhook URL 时,可以授予在 Webhook 中接收详细信息的权限。为此,请在发布商帐户的项目设置 > Webhooks> 高级设置部分中将所需开关设置为打开状态。 注: 如果是在2025年1月22日或之前注册的发布商帐户,可在项目设置> Webhooks> 测试 > 付款> 高级设置 部分中找到这些开关。 开关 描述 仅发送不含敏感数据的必要用户参数 Webhook中仅传递用户的以下信息:ID国家/地区 发送自定义参数 自定义令牌参数的信息在webhook中传递。 ### Web商店中的用户验证 - [POST user-validation-in-webshop](https://developers.xsolla.com/zh/webhooks/user-validation/user-validation-in-webshop.md): 艾克索拉从网页商城网站发送一个Webhook以检查游戏中是否存在该用户。该Webhook从以下IP地址发送:34.102.38.178。 注: Webhook仅用于Web商店中的用户验证。有关在建站器中配置Webhook的更多信息,请参阅这些说明。 ## 付款 ### 添加支付帐户 - [POST add-payment-account](https://developers.xsolla.com/zh/webhooks/payments/add-payment-account.md): 每当用户在游戏内购买商品时添加或保存了一个支付帐户时,艾克索拉向Webhook URL发送一个payment_account_add类型的Webhook。要接收该Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ### 部分退款 - [POST partial-refund](https://developers.xsolla.com/zh/webhooks/payments/partial-refund.md): 进行部分退款后,艾克索拉向Webhook URL发送一个partial_refund类型的Webhook并在其中包含已取消交易的详细信息。有关部分退款流程的详细信息请参阅这些说明。 在发布商帐户中保存 Webhook URL 时,可以授予在 Webhook 中接收详细信息的权限。为此,请在发布商帐户的项目设置 > Webhooks> 高级设置部分中将以下开关设置为打开状态。 注: 如果是在2025年1月22日或之前注册的发布商帐户,可在项目设置> Webhooks> 测试 > 付款> 高级设置 部分中找到这些开关。 开关 描述 显示通过保存的支付方式进行的交易的信息 信息在Webhook的以下自定义参数中传递:saved_payment_method:0 — 未使用保存的支付方式1 — 进行当前付款时保存了支付方式2 — 使用了之前保存的支付方式payment_type:1 — 一次性支付2 — 定期支付 退款代码: 代码 退款理由 描述 1 Cancellation by the user request / the game request 用于在发布商帐户中发起取消的情况。 3 Integration error 用于艾克索拉与游戏之间存在集成问题的情况。这种情况下,我们不建议将用户列入黑名单。 5 Test payment 用于测试交易然后取消的情况。这种情况下,我们不建议将用户列入黑名单。 7 Fraud notification from PS 支付被支付系统拒绝。 支付系统检测到潜在欺诈行为。建议:将该用户添加到黑名单。 9 Cancellation by the user request 用于用户请求取消的情况。可能出于某些原因导致用户对游戏或购买产生不满的情况下发生。这种情况下,我们不建议将用户列入黑名单。 10 Cancellation by the game request 用于游戏请求取消的情况。这种情况下,我们不建议将用户列入黑名单。 ### 支付 - [POST payment](https://developers.xsolla.com/zh/webhooks/payments/payment.md): 当用户完成付款时,艾克索拉会向Webhook URL发送一个包含付款详细信息的payment类型的Webhook。 预期响应代码的说明见Responses 部分,您也可以使用其他响应代码: 响应代码 描述 200、201、204 成功的响应。 4xx 发生错误。例如,如果未找到指定的用户或传递了无效的签名。 5xx 服务器临时错误。收到此响应后,艾克索拉将自动重试发送Webhook,并逐渐增加尝试间隔,直到监听器确认接收。48小时内最多可重试12次。 在发布商帐户中保 存Webhook URL时,还可以设置在Webhook中接收额外信息。 注: 如果是在2025年1月22日或之前注册的发布商帐户,可在项目的设置> Webhooks> 测试 > 付款> 高级设置部分中找到这些开关。 开关 描述 显示保存的支付帐户的信息 有关保存的支付方式的信息在payment_account自定义对象中传递。 显示通过保存的支付方式进行的交易的信息 信息在Webhook的以下自定义参数中传递:saved_payment_method:0 — 未使用保存的支付方式1 — 进行当前付款时保存了支付方式2 — 使用了之前保存的支付方式payment_type:1 — 一次性支付2 — 定期支付 将订单对象添加到Webhook 有关订单的信息在支付Webhook的order对象中传递。 仅发送不含敏感数据的必要用户参数 Webhook中仅传递用户的以下信息:ID国家/地区 显示银行卡BIN和后缀码 Webhook中传递以下银行卡号的信息:card_bin参数中的前6位数字card_suffix中的后4位数字 显示银行卡品牌 用于付款的银行卡的品牌。例如,Mastercard或Visa。 显示国家/地区预扣税和用户获取费。 payment_details.​country_wht和payment_details.​user_acquisition_fee对象将通过Webhook发送。此开关默认开启。 发送3DS信息。 包含3-D Secure验证数据的cards对象将通过Webhook发送。 注意 Webhook中发送的字段集取决于:在发布商帐户中配置的高级设置在艾克索拉侧配置的自定义设置如果您有任何疑问,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ### 付款被拒 - [POST payment-declined](https://developers.xsolla.com/zh/webhooks/payments/payment-declined.md): 当支付系统拒绝交易时,艾克索拉会将交易详情通过ps_declined类型的Webhook发送至您配置的Webhook URL。该Webhook在授权或支付处理阶段发送。此情况下,不会发送payment\ order_paid Webhook。 支付系统拒绝的常见原因: * 卡片授权失败(例如,支付系统因技术错误或银行无响应而无法完成授权流程)或被拒(例如,银行已响应但因资金不足或卡片信息无效而拒绝交易)。 * 3-D Secure验证失败、未完成或用户确认超时。 * 处理方或收单银行暂时不可用,或因不可逆转的错误(如账户已关闭或卡号无效)而强制拒绝。在不解决根本问题的情况下重试将无法成功完成交易。 不应与以下情况混淆: * 反欺诈拒绝,这类情况通过afs_reject Webhook报告。 * 成功支付后的退款和部分退款,这类情况通过refund和partial_refund webhook报告。 注意 如需接收ps_declined Webhook,请联系您的客户成功经理或发送邮件至csm@xsolla.com。 ### 退款 - [POST refund](https://developers.xsolla.com/zh/webhooks/payments/refund.md): 当支付取消时,艾克索拉会向Webhook URL发送带有refund类型的Webhook,其中包含已取消交易的详细信息。 Webhook重试机制取决于退款发起方: * 若退款由您方发起,系统不会重新发送Webhook。无论对Webhook的响应如何,款项都将退还给用户。 * 若退款由第三方发起(如支付系统或艾克索拉客户支持团队),且Webhook响应返回5xx状态码,系统会按递增间隔重新发送Webhook。最多重试12次,时间范 围为首次尝试后的48小时内。 有关退款流程的详细信息,请参阅相关说明。 注意 如满足以下所有条件,款项仍将退还给用户:退款由艾克索拉发起。Webhook响应返回4xx状态码,或在所有重试后未收到响应,或返回5xx状态码。 在发布商帐户中保 存Webhook URL时,还可以设置在Webhook中接收额外信息。 注: 如果是在2025年1月22日或之前注册的发布商帐户,可在项目的设置> Webhooks> 测试 > 付款> 高级设置部分中找到这些开关。 开关 描述 显示通过保存的支付方式进行的交易的信息 信息在Webhook的以下自定义参数中传递:saved_payment_method:0 — 未使用保存的支付方式1 — 进行当前付款时保存了支付方式2 — 使用了之前保存的支付方式payment_type:1 — 一次性支付2 — 定期支付 显示退款原因信息。 退款原因的详细信息。 退款代码: 代码 退款理由 描述 1 Cancellation by the user request / the game request 用于在发布商帐户中发起取消的情况。 2 Chargeback 交易存在退单的情况。 3 Integration error 用于艾克索拉与游戏之间存在集成问题的情况。这种情况下,我们不建议将用户列入黑名单。 4 Potential fraud 存在欺诈嫌疑。建议:将该用户添加到黑名单。 5 Test payment 用于测试交易然后取消的情况。这种情况下,我们不建议将用户列入黑名单。 6 User invoice expired 用于通过用后付费模式的支付系统进行交易的情况。 7 Fraud notification from PS 支付被支付系统拒绝。 支付系统检测到潜在欺诈行为。建议:将该用户添加到黑名单。 8 Cancellation by the PS request 用于支付系统请求取消的情况。这种情况下,我们不建议将用户列入黑名单。 9 Cancellation by the user request 用于用户请求取消的情况。可能出于某些原因导致用户对游戏或购买产生不满的情况下发生。这种情况下,我们不建议将用户列入黑名单。 10 Cancellation by the game request 用于游戏请求取消的情况。这种情况下,我们不建议将用户列入黑名单。 11 Account holder called to report fraud 用于账户持有人通知我们其未进行此交易的情况。 12 Friendly fraud 用于接收到友好型欺诈相关消息的情况。 13 Duplicate 有同一张发票的重复交易。 ### 删除支付帐户 - [POST remove-payment-account](https://developers.xsolla.com/zh/webhooks/payments/remove-payment-account.md): 用户从保存的支付帐户中删除支付帐户时,艾克索拉会向Webhook URL发送一个payment_account_remove类型的Webhook。要接收该Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ## 组合Webhook ### 订单取消(包含付款和交易详情) - [POST order-cancellation](https://developers.xsolla.com/zh/webhooks/combined-webhooks/order-cancellation.md): 当订单被用户、合作伙伴取消或系统自动取消时,艾克索拉会向指定URL发送order_canceled Webhook。此Webhook包含已退回商品的信息、付款数据和已取消订单的详细内容。 如果付款不成功,则不会发送该Webhook,例如: * 打开了支付UI,但用户没有为订单付款 * 打开了支付UI,但付款过程中出现错误 推荐的Webhook处理时间为3秒以内。 ### 订单成功支付(包含付款和交易详情) - [POST successful-order-payment](https://developers.xsolla.com/zh/webhooks/combined-webhooks/successful-order-payment.md): 当用户订单支付成功时艾克索拉发送order_paid webhook到指定的URL。 order_paid webhook包含所购商品、付款数据和交易详细信息。 如果付款不成功,则不会发送order_paid webhook,例如: * 打开了支付表单,但用户没有为订单付款 * 打开了支付表单,但付款过程中出现错误 建议order_paid webhook的处理时间小于3秒。 注意 Webhook中发送的字段集取决于以下设置:您在发布商帐户项目设置 > Webhooks > 高级设置部分中配置的设置您在艾克索拉侧配置的设置如果您有任何疑问,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 响应部分描述了预期的回答。您可以使用其他响应代码。根据响应码和自动退款功能的连接,艾克索拉侧的webhook处理逻辑如下: 响应代码 禁用了自动退款(默认) 启用了自动退款 400、401、402、403、404、409、422、415 无操作 自动退款给用户 200、201、204 无操作 无操作 不同代码或对webhook无响应 在指定的时间间隔内发送多个webhook:2次间隔5分钟的尝试,7次间隔15分钟的尝试,10次间隔60分钟的尝试。 在指定的时间间隔内发送多个webhook:2次间隔5分钟的尝试,7次间隔15分钟的尝试,10次间隔60分钟的尝试。如果发送了所有webhook后仍未收到成功响应,则会向用户自动退款。 要连接自动退款功能,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ## 独立Webhook ### 订单取消(不包含付款和交易详情) - [POST order-cancellation-separate](https://developers.xsolla.com/zh/webhooks/separate-webhooks/order-cancellation-separate.md): 当订单被用户、合作伙伴取消或系统自动取消时,艾克索拉会向指定URL发送order_canceled Webhook。此Webhook包含已退回商品的信息和已取消订单的详细内容。 如果付款不成功,则不会发送该Webhook,例如: * 打开了支付UI,但用户没有为订单付款 * 打开了支付UI,但付款过程中出现错误 推荐的Webhook处理时间为3秒以内。 ### 订单成功支付(不包含付款和交易详情) - [POST successful-order-payment-separate](https://developers.xsolla.com/zh/webhooks/separate-webhooks/successful-order-payment-separate.md): 当满足以下条件时,艾克索拉将order_paid webhook发送到指定的 URL: 1. 用户成功支付订单。 2. 艾克索拉收到成功处理支付 Webhook的响应。 order_paid webhook包含所购商品和交易详细信息。 如果出现以下情况,则不发送order_paid webhook: * 支付不成功,例如: * 打开了支付表单,但用户没有为订单付款 * 打开了支付表单,但付款过程中出现错误 * 尚未收到成功处理支付 Webhook的响应。 建议order_paid webhook的处理时间小于3秒。 响应部分描述了预期的回答。您可以使用其他响应代码。根据响应码和自动退款功能的连接,艾克索拉侧的webhook处理逻辑如下: 响应代码 禁用了自动退款(默认) 启用了自动退款 400、401、402、403、404、409、422、415 无操作 自动退款给用户 200、201、204 无操作 无操作 不同代码或对webhook无响应 在指定的时间间隔内发送多个webhook:2次间隔5分钟的尝试,7次间隔15分钟的尝试,10次间隔60分钟的尝试。 在指定的时间间隔内发送多个webhook:2次间隔5分钟的尝试,7次间隔15分钟的尝试,10次间隔60分钟的尝试。如果发送了所有webhook后仍未收到成功响应,则会向用户自动退款。 要连接自动退款功能,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ## 个性化Webhook ### 合作伙伴侧的目录个性化 - [POST personalized-partner-catalog](https://developers.xsolla.com/zh/webhooks/personalization/personalized-partner-catalog.md): 当用户与商店交互时,艾克索拉会将包含用户和项目参数的partner_side_catalog webhook发送到webhook URL。 返回适用于该用户的item_id或商品SKU的列表。您也可以在其中包含指定用户对指定商品的可购买次数的信息。此功能允许您控制用户可添加 到购物车及可购买的商品种类和次数。 注意 处理Webhook时,请注意以下限制:Webhook必须在3秒内处理完成。如果处理时间超过3秒,获取虚拟物品列表、创建支付令牌和创建订单API调用将返回错误。Webhook响应大小不得超过64KB。超过该限制的响应不会被处理,用户将看到空目录,且无法购买任何物品。如需调整响应大小上限,请联系您的客户成功经理,或发送电子邮件至csm@xsolla.com。 ## 反欺诈 ### 反欺诈黑名单更新 - [POST afs-rejected-blocklist](https://developers.xsolla.com/zh/webhooks/anti-fraud/afs-rejected-blocklist.md): 当反欺诈系统的黑名单发生更新(添加或删除了参数)时,艾克索拉会向Webhook URL发送一个afs_black_list类型的Webhook。参数添加在艾克索拉侧自动执行或根据您的请求执行。删除参数只能根据您的请求才可以进行。要接收此Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ### 反欺诈系统拒绝了交易 - [POST afs-rejected-transaction](https://developers.xsolla.com/zh/webhooks/anti-fraud/afs-rejected-transaction.md): 当交易在反欺诈系统检查期间被拒绝时,艾克索拉会向Webhook URL发送一个包含交易详情的afs_reject类型的Webhook。要接收该Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 在发布商帐户中保存 Webhook URL 时,可以授予在 Webhook 中接收详细信息的权限。为此,请在发布商帐户的项目设置 > Webhooks> 高级设置部分中将以下开关设置为打开状态。 注: 如果是在2025年1月22日或之前注册的发布商帐户,可在项目设置> Webhooks> 测试 > 付款> 高级设置 部分中找到这些开关。 开关 描述 显示通过保存的支付方式进行的交易的信息 信息在Webhook的以下自定义参数中传递:saved_payment_method:0 — 未使用保存的支付方式1 — 进行当前付款时保存了支付方式2 — 使用了之前保存的支付方式payment_type:1 — 一次性支付2 — 定期支付 ### 争议 - [POST dispute](https://developers.xsolla.com/zh/webhooks/anti-fraud/dispute.md): 提起新争议或争议状态发生变化时,艾克索拉会向Webhook URL发送一个包含dispute类型的Webhook。要接收该Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ## 订阅 ### 已取消订阅 - [POST canceled-subscription](https://developers.xsolla.com/zh/webhooks/subscriptions/canceled-subscription.md): 取消订阅时,艾克索拉会向Webhook URL发送一个cancel_subscription类型的Webhook。 ### 已创建订阅 - [POST created-subscription](https://developers.xsolla.com/zh/webhooks/subscriptions/created-subscription.md): 当用户创建订阅时,艾克索拉会向Webhook URL发送一个create_subscription类型的Webhook。 ### 到期性订阅 - [POST nonrenewing-subscription](https://developers.xsolla.com/zh/webhooks/subscriptions/nonrenewing-subscription.md): 当订阅状态设置为非续订时,艾克索拉会向Webhook URL发送一个non_renewal_subscription类型的Webhook。要接收该Webhook,请联系您的客户成功经理或发送电子邮件至csm@xsolla.com。 ### 已更新订阅 - [POST updated-subscription](https://developers.xsolla.com/zh/webhooks/subscriptions/updated-subscription.md): 如果订阅中的某些参数(plan_id、date_next_charge)发生更改,以及每次订阅续订时,艾克索拉都会向Webhook URL发送一个update_subscription类型的Webhook。