ウェブフック (1.0)
ウェブフックは、システムで発生するイベントに関する通知です。特定のイベントが発生すると、エクソーラはHTTPリクエストを送信し、イベントデータがアプリケーション に送信されます。通常、これはJSON形式のPOSTリクエストです。
イベントの例:
- アイテムカタログとのユーザーインタラクション
- 注文の決済またはキャンセル
設定されたイベントが発生すると、エクソーラはウェブフック経由でシステムにそれを通知します。その結果、次のようなアクションを実行できます:
- ユーザーの残高を補充する
- 支払いを返金する
- ユーザーアカウントから新しいアイテムをクレジットまたはデビットする
- サブスクリプションの提供を開始する
- 詐欺の疑いがある場合にユーザーをブロックする
支払い処理ウェブフックのワークフローの例:
注意
使用されるソリューションとその統合のタイプに応じて、ウェブフックのセットとインタラクションのシーケンスは、提供された例とは異なる場合があります。
エクソーラウェブフック統合のビデオガイド:
エクソーラ製品およびソリューションを使用する場合のウェブフック設定:
| 製品/ソリューション | 必須/任意 | ウェブフックは何に使用されますか |
|---|---|---|
| 決済ソリューション | 必須 |
|
| インゲームストア | 必須 |
|
| ゲーム販売 | 任意 | ゲームキーの販売では、ユーザーの検証やアイテムの付与は必要ありません。支払いや注文キャンセルなどのイベントに関する情報を受け取りたい場合は、ウェブフックを接続することができます。 ウェブフックを接続する場合は、すべての受信した必要なウェブフックを処理することが重要です。 |
| サブスクリプション | 任意 | サブスクリプションの作成、更新、またはキャンセルに関する情報を受け取ります。または、API経由で情報をリクエストすることもできます。 |
| ウェブショップ | 必須 |
|
| デジタル配信ソリューション | 必須 |
ウェブフックの設定に関する詳細情報は、デジタル配信ソリューションのドキュメンテーションを参照してください。 |
| ログイン | 任意 | イベント情報の受信:
ウェブフックの設定の詳細については、ログインのドキュメンテーションを参照してください。 |
ウェブフックの操作が必要な製品やソリューションを使用している場合、パブリッシャーアカウントでウェブフックを有効化してテストし、その処理をセットアップします。特定のイベントが発生する時、ウェブフックが順次送信されます。したがって、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) — 返金ウェブフックが正常に処理されたとき、またはサブスクリプションが別の理由でキャンセ ルされたときに送信されます。サブスクリプションとユーザーデータに関する情報を含んでいます。ウェブフックデータを使用して、ユーザーから購入したサブスクリプションを 差し引きます。
ウェブフックの受信を有効にするには:
- パブリッシャーアカウントのプロジェクトで、プロジェクト設定 > ウェブフックセクションに移動します。
- ウェブフックサーバーフィールドには、ウェブフックを受信したいサーバーのURLを
https://example.com形式で指定します。ウェブフックをテストするツールで見つけたURLを指定することもできます。
注意。
データ転送にはHTTPSプロトコルが使用されます。HTTPプロトコルはサポートされていません。
- プロジェクトのウェブフックに署名するための秘密鍵は、デフォルトで生成されます。新しい秘密鍵を生成したい場合は、更新アイコンをクリックします。
- 「ウェブフックを有効にする」をクリックします。

