ペイステーションとFirebase認証の組み合わせ使用について
Firebaseを使用してアプリケーションにユーザー認証を実装済みの場合、Firebase側で決済トークンを生成し、アプリケーションのクライアント側に渡して決済UIを開くことができます。
この統合オプションを使用すると、購入代金を支払うユーザーの国と通貨を決定するロジックを独自に実装する必要があります。
統合フロー:
- パブリッシャーアカウントに新規登録して、新しいプロジェクトを作成します。今後の手順で作成したプロジェクトのIDが必要です。
- カタログをセットアップします:
- エクソーラ側のアイテムカタログを作成します。アイテムを手動で追加するか、Google PlayまたはPlayFabからインポートすることができます。
- SDKを使用して、アプリケーションのクライアント側でカタログを取得して表示する機能を実装します。
- アイテム購入をセットアップします:
- アプリケーションのクライアント側で、Firebase Cloud Functionsを使って、ユーザーとアイテムのデータを含む注文を作成します。
- SDKを使用して、アプリケーションのクライアント側で決済UIを開くように実装します。
統合を完了し、実際の支払いを受け付けるようにするには、エクソーラとライセンス契約を結ぶ必要があります。
ライセンス契約はいつでも統合のどのステップでも署名できますが、審査プロセスには最大で3営業日かかる可能性があることを覚えておいてください。
ペイステーションとFirebase認証の組み合わせ使用例として、サンプルウェブアプリケーションを使用してください。サンプルアプリケーションのソースコードはGitHubで利用可能です。
プロジェクトを作成する
パブリッシャーアカウントの新規登録
パブリッシャーアカウントは、エクソーラの機能を構成し、アナリティクスや取引を処理するための主要なツールです。
登録時に指定された会社とアプリケーションに関するデータは、エクソーラとのライセンス契約のドラフトを作成し、あなたに適したソリューションを提案するために使用されます。データは後で変更することができますが、新規登録時に正しいデータを提供することで、ライセンス契約締結までのプロセスが迅速化されます。
新規登録するには、パブリッシャーアカウントにアクセスし、アカウントを作成してください。
パブリッシャーアカウントのパスワードはラテン文字、数字、特殊文字で構成でき、少なくとも以下を含む必要があります:
- 8文字以上
- 1桁
- 大文字1つ
- 小文字1つ
パスワードのセキュリティを確保するために、以下をことを推奨します:
- 少なくとも90日に1回はパスワードを変更する
- アカウントの過去4回のパスワードと一致しない新しいパスワードを使用する
- 他の場所で使用されているパスワードと一致しない固有のパスワードを使用する
- 簡単にアクセスできる場所にパスワードを保存しない
- パスワードマネージャーを使用してパスワードを保存する
パブリッシャーアカウントでは2要素認証が使用され、認証を試行するたびに確認コードが送信されます。
パブリッシャーアカウントでのプロジェクト作成
複数のアプリケーションを所有している場合は、それぞれのアプリケーションに対して個別のプロジェクトを作成することをお勧めします。プロジェクト作成時に指定したデータに基づいて、エクソーラはあなたに適したソリューションを提案します。
新たしいプロジェクトを作成するには:
- パブリッシャーアカウントを開きます。
- サイドメニューで、プロジェクトの作成をクリックします。

- プロジェクト名を英語で入力してください(必須)。
- ゲームのリリースプラットフォームを1つまたは複数選択します(必須)。
- ゲームへのリンクを追加します。ゲームが独自のサイトを持っていない場合は、メインサイトまたはゲームがリストされているセクションへのリンクを提供してください(必須)。
- ゲームエンジンを選択します。
- 使用している、または使用する予定の収益化オプションを選択します。
- ゲームがすでにリリースされているかどうかを指定します。ゲームがまだリリースされていない場合は、予定公開日を指定します。
- 「プロジェクトを作成する」をクリックします。おすすめのエクソーラ製品のページが表示されます。
統合プロセスでは、パブリッシャーアカウントでプロジェクト名の横に表示されているプロジェクトIDを入力する必要があります。

