コンテンツへスキップ

ウェブフック (1.0)

概要

ウェブフックは、システムで発生するイベントに関する通知です。特定のイベントが発生すると、エクソーラはHTTPリクエストを送信し、イベントデータがアプリケーション に送信されます。通常、これはJSON形式のPOSTリクエストです。

イベントの例:

  • アイテムカタログとのユーザーインタラクション
  • 注文の決済またはキャンセル

設定されたイベントが発生すると、エクソーラはウェブフック経由でシステムにそれを通知します。その結果、次のようなアクションを実行できます:

  • ユーザーの残高を補充する
  • 支払いを返金する
  • ユーザーアカウントから新しいアイテムをクレジットまたはデビットする
  • サブスクリプションの提供を開始する
  • 詐欺の疑いがある場合にユーザーをブロックする

支払い処理ウェブフックのワークフローの例:

支払い処理ウェブフック

注意

使用されるソリューションとその統合のタイプに応じて、ウェブフックのセットとインタラクションのシーケンスは、提供された例とは異なる場合があります。

エクソーラウェブフック統合のビデオガイド:

エクソーラ製品およびソリューションを使用する場合のウェブフック設定:

製品/ソリューション必須/任意ウェブフックは何に使用されますか
決済ソリューション必須
  • ユーザー検証。
  • 支払いが成功した場合や支払いが返金された場合に、トランザクションの詳細情報を受け取ります。
  • ユーザーに購入したアイテムを付与し、注文がキャンセルされた場合にアイテムを差し引きます。
ストア必須
  • ユーザー検証。
  • 支払いが成功した場合や支払いが返金された場合に、トランザクションの詳細情報を受け取ります。
  • ユーザーに購入したアイテムを付与し、注文がキャンセルされた場合にアイテムを差し引きます。
ゲーム販売任意ゲームキーの販売では、ユーザーの検証やアイテムの付与は必要ありません。支払いや注文キャンセルなどのイベントに関する情報を受け取りたい場合は、ウェブフックを接続することができます。
ウェブフックを接続する場合は、すべての受信した必要なウェブフックを処理することが重要です。
サブスクリプション任意サブスクリプションの作成、更新、またはキャンセルに関する情報を受け取ります。または、API経由で情報をリクエストすることもできます。
ウェブショップ必須
  • ユーザー検証。
  • 支払いが成功した場合や支払いが返金された場合に、トランザクションの詳細情報を受け取ります。
  • ユーザーに購入したアイテムを付与し、注文がキャンセルされた場合にアイテムを差し引きます。
  • ユーザー認証、ユーザーIDによる認証を使用する場合。または、エクソーラログイン経由でユーザー認証を使用することもできます。
デジタル配信ソリューション必須
  • ユーザー検証。
  • エクソーラ側のトランザクションIDをシステムのトランザクションIDにリンクします。
  • 注文に追加のトランザクションパラメータを転送します。
  • ユーザーに購入したアイテムを付与し、注文がキャンセルされた場合にアイテムを差し引きます。

ウェブフックの設定に関する詳細情報は、デジタル配信ソリューションのドキュメントを参照してください。

ログイン任意

イベント情報の受信:

  • ユーザー登録/認証
  • ユーザーメールアドレスの確認
  • ユーザーのソーシャルメディアアカウントをリンクする

ウェブフックの設定の詳細については、ログインに関するドキュメントを参照してください。

Xsolla Business Accounts

Business Account is the primary tool for configuring Xsolla features, as well as for working with analytics and transactions.

Xsolla develops tools for different business categories. To simplify navigation, these tools are grouped into separate accounts by target audience:

注意

Publisher Account is a legacy tool.

必須なウェブフックのリスト

If you use products and solutions that require working with webhooks, enable and test the webhooks in your account and set up their processing.

When specific events occur, webhooks are sent sequentially. Therefore, if you do not process one of the webhooks, subsequent webhooks will not be sent. The list of required webhooks is presented below.

ストアと決済ソリューション

