搭配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" +
 17      `&client_secret=${serverOauthClientSecret}`+
 18      `&client_id=${serverOauthClientId}`;
 19
 20  const options = {
 21    hostname: "login.xsolla.com",
 22    port: 443,
 23    path: "/api/oauth2/token",
 24    method: "POST",
 25    headers: {
 26      "Content-Type": "application/x-www-form-urlencoded",
 27      "Content-Length": postData.length,
 28    },
 29  };
 30
 31  return new Promise( (resolve, reject) => {
 32    const req = https.request(options, (res) => {
 33      if (res.statusCode !== 200) {
 34        reject(
 35            new functions.https.HttpsError(
 36                "internal",
 37                "Server token not received"
 38            )
 39        );
 40      }
 41      let body = [];
 42      res.on("data", (d) => {
 43        body.push(d);
 44      });
 45      res.on("end", () => {
 46        try {
 47          body = JSON.parse(Buffer.concat(body).toString());
 48        } catch (e) {
 49          reject(
 50              new functions.https.HttpsError(
 51                  "internal",
 52                  "Malformed server token response"
 53              )
 54          );
 55        }
 56        getClientToken(context.auth.uid, body.access_token, resolve, reject);
 57      });
 58    });
 59    req.on("error", (e) => {
 60      reject(new functions.https.HttpsError(
 61          "internal",
 62          "Internal error while server token flow"
 63      ));
 64    });
 65
 66    req.write(postData);
 67    req.end();
 68  });
 69});
 70
 71// eslint-disable-next-line require-jsdoc
 72function getClientToken(userId, serverToken, resolve, reject) {
 73  const postData = JSON.stringify(
 74      {
 75        "server_custom_id": userId,
 76      }
 77  );
 78
 79  const path =
 80      "/api/users/login/server_custom_id?" +
 81      `projectId=${loginProjectId}&` +
 82      `publisher_project_id=${projectId}`;
 83
 84  const options = {
 85    hostname: "login.xsolla.com",
 86    port: 443,
 87    path: path,
 88    method: "POST",
 89    headers: {
 90      "Content-Type": "application/json",
 91      "Content-Length": postData.length,
 92      "X-Server-Authorization": serverToken,
 93    },
 94  };
 95
 96  const req = https.request(options, (res) => {
 97    if (res.statusCode !== 200) {
 98      reject(
 99          new functions.https.HttpsError(
100              "internal",
101              "Client token not received"
102          )
103      );
104    }
105    let body = [];
106    res.on("data", (d) => {
107      body.push(d);
108    });
109    res.on("end", () => {
110      try {
111        body = JSON.parse(Buffer.concat(body).toString());
112      } catch (e) {
113        reject(
114            new functions.https.HttpsError(
115                "internal",
116                "Malformed client token response"
117            )
118        );
119      }
120      resolve({
121        "token": body.token,
122      });
123    });
124  });
125  req.on("error", (e) => {
126    reject(new functions.https.HttpsError(
127        "internal",
128        "Internal error while client token flow"
129    ));
130  });
131
132  req.write(postData);
133  req.end();
134}
  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" +
11        `&client_secret=${serverOauthClientSecret}` +
12        `&client_id=${serverOauthClientId}`;
13
14    const serverTokenResponse = JSON.parse(
15        http.request(
16            "https://login.xsolla.com/api/oauth2/token",
17            "post",
18            getServerTokenBody,
19            "application/x-www-form-urlencoded",
20            {})
21    );
22
23    let serverToken = ""
24    if ('access_token' in serverTokenResponse) {
25        serverToken = serverTokenResponse.access_token;
26    } else {
27        return {
28            "error_message": "Server token not received"
29        }
30    }
31
32    const getUserTokenHeaders = {
33        "X-Server-Authorization": serverToken
34    }
35
36    const getUserTokenBody = JSON.stringify(
37        {
38            "server_custom_id": currentPlayerId,
39        }
40    );
41
42    const getUserTokenPath =
43        "/api/users/login/server_custom_id?" +
44        `projectId=${loginProjectId}&` +
45        `publisher_project_id=${projectId}`;
46
47    const userTokenResponse = JSON.parse(
48        http.request(
49            "https://login.xsolla.com" + getUserTokenPath,
50            "post",
51            getUserTokenBody,
52            "application/json",
53            getUserTokenHeaders)
54    );
55
56    if ('token' in userTokenResponse) {
57        return {
58            "token": userTokenResponse.token
59        }
60    } else {
61        return {
62            "error_message": "User token not received"
63        }
64    }
65}

  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年9月19日

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

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