注意
ウェブフックをテストするには、webhook.siteのような専用のウェブサイトか、ngrokのようなプラットフォームを選択できます。
注意
異なるURLに同時にウェブフックを送信することはできません。パブリッシャーアカウントでは、まずテスト用のURLを指定し、それを実際のURLに置き換えることができます。
ウェブフックの受信を無効にするには:
- パブリッシャーアカウントのプロジェクトで、プロジェクト設定 > ウェブフックセクションに移動します。
- 「ウェブフックを無効にする」をクリックします。
ウェブフックについては決済ソリューションとストアセクションでは、詳細設定が利用できます。「ウェブフックを取得」ボタンをクリックする と、自動的に一般設定ブロックの下に表示されます。
注意
詳細設定が表示されない場合は、一般設定でウェブフック受信が接続されていることを確認し、テスト > 決済ソリューションとストアタブにいることを確認してください。
このセクションでは、ウェブフックでの追加情報の受信を設定できます。これを行うには、対応するスイッチをアクティブポジションに設定します。各権限の行は、設定の変更に よって影響を受けるウェブフックを示します。
| トグル | 説明 |
|---|---|
| 保存された決済アカウントに関する情報を表示する | 保存された決済方法に関する情報は、payment_accountカスタムオブジェクト。 |
| 保存された決済方法による取引に関する情報を表示する | 情報は、ウェブフックの以下のカスタムパラメータに渡されます:
|
| 注文オブジェクトをウェブフックに追加する | 注文に関する情報は、決済ウェブフックのorderオブジェクトに渡されます。 |
| 機密データは含まず、必要なユーザーパラメータのみを送信する | ウェブフックでは、ユーザーに関する次の情報のみが渡されます:
|
| カスタムパラメータを送信する | カスタムトークンパラメータに関する情報は、ウェブフックで渡されます。 |
| カードのBINとサフィックスを表示する | ウェブフックには、銀行カード番号に関する以下の情報が渡されます:
|
| カードブランドを表示する | 決済に使用したカードのブランド。例えば、MastercardやVisaなど。 |

ウェブフックをテストすると、ユーザー側とエクソーラ側の両方でプロジェクトが正しく設定されていることを確認できます。
ウェブフックが正常にセットアップした場合、ウェブフック設定セクションの下にウェブフックのテストセクションが表示されます。

パブリッシャーアカウントのテストセクションは、ウェブフック受信オプションによって異なります。
まとめたウェブフックを受信する場合:
個別のウェブフックを受信する場合:
注意
テストセクションにテストがパスしていないという警告が表示された場合は、ウェブフックリスナーのウェブフック応答設定を確認します。テストでのエラーの理由はテスト結果に示されます。
例:
テストには、専門サイトwebhook.siteを使用します。
「無効な署名に対する応答のテスト」セクションにエラーが表示されます。
エクソーラが間違った署名を持つウェブフックを送信し、ハンドラーがINVALID_SIGNATUREエラーコードを指定する4xx HTTPコードで応答することを期待しているために発生します。
webhook.siteは、署名が間違ったウェブフックを含むすべてのウェブフックに応答して200 HTTPコードを送信します。期待される4xx HTTPコードが得られないため、テスト結果にエラーが表示されます。

まとめたウェブフックを使用したシナリオのテストプロセスを以下に説明します。
「決済ソリューションとストア」タブでは、次のウェブフックをテストできます:
- ユーザー検証(
user_validation) - 注文の支払いが完了しました(
order_paid) - 注文キャンセル(
order_canceled)
テストするには:
ウェブフックのテストセクションで、「決済ソリューションとストア」タブに移動します。
ドロップダウンメニューで、アイテムのタイプを選択します。選択したタイプのアイテムがパブリッシャーアカウントに設定されていない場合は、以下をクリックします:
- 接続 – このタイプのアイテムを含むモジュールが接続されていない場合
- 構成 – 以前にモジュールを接続したが、セットアップを完了していない場合
ボタンをクリックすると、選択したアイテムのタイプに対応するパブリッシャーアカウントのセク ションにリダイレクトされます。アイテムを作成したら、ウェブフックのテストセクションに戻り、次のステップに進みます。
必要なフィールドに入力します:
- ドロップダウンリストからアイテムのSKUを選択し、数量を指定します。同じタイプのアイテムを複数選択するには、「 + 」をクリックして新しい行に追加します。
- ユーザーID — テスト時には、文字と数字の任意の組み合わせを使用できます。
- パブリックユーザーID — ユーザーに知られているID(メールアドレスやニックネームなど)。このフィールドは、プロジェクトで「ペイステーション > 設定」でパブリックユーザーIDが有効になっている場合に表示されます。
- エクソーラ注文IDフィールドに任意の値を入力します。
- エクソーラ請求書ID — エクソーラ側のトランザクションID。テスト時には任意の数値を使用できます。
- 請求書ID — ゲーム側のトランザクションID。 テスト時には、文字と数字の任意の組み合わせを使用できます。これは支払いを成功させるために必要なパラメータではありませんが、これを渡すことで、ゲーム側のトランザク ションIDをエクソーラ側のトランザクション ID にリンクできます。
- 金額 — 支払い金額。テスト時には任意の数値を使用できます。
- 通貨 — ドロップダウンリストから通貨を選択します。
「ウェブフックをテスト」をクリックします。
プロジェクトでパブリックユーザー IDが有効になっている場合は、ユーザー検索チェックの結果も表示されます。
各ウェブフックについて、成功したシナリオとエラーが発生したシナリオの両方の処理を設定する必要があります。