エクソーラ側では、サイトでアイテムを購入して返品する際に2 つのウェブフック送信オプションが設定されています。支払いと取引データ、および購入したアイテムに関する情報は、個別に提供することも、1つのウェブフックにまとめるこ ともできます。

まとめたウェブフックで情報を受け取ります:

If you registered in the account after January 22, 2025, you receive all the information in the Successful payment for order (order_paid) and Order cancellation (order_canceled) webhooks. In this case, you do not need to process the Payment (payment) and Refund (refund) webhooks.

個別のウェブフックで情報を受け取ります:

If you registered in Publisher Account on or before January 22, 2025, you receive the following webhooks:

You need to process all incoming webhooks. To switch to the new option with receiving combined webhooks, contact your Customer Success Managers or email to [email protected].

インゲームストアと決済管理の完全な運用のためには、主要なウェブフックの処理を実装する必要があります。

まとめたウェブフックを受信する場合:

ウェブフック名とタイプ説明
ユーザー検証 >ユーザー検証(user_validation)ユーザーがゲームに登録されていることを確認するために、支払いプロセスのさまざまな段階で送信されます。
ゲームサービス > まとめたウェブフック >注文支払い完了(order_paid)支払いデータ、取引の詳細、購入されたアイテムに関する情報が含まれます。ウェブフックからのデータを使用して、ユーザーにアイテムを追加します。
ゲームサービス > まとめたウェブフック >注文キャンセル(order_canceled)キャンセルされた支払のデータ、取引の詳細、および購入したアイテムに関する情報が含まれています。ウェブフックからのデータを使用して、購入されたアイテムを削除します。

個別のウェブフックを受信する場合:

ウェブフック名とタイプ説明
ユーザー検証 >ユーザー検証(user_validation)ユーザーがゲームに登録されていることを確認するために、支払いプロセスのさまざまな段階で送信されます。
決済ソリューション >支払い(payment)支払いデータと取引の詳細が含まれています。
ゲームサービス> 個別のウェブフック>注文支払い完了(order_paid)購入したアイテムに関する情報が含まれています。ウェブフックからのデータを使用して、ユーザーにアイテムを追加します。
決済ソリューション >返金(refund)支払いデータと取引の詳細が含まれています。
ゲームサービス > 個別のウェブフック >注文キャンセル(order_canceled)購入したアイテムとキャンセルされたトランザクションのIDに関する情報が含まれます。ウェブフックのデータを使用して、購入したアイテムを削除します。

If item catalog personalization is implemented on your application’s side, set up processing of Catalog personalization on the partner’s side webhook.

注意

実際の支払いを受け取るには、ライセンス契約に署名し、以下のウェブフック処理を実装するだけです:

サブスクリプション

サブスクリプションプランを自動的に管理するには、主要なウェブフックの処理を実装する必要があります:

  • User validation (user_validation) — is sent at different stages of the payment process to ensure the user is registered in the game.
  • Payment (payment) — is sent when an order is paid and contains payment data and transaction details.
  • Created subscription (create_subscription) — is sent when a Payment webhook has been successfully processed or the user has purchased a subscription with a trial period. It contains the details of the purchased subscription and user data. Use the webhook data to add a subscription to the user.
  • Updated subscription (update_subscription) — is sent when a subscription is renewed or changed, when a Payment webhook has been successfully processed. It contains the details of the purchased subscription and user data. Use the webhook data to extend the user's subscription or change the subscription parameters.
  • Refund (refund) — is sent when an order is canceled and contains the canceled payment data and transaction details.
  • Canceled subscription (cancel_subscription) — is sent when a Refund webhook has been successfully processed or the subscription was canceled for another reason. It contains information about the subscription and user data. Use the webhook data to deduct purchased subscriptions from the user.

Set up webhooks in account

一般設定

ウェブフックの受信を有効にするには:

  1. In the project in the account go to the Settings > Webhooks section.
  2. In the Webhook server field, specify the URL of your server where you want to receive webhooks in the https://example.com format. You can also specify the URL you find in a tool for testing webhooks.

注意。

