Как использовать Pay Station совместно с аутентификацией Firebase
Если вы уже реализовали аутентификацию пользователей в приложении с использованием Firebase, вы можете формировать платежный токен на стороне Firebase, а затем передавать его в клиентскую часть приложения для открытия платежного интерфейса.
При таком способе интеграции вам потребуется самостоятельно реализовать логику определения страны пользователя и валюты для оплаты покупки.
Сценарий интеграции:
- Зарегистрируйтесь в Личном кабинете и создайте новый проект. ID созданного проекта потребуется вам на дальнейших шагах.
- Настройте каталог товаров:
- Создайте каталог товаров на стороне Xsolla. Вы можете добавить товары вручную или импортировать их из Google Play или PlayFab.
- Реализуйте получение каталога с помощью методов SDK и его отображение на клиенте приложения.
- Настройте покупку товара:
- Реализуйте создание заказа с данными о пользователе и товаре c помощью облачной функции Firebase.
- Реализуйте открытие платежного интерфейса на клиентской части приложения с помощью SDK.
Чтобы завершить интеграцию и начать принимать реальные платежи, вам требуется подписать Лицензионный договор с Xsolla.
Вы можете подписать лицензионный договор на любом этапе интеграции, но обратите внимание, что процесс рассмотрения заявки занимает до 3 рабочих дней.
В качестве примера реализации совместного использования аутентификации Firebase и Pay Station используйте тестовое приложение. Исходный код тестового приложения доступен на GitHub.
Создание проекта
Регистрация в Личном кабинете
Личный кабинет — основной инструмент для настройки возможностей Xsolla, а также для работы с аналитикой и транзакциями.
Указанные на этапе регистрации данные о компании и вашем приложении будут использоваться для формирования рекомендаций с подходящими для вас решениями и создания черновика договора с Xsolla. Вы сможете изменить данные позже, но указание при регистрации верных данных ускорит процесс согласования договора.
Чтобы зарегистрироваться, перейдите в Личный кабинет и создайте аккаунт.
Пароль от Личного кабинета должен содержать не менее:
- 8 символов;
- одной цифры;
- одной прописной буквы латинского алфавита;
- одной строчной буквы латинского алфавита.
Для обеспечения безопасности пароля рекомендуется:
- менять пароль не реже одного раза в 90 дней;
- использовать новый пароль, который не совпадает с последними 4 паролями вашей учетной записи;
- использовать уникальный пароль, который не совпадает с паролями в других сервисах;
- не хранить пароль в легкодоступных местах;
- использовать менеджеры паролей для хранения пароля.
Личный кабинет использует двухфакторную аутентификацию и отправляет код подтверждения при каждой попытке аутентификации.
Создание проекта в Личном кабинете
Если у вас есть несколько приложений, мы рекомендуем создавать отдельный проект для каждого приложения. На основе данных, указанных при создании проекта, Xsolla сформирует рекомендации по подходящим для вас решениям.
Чтобы создать новый проект:
- Откройте Личный кабинет.
- В боковом меню нажмите Создать проект.

- Введите название проекта на английском языке (обязательно).
- Выберите одну или несколько платформ релиза вашей игры (обязательно).
- Укажите ссылку на игру. Если у вашей игры нет сайта, укажите ссылку на источник, который содержит информацию об игре (обязательно).
- Выберите игровой движок.
- Выберите способ монетизации, который вы используете или собираетесь использовать.
- Укажите, вышла ли уже ваша игра. Если игра не вышла, укажите предполагаемую дату релиза.
- Нажмите Создать проект. Вы увидите страницу с рекомендованными для вас продуктами Xsolla.
В процессе интеграции вам потребуется ID проекта. Вы можете найти его в Личном кабинете рядом с названием проекта.

Настройка каталога товаров
Создание предметов в Личном кабинете
Вам потребуется создать каталог товаров на стороне Xsolla. Вы можете добавить товары вручную или импортировать их из Google Play или PlayFab. При импорте из Google Play можно импортировать не более 100 предметов за раз.
В рамках этой инструкции приведены шаги по базовой настройке виртуального предмета. Позже вы сможете дополнить свой каталог другими товарами (виртуальными валютами, бандлами, игровыми ключами), создать группы товаров, настроить акционные кампании, региональные цены и т. д.
Чтобы добавить в каталог виртуальный предмет с базовыми настройками:
- Откройте проект в Личном кабинете.
- Нажмите Store в боковом меню.
- В панели Виртуальные предметы нажмите Подключить.
- В раскрывающемся меню выберите Создать предмет.
- Задайте базовые настройки предмета в следующих полях:
- Изображение (опционально).
- Артикул (уникальный ID предмета).
- Название предмета.
- Описание (опционально).
- Задайте цену предмета:
- Установите переключатель Цены в реальной валюте в положение Вкл.
- В поле Цена в реальной валюте измените валюту (опционально) и укажите цену предмета.
- Если вы изменили валюту в поле Цена в реальной валюте, укажите эту же валюту в поле Валюта по умолчанию.
- Измените статус предмета на Доступен.
- Нажмите Создать предмет.
Отображение каталога в клиентской части приложения
- Скачайте последнюю версию SDK (рекомендуется) или выберите требуемую версию SDK на GitHub и скачайте ее.
- Распакуйте архив.
- В главном меню редактора Unity выберите пункт
Assets > Import Package > Custom Package и укажите скачанный SDK. - В главном меню выберите пункт
Window > Xsolla > Edit Settings . - В панели
Inspector в полеProject ID укажите ID проекта, который можно найти в Личном кабинете рядом с названием проекта.

