コンテンツへスキップ

ウェブフック (1.0)

概要

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

イベントの例:

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

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

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

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

支払い処理ウェブフック

注意

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

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

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

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

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

ログイン任意

イベント情報の受信:

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

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

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

ウェブフックの操作が必要な製品やソリューションを使用している場合、パブリッシャーアカウントでウェブフックを有効化してテストし、その処理をセットアップします。特定のイベントが発生する時、ウェブフックが順次送信されます。したがって、1つのウェブフックを処理しない 場合、その後のウェブフックは送信されません。必要なウェブフックのリストは以下の通りです。

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

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

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

2025年1月22日以降にパブリッシャーアカウントに登録した場合は、注文支払い完了order_paid)と注文キャンセルorder_canceled)ウェブフックですべての情報を受け取ります。この場合、支払いpayment)と返金refund)ウェブフックを処理する必要はありません。

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

2025年1月22日以前にパブリッシャーアカウントに登録した場合は、以下のウェブフックを受け取ります:

すべての受信ウェブフックを処理する必要があります。まとめたウェブフックを受信する新しいオプションに切り替えるには、カスタマーサクセスマネージャーにご連絡いただく か、csm@xsolla.comまで電子メールをお送りください。

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

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

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

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

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

アイテムカタログの個人用設定がアプリケーション側で実装されている場合は、パートナー側でのカタログ個人用設定ウェブフックの処理を設定します。

注意

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

サブスクリプション

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

  • ユーザーの検証user_validation) — 決済プロセスのさまざまな段階で送信され、ユーザーがゲームに登録されていることを確認します。
  • 決済payment) — 注文が支払われたときに送信され、決済データとトランザクションの詳細が含まれます。
  • 作成されたサブスクリプションcreate_subscription) — 決済ウェブフックが正常に処理されたとき、またはユーザーが試用期間付きのサブスクリプ ションを購入したときに送信されます。これは購入したサブスクリプションの詳細とユーザーデータを含んでいます。ウェブフックデータを使用して、ユーザーにサブスクリプシ ョンを追加します。
  • 更新されたサブスクリプションupdate_subscription) — サブスクリプションが更新または変更されたとき、決済ウェブフックが正常に 処理されたときに送信されます。購入したサブスクリプションの詳細とユーザーデータが含まれます。ウェブフックデータを使用して、ユーザーのサブスクリプションを延長する か、サブスクリプションパラメータを変更します。
  • 返金refund) — 注文がキャンセルされたときに送信され、キャンセルされた決済データとトランザクションの詳細が含まれます。
  • キャンセルされたサブスクリプションcancel_subscription) — 返金ウェブフックが正常に処理されたとき、またはサブスクリプションが別の理由でキャンセ ルされたときに送信されます。サブスクリプションとユーザーデータに関する情報を含んでいます。ウェブフックデータを使用して、ユーザーから購入したサブスクリプションを 差し引きます。

パブリッシャーアカウントでウェブフックをセットアップする

一般設定

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

  1. パブリッシャーアカウントのプロジェクトで、プロジェクト設定 > ウェブフックセクションに移動します。
  2. ウェブフックサーバーフィールドには、ウェブフックを受信したいサーバーのURLを https://example.com 形式で指定します。ウェブフックをテストするツールで見つけたURLを指定することもできます。

注意。

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

  1. プロジェクトのウェブフックに署名するための秘密鍵は、デフォルトで生成されます。新しい秘密鍵を生成したい場合は、更新アイコンをクリックします。
  2. ウェブフックを有効にする」をクリックします。

Enable 
webhooks

注意

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

注意

異なるURLに同時にウェブフックを送信することはできません。パブリッシャーアカウントでは、まずテスト用のURLを指定し、それを実際のURLに置き換えることができます。

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

  1. パブリッシャーアカウントのプロジェクトで、プロジェクト設定 > ウェブフックセクションに移動します。
  2. ウェブフックを無効にする」をクリックします。

詳細設定