データ転送にはHTTPSプロトコルが使用されます。HTTPプロトコルはサポートされていません。

  1. 秘密鍵を生成します:
    1. 秘密鍵セクションで、「キーを追加」クリックします。
    2. 開いたモーダルウィンドウに、一般リストからこの項目を識別するためのキー名を入力してください。。
    3. 「キーを作成」クリックします。
    4. 「シークレットをコピー」をクリックし、作成されたキーをユーザー側で保存してください。
    5. 「完了」クリックします。
    6. キーが保存されたことを確認し、「OK、閉じる」をクリックします。

Add key

Notice

Key recommendations:

  • Save the generated secret key on your side. You can view the key in the account only once when it is created.
  • Don’t share your secret key with anyone.
  • The secret key must be stored on your server and never in binaries or on the front end.

  1. 「ウェブフックを有効にする」をクリックします。

注意

ウェブフックをテストするには、webhook.siteのような専用のウェブサイトか、ngrokのようなプラットフォームを選択できます。

Notice

You can’t simultaneously send webhooks to different URLs. What you can do in the account is specify a URL for testing first, and then replace it with the real one.

ウェブフックの受信を無効にするには:

  1. In the project in the account, go to the Settings > Webhooks section.
  2. Click Disable webhooks.

秘密鍵のローテーション

秘密鍵を定期的に更新することで、統合のセキュリティを向上させることができます。プロジェクトでは最大5つの秘密鍵を作成し、ローテーションを有効にすることができます 。これを行うには:

  1. In the Settings > Webhooks section, click Add key.

Add key

  1. 開いたモーダルウィンドウに、一般リストからこの項目を識別するためのキー名を入力してください。。
  2. 「キーを作成」をクリックします。
  3. 「シークレットをコピー」をクリックし、作成されたキーをユーザー側で保存してください。
  4. 「完了」をクリックします。
  5. キーが保存されたことを確認し、「OK、閉じる」をクリックします。

Notice

Key recommendations:

  • Save the generated secret key on your side. You can view the key in the account only once when it is created.
  • Don’t share your secret key with anyone.
  • The secret key must be stored on your server and never in binaries or on the front end.

プロジェクトごとに有効な秘密鍵は1つだけです。変更したい場合は、別のキーの行にある「有効にする」をクリックして操作を確定してください。新しいキーへの移行 が正常に完了した後は、無効化されたキーを削除することをお勧めします。

Change active 
key

詳細設定

For the webhooks in the Payments and Store section, advanced settings are available. They will automatically appear under the General settings block after you click the Enable webhooks button.

注意

詳細設定が表示されない場合は、一般設定でウェブフック受信が接続されていることを確認し、テスト > 決済ソリューションとストアタブにいることを確認してください。

このセクションでは、ウェブフックでの追加情報の受信を設定できます。これを行うには、対応するスイッチをアクティブポジションに設定します。各権限の行は、設定の変更に よって影響を受けるウェブフックを示します。

トグル説明
保存された決済アカウントに関する情報を表示します(2025年1月22日以前にパブリッシャーアカウントに登録し、個別のウェブフックを受信している場合にのみ表示されます)。保存された決済方法に関する情報は、payment_accountカスタムオブジェクト。
保存された決済方法によるトランザクションに関する情報を表示します。

情報は、ウェブフックの以下のカスタムパラメータに渡されます:

  • saved_payment_method:
    • 0 — 保存された決済方法は使用されませんでした
    • 1 — 決済方法は現在の支払い時に保存されました
    • 2 — 保存された決済方法は使用されませんでした
  • payment_type:
    • 1 — 一回払い
    • 2 — 定期支払い
ウェブフックにorderオブジェクトを追加します(2025年1月22日以前にパブリッシャーアカウントに登録し、個別のウェブフックを受信している場合にのみ表示されます)。注文に関する情報は、決済ウェブフックのorderオブジェクトに渡されます。
機密データを含めず、必須のユーザーパラメータのみを送信します。

ウェブフックでは、ユーザーに関する次の情報のみが渡されます:

  • ID
  • 国
カスタムパラメータを送信します。カスタムトークンパラメータに関する情報は、ウェブフックで渡されます。
カードのBINおよびサフィックスを表示します。