- В клиентской части приложения добавьте UI для отображения каталога товаров.
- Реализуйте запрос каталога товаров с серверов Xsolla.
GetCatalog
. Вы также можете получать информацию о товарах из каталога с помощью других методов.Настройка покупки товара
Создание заказа с помощью облачной функции
Чтобы на стороне Xsolla сформировать заказ с данными о пользователе и товаре, добавьте в проект облачную функцию, вызывающую метод API Создание платежного токена для покупки. Этот метод вернет платежный токен, который потребуется для открытия платежного интерфейса и совершения оплаты.
Ограничения:
- Вам необходимо передать в запросе платежного токена либо страну, либо IP-адрес пользователя.
- Если вы не передали валюту при запросе платежного токена, она будет определяться согласно стране пользователя.
- Если вы передали валюту при запросе платежного токена, пользователь будет оплачивать заказ в этой валюте.
Добавление облачной функции в проект Firebase
- Установите Firebase CLI (Command-Line Interface — командная строка), для этого выполните CLI-команду:
1npm install -g firebase-tools
- Чтобы связать ваш проект с проектом Firebase, инициализируйте проект Firebase, для этого выполните CLI-команду:
1firebase init 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. Он отображается в Личном кабинете только при создании и должен храниться на вашей стороне. Создать ключ можно в следующих разделах:- Настройки компании > Ключи API;
- Настройки проекта > Ключи API.
- Чтобы протестировать работу облачной функции с помощью эмулятора, используйте CLI-команду:
1firebase emulators:start
- После запуска облачной функции вы можете вызывать в клиентской части вашего приложения следующие методы:
getXsollaPaymentToken
— возвращает платежный токен для открытия платежного интерфейса.webhookFakeResponse
— отправляет200
HTTP-код в ответ на вебхук Успешный платеж. Метод не содержит логику валидации покупки и используется только для тестирования. Полный список вебхуков и общая информация о работе с ними приведена в документации по вебхукам.
- Локально методы доступны для вызова по URL-адресам
https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken
иhttps://localhost:5001/{firebase-project-id}/us-central1/webhookFakeResponse
, где{firebase-project-id}
— ID проекта Firebase (консоль Firebase > Project Settings > Project ID).
- Чтобы запустить облачную функцию в боевой среде, используйте CLI-команду:
1firebase deploy --only functions
- После запуска в боевой среде методы станут доступны для вызова по URL-адресам
https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken
иhttps://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse
, где{firebase-project-id}
— ID проекта Firebase (консоль Firebase > Project Settings > Project ID). Подробная информация о запуске функции в боевой среде приведена в документации Firebase.
Создание заказа и открытие платежного интерфейса в проекте Unity
- Откройте ваш проект Unity.
- Внесите изменения в скрипт контроллера страницы:
- Добавьте метод вызова облачной функции
MakeCloudFunctionRequest
. Для вызова методаgetXsollaPaymentToken
укажите один из следующих URL-адресов, где{firebase-project-id}
— ID проекта Firebase (консоль Firebase > Project Settings > Project 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 }
- Добавьте вызов облачной функции при нажатии кнопки покупки товара:
- 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
.
Для отслеживания используется метод SDK AddOrderForTracking
. Подробные сведения о работе метода приведены в разделе Отслеживание статуса заказа.
Получение статуса заказа на серверной части
SDK позволяет отслеживать статус заказа на клиентской части вашего приложения. Однако мы рекомендуем настроить обработку вебхука Успешный платеж и получать информацию о заказе на серверной части вашего приложения. Это позволит реализовать дополнительную валидацию совершенных покупок.
Полный список вебхуков и общая информация о работе с ними приведены в документации по вебхукам.
Чтобы настроить вебхуки на стороне Xsolla:
- Откройте проект в Личном кабинете.
- Нажмите Настройки проекта в боковом меню и перейдите в раздел Вебхуки.
- В поле Сервер для вебхуков укажите URL-адрес, на который Xsolla будет отправлять вебхуки.
Для тестирования вы можете указать https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse
, где {firebase-project-id}
— ID проекта Firebase (консоль Firebase > Project Settings > Project ID). В этом случае Firebase будет имитировать успешную обработку вебхука. Для реального проекта вам потребуется добавить логику валидации покупки.
Для тестирования вебхуков вы также можете выбрать любой специализированный сайт, например webhook.site, или платформу, например ngrok.
- Скопируйте и сохраните значение из поля Секретный ключ. Этот ключ генерируется по умолчанию и используется для подписи вебхуков. Если вы хотите изменить его, нажмите значок обновления.
- Нажмите Получать вебхуки.

Нашли опечатку или ошибку в тексте? Выделите ее и нажмите Ctrl+Enter.