カタログのセットアップ
パブリッシャーアカウントでのアイテム作成
エクソーラ側でカタログを作成する必要があります。アイテムを手動で追加するか、Google PlayまたはPlayFabからインポートすることができます。Google Playからインポートする場合、一度に最大100個のアイテムをインポートできます。
これらの説明は、仮想アイテムの基本設定手順を提供します。後で、カタログに他のアイテム(仮想通貨、バンドル、ゲームキー)を追加したり、アイテムグループを作成したり、プロモーションキャンペーンや地域価格などを設定したりすることができます。
基本設定の仮想アイテムをカタログに追加するには:
- パブリッシャーアカウントでプロジェクトを開き、アイテムカタログ > 仮想アイテムセクションに移動します。
- ドロップダウンメニューで、アイテムを作成を選択します。
- 以下のフィールドでアイテムの基本設定を行います:
- イメージ(任意)
- SKU(アイテムの一意のID)
- アイテム名
- 説明(任意)
- アイテム価格を指定します:
- 「実際通貨での価格」のトグルを「オン」に設定します。
- 「デフォルト通貨」フィールドで通貨を変更し(任意)、アイテム価格を指定します。
- 「デフォルト通貨」フィールドで通貨を変更した場合は、「実際通貨での価格」フィールドで同じ通貨を選択します。
- アイテムのステータスを「利用可能」に変更します。
- 「アイテムを作成」をクリックします。
アプリケーションのクライアント側にカタログの表示
- 最新のSDKバージョンをダウンロードする(推奨)またはGitHubで必要なSDKバージョンを選択してダウンロードします。
- パッケージを解凍します。
- メインメニューで、
Assets > Import Package > Custom Package に移動し、ダウンロードしたSDKを選択します。 - メインメニューで、
Window > Xsolla > Edit Settings に移動します。 Inspector パネルに移動します。Project ID フィールドで、プロジェクト名の横のパブリッシャーアカウントにあるプロジェクトIDを指定します。

- アプリケーションのクライアント側で、アイテムカタログを表示するUIを追加します。
- エクソーラサーバーからのアイテムカタログのリクエストを実装します。
GetCatalog
SDKメソッドを使用します。また、その他のSDKメソッドを使用して、カタログアイテムに関する情報を取得することもできます。アイテム購入のセットアップ
Cloud Functionsを利用して注文を作成する
エクソーラ側でユーザーとアイテムのデータを使用して注文を作成するには、購入用の決済トークンを作成するAPIコールを使用するCloud Functionsをプロジェクトに追加します。このコールは決済UIを開き、購入を行うために必要な決済トークンを返します。
制限事項:
- 決済トークンをリクエストする際に、ユーザーの国かIPアドレスを渡す必要があります。
- トークンに通貨を渡さなかった場合、通貨は国によって決定されます。
- トークンに通貨を渡すと、ユーザーはこの通貨で支払います。
プロジェクトにCloud Functionsを追加するには:
- Firebase CLI(コマンドラインインターフェース)をインストールします。これを行うには、CLIコマンドを実行します:
1npm install -g firebase-tools
- プロジェクトをFirebaseプロジェクトにリンクするには、CLIコマンドを実行してFirebaseプロジェクトを初期化します:
1firebase init functions
- インストーラーの指示に従って設定を構成します:
- 既存のコードベースを選択します。
- Cloud Functionsを作成する言語としてJavaScriptを指定します。
- 依存関係をインストールします。
functions/index.js
を開いて修正します:
- javascript
1// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
2const functions = require('firebase-functions/v1');
3
4const projectId = <projectId>;
5const apiKey = <apiKey>;
6
7exports.getXsollaPaymentToken = functions.https.onRequest((req, res) => {
8
9 const requestBody = req.body;
10 if (!requestBody) {
11 res.status(400).send('Request body is missing');
12 return;
13 }
14
15 const userId = requestBody.data.uid;
16 const email = requestBody.data.email;
17 const sku = requestBody.data.sku;
18 const returnUrl = requestBody.data.returnUrl;
19
20 const payload = {
21 user: {
22 id: {value: userId},
23 name: {
24 value: email
25 },
26 email: {
27 value: email
28 },
29 country: {
30 value: 'US',
31 allow_modify: false
32 }
33 },
34 purchase: {
35 items: [
36 {
37 sku: sku,
38 quantity: 1
39 }
40 ]
41 },
42 sandbox: true,
43 settings: {
44 language: 'en',
45 currency: 'USD',
46 return_url: returnUrl,
47 ui: {
48 theme: '63295aab2e47fab76f7708e3'
49 }
50 }
51 }
52
53 let url = "https://store.xsolla.com/api/v3/project/" + projectId.toString() + "/admin/payment/token";
54
55 fetch(
56 url,
57 {
58 method: "POST",
59 headers: {
60 'Content-Type': 'application/json',
61 Authorization: 'Basic ' + btoa(`${projectId}:${apiKey}`)
62 },
63 body: JSON.stringify(payload)
64 },
65 )
66 .then(xsollaRes => {
67 // Handle the response data
68 if (xsollaRes.ok) {
69 return xsollaRes.json();
70 } else {
71 throw new Error(`HTTP request failed with status ${xsollaRes.status} and statusText: ${xsollaRes.statusText}`)
72 }
73 })
74 .then(data => {
75 res.send(JSON.stringify(data));
76 })
77 .catch(error => {
78 res.send("Error = " + error);
79 });
80});
81
82exports.webhookFakeResponse = functions.https.onRequest((request, response) => {
83 response.status(200).send()
84})
- スクリプトの中で、変数の値を指定指定します:
projectId
— パブリッシャーアカウントのプロジェクト名の横にあるプロジェクトID。