ウェブフックには、銀行カード番号に関する以下の情報が渡されます:

  • card_binパラメータの最初の6桁
  • card_suffixパラメータの最後の4桁
カードブランドを表示します。決済に使用したカードのブランド。例えば、MastercardやVisaなど。
返金理由に関する情報を表示します。返金理由に関する詳細情報。
国別の源泉徴収税および顧客獲得料を表示します。payment_details.​country_whtとpayment_details.​user_acquisition_feeオブジェクトがウェブフックに含めて送信されます。このトグルはデフォルトでオンになっています。
3DS情報を送信します。3-Dセキュア認証に関するデータを含むcardsオブジェクトがウェブフックで渡されます。

Advanced 
settings

Test webhooks in account

ウェブフックをテストすると、ユーザー側とエクソーラ側の両方でプロジェクトが正しく設定されていることを確認できます。

If webhooks are set up successfully, a webhooks testing section is displayed below the webhooks setup section.

Webhooks testing 
section

The testing section in the account varies depending on the webhook receiving option.

If you registered in the account after January 22, 2025, you receive combined webhooks:

ウェブフックテストのタブ名ウェブフック名とタイプ
決済ソリューションとストアユーザー検証 >ユーザー検証(user_validation)
ゲームサービス > まとめたウェブフック >注文支払い完了(order_paid)
ゲームサービス > まとめたウェブフック >注文キャンセル(order_canceled)
サブスクリプションユーザー検証 >ユーザー検証(user_validation)
決済ソリューション >支払い(payment)

2025年1月22日以前にパブリッシャーアカウントに登録した場合、個別のウェブフックが送信されます:

ウェブフックテストのタブ名ウェブフック名とタイプ
ストアゲームサービス> 個別のウェブフック>注文支払い完了(order_paid)
ゲームサービス > 個別のウェブフック >注文キャンセル(order_canceled)
決済ソリューションユーザー検証 >ユーザー検証(user_validation)
決済ソリューション >支払い(payment)
サブスクリプションユーザー検証 >ユーザー検証(user_validation)
決済ソリューション >支払い(payment)

注意

テストセクションにテストがパスしていないという警告が表示された場合は、ウェブフックリスナーのウェブフック応答設定を確認します。テストでのエラーの理由はテスト結果に示されます。

例:

テストには、専門サイトwebhook.siteを使用します。

「無効な署名に対する応答のテスト」セクションにエラーが表示されます。

エクソーラが間違った署名を持つウェブフックを送信し、ハンドラーがINVALID_SIGNATUREエラーコードを指定する4xx HTTPコードで応答することを期待しているために発生します。

webhook.siteは、署名が間違ったウェブフックを含むすべてのウェブフックに応答して200 HTTPコードを送信します。期待される4xx HTTPコードが得られないため、テスト結果にエラーが表示されます。

まとめたウェブフックを使用したシナリオのテストプロセスを以下に説明します。

決済ソリューションとストア

「決済ソリューションとストア」タブでは、次のウェブフックをテストできます:

ウェブフックをテストするには:

  1. ウェブフックのテストセクションで、「決済ソリューションとストア」タブに移動します。
  2. In the drop-down list, select the item type. If you haven’t set up this item type in the account yet, click the button to configure it. After creating the item, return to the webhook testing section and proceed to the next step.
  3. 必要なフィールドに入力します:
    • ユーザーID — テスト時には、任意の文字と数字の組み合わせを使用できます。
    • 「エクソーラ注文ID」フィールドに任意の値を入力してください。
    • エクソーラインボイスID — エクソーラ側のトランザクションID。テスト時には、任意の数値を使用できます。
    • インボイスID — ゲーム側のトランザクションID。テスト時には、任意の文字と数字の組み合わせを使用できます。これは成功した支払いに必須のパラメータではありませんが、トランザクショ ンIDをエクソーラ側のトランザクションIDとリンクさせるために渡すことができます。
    • 金額 — 決済金額。テスト時には、任意の数値を使用できます。
    • 通貨 — ドロップダウンリストから通貨を選択します。
    • ドロップダウンリストからアイテムのSKUを選択し、数量を指定します。同じタイプの複数のアイテムを選択するには、「+」をクリックして新しい行に追加します。
  4. 「ウェブフックをテスト」をクリックします。

