搭配BaaS授权使用建店器

您可以搭配BaaS授权系统使用建店器来销售游戏内商品。该方案的交互过程如下:

  1. 用户通过BaaS授权系统登录您的应用程序。
  2. BaaS服务通过传入用户ID向艾克索拉服务器请求用户Web令牌(JWT)。
  3. 艾克索拉服务器向BaaS服务返回用户JWT。
  4. BaaS服务将用户JWT传给应用程序。
  5. 应用程序使用API通过用户JWT与艾克索拉服务器交互。
注:
如果您未实现用户授权的逻辑,可集成艾克索拉登录管理器并在PlayFab或Firebase中设置用户数据的存储。这样您就可以使用Login API来认证用户身份并接收JWT来与其他艾克索拉产品的API交互。
要获取用户JWT:
  1. 在发布商帐户中,将标准登录管理器项目连接到您的项目
  2. 设置服务器OAuth 2.0客户端
  3. 按照FirebasePlayFab的说明将现成函数添加到您的项目。

设置服务器OAuth 2.0客户端

  1. 发布商帐户中打开您的项目,然后前往玩家 > 登录管理器部分。
  2. 在登录管理器项目面板中单击配置
  3. 前往安全性区块,然后选择OAuth 2.0部分。
  4. 单击添加OAuth 2.0
  5. 指定OAuth 2.0重定向URI
  6. 勾选服务器(服务器对服务器连接)复选框。
  7. 单击连接
  8. 复制并保存客户端ID和密钥。

向Firebase项目添加云函数

  1. 初始化您的Firebase项目
  2. 导入并配置用户JWT的接收函数,其中:
    • <ProjectID>是项目ID,可在您发布商帐户中项目名称的旁边找到。
    • <LoginID>是登录管理器ID,获取方式是打开发布商帐户,前往玩家 > 登录管理器 > 仪表板 > 您的登录管理器项目部分,然后单击登录管理器项目名称旁边的复制ID
    • <OAuthClientID>设置服务器OAuth 2.0客户端时收到的客户端ID。
    • <OAuthSecretKey>设置服务器OAuth 2.0客户端时收到的密钥。

接收用户JWT的函数代码:

Copy
Full screen
Small screen
  1const projectId = "<ProjectID>";
  2const loginProjectId = "<LoginID>";
  3
  4const serverOauthClientId = <OAuthClientID>;
  5const serverOauthClientSecret = "<OAuthSecretKey>";
  6
  7exports.getXsollaLoginToken = functions.https.onCall((data, context) => {
  8  if (!context.auth) {
  9    throw new functions.https.HttpsError(
 10        "failed-precondition",
 11        "The function must be called while authenticated."
 12    );
 13  }
 14
 15  const postData =
 16      `grant_type=client_credentials&client_secret=${serverOauthClientSecret}&client_id=${serverOauthClientId}`;
 17
 18  const options = {
 19    hostname: "login.xsolla.com",
 20    port: 443,
 21    path: "/api/oauth2/token",
 22    method: "POST",
 23    headers: {
 24      "Content-Type": "application/x-www-form-urlencoded",
 25      "Content-Length": postData.length,
 26    },
 27  };
 28
 29  return new Promise( (resolve, reject) => {
 30    const req = https.request(options, (res) => {
 31      if (res.statusCode !== 200) {
 32        reject(
 33            new functions.https.HttpsError(
 34                "internal",
 35                "Server token not received"
 36            )
 37        );
 38      }
 39      let body = [];
 40      res.on("data", (d) => {
 41        body.push(d);
 42      });
 43      res.on("end", () => {
 44        try {
 45          body = JSON.parse(Buffer.concat(body).toString());
 46        } catch (e) {
 47          reject(
 48              new functions.https.HttpsError(
 49                  "internal",
 50                  "Malformed server token response"
 51              )
 52          );
 53        }
 54        getClientToken(context.auth.uid, body.access_token, resolve, reject);
 55      });
 56    });
 57    req.on("error", (e) => {
 58      reject(new functions.https.HttpsError(
 59          "internal",
 60          "Internal error while server token flow"
 61      ));
 62    });
 63
 64    req.write(postData);
 65    req.end();
 66  });
 67});
 68
 69// eslint-disable-next-line require-jsdoc
 70function getClientToken(userId, serverToken, resolve, reject) {
 71  const postData = JSON.stringify(
 72      {
 73        "server_custom_id": userId,
 74      }
 75  );
 76
 77  const path =
 78      `/api/users/login/server_custom_id?projectId=${loginProjectId}&publisher_project_id=${projectId}`;
 79
 80  const options = {
 81    hostname: "login.xsolla.com",
 82    port: 443,
 83    path: path,
 84    method: "POST",
 85    headers: {
 86      "Content-Type": "application/json",
 87      "Content-Length": postData.length,
 88      "X-Server-Authorization": serverToken,
 89    },
 90  };
 91
 92  const req = https.request(options, (res) => {
 93    if (res.statusCode !== 200) {
 94      reject(
 95          new functions.https.HttpsError(
 96              "internal",
 97              "Client token not received"
 98          )
 99      );
100    }
101    let body = [];
102    res.on("data", (d) => {
103      body.push(d);
104    });
105    res.on("end", () => {
106      try {
107        body = JSON.parse(Buffer.concat(body).toString());
108      } catch (e) {
109        reject(
110            new functions.https.HttpsError(
111                "internal",
112                "Malformed client token response"
113            )
114        );
115      }
116      resolve({
117        "token": body.token,
118      });
119    });
120  });
121  req.on("error", (e) => {
122    reject(new functions.https.HttpsError(
123        "internal",
124        "Internal error while client token flow"
125    ));
126  });
127
128  req.write(postData);
129  req.end();
130}
  1. 参照此示例将该函数部署到生产环境。
  2. 添加客户端侧逻辑从您的应用程序调用函数。将getXsollaLoginToken指定为函数名称。传参为非必需。
  3. 在应用程序中,自行实现与API交互的方法或使用艾克索拉SDK

向PlayFab项目添加云脚本

  1. 创建包含用户JWT接收函数的JS文件,其中:
    • <ProjectID>是项目ID,可在您发布商帐户中项目名称的旁边找到。
    • <LoginID>是登录管理器ID,获取方式是打开发布商帐户,前往玩家 > 登录管理器 > 仪表板 > 您的登录管理器项目部分,然后单击登录管理器项目名称旁边的复制ID
    • <OAuthClientID>设置服务器OAuth 2.0客户端时收到的客户端ID。
    • <OAuthSecretKey>设置服务器OAuth 2.0客户端时收到的密钥。
注:
如果已在项目中使用了云脚本,请将接收用户JWT的函数添加到该代码的末尾。
接收用户JWT的函数代码:
Copy
Full screen
Small screen
 1handlers.GetXsollaLoginToken = function (args) {
 2
 3    // TODO replace with production credentials
 4    const projectId = <ProjectID>;
 5    const loginProjectId = "<LoginID>";
 6    const serverOauthClientId = <OAuthClientID>;
 7    const serverOauthClientSecret = "<OAuthSecretKey>";
 8
 9    const getServerTokenBody =
10        `grant_type=client_credentials&client_secret=${serverOauthClientSecret}&client_id=${serverOauthClientId}`;
11
12    const serverTokenResponse = JSON.parse(
13        http.request(
14            "https://login.xsolla.com/api/oauth2/token",
15            "post",
16            getServerTokenBody,
17            "application/x-www-form-urlencoded",
18            {})
19    );
20
21    let serverToken = ""
22    if ('access_token' in serverTokenResponse) {
23        serverToken = serverTokenResponse.access_token;
24    } else {
25        return {
26            "error_message": "Server token not received"
27        }
28    }
29
30    const getUserTokenHeaders = {
31        "X-Server-Authorization": serverToken
32    }
33
34    const getUserTokenBody = JSON.stringify(
35        {
36            "server_custom_id": currentPlayerId,
37        }
38    );
39
40    const getUserTokenPath =
41        `/api/users/login/server_custom_id?projectId=${loginProjectId}&publisher_project_id=${projectId}`;
42
43    const userTokenResponse = JSON.parse(
44        http.request(
45            `https://login.xsolla.com${getUserTokenPath}`,
46            "post",
47            getUserTokenBody,
48            "application/json",
49            getUserTokenHeaders)
50    );
51
52    if ('token' in userTokenResponse) {
53        return {
54            "token": userTokenResponse.token
55        }
56    } else {
57        return {
58            "error_message": "User token not received"
59        }
60    }
61}

  1. 前往PlayFab项目设置。
  2. 上传云脚本文件。
  3. 在生产环境中运行云脚本。
  4. 添加客户端侧逻辑从应用程序调用函数。将GetXsollaLoginToken指定为函数名。传参为非必需。