apiKey
― APIキー。作成時に一度だけパブリッシャーアカウントに表示され、ユーザー側に保存する必要があります。次のセクションで新しいキーを作成できます:
- エミュレータでCloud Functionsをテストするには、CLIコマンドを実行します:
1firebase emulators:start
- Cloud Functionsを実行した後、アプリケーションのクライアント側で以下のメソッドを呼び出すことができます:
getXsollaPaymentToken
— 決済インターフェースを開くための決済トークンを返します。webhookFakeResponse
— 支払いウェブフックへの応答として、HTTPコード200
を送信します。このメソッドには購入検証ロジック(テスト用のみ)は含まれていません。ウェブフックの全リストと、ウェブフックの操作に関する一般的な情報については、ウェブフックのドキュメントを参照してください。
- メソッドをローカルで呼び出すには、
https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken
とhttps://localhost:5001/{firebase-project-id}/us-central1/webhookFakeResponse
のURLを使用します。{firebase-project-id}
はFirebaseプロジェクトID(Firebaseコンソール > プロジェクト設定 > プロジェクトID)です。
- 本番環境にCloud Functionsをデプロイするには、CLIコマンドを実行します:
1firebase deploy --only functions
- 本番環境にデプロイされた場合、
https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken
とhttps://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse
のURLからメソッドを呼び出すことができます。{firebase-project-id}
はFirebaseのプロジェクトIDです(Firebaseコンソール > プロジェクト設定 > プロジェクトID)。本番環境での機能実行の詳細については、Firebaseに関するドキュメントを参照してください。
Unityプロジェクトで注文を作成し、決済UIを開く
- Unityプロジェクトを開きます。
- ページコントローラーのスクリプトを変更します:
MakeCloudFunctionRequest
メソッドを追加して、Cloud Functionsを呼び出します。getXsollaPaymentToken
メソッドを呼び出すには、次のURLのいずれかを指定します。そこで、{firebase-project-id}
はFirebase プロジェクトID(Firebaseコンソール > プロジェクト設定 > プロジェクトID):
- ローカルアクセス用 —
https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken
- 本番環境でのアクセス用 —
https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken
- ローカルアクセス用 —
- C++
1IEnumerator MakeCloudFunctionRequest(string sku)
2 {
3 string url = "https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken";
4
5 using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
6 {
7 var userData = new UserData()
8 {
9 data = new UserData.Data() {
10 uid = user.UserId,
11 email = user.Email,
12 sku = sku,
13 returnUrl = "app://xpayment.com.xsolla.unitysample"
14 }
15 };
16
17 byte[] data = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(userData, true));
18 UploadHandlerRaw upHandler = new UploadHandlerRaw(data);
19 upHandler.contentType = "application/json";
20 webRequest.uploadHandler = upHandler;
21 webRequest.method = "POST";
22 yield return webRequest.SendWebRequest();
23
24 if (webRequest.result != UnityWebRequest.Result.Success)
25 {
26 Debug.LogError("Error: " + webRequest.error);
27 }
28 else
29 {
30 var paymentToken = "";
31 XsollaWebBrowser.OpenPurchaseUI(
32 paymentToken,
33 false);
34 Debug.Log("Response: " + webRequest.downloadHandler.text);
35 }
36 }
37 }
- 購入ボタンのクリック時にCloud Functionsのコールバックを追加します:
- C++
1private void OnItemsRequestSuccess(StoreItems storeItems)
2 {
3 foreach (var storeItem in storeItems.items)
4 {
5 var widgetGo = Instantiate(WidgetPrefab, WidgetsContainer, false);
6 var widget = widgetGo.GetComponent<StoreItemWidget>();
7
8 widget.BuyButton.onClick.AddListener(() =>
9 {
10 StartCoroutine(MakeCloudFunctionRequest(storeItem.sku));
11 });
12
13 widget.NameText.text = storeItem.name;
14 widget.DescriptionText.text = storeItem.description;
15
16 if (storeItem.price != null)
17 {
18 var realMoneyPrice = storeItem.price;
19 widget.PriceText.text = $"{realMoneyPrice.amount} {realMoneyPrice.currency}";
20 }
21
22 ImageLoader.LoadSprite(storeItem.image_url, sprite => widget.IconImage.sprite = sprite);
23 }
24 }
テストプロジェクトを実装例として使用できます。UnityプロジェクトのソースコードはGitHubで公開されています。
ページコントローラースクリプトの例:
- C++
1using Firebase.Extensions;
2using System;
3using System.Collections;
4using UnityEngine;
5using UnityEngine.Networking;
6using UnityEngine.UI;
7using Xsolla.Catalog;
8using Xsolla.Core;
9
10[Serializable]
11public class UserData
12{
13 public Data data;
14
15 [Serializable]
16 public class Data
17 {
18 public string uid;
19 public string email;
20 public string sku;
21 public string returnUrl;
22 }
23}
24
25public class FirebaseExamplePage : MonoBehaviour
26{
27 public GameObject LoginContainer;
28 public GameObject StoreItemsContainer;
29
30 public InputField EmailInputField;
31 public InputField PasswordInputField;
32 public Button LoginButton;
33 public Button RegisterButton;
34
35 public Transform WidgetsContainer;
36 public GameObject WidgetPrefab;
37
38 protected Firebase.Auth.FirebaseAuth auth;
39 Firebase.Auth.FirebaseUser user = null;
40
41 Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;
42
43 public virtual void Start()
44 {
45 Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
46 dependencyStatus = task.Result;
47 if (dependencyStatus == Firebase.DependencyStatus.Available)
48 {
49 InitializeFirebase();
50 }
51 else
52 {
53 Debug.LogError(
54 "Could not resolve all Firebase dependencies: " + dependencyStatus);
55 }
56 });
57 }
58
59 protected void InitializeFirebase()
60 {
61 StoreItemsContainer.SetActive(false);
62
63 Debug.Log("Setting up Firebase Auth");
64 auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
65 auth.StateChanged += AuthStateChanged;
66 RegisterButton.onClick.AddListener(() =>
67 {
68 auth.CreateUserWithEmailAndPasswordAsync(EmailInputField.text, PasswordInputField.text).ContinueWith(task =>
69 {
70 if (task.IsCanceled)
71 {
72 Debug.LogError("CreateUserWithEmailAndPasswordAsync was canceled.");
73 return;
74 }
75 if (task.IsFaulted)
76 {
77 Debug.LogError("CreateUserWithEmailAndPasswordAsync encountered an error: " + task.Exception);
78 return;
79 }
80
81 Firebase.Auth.AuthResult result = task.Result;
82 Debug.LogFormat("Firebase user created successfully: {0} ({1})",
83 result.User.DisplayName, result.User.UserId);
84 });
85 });
86
87 LoginButton.onClick.AddListener(() =>
88 {
89 auth.SignInWithEmailAndPasswordAsync(EmailInputField.text, PasswordInputField.text).ContinueWith(task =>
90 {
91 if (task.IsCanceled)
92 {
93 Debug.LogError("SignInWithEmailAndPasswordAsync was canceled.");
94 return;
95 }
96 if (task.IsFaulted)
97 {
98 Debug.LogError("SignInWithEmailAndPasswordAsync encountered an error: " + task.Exception);
99 return;
100 }
101
102 Firebase.Auth.AuthResult result = task.Result;
103 Debug.LogFormat("Firebase user logged in successfully: {0} ({1})",
104 result.User.DisplayName, result.User.UserId);
105 });
106 });
107 }
108
109 void AuthStateChanged(object sender, System.EventArgs eventArgs)
110 {
111 Firebase.Auth.FirebaseAuth senderAuth = sender as Firebase.Auth.FirebaseAuth;
112 if (senderAuth == auth && senderAuth.CurrentUser != user)
113 {
114 bool signedIn = user != senderAuth.CurrentUser && senderAuth.CurrentUser != null;
115 if (!signedIn && user != null)
116 {
117 Debug.Log("Signed out " + user.UserId);
118 }
119 user = senderAuth.CurrentUser;
120 if (signedIn)
121 {
122 Debug.Log("AuthStateChanged Signed in " + user.UserId);
123 LoadCatalog();
124 }
125 }
126 }
127
128 void OnDestroy()
129 {
130 if (auth != null)
131 {
132 auth.SignOut();
133 auth.StateChanged -= AuthStateChanged;
134 auth = null;
135 }
136 }
137 private void LoadCatalog()
138 {
139 LoginContainer.SetActive(false);
140 StoreItemsContainer.SetActive(true);
141 XsollaCatalog.GetCatalog(OnItemsRequestSuccess, OnError);
142 }
143
144 private void OnItemsRequestSuccess(StoreItems storeItems)
145 {
146
147 foreach (var storeItem in storeItems.items)
148 {
149 var widgetGo = Instantiate(WidgetPrefab, WidgetsContainer, false);
150 var widget = widgetGo.GetComponent<StoreItemWidget>();
151
152 if(widget != null)
153 {
154 widget.NameText.text = storeItem.name;
155 widget.DescriptionText.text = storeItem.description;
156
157 widget.BuyButton.onClick.AddListener(() =>
158 {
159 StartCoroutine(MakeCloudFunctionRequest(storeItem.sku));
160 });
161
162 if (storeItem.price != null)
163 {
164 var realMoneyPrice = storeItem.price;
165 widget.PriceText.text = $"{realMoneyPrice.amount} {realMoneyPrice.currency}";
166 }
167
168 ImageLoader.LoadSprite(storeItem.image_url, sprite => widget.IconImage.sprite = sprite);
169 }
170 }
171 }
172 IEnumerator MakeCloudFunctionRequest(string sku)
173 {
174 string url = "https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken";
175
176 using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
177 {
178 var userData = new UserData()
179 {
180 data = new UserData.Data() {
181 uid = user.UserId,
182 email = user.Email,
183 sku = sku,
184 returnUrl = "app://xpayment.com.xsolla.unitysample"
185 }
186 };
187
188 byte[] data = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(userData, true));
189 UploadHandlerRaw upHandler = new UploadHandlerRaw(data);
190 upHandler.contentType = "application/json";
191 webRequest.uploadHandler = upHandler;
192 webRequest.method = "POST";
193 yield return webRequest.SendWebRequest();
194
195 if (webRequest.result != UnityWebRequest.Result.Success)
196 {
197 Debug.LogError("Error: " + webRequest.error);
198 }
199 else
200 {
201 string responseJson = webRequest.downloadHandler.text;
202 var responseData = JsonUtility.FromJson<OrderData>(responseJson);
203
204 var paymentToken = responseData.token;
205 int orderId = responseData.order_id;
206
207 XsollaWebBrowser.OpenPurchaseUI(
208 paymentToken,
209 false);
210 Debug.Log("Response: " + webRequest.downloadHandler.text);
211 }
212 }
213 }
214
215 private void OnError(Error error)
216 {
217 Debug.LogError($"Error: {error.errorMessage}");
218 }
219}
注文状況追跡のセットアップ
注文状況の追跡は、支払いが成功したことを確認し、ユーザーにアイテムを付与するために必要です。
クライアント側で注文状況の取得
注文追跡ロジックはGetXsollaPaymentToken
メソッドに含まれています。成功した購入を処理するには、注文ステータスがdone
に変わったときに呼び出される関数を渡す必要があります。
AddOrderForTracking
SDKメソッドは追跡に使用されます。このメソッドがどのように動作するかの詳細については、注文状況の追跡を参照してください。
サーバー側で注文状況の取得
SDKはアプリケーションのクライアント側で注文状況を追跡することを可能にします。ただし、完了した購入の追加の検証を実装するために、アプリケーションのバックエンドで支払いウェブフックハンドラを設定することをお勧めします。これにより、注文情報を受け取ることができます
ウェブフックの完全なリストと、ウェブフックの操作に関する一般情報については、ウェブフックに関するドキュメントを参照してください。
エクソーラ側でウェブフックを構成するには:
- パブリッシャーアカウントでプロジェクトを開き、プロジェクト設定 >ウェブフックセクションに移動します。
- 「ウェブフックサーバー」フィールドに、エクソーラがウェブフックを送信するURLを入力します。
テストの場合は、https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse
を指定できます。ここで、{firebase-project-id}
はFirebaseプロジェクトIDです(Firebaseコンソール > プロジェクト設定 > プロジェクトID)。この場合、Firebaseはウェブフックの処理が成功したかのようにシミュレートします。実際のプロジェクトでは、購入の検証ロジックを追加する必要があります。
ウェブフックをテストするには、webhook.siteなどの専門サイトや、ngrokなどのプラットフォームを選択することもできます。
- 「秘密鍵」フィールドの値をコピーして保存します。このキーはデフォルトで生成され、ウェブフックの署名に使用されます。変更したい場合は、更新アイコンをクリックします。
- 「ウェブフックを有効にする」をクリックします。

誤字脱字などのテキストエラーを見つけましたか? テキストを選択し、Ctrl+Enterを押します。