User validation, Successful payment for order and Order cancellation webhooks with the specified data are sent to the provided URL. The results of testing each webhook type are displayed below the Test webhooks button.

If the Use public user ID box is checked in your account in the **Settings

Integration settings** section, the User search webhook will also be sent to your webhook server URL and the testing result will be displayed.

各ウェブフックについて、成功したシナリオとエラーが発生したシナリオの両方の処理を設定する必要があります。

Payments testing 
section

サブスクリプション

Note

To test webhooks, you should have at least one subscription plan created in the account in the Items catalog > Subscriptions section.

「サブスクリプション」タブでは、次のウェブフックをテストできます:

注意

その他のサブスクリプション管理シナリオのテストに関する詳細情報については、統合ガイドを参照してください。

ウェブフックをテストするには:

  1. テストセクションで、サブスクリプションタブに移動します。
  2. 必要なフィールドに入力します:
    • ユーザーID — テスト時には、任意の文字と数字の組み合わせを使用できます。
    • エクソーラインボイスID — エクソーラ側のトランザクションID。テスト時には、任意の数値を使用できます。
    • Public user ID — ID known to a user, e.g., an email or nickname. This field is displayed if you checked the Use public user ID box in your account in the Settings > Integration settings section.
    • 金額 — 決済金額。テスト時には、任意の数値を使用できます。
    • 通貨 — ドロップダウンリストから通貨を選択します。
    • プランID — サブスクリプションプラン。ドロップダウンリストからプランを選択します。
    • サブスクリプション製品 — ドロップダウンリストから製品を選択します(オプション)。このリストは、プロジェクトで製品が設定されている場合に表示されます。
    • インボイスID — ゲーム側のトランザクションID。テスト時には、任意の文字と数字の組み合わせを使用できます。これは成功した支払いに必須のパラメータではありませんが、トランザクショ ンIDをエクソーラ側のトランザクションIDとリンクさせるために渡すことができます。
    • 試行期間。お試し期間なしでのサブスクリプションの購入をテストす るか、またはサブスクリプションの更新 をテストするには、値を0に指定します。
    1. 「テスト」をクリックします。

指定したURLに、データが入力されたウェブフックを受け取ります。各ウェブフックのテスト結果は、成功したシナリオとエラーが発生したシナリオの両方で、「テスト 」ボタンの下に表示されます。

ウェブフックリスナー

Webhook listener is program code that allows receiving incoming webhooks at a specified URL address, generating a signature, and sending a response to the Xsolla webhook server.

注意

Pay Station PHP SDKライブラリには、ウェブフックを処理するための既製のクラスが含まれています。

アプリケーション側で、以下のIPアドレスからのウェブフックの受信を実装してください:

  • 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

If you integrated Login product, additionally add processing webhooks from the following IP addresses:

  • 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で成功したトランザクションが複数存在することは許可されていません。
  • ウェブフックリスナーがデータベースにすでに存在するIDを持つウェブフックを受信した場合、このトランザクションの以前の処理結果を返す必要があります。ユーザーに重複 した購入をクレジットし、データベースに重複したレコードを作成することは推奨されません。

署名の生成

安全なデータ転送を確保するには、ウェブフックが実際にエクソーラサーバーから送信され、転送中に改ざんされていないことを確認する必要があります。これを行うには、受信 したリクエスト本文のペイロードに基づいて独自の署名を生成し、それを着信リクエストのauthorizationヘッダーで提供される署名と比較してください。両方 の署名が一致する場合、そのウェブフックは正規のものであり、安全に処理できます。

