如何结合Firebase身份认证使用支付中心
如已使用Firebase在应用程序中实现用户身份认证,可在Firebase侧生成一个支付令牌,然后将其传入应用程序的客户端侧来打开支付UI。
如使用此集成方案,需独立实现决定用户国家/地区和支付所使用的货币的逻辑。
集成过程:
- 创建项目。
- 注册发布商帐户并新建一个项目。后续步骤需要用到所创建项目的ID。
- 设置目录:
- 在艾克索拉侧创建一个商品目录。您可以手动添加商品或从Google Play或PlayFab导入。
- 使用
Store 库实现在应用程序客户端侧获取和显示目录。
- 设置商品购买:
- 通过Firebase云函数使用用户和商品数据在应用程序客户端侧创建一个订单。
- 使用
Payments 库实现在应用程序客户端侧打开支付UI。
要完成集成并开始接收真实付款,您需要签署与艾克索拉的许可协议。
您可以在集成的任意阶段签署许可协议,但请知晓审核过程最长可能需要3个工作日。
请使用样本Web应用程序作为示例来实现Firebase身份认证和支付中心的结合使用。样本Web应用程序的源代码在GitHub上提供。
创建项目
注册发布商帐户
发布商帐户是配置艾克索拉功能以及对分析和交易进行操作的主要工具。
注册时指定的公司及应用程序信息将用于创建与艾克索拉许可协议的草稿以及生成合适解决方案的推荐。后续您可以更改这些数据,但在注册时提供正确的信息可以加快签署许可协议的速度。
要注册,请前往发布商帐户,然后创建一个帐户。
发布商帐户的密码可以由英文字母、数字和特殊字符组成,必须包含至少:
- 8个字符
- 一个数字
- 一个大写字母
- 一个小写字母
为确保密码安全性,建议您:
- 每90天至少更改一次密码
- 使用与帐户中过去4个密码不同的新密码
- 使用与其他地方的密码不同的特殊密码
- 勿将密码保存在可以轻易访问的地方
- 使用密码管理器来保存您的密码
发布商帐户使用双因素认证,每次尝试认证时会向您发送一个验证码。
在发布商帐户中创建项目
如有多个应用程序,建议您为每个应用程序创建单独的项目。艾克索拉会根据项目创建过程中指定的数据为您推荐适合的解决方案。
要创建新项目:
- 打开发布商帐户。
- 在侧边栏中,单击创建项目。
- 用英文输入您的项目名称(必需)。
- 选择游戏的一个或多个发布平台(必需)。
- 添加游戏的链接。如游戏没有网站,请添加一个包含游戏信息的资源链接(必需)。
- 选择游戏引擎。
- 选择使用或打算使用的盈利方式。
- 指定游戏是否已发布。如游戏尚未发布,则指定计划发布日期。
- 单击创建项目。您将看到一个包含推荐艾克索拉推荐产品的页面。
集成过程中需提供项目ID,可在发布商帐户中项目名称的旁边找到。
设置目录
在发布商帐户中创建商品
您需要在艾克索拉侧创建一个目录。您可以手动添加商品或从Google Play或PlayFab导入商品。从Google Play导入时,一次最多可以导入100个商品。
以下说明提供的是设置虚拟物品的基本步骤。您可以在后来向目录添加其他商品(虚拟货币、捆绑包、游戏密钥等)、创建商品组、设置促销活动和区域价格等。
要向目录添加具有基本设置的虚拟物品:
- 在发布商帐户中打开您的项目。
- 在侧边栏中单击商店。
- 在虚拟物品窗格中,单击连接。
- 在下拉列表中,选择创建物品。
- 在以下字段中完成物品的基本设置:
- 图片(可选)
- SKU(物品唯一ID)
- 物品名称
- 描述(可选)
- 指定物品价格:
- 将真实货币定价开关设置为开。
- 在默认货币字段,更改货币(可选)并指定物品价格。
- 如更改了默认货币字段中的货币,请在真实货币定价字段中选择同样的货币。
- 将物品状态更改为可用。
- 单击创建物品。
在应用程序客户端侧显示目录
- 将
Store 库添加到您的项目。方法是打开build.gradle
,然后在依赖关系部分添加以下行:
implementation("com.xsolla.android:store:latest.release")
- 在应用程序的客户端侧,添加显示产品目录的UI。
- 实现向艾克索拉服务器请求商品目录。
XStore.getVirtualItems
方法来获取虚拟物品列表。您也可以使用其他示例(样本web应用程序的
- kotlin
package com.xsolla.androidsample
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.xsolla.android.store.XStore
import com.xsolla.android.store.callbacks.GetVirtualItemsCallback
import com.xsolla.android.store.entity.response.items.VirtualItemsResponse
import com.xsolla.androidsample.adapter.BuyItemAdapter
class StoreActivity : AppCompatActivity() {
private lateinit var itemsView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_store)
XStore.init(<projectId>)
initUI()
loadVirtualItems()
}
private fun initUI() {
itemsView = findViewById(R.id.buy_recycler_view)
itemsView.layoutManager = LinearLayoutManager(this)
}
private fun loadVirtualItems() {
val parentActivity = this
XStore.getVirtualItems(object : GetVirtualItemsCallback {
override fun onSuccess(response: VirtualItemsResponse) {
itemsView.adapter = BuyItemAdapter(parentActivity, response.items.filter { item -> item.virtualPrices.isEmpty() && !item.isFree })
}
override fun onError(throwable: Throwable?, errorMessage: String?) {
showNotificationMessage(errorMessage ?: throwable?.javaClass?.name ?: "Error")
}
})
}
private fun showNotificationMessage(message: String) {
Toast.makeText(
baseContext,
message,
Toast.LENGTH_SHORT,
).show()
}
}
在脚本的XStore.init()
初始化区块,指定可在发布商帐户项目名称旁边找到的项目ID。
设置商品购买
使用云函数创建订单
要使用用户和商品数据在艾克索拉侧创建订单,请添加使用为购买创建支付令牌API调用的云函数至项目。该调用将返回一个支付令牌,用于打开支付UI并进行购买。
限制:
- 需在请求支付令牌时传入用户国家/地区或用户IP地址。
- 如未在令牌中传入货币,则货币由国家/地区决定。
- 如在令牌中传入了货币,则用户用该货币进行支付。
要将云函数添加至项目:
- 安装Firebase CLI(命令行界面)。方法是运行以下CLI命令:
npm install -g firebase-tools
- 要将您的项目关联至Firebase项目,请运行以下CLI命令来初始化Firebase项目:
firebase init 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密钥
- 要使用模拟器测试云函数,请运行CLI命令:
firebase emulators:start
- 运行云函数后,您可以在应用程序客户端侧调用以下方法:
- 要在本地调用方法,请使用
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控制台 > Project Settings > Project ID)。
- 要在生产中部署云函数,请运行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控制台 > Project Settings > Project ID)。关于在生产环境中运行功能的详细信息,请参阅Firebase文档。
设置支付UI的启动
- 将
Payments 库添加到您的项目。方法是打开build.gradle
,然后在依赖关系部分添加以下行:
implementation("com.xsolla.android:payments:latest.release")
- 打开
AndroidManifest.xml
,然后添加网络访问的权限:
- xml
<uses-permission android:name="android.permission.INTERNET" />
- 添加订单创建逻辑(调用云函数的
XStore.getXsollaPaymentToken
方法)并使用收到的支付令牌(XPayments.createIntentBuilder()
类打开支付UI)。
- 要调用
getXsollaPaymentToken
方法,请提供以下URL之一,其中{firebase-project-id}
是Firebase项目ID(Firebase控制台 > Project Settings > Project ID):- 对于本地访问 —
https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken
- 对于生产环境中的访问 —
https://us-central1-{firebase-project-id}.cloudfunctions.net/getXsollaPaymentToken
- 对于本地访问 —
示例(样本web应用程序的
- kotlin
package com.xsolla.androidsample.adapter
import android.R.attr.duration
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.xsolla.android.payments.XPayments
import com.xsolla.android.payments.data.AccessToken
import com.xsolla.android.store.entity.response.items.VirtualItemsResponse
import com.xsolla.androidsample.R
import com.xsolla.androidsample.StoreActivity
import org.json.JSONObject
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
class BuyItemAdapter(private val parentActivity: StoreActivity, private val items: List<VirtualItemsResponse.Item>) :
RecyclerView.Adapter<BuyItemViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BuyItemViewHolder {
return BuyItemViewHolder( LayoutInflater.from(parent.context)
.inflate(R.layout.buy_item_sample, parent, false))
}
override fun onBindViewHolder(holder: BuyItemViewHolder, position: Int) {
val item = items[position]
Glide.with(holder.view).load(item.imageUrl).into(holder.itemImage)
holder.itemName.text = item.name
holder.itemDescription.text = item.description
var priceText: String
if(item.virtualPrices.isNotEmpty()) {
priceText = item.virtualPrices[0].getAmountRaw() + " " + item.virtualPrices[0].name
} else {
priceText = item.price?.getAmountRaw() + " " + item.price?.currency.toString()
}
holder.itemPrice.text = priceText
holder.itemButton.setOnClickListener {
Thread {
purchase(item.sku!!)
}.start()
}
}
private fun purchase(sku: String) {
val uid = parentActivity.intent.getStringExtra("uid")
val email = parentActivity.intent.getStringExtra("email")
val jsonBody = JSONObject()
jsonBody.put("data", JSONObject().apply {
put("uid", uid)
put("email", email)
put("sku", sku)
put("returnUrl", "app://xpayment." + parentActivity.packageName)
})
val connection = URL(https://localhost:5001/{firebase-project-id}/us-central1/getXsollaPaymentToken).openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.doOutput = true
val outputStream: OutputStream = connection.outputStream
val writer = BufferedWriter(OutputStreamWriter(outputStream))
writer.write(jsonBody.toString())
writer.flush()
writer.close()
val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val response = connection.inputStream.bufferedReader().use(BufferedReader::readText)
connection.disconnect()
val jsonObject = JSONObject(response)
val token = jsonObject.getString("token")
val orderId = jsonObject.getString("order_id")
val intent = XPayments.createIntentBuilder(parentActivity)
.accessToken(AccessToken(token))
.isSandbox(true)
.build()
parentActivity.startActivityForResult(intent, 1)
} else {
Handler(Looper.getMainLooper()).post {
showNotificationMessage("HTTP request failed with error: $responseCode")
}
}
}
override fun getItemCount() = items.size
private fun showNotificationMessage(message: String) {
Toast.makeText(
parentActivity,
message,
Toast.LENGTH_SHORT,
).show()
}
}
- 添加
onActivityResult()
方法以处理支付结果。
示例(样本web应用程序的
- kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1) {
val (status, _) = XPayments.Result.fromResultIntent(data)
when (status) {
XPayments.Status.COMPLETED -> showNotificationMessage("Payment completed")
XPayments.Status.CANCELLED -> showNotificationMessage("Payment canceled")
XPayments.Status.UNKNOWN -> showNotificationMessage("Payment error")
}
}
}
设置订单状态跟踪
您需要跟踪项目状态,以确保支付成功并向用户发放商品。
获取客户端侧订单状态
要订阅应用程序客户端侧状态更改,请调用XStore.getOrderStatus
方法,并向方法传入以下参数:
listener
—OrderStatusListener
类型的侦听器对象。orderId
— 从购物车下单、一键购买或用虚拟货币购买收到的订单ID。
关于方法的详细使用信息,请参阅跟踪订单状态部分。
在服务器侧获取订单状态
要在艾克索拉侧配置Webhook:
- 在发布商帐户中打开您的项目。
- 在侧边栏中单击项目设置,然后前往Webhooks部分。
- 在Webhook服务器字段中,输入艾克索拉要向其发送Webhook的URL。
进行测试时,可指定https://us-central1-{firebase-project-id}.cloudfunctions.net/webhookFakeResponse
,其中{firebase-project-id}
是Firebase项目ID(Firebase控制台 > Project Settings > Project ID)。本例中,Firebase模拟了对Webhook的成功处理。对于真实项目,您需要添加购买验证逻辑。
要测试Webhook,您还可以选择任意专门网站(如webhook.site)或平台(如ngrok)。
- 复制并保存密钥字段的值。该密钥默认生成,并用于Webhook签名。如要更改该密钥,请单击更新图标。
- 单击启用Webhook。
发现了错别字或其他内容错误? 请选择文本,然后按Ctrl+Enter。