ウェブフックについては決済ソリューションとストアセクションでは、詳細設定が利用できます。「ウェブフックを取得」ボタンをクリックする と、自動的に一般設定ブロックの下に表示されます。

注意

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

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

トグル説明
保存された決済アカウントに関する情報を表示する保存された決済方法に関する情報は、payment_accountカスタムオブジェクト。
保存された決済方法による取引に関する情報を表示する

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

  • saved_payment_method:
    • 0 — 保存された決済方法は使用されませんでした
    • 1 — 決済方法は現在の支払い時に保存されました
    • 2 — 保存された決済方法は使用されませんでした
  • payment_type:
    • 1 — 一回払い
    • 2 — 定期支払い
注文オブジェクトをウェブフックに追加する注文に関する情報は、決済ウェブフックのorderオブジェクトに渡されます。
機密データは含まず、必要なユーザーパラメータのみを送信する

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

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

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

  • card_binパラメータの最初の6桁
  • card_suffixパラメータの最後の4桁
カードブランドを表示する決済に使用したカードのブランド。例えば、MastercardやVisaなど。

高度な設定

パブリッシャーアカウントでウェブフックをテストする

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

ウェブフックが正常にセットアップした場合、ウェブフック設定セクションの下にウェブフックのテストセクションが表示されます。

ウェブフックのテストセクション

パブリッシャーアカウントのテストセクションは、ウェブフック受信オプションによって異なります。

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

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

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

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

注意

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

例:

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

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

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

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

テストエラー

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

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

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