调用用户JWT接收函数的示例:

Copy
Full screen
Small screen

kotlin

  • kotlin
  • C#
  • C++
1val tokenRequest = PlayFabClientModels.ExecuteCloudScriptRequest()
2tokenRequest.FunctionName = "GetXsollaLoginToken"
3val res = PlayFabClientAPI.ExecuteCloudScript(tokenRequest)
4val result = res.Result.FunctionResult as Map<*, *>
5val token = result["token"]
6val errorMessage = result["error_message"]
 1var tokenRequest = new ExecuteCloudScriptRequest{
 2  FunctionName = "GetXsollaLoginToken"
 3};
 4
 5PlayFabClientAPI.ExecuteCloudScript(
 6  tokenRequest,
 7  scriptResult =>
 8  {
 9     var functionResult = scriptResult.FunctionResult as Dictionary<string, string>;
10     var token = functionResult["token"];
11  },
12  playfabError => { Debug.LogError($"GetXsollaAccessToken error: {playfabError.ErrorMessage}"); });
 1void UMyClass::GetXsollaToken()
 2{
 3    FClientExecuteCloudScriptRequest tokenRequest;
 4    tokenRequest.FunctionName = TEXT("GGetXsollaLoginToken");
 5
 6    UPlayFabClientAPI::FDelegateOnSuccessExecuteCloudScript onSuccess;
 7    onSuccess.BindUFunction(this, "OnTokenRecieved");
 8
 9    UPlayFabClientAPI::FDelegateOnFailurePlayFabError onFailure;
10    onSuccess.BindUFunction(this, "OnError");
11
12    UPlayFabClientAPI::ExecuteCloudScript(tokenRequest, onSuccess, onFailure, nullptr);
13}
14
15void UMyClass::OnTokenRecieved(FClientExecuteCloudScriptResult result, UObject* customData)
16{
17    const FString& token = result.FunctionResult->GetStringField(TEXT("token"));
18
19    // do something with a token
20}
21
22void UMyClass::OnError(FPlayFabError error, UObject* customData)
23{
24    // handle errors
25}
注:
本例中使用PlayFab SDK方法向云脚本发送请求。您也可以不在项目中添加PlayFab SDK,而是自行实现请求和响应过程。
  1. 在应用程序中,自行实现与API交互的方法或使用艾克索拉SDK
本文对您的有帮助吗?
谢谢!
我们还有其他可改进之处吗? 留言
非常抱歉
请说明为何本文没有帮助到您。 留言
感谢您的反馈!
我们会查看您的留言并运用它改进用户体验。

有用链接

上次更新时间: 2025年10月10日

发现了错别字或其他内容错误? 请选择文本,然后按Ctrl+Enter。

报告问题
我们非常重视内容质量。您的反馈将帮助我们做得更好。
请留下邮箱以便我们后续跟进
感谢您的反馈!
无法发送您的反馈
请稍后重试或发送邮件至doc_feedback@xsolla.com与我们联系。