ペイステーションと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)
- アイテム名
- 説明(任意)
- アイテム価格を指定します:
- 「実際通貨での価格」のトグルを「オン」に設定します。
- 「デフォルト通貨」フィールドで通貨を変更し(任意)、アイテム価格を指定します。
- 「デフォルト通貨」フィールドで通貨を変更した場合は、「実際通貨での価格」フィールドで同じ通貨を選択します。
- アイテムのステータスを「利用可能」に変更します。
- 「アイテムを作成」をクリックします。
アプリケーションのクライアント側にカタログの表示
- CDNまたは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コマンドを実行します:
npm install -g firebase-tools
- プロジェクトをFirebaseプロジェクトにリンクするには、CLIコマンドを実行してFirebaseプロジェクトを初期化します:
firebase init functions
- インストーラーの指示に従って設定を構成します:
- 既存のコードベースを選択します。
- Cloud Functionsを作成する言語としてJavaScriptを指定します。
- 依存関係をインストールします。
functions/index.js
を開いて修正します:
- javascript
// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.
const functions = require('firebase-functions/v1');
const projectId = <projectId>;
const apiKey = <apiKey>;
exports.getXsollaPaymentToken = functions.https.onRequest((req, res) => {
const requestBody = req.body;
if (!requestBody) {
res.status(400).send('Request body is missing');
return;
}
const userId = requestBody.data.uid;
const email = requestBody.data.email;
const sku = requestBody.data.sku;
const returnUrl = requestBody.data.returnUrl;
const payload = {
user: {
id: {value: userId},
name: {
value: email
},
email: {
value: email
},
country: {
value: 'US',
allow_modify: false
}
},
purchase: {
items: [
{
sku: sku,
quantity: 1
}
]
},
sandbox: true,
settings: {
language: 'en',
currency: 'USD',
return_url: returnUrl,
ui: {
theme: '63295aab2e47fab76f7708e3'
}
}
}
let url = "https://store.xsolla.com/api/v3/project/" + projectId.toString() + "/admin/payment/token";
fetch(
url,
{
method: "POST",
headers: {
'Content-Type': 'application/json',
Authorization: 'Basic ' + btoa(`${projectId}:${apiKey}`)
},
body: JSON.stringify(payload)
},
)
.then(xsollaRes => {
// Handle the response data
if (xsollaRes.ok) {
return xsollaRes.json();
} else {
throw new Error(`HTTP request failed with status ${xsollaRes.status} and statusText: ${xsollaRes.statusText}`)
}
})
.then(data => {
res.send(JSON.stringify(data));
})
.catch(error => {
res.send("Error = " + error);
});
});
exports.webhookFakeResponse = functions.https.onRequest((request, response) => {
response.status(200).send()
})
- スクリプトの中で、変数の値を指定指定します:
projectId
— アドミンページのプロジェクト名の横にあるプロジェクトID。
apiKey
― APIキー。作成時に一度だけアドミンページに表示され、ユーザー側に保存する必要があります。次のセクションで新しいキーを作成できます:- 会社設定 > APIキー
- プロジェクト設定 > APIキー
- エミュレータでCloud Functionsをテストするには、CLIコマンドを実行します:
firebase 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コマンドを実行します:
firebase 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++
IEnumerator MakeCloudFunctionRequest(string sku)
{
string url = "https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken";
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
var userData = new UserData()
{
data = new UserData.Data() {
uid = user.UserId,
email = user.Email,
sku = sku,
returnUrl = "app://xpayment.com.xsolla.unitysample"
}
};
byte[] data = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(userData, true));
UploadHandlerRaw upHandler = new UploadHandlerRaw(data);
upHandler.contentType = "application/json";
webRequest.uploadHandler = upHandler;
webRequest.method = "POST";
yield return webRequest.SendWebRequest();
if (webRequest.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Error: " + webRequest.error);
}
else
{
var paymentToken = "";
XsollaWebBrowser.OpenPurchaseUI(
paymentToken,
false);
Debug.Log("Response: " + webRequest.downloadHandler.text);
}
}
}
- 購入ボタンのクリック時にCloud Functionsのコールバックを追加します:
- C++
private void OnItemsRequestSuccess(StoreItems storeItems)
{
foreach (var storeItem in storeItems.items)
{
var widgetGo = Instantiate(WidgetPrefab, WidgetsContainer, false);
var widget = widgetGo.GetComponent<StoreItemWidget>();
widget.BuyButton.onClick.AddListener(() =>
{
StartCoroutine(MakeCloudFunctionRequest(storeItem.sku));
});
widget.NameText.text = storeItem.name;
widget.DescriptionText.text = storeItem.description;
if (storeItem.price != null)
{
var realMoneyPrice = storeItem.price;
widget.PriceText.text = $"{realMoneyPrice.amount} {realMoneyPrice.currency}";
}
ImageLoader.LoadSprite(storeItem.image_url, sprite => widget.IconImage.sprite = sprite);
}
}
テストプロジェクトを実装例として使用できます。UnityプロジェクトのソースコードはGitHubで公開されています。
ページコントローラースクリプトの例:
- C++
using Firebase.Extensions;
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Xsolla.Catalog;
using Xsolla.Core;
[Serializable]
public class UserData
{
public Data data;
[Serializable]
public class Data
{
public string uid;
public string email;
public string sku;
public string returnUrl;
}
}
public class FirebaseExamplePage : MonoBehaviour
{
public GameObject LoginContainer;
public GameObject StoreItemsContainer;
public InputField EmailInputField;
public InputField PasswordInputField;
public Button LoginButton;
public Button RegisterButton;
public Transform WidgetsContainer;
public GameObject WidgetPrefab;
protected Firebase.Auth.FirebaseAuth auth;
Firebase.Auth.FirebaseUser user = null;
Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;
public virtual void Start()
{
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available)
{
InitializeFirebase();
}
else
{
Debug.LogError(
"Could not resolve all Firebase dependencies: " + dependencyStatus);
}
});
}
protected void InitializeFirebase()
{
StoreItemsContainer.SetActive(false);
Debug.Log("Setting up Firebase Auth");
auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
auth.StateChanged += AuthStateChanged;
RegisterButton.onClick.AddListener(() =>
{
auth.CreateUserWithEmailAndPasswordAsync(EmailInputField.text, PasswordInputField.text).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("CreateUserWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("CreateUserWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.AuthResult result = task.Result;
Debug.LogFormat("Firebase user created successfully: {0} ({1})",
result.User.DisplayName, result.User.UserId);
});
});
LoginButton.onClick.AddListener(() =>
{
auth.SignInWithEmailAndPasswordAsync(EmailInputField.text, PasswordInputField.text).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithEmailAndPasswordAsync was canceled.");
return;
}
if (task.IsFaulted)
{
Debug.LogError("SignInWithEmailAndPasswordAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.AuthResult result = task.Result;
Debug.LogFormat("Firebase user logged in successfully: {0} ({1})",
result.User.DisplayName, result.User.UserId);
});
});
}
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
Firebase.Auth.FirebaseAuth senderAuth = sender as Firebase.Auth.FirebaseAuth;
if (senderAuth == auth && senderAuth.CurrentUser != user)
{
bool signedIn = user != senderAuth.CurrentUser && senderAuth.CurrentUser != null;
if (!signedIn && user != null)
{
Debug.Log("Signed out " + user.UserId);
}
user = senderAuth.CurrentUser;
if (signedIn)
{
Debug.Log("AuthStateChanged Signed in " + user.UserId);
LoadCatalog();
}
}
}
void OnDestroy()
{
if (auth != null)
{
auth.SignOut();
auth.StateChanged -= AuthStateChanged;
auth = null;
}
}
private void LoadCatalog()
{
LoginContainer.SetActive(false);
StoreItemsContainer.SetActive(true);
XsollaCatalog.GetCatalog(OnItemsRequestSuccess, OnError);
}
private void OnItemsRequestSuccess(StoreItems storeItems)
{
foreach (var storeItem in storeItems.items)
{
var widgetGo = Instantiate(WidgetPrefab, WidgetsContainer, false);
var widget = widgetGo.GetComponent<StoreItemWidget>();
if(widget != null)
{
widget.NameText.text = storeItem.name;
widget.DescriptionText.text = storeItem.description;
widget.BuyButton.onClick.AddListener(() =>
{
StartCoroutine(MakeCloudFunctionRequest(storeItem.sku));
});
if (storeItem.price != null)
{
var realMoneyPrice = storeItem.price;
widget.PriceText.text = $"{realMoneyPrice.amount} {realMoneyPrice.currency}";
}
ImageLoader.LoadSprite(storeItem.image_url, sprite => widget.IconImage.sprite = sprite);
}
}
}
IEnumerator MakeCloudFunctionRequest(string sku)
{
string url = "https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken";
using (UnityWebRequest webRequest = UnityWebRequest.Get(url))
{
var userData = new UserData()
{
data = new UserData.Data() {
uid = user.UserId,
email = user.Email,
sku = sku,
returnUrl = "app://xpayment.com.xsolla.unitysample"
}
};
byte[] data = System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(userData, true));
UploadHandlerRaw upHandler = new UploadHandlerRaw(data);
upHandler.contentType = "application/json";
webRequest.uploadHandler = upHandler;
webRequest.method = "POST";
yield return webRequest.SendWebRequest();
if (webRequest.result != UnityWebRequest.Result.Success)
{
Debug.LogError("Error: " + webRequest.error);
}
else
{
string responseJson = webRequest.downloadHandler.text;
var responseData = JsonUtility.FromJson<OrderData>(responseJson);
var paymentToken = responseData.token;
int orderId = responseData.order_id;
XsollaWebBrowser.OpenPurchaseUI(
paymentToken,
false);
Debug.Log("Response: " + webRequest.downloadHandler.text);
}
}
}
private void OnError(Error error)
{
Debug.LogError($"Error: {error.errorMessage}");
}
}
注文状況追跡のセットアップ
注文状況の追跡は、支払いが成功したことを確認し、ユーザーにアイテムを付与するために必要です。
クライアント側で注文状況の取得
注文追跡ロジックは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を押します。