「サブスクリプション」タブでは、次のウェブフックをテストできます:
注意
パブリッシャーアカウントでは、基本的なユーザー検証および支払いウェブフックのみをテストできます。他のウェブフックタイプをテストするには、次の場所に移動します:
注意
ウェブフックをテストするには、パブリッシャーアカウント > サブスクリプション > サブスクリプションプランセクションで、少なくとも1つのサブスクリプションプランが作成されている必要があります。
テストするには:
- テストセクションで、サブスクリプションタブに移動します。
- ユーザーID — テスト時には、文字と数字を任意に組み合わせて使用できます。
- エクソーラインボイスID — エクソーラ側のトランザクションID。テスト時には、任意の数値を使用できます。
- パブリックユーザーID — メールアドレスやニックネームなど、ユーザーに知られているID。このフィールドは、「ペイステーション > 設定 > 追加設定」セクションでプロジェクトのパブリックユーザーIDが有効になっている場合に表示されます。
- 通貨— ドロップダウンリストから通貨を選択します。
- プランID — サブスクリプションプラン。ドロップダウンリストからプランを選択します。
- サブスクリプション製品 — ドロップダウンリストから製品を選択します(任意)。
- 金額- 支払金額。テスト時には、任意の数値を使用できます。
- インボイスID — ゲーム側のトランザクションID。テスト時には、文字と数字を任意に組み合わせて使用できます。これは決済を成功させるために必要なパラメータではありませんが、エクソー ラ側のトランザクションIDとあなた側のトランザクションIDをリンクさせるために渡すことができます。
- 試用期間。試用期間
のないサブスクリプションの購入をテストする、またはサブスクリプションの更新をテストするには、値
0を指定します。
指定したURLに、データが入力されたウェブフックを受け取ります。各ウェブフックのテスト結果は、成功したシナリオとエラーが発生したシナリオの両方で、「ウェブ フックをテストする」ボタンの下に表示されます。
ウェブフックリスナーは、指定されたURLアドレスでウェブフックを受信し、署名を生成し、エクソーラウェブフックサーバーに応答を送信することができるプログラムコードです。
注意
Pay Station PHP SDKライブラリには、ウェブフックを処理するための既製のクラスが含まれています。
アプリケーション側で、以下のIPアドレスからのウェブフックの受信を実装してください:
185.30.20.0/24185.30.21.0/24185.30.22.0/24185.30.23.0/2434.102.38.17834.94.43.20735.236.73.23434.94.69.4434.102.22.197
もし「ログイン」製品を統合している場合、以下のIPアドレスからのウェブフックの処理も追加してください:
34.94.0.8534.94.14.9534.94.25.3334.94.115.18534.94.154.2634.94.173.13234.102.48.3035.235.99.24835.236.32.13135.236.35.10035.236.117.164
制限:
- アプリケーションのデータベースに同じIDで成功したトランザクションが複数存在することは許可されていません。
- ウェブフックリスナーがデータベースにすでに存在するIDを持つウェブフックを受信した場合、このトランザクションの以前の処理結果を返す必要があります。ユーザーに重複 した購入をクレジットし、データベースに重複したレコードを作成することは推奨されません。
安全なデータ転送を確保するには、ウェブフックが実際にエクソーラサーバーから送信され、転送中に改ざんされていないことを確認する必要があります。これを行うには、受信 したリクエスト本文のペイロードに基づいて独自の署名を生成し、それを着信リクエストのauthorizationヘッダーで提供される署名と比較してください。両方 の署名が一致する場合、そのウェブフックは正規のものであり、安全に処理できます。
検証ステップ:
着信ウェブフックリクエストの
authorizationヘッダーから署名を取得します。ヘッダーの形式はSignature <signature_value>です。ウェブフックのリクエスト本文をJSON形式で取得してください。
注意
JSONペイロードは、受信したものをそのまま使用してください。ペイロード をパースしたり再エンコードしたりしないでください。フォーマットが変更され、署名検証が失敗する原因となります。
比較用に独自の署名を生成します:
- JSONペイロードとプロジェクトの秘密鍵を連結します。秘密鍵は文字列の末尾に追加してください。
- 結果の文字列にSHA-1暗号ハッシュ関数を適用します。結果は小文字の16進数文字列になります。
生成した署名を
authorizationヘッダーからの署名と比較してください。両者が一致すれば、ウェブフックは信頼できるものです。
以下に、署名生成の実装例を各種プログラミング言語で示します: C#、C++、Go、PHP、Node.js。
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 -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"
}
}'注
このコードサンプルは、.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;
}
}注
Convert.ToHexStringメソッドを使用するには、.NET 5.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;
}
}注
.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);
}#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;
}
};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
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));
}
}
?>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コード。 400HTTPコードは問題の説明を含む、指定されたユーザーが見つからないか、無効な署名が渡 された場合に送信されます。ウェブフックハンドラーは、サーバーで一時的な問題が発生した場合に5xxHTTPコードを返すこともあります。
- 5分間隔で2回の試行
- 15分間隔で7回の試行
- 60分間隔で10回の試行
ウェブフックの送信は、最初の送信から12時間以内に最大20回まで試行されます。
注意
以下の条件がすべて満たされている場合でも、支払いはユーザーに返金されます:
- 返金はエクソーラによって開始されました。
- ウェーブフックに対する応答として、
4xxステータスコードが返された、またはすべての再試行後に応答がなかった、あるいは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_reject | AFSチェック中にトランザクションが拒否された場合に送信されます。 |
| AFSが更新したトランザクション | afs_black_list | AFSブロックリストが更新される場合に送信されます。 |
| 作成されたサブスクリプション | create_subscription | ユーザーがサブスクリプションを作成する場合に送信されます。 |
| 更新されたサブスクリプション | update_subscription | サブスクリプションが更新または変更された場合に送信されます。 |
| キャンセルされたサブスクリプション | cancel_subscription | サブスクリプションがキャンセルされた場合に送信されます。 |
| 非更新サブスクリプション | non_renewal_subscription | ステータスが非更新に設定される場合に送信されます。 |
| 決済アカウントを追加する | payment_account_add | ユーザーが支払いアカウントを追加または保存した場合に送信されます。 |
| 決済アカウントを削除する | payment_account_remove | ユーザーが保存済みアカウントから決済アカウントを削除する場合に送信されます。 |
| ウェブショップでのユーザー検証 | - | ウェブショップのサイトから送信され、ゲーム内にユーザーが存在するかどうかを確認します。 |
| パートナー側でのカタログ個人用設定 | partner_side_catalog | ユーザーがストアと直接交信する時に送信されます。 |
| 注文支払い完了 | order_paid | 注文が支払われたときに送信されます。 |
| 注文キャンセル | order_canceled | 注文がキャンセルされたときに送信されます。 |
| 紛争 | dispute | 新しい紛争手続きが開かれたときに送信されます。 |
リクエスト
支払いがキャンセルされると、エクソーラはキャンセルされた取引の詳細を、refundタイプのウェブフックとしてウェブフックURLに送信します。
ウェブフックの再試行メカニズムは、誰が返金を開始したかによって異なります:
- 返金がお客様側で開始された場合、ウェブフックは再送されません。ウェブフックへの応答に関係なく、支払いはユーザーに返金されます。
- 返金がサードパーティ(例:決済システム、またはエクソーラカスタマーサポートチームなど)によって開始され、かつウェブフックへの応答として
5xxステータスコード が返された場合、ウェブフックは間隔を空けながら再送されます。最大再試行回数は、最初の試行から48時間以内に12回です。
返金プロセスに関する詳細な情報は、こちらの説明.をご参照ください。
注意
以下の条件がすべて満たされている場合でも、支払いはユーザーに返金されます:
- 返金はエクソーラによって開始されました。
- ウェーブフックに対する応答として、
4xxステータスコードが返された、またはすべての再試行後に応答がなかった、あるいは5xxステータスコードが返された場合。
パブリッシャーアカウン トでウェブフックURLを保存すると、ウェブフックで追加情報を受信するように設定することもできます。
注意
2025年1月22日以前にパブリッシャーアカウントに登録した場合は、プロジェクトで設定 > ウェブフック > テスト > 決済ソリューション > 詳細設定セクションでトグルを見つけます。
| トグル | 説明 |
|---|---|
| 保存された決済方法による取引に関する情報を表示する | 情報は、ウェブフックの以下のカスタムパラメータに渡されます:
|
返金コード:
| コード | 理由 | 説明 |
|---|---|---|
| 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 | 同じインボイスのトランザクションが重複しています。 |
支払明細(オブジェクト)。
トランザクションの詳細(オブジェクト)。
- Mock serverhttps://xsolla.redocly.app/_mock/ja/webhooks/refund
- https://api.xsolla.com/merchant/v2/refund
- CURL
- PHP
- Payload
curl -v 'https://your.hostname/your/uri' \
-X POST \
-d '{
"notification_type": "refund",
"settings": {
"project_id": 18404,
"merchant_id": 2340
},
"purchase": {
"subscription": {
"plan_id": "b5dac9c8",
"subscription_id": "10",
"date_create": "2014-09-22T19:25:25+04:00",
"currency": "USD",
"amount": 9.99
},
"checkout": {
"currency": "USD",
"amount": 50
},
"total":{
"currency": "USD",
"amount": 200
}
},
"user": {
"ip": "127.0.0.1",
"phone": "18777976552",
"email": "email@example.com",
"id": "1234567",
"name": "John Smith",
"country": "US"
},
"transaction": {
"id": 1,
"external_id": 1,
"dry_run": 1,
"agreement": 1
},
"refund_details": {
"code": 4,
"reason": "Potential fraud"
},
"payment_details": {
"sales_tax": {
"currency": "USD",
"amount": 0
},
"direct_wht": {
"currency": "USD",
"amount": 0.70
},
"xsolla_fee": {
"currency": "USD",
"amount": "10"
},
"payout": {
"currency": "USD",
"amount": "200"
},
"payment_method_fee": {
"currency": "USD",
"amount": "20"
},
"payment": {
"currency": "USD",
"amount": "230"
},
"repatriation_commission": {
"currency": "USD",
"amount": 10
}
}
}
}'リクエスト
ユーザーが保存アカウントから決済アカウントを削除すると、エクソーラは payment_account_removeタイプのウェブフックをウェブフックURLに送信します。このウェブフックを受信するには、カスタマーサクセスマネージャーにご連絡いただくか、csm@xsolla.comまで電子メールでお問い合わせください。
- Mock serverhttps://xsolla.redocly.app/_mock/ja/webhooks/remove-payment-account
- https://api.xsolla.com/merchant/v2/remove-payment-account
- CURL
- Payload
curl -v 'https://your.hostname/your/uri' \
-X POST \
-H 'accept: application/json' \
-H 'content-type: application/json' \
-H 'authorization: Signature d09695066c52c1b8bdae92f2d6eb59f5b5f89843' \
-d '{
"notification_type": "payment_account_remove",
"settings": {
"project_id": 18404,
"merchant_id": 2340
},
"user": {
"email": "email@example.com",
"id": "1234567",
"name": "John Smith"
},
"payment_account": {
"id": "12345678",
"name": "email@example.com",
"payment_method": "24",
"type": "paypal"
}
}'