テストするには:

  1. ウェブフックのテストセクションで、「決済ソリューションとストア」タブに移動します。

  2. ドロップダウンメニューで、アイテムのタイプを選択します。選択したタイプのアイテムがパブリッシャーアカウントに設定されていない場合は、以下をクリックします:

    • 接続 – このタイプのアイテムを含むモジュールが接続されていない場合
    • 構成 以前にモジュールを接続したが、セットアップを完了していない場合
      ボタンをクリックすると、選択したアイテムのタイプに対応するパブリッシャーアカウントのセク ションにリダイレクトされます。アイテムを作成したら、ウェブフックのテストセクションに戻り、次のステップに進みます。
  3. 必要なフィールドに入力します:

    1. ドロップダウンリストからアイテムのSKUを選択し、数量を指定します。同じタイプのアイテムを複数選択するには、「 + 」をクリックして新しい行に追加します。
    2. ユーザーID — テスト時には、文字と数字の任意の組み合わせを使用できます。
    3. パブリックユーザーID — ユーザーに知られているID(メールアドレスやニックネームなど)。このフィールドは、プロジェクトで「ペイステーション > 設定」でパブリックユーザーIDが有効になっている場合に表示されます。
    4. エクソーラ注文IDフィールドに任意の値を入力します。
    5. エクソーラ請求書ID — エクソーラ側のトランザクションID。テスト時には任意の数値を使用できます。
    6. 請求書ID — ゲーム側のトランザクションID。 テスト時には、文字と数字の任意の組み合わせを使用できます。これは支払いを成功させるために必要なパラメータではありませんが、これを渡すことで、ゲーム側のトランザク ションIDをエクソーラ側のトランザクション ID にリンクできます。
    7. 金額 — 支払い金額。テスト時には任意の数値を使用できます。
    8. 通貨 — ドロップダウンリストから通貨を選択します。
    9. ウェブフックをテスト」をクリックします。

    指定されたデータを含むユーザー検証注文支払い完了および注文キャンセルウェブフックが、指定されたURLに送信されます。各ウェブフックタイプのテスト結果は、「ウェブフックをテス 」ボタンの下に表示されます。

    プロジェクトでパブリックユーザー IDが有効になっている場合は、ユーザー検索チェックの結果も表示されます。

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

    支払いテストセクション

    サブスクリプション

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

    注意

    パブリッシャーアカウントでは、基本的なユーザー検証および支払いウェブフックのみをテストできます。他のウェブフックタイプをテストするには、次の場所に移動します:

    注意

    ウェブフックをテストするには、パブリッシャーアカウント > サブスクリプション > サブスクリプションプランセクションで、少なくとも1つのサブスクリプションプランが作成されている必要があります。

    テストするには:

    1. テストセクションで、サブスクリプションタブに移動します。

  4. 必要なフィールドに入力します:
    1. ユーザーID — テスト時には、文字と数字を任意に組み合わせて使用できます。
    2. エクソーラインボイスID — エクソーラ側のトランザクションID。テスト時には、任意の数値を使用できます。
    3. パブリックユーザーID — メールアドレスやニックネームなど、ユーザーに知られているID。このフィールドは、「ペイステーション > 設定 > 追加設定」セクションでプロジェクトのパブリックユーザーIDが有効になっている場合に表示されます。
    4. 通貨— ドロップダウンリストから通貨を選択します。
    5. プランID — サブスクリプションプラン。ドロップダウンリストからプランを選択します。
    6. サブスクリプション製品 — ドロップダウンリストから製品を選択します(任意)。
    7. 金額- 支払金額。テスト時には、任意の数値を使用できます。
    8. インボイスID — ゲーム側のトランザクションID。テスト時には、文字と数字を任意に組み合わせて使用できます。これは決済を成功させるために必要なパラメータではありませんが、エクソー ラ側のトランザクションIDとあなた側のトランザクションIDをリンクさせるために渡すことができます。
    9. 試用期間試用期間 のないサブスクリプションの購入をテストする、またはサブスクリプションの更新をテストするには、値0を指定します。
  5. ウェブフックをテストする」をクリックします。
  6. 指定したURLに、データが入力されたウェブフックを受け取ります。各ウェブフックのテスト結果は、成功したシナリオとエラーが発生したシナリオの両方で、「ウェブ フックをテストする」ボタンの下に表示されます。

    ウェブフックリスナー

    ウェブフックリスナーは、指定されたURLアドレスでウェブフックを受信し、署名を生成しエクソーラウェブフックサーバーに応答を送信することができるプログラムコードです。

    注意

    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

    もし「ログイン」製品を統合している場合、以下のIPアドレスからのウェブフックの処理も追加してください:

    • 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@example.com",
            "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@example.com",
              "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;
              }
          }
      }

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

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

      • 成功した応答の場合は、200201、または204HTTPコード。
      • 400HTTPコードは問題の説明を含む、指定されたユーザーが見つからないか、無効な署名が渡 された場合に送信されます。ウェブフックハンドラーは、サーバーで一時的な問題が発生した場合に5xxHTTPコードを返すこともあります。

      エクソーラサーバーが 注文支払い完了注文キャンセルへのレスポンスを受け取らなかった場合、または5xxコードのレスポンスを受け取った場合、ウェブフックは以下の スケジュールに従って再送されます:

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

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

      決済返金ウェブフックの再試行ロジックは、対応するウェーブフックページに記載されています。

      注意

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

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

      エクソーラサーバーがユーザー認証ウェブフックへの応答を受信しなかった場合、または400または5xxのコードで応答を受信した場合、ユーザー認証ウェブフックは再送信されません。この場合、ユーザーにはエラーが表示され、支払い注文支払い完了ウェブフックは送信されません。

      エラー

      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"
          }
      }

      ウェブフックリスト

      注意

      通知タイプは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記述をダウンロード
      言語
      サーバー
      Mock server
      https://xsolla.redocly.app/_mock/ja/webhooks/
      https://api.xsolla.com/merchant/v2/

      ユーザー検証

      Webhook

      決済ソリューション

      Webhook

      まとめたウェブフック

      Webhook

      個別のウェブフック

      Webhook

      個人用設定ウェブフック

      Webhook

      パートナー側でのカタログ個人用設定Webhook

      リクエスト

      エクソラは、ユーザーがストアと対話するときに、ユーザーとプロジェクトのパラメータを含む partner_side_catalogウェブフックをウェブフックURLに送信します。

      応答として、ユーザが利用可能なitem_idまたはアイテムSKUのリストを返します。この場合、特定のユーザーが特定の商品を指定回数購入 できるという情報を含めることも可能です。この機能により、ユーザーがカートに追加して購入できる商品の数やタイプを制御することができます。

      partner_side_catalogウェブフックの処理時間は3秒未満にすることをお勧めします。

      ボディapplication/json
      userobject必須

      ユーザーの詳細(オブジェクト)。

      user.​countrystring(user.country)

      ユーザーの国。2文字の国コード(大文字)は、ISO 3166-1 alpha-2に従って使用されます。

      user.​currencystring(currency)

      通貨。ISO 4217に基づく3文字の通貨コード。

      user.​localestring(locale)

      ユーザー言語。2文字の小文字言語コード

      user.​user_idstring必須

      ユーザーID。ユーザーが認証されていない場合は、値nullが送信されます。

      curl -v 'https://your.hostname/your/uri' \
      -X POST \
      -H 'accept: application/json' \
      -H 'content-type: application/json' \
      -H 'authorization: Signature d90d319f05df7b0f86d2485f48e7079f0f752523' \
      -d '{
              "notification_type": "partner_side_catalog",
              "user": {
                  "user_id": "12345",
                  "country": "US"
              }
          }'

      レスポンス

      ユーザーが購入できるアイテムのリストと、特定のユーザーが特定のアイテムを購入できる回数を返します。

      ボディapplication/jsonArray [
      Any of:
      One of:
      contentArray of objects(bundle_content)

      カタログに表示されるバンドルコンテンツを含むオブジェクトの配列。これはバンドルタイプpartner_side_contentでのみ使用できます。

      date_fromstring(date_from)

      アイテムが販売可能になる日付。RFC 3339標準に従ってフォーマットされています。

      date_untilstring(date_until)

      アイテムが販売不可能になる日付。RFC 3339標準に従ってフォーマットされています。

      descriptionstring(description)<= 255 characters

      カタログに表示されるアイテムの説明。アイテムに保存されている説明を上書きします。

      image_urlstring(uri)(image_url)<= 255 characters

      カタログに表示される画像URLです。アイテムに保存されている画像URLを上書きします。HTTPSプロトコルのみがサポートされています。もしURLが要件を満たさない場合、無視されます。

      json_attributesobject(json)(json_attributes)<= 500 characters

      アイテムの属性と値を含むJSONオブジェクト。アイテムに保存されているcustom_attributesを上書きします。JSONが要件を満たさない場合や、最大長を超える場合は無視されます。

      namestring(name)<= 255 characters

      カタログに表示されるアイテム名。これはアイテムに保存されている名前を上書きします。

      skustring(sku)必須

      パブリッシャーアカウントで指定したアイテムの一意のID。 skuまたはitem_idのいずれかを渡す必要があります。

      quantityinteger(quantity)非推奨

      1人のユーザーが購入できるアイテムの数。無制限に設定するには、nullを渡します。アイテムを購入不可にしてカタログに表示するには、0を渡します。

      このパラメータを使用すると、カタログに購入制限情報が表示されません。カタログに購入制限情報(例:7/10)を表示するには、quantityの代わりにavailabletotalを渡します。

      ]
      レスポンス
      application/json
      [ { "date_from": "2024-08-11T23:59:59+08:00", "date_until": "2024-08-12T23:59:59+08:00", "quantity": 0, "sku": "com.xsolla.boots_1" }, { "date_from": "2024-08-11T23:59:59+08:00", "date_until": "2024-08-12T23:59:59+08:00", "quantity": null, "sku": "com.xsolla.sword_1" }, { "quantity": 1, "sku": "com.xsolla.helmet_1" }, { "content": [], "description": "Custom description", "image_url": "https://example.com/image.png", "json_attributes": {}, "name": "Custom name", "quantity": null, "sku": "com.xsolla.custom_item_1" }, { "available": 7, "sku": "com.xsolla.limited_item_1", "total": 10 } ]

      不正決済防止

      Webhook

      サブスクリプション

      Webhook