検証ステップ:

  1. 着信ウェブフックリクエストのauthorizationヘッダーから署名を取得します。ヘッダーの形式はSignature <signature_value>です。

  2. ウェブフックのリクエスト本文をJSON形式で取得してください。

    注意

    JSONペイロードは、受信したものをそのまま使用してください。ペイロード をパースしたり再エンコードしたりしないでください。フォーマットが変更され、署名検証が失敗する原因となります。

  3. 比較用に独自の署名を生成します:

    1. JSONペイロードとプロジェクトの秘密鍵を連結します。秘密鍵は文字列の末尾に追加してください。
    2. 結果の文字列にSHA-1暗号ハッシュ関数を適用します。結果は小文字の16進数文字列になります。
    3. 生成した署名をauthorizationヘッダーからの署名と比較してください。両者が一致すれば、ウェブフックは信頼できるものです。

    以下に、署名生成の実装例を各種プログラミング言語で示します: C#、C++、Go、PHP、Node.js。

    ウェブフック(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 protected]",
          "id":1234567,
          "name":"Xsolla User",
          "country":"US"
      }
    }

    ウェブフック(curl)の例:

    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 protected]",
            "id": 1234567,
            "name": "Xsolla User",
            "country": "US"
          }
        }'

    C#で署名生成の実装例(一般サンプル):

    注

    このコードサンプルは、.NET Framework 4.0以降、および.NET Coreやその他の最新の.NETバージョンと互換性があります。署名検証には、タイミング攻撃を防ぐために、ConstantTimeEqualsメソッドによる定数時間比較が使用されています。

    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を使用することもできます。

    // 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メソッドを使用できます。

    // 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++での署名生成の実装例:

    #include <string>
    #include <sstream>
    #include <iomanip>
    #include <openssl/sha.h>
    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<const unsigned char*>(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<unsigned int>(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での署名生成の実装例:

    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
    class XsollaWebhookSignature
    {
        /**
         * Compute SHA1 signature from webhook JSON body and secret key
         *
         * @param string $jsonBody The raw JSON body from webhook
         * @param string $secretKey The project's secret key
         * @return string The lowercase SHA1 signature
         */
        public static function computeSha1(string $jsonBody, string $secretKey): string
        {
            // Concatenation of the JSON from the request body and the project's secret key
            $dataToSign = $jsonBody . $secretKey;
            // Generate SHA1 signature
            $signature = sha1($dataToSign);
            return strtolower($signature);
        }
        /**
         * Verify webhook signature using timing-safe comparison
         *
         * @param string $jsonBody The raw JSON body from webhook
         * @param string $secretKey The project's secret key  
         * @param string $receivedSignature The signature from authorization header
         * @return bool True if signature is valid, false otherwise
         */
        public static function verifySignature(string $jsonBody, string $secretKey, string $receivedSignature): bool
        {
            $computedSignature = self::computeSha1($jsonBody, $secretKey);
            // Use hash_equals for timing-safe comparison
            return hash_equals($computedSignature, strtolower($receivedSignature));
        }
    }
    ?>

    Node.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;
            }
        }
    }

    ウェブフックへの応答の送信

    ウェブフックの受信を確認するには、サーバーは以下を返す必要があります:

    • 成功した応答の場合は、200、201、または204HTTPコード。
    • 400 HTTP code with description of the problem if the specified user was not found or an invalid signature was passed. Your webhook handler may also return a 5xx HTTP code in case of temporary issues on your server.

    If the Xsolla server did not receive a response to Successful payment for order and Order cancellation webhooks or received a response with a 5xx code, the webhooks are resent according to the following schedule:

    • 5分間隔で2回の試行
    • 15分間隔で7回の試行
    • 60分間隔で10回の試行

    ウェブフックの送信は、最初の送信から12時間以内に最大20回まで試行されます。

    The retry logic for the Payment and Refund webhooks is described on the respective webhook page.

    注意

    以下の条件がすべて満たされている場合でも、支払いはユーザーに返金されます:

    • 返金はエクソーラによって開始されました。
    • ウェーブフックに対する応答として、4xxステータスコードが返された、またはすべての再試行後に応答がなかった、あるいは5xxステータスコードが返された場合。

    If the Xsolla server did not receive a response to the User validation webhook or received a response with a code of 400 or 5xx, the User validation webhook is not resent. In this case, the user sees an error and the Payment and Successful payment for order webhooks are not sent.

    エラー

    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に含めたり、エラーメッセージに技術的な詳細情報を記載したりしないでください。
    • ウェーブフックのエンドポイントをCSRFミドルウェアの対象から除外してください — エクソーラからの受信リクエストにはCSRFトークンが含まれていないため、この設定がないと拒否されます。
    • エクソーラIPアドレスを許可リストに追加してください。

    ウェブフックハンドラのアーキテクチャ

    以下のガイドラインに従ってください:

    1. 本文とヘッダーを変更することなく、そのままの状態でPOSTリクエストを受け取ります。
    2. ウェブフックの署名を検証し 、適切なステータスコードを返してください。
      • 4xx — 署名が一致しない場合。
      • 2xx —成功した場合。メインのビジネスロジックを実行する前に、「204 No Content」を返すことを推奨します。「200 OK」も受け入れ可能です。
    3. ペイロードを非同期ジョブまたはキューに渡し、さらなる処理を行います。
    4. 冪等性を実装してくだ さい。同じウェーブフックを複数回受信した場合でも、システムが適切に処理できるようにする必要があります。

    フローの例:

    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および/または external ID、注文IDを冪等性キーとし て使用してください。
    • 処理済みのIDを保存し、重複するリクエストを受信した場合は前回の結果を返却します。
    • アイテムの重複付与、データベースへの二重登録、および二重課金を回避してください。
    • 逐次配信においては、前のイベントでエラーが発生すると、それ以降のすべてのイベントの処理がブロックされる点に注意してください。

    システムの回復性

    以下のガイドラインに従ってください:

    • サードパーティへのAPIコール、請求処理、アイテム付与などのリソース消費の激しい操作には、キューと非同期処理を使用してください。
    • ウェブフックハンドラにタイムアウト(1〜3秒)を設定してください。一時的な障害の場合は、エクソーラのの再試行メカニズムを活用してください。
    • フックハンドラ内で再試行を実装しないでください。再配信はエクソーラによって処理されます。
    • ウェブフックの配信日時と処理ステータスをログに記録してください。また、5xx エラーの急増や再配信の発生に対するアラートを設定してください。
    • ウェブフックに含まれる相関IDを、自システムのログやモニタリングシステム(APM)に伝播させてください。
    • エラーログの記録とモニタリングをセットアップしてください。修復不可能な障害については、ジョブをデッドレターキュー(DLQ)に移動させてください。また、冪等性メカ ニズムによって保護された、イベントを再実行するための安全なツールを開発してください。

    実装例

    購入成功 - 初回試行でアイテムが付与されました:

    購入

    重複配信(初回試行時にパートナーのタイムアウトが発生しました)

    タイムアウト

    返金:

    返金

    パートナー側の機能障害:

    パートナー側の機能障害

    よくある質問

    ウェブフックプロトコルにはHTTPSを使用する必要がありますか?

    はい。

    複数のURLで支払いに関するウェブフックを受信することはできますか?

    いいえ。支払いウェブフックはサーバー間プロトコルを使用しており、プロジェクト設定で指定された単一のURLに送信されます。ゲーム、ウェブサイト、またはモバイルアプリケーションで通知を受け取りたい場合は、お客様のサーバー側でウェ ブフックの送信設定を行い、エクソーラとお使いのゲーム間でデータを渡すようにしてください。また、開発者コンソールからウェブフックのテストを行うことも可能です。

    Note

    If you are testing the integration locally, POST requests from Xsolla don’t reach URLs like http://localhost:3000/my-webhook-endpoint. Use services such as Ngrok that allow you to create a tunnel for external access, enabling you to receive requests from Xsolla locally. E.g., you can read more about this in the ngrok documentation.

    エクソーラからの通知がウェブフックURLに送信されなかったのはなぜですか?

    ウェブフックサーバーが、HTTPリクエストのPOSTおよびGETメソッドをサポートしていることを確認してください。

    処理中のトランザクションIDの重複を防ぐにはどうすればよいですか?

    external IDを使用してください。これはお客様のシステム内の注文に割り当てられた、ゲーム内でのトランザクションIDを指します。エクソーラ側では、external IDはトランザクションIDにリンクされており、これによりエクソーラは同じトランザクションに対する重複支払いを防止できます。設定の詳細については、こちらのドキュ メントを参照してください。

    ウェブフックを利用する際の適用事例はありますか?

    以下の対応を推奨します:

    • 署名検証後、直ちに「204」または「200」を返します。
    • 変更を加えず、生のリクエスト本文に対してウェブフックの署名を検証します。
    • すべての演算に対して冪等性を実装します。
    • すべてのイベントをログに記録し、エラーモニタリングをセットアップします。
    • URLに機密データを含めることを避け、エラーメッセージに技術的な詳細情報を記載したりしないでください。

    詳細については、適用事例セクションを参照してください。

    ウェブフック統合のチェックリスト

    ウェブフックを正しく機能させるために、本番環境へのリリース前に以下のことが整っていることを確認してください:

    • HTTPSが使用されています。
    • ウェブフックの署名検証が、リクエスト本文を変更することなく、生データに対して実装されています。
    • 署名が確認されるとすぐに「204/200」という応答が返されます。
    • すべての操作に対して、冪等性が実装されています。
    • エラーログの記録とモニタリングが設定されています。
    • 機密データはURLには含まれず、技術的な詳細はエラーメッセージにも表示されません。
    • エクソーラの再試行ロジックに基づき、ウェブフックの再試行がサポートされています。
    • 統合プロセス全体がドキュメント化されています。

    ウェブフックリスト

    注意

    通知タイプはnotification_typeパラメータで送信されます。

    ウェブフック通知タイプ説明
    ユーザー検証user_validationユーザーがゲーム内に存在するかどうかを確認するために送信されます。
    ユーザー検索user_searchパブリックユーザーIDに基づいてユーザー情報を取得するために送信されます。
    決済paymentユーザーが決済を完了した場合に送信されます。
    返金refund何らかの理由で決済をキャンセルする必要がある場合に送信されます。
    一部返金partial_refund何らかの理由で決済を一部キャンセルする必要がある場合に送信されます。
    支払いが拒否されましたps_declined支払いが決済システムによって拒否されたときに送信されます。
    AFSが拒否したトランザクションafs_rejectAFSチェック中にトランザクションが拒否された場合に送信されます。
    AFSが更新したトランザクションafs_black_listAFSブロックリストが更新される場合に送信されます。
    作成されたサブスクリプションcreate_subscriptionユーザーがサブスクリプションを作成する場合に送信されます。
    更新されたサブスクリプションupdate_subscriptionサブスクリプションが更新または変更された場合に送信されます。
    キャンセルされたサブスクリプションcancel_subscriptionサブスクリプションがキャンセルされた場合に送信されます。
    非更新サブスクリプションnon_renewal_subscriptionステータスが非更新に設定される場合に送信されます。
    決済アカウントを追加するpayment_account_addユーザーが支払いアカウントを追加または保存した場合に送信されます。
    決済アカウントを削除するpayment_account_removeユーザーが保存済みアカウントから決済アカウントを削除する場合に送信されます。
    ウェブショップでのユーザー検証-ウェブショップのサイトから送信され、ゲーム内にユーザーが存在するかどうかを確認します。
    パートナー側でのカタログ個人用設定partner_side_catalogユーザーがストアと直接交信する時に送信されます。
    注文支払い完了order_paid注文が支払われたときに送信されます。
    注文キャンセルorder_canceled注文がキャンセルされたときに送信されます。
    紛争dispute新しい紛争手続きが開かれたときに送信されます。
    OpenAPI記述をダウンロード
    言語
    サーバー
    https://api.xsolla.com/merchant/v2
    Mock server
    https://xsolla.redocly.app/_mock/ja/webhooks

    ユーザー検証

    Webhook

    決済ソリューション

    Webhook

    まとめたウェブフック

    Webhook

    個別のウェブフック

    Webhook

    個人用設定ウェブフック

    Webhook

    不正決済防止

    Webhook

    サブスクリプション

    Webhook