跳到主要内容

3.2.0

注意

该门户网站正在开发中。文档的完整版本请看这里.

您可以查看快速集成应用程序支付的相关部分

实现示例

查看示例应用程序,了解如何正确集成支付。

支付条件

为了进行支付,必须遵守所有条件:

  1. 用户的设备上安装了RuStore应用程序。
  2. 用户在RuStore应用程序中已授权。
  3. 用户和应用程序不应在RuStore中被封锁。
  4. RuStore 控制台系统中为应用程序启用了购买功能。

服务在俄罗斯联邦以外有一些运作限制。

添加存储库

连接存储库:

repositories {
maven {
url = uri("https://artifactory-external.vkpartner.ru/artifactory/maven")
}
}

集成依赖项

在您的配置文件中添加以下代码以连接依赖项:

dependencies {
implementation("ru.rustore.sdk:billingclient:3.2.0")
}

初始化

在调用库的方法之前,需要先进行其初始化。

使用 RuStoreBillingClientFactory.create() 创建 RuStoreBillingClient:

创建 RuStoreBillingClient

val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
// Опциональные параметры
themeProvider = null,
debugLogs = false,
externalPaymentLoggerFactory = null,
)
  • context - Android 的上下文。可以是任何上下文,在实现中使用 applicationContext。
  • consoleApplicationId - 来自RuStore开发者控制台的应用程序代码(例如:https://console.rustore.ru/apps/111111)。
  • deeplinkScheme - 用于通过第三方应用程序支付后返回您的应用程序的deeplink方案 (例如通过 SberPay 或快速支付系统(SBP))。SDK 会为这个方案生成它自己的宿主。
  • themeProvider - 提供 BillingClientTheme 主题的接口。有2种 BillingClientTheme 主题实现:亮色(Light)和暗色(Dark)。这个接口是可选的,默认情况下使用亮色主题。
  • externalPaymentLoggerFactory - 提供对外部日志器访问的接口。事件日志记录
  • debugLogs - 控制日志记录的标志(Release 构建将自动关闭日志)。事件日志记录

**重要提示。**在 build.gradle 中指定的 ApplicationId 必须与您在 applicationIdRuStore 控制台[系统中发布的] [apk] 文件的 匹配。

传递给 deeplinkScheme 的 deeplink 方案必须与 AndroidManifest.xml 中[处理] deeplink部分指定的方案匹配。

keystore 的签名必须与发布在 RuStore 控制台系统中的应用程序签名匹配。确保使用的 buildType(debug)使用与发布的应用程序相同的签名。

为了通过第三方应用程序(如快速支付系统(SBP)或 SberPay)正确进行支付,您需要正确实现 deeplink 的处理。为此,您需要在 AndroidManifest.xml 中指定带有您项目 scheme 的 intent-filter:

AndroidManifest.xml

<activity
android:name=".YourBillingActivity">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="yourappscheme" />
</intent-filter>

</activity>

中"yourappscheme" - 是您的 deeplink 方案,可以更改为其他方案。

这个方案必须与传递给 init() 的 deeplinkScheme 参数匹配。

接下来,在需要在支付后返回的 Activity(您的商店页面)中,您需要添加:

class YourBillingActivity: AppCompatActivity() {

// Previously created with RuStoreBillingClientFactory.create()
private val billingClient: RuStoreBillingClient = YourDependencyInjection.getBillingClient()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
billingClient.onNewIntent(intent)
}
}

override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
billingClient.onNewIntent(intent)
}
}

了在通过 deeplink 返回时恢复您的应用程序状态,您需要在 AndroidManifest.xml 中添加:

android:launchMode="singleTask"

AndroidManifest.xml

<activity
android:name=".YourBillingActivity"
android:launchMode="singleTask"
android:exported="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">

检查支付功能的可用性

要检查支付功能的可用性,请调用 checkPurchasesAvailability 方法。在调用时,将检查以下条件:

  1. 用户的设备上必须安装 RuStore。
  2. RuStore 必须支持支付功能。
  3. 用户必须在 RuStore 中获得授权。
  4. 用户和应用程序不应在RuStore中被封锁。
  5. 应用程序必须在 RuStore 控制台系统中启用购买功能。

果所有条件都满足,则返回 FeatureAvailabilityResult.Available。否则,返回 FeatureAvailabilityResult.Unavailable(val cause: RuStoreException),其中 cause - 是未满足条件的错误。所有可能的 RuStoreException 错误都被描述在错误处理部分。其他错误(例如,"没有互联网连接")将在 onFailure 中返回。

val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
themeProvider = null,
debugLogs = false,
externalPaymentLoggerFactory = null,
)

中 context - 是 Android 的上下文。

获取产品列表的最新信息

关键词:RuStore 开发者,文档RuStoreSDK,连接支付,产品列表,产品。 要获取产品列表,请使用 getProducts 方法:

val productsUseCase: ProductsUseCase = billingClient.products
productsUseCase.getProducts(productIds = listOf("id1", "id2"))
.addOnSuccessListener { products: List<Product> ->
// Process success
}
.addOnFailureListener { throwable: Throwable ->
// Process error
}
  • productIds: list - 产品标识符列表。列表中的最大长度为 - 2083个字符。

此方法返回

data class Product(
val productId: String,
val productType: ProductType?,
val productStatus: ProductStatus,
val priceLabel: String?,
val price: Int?,
val currency: String?,
val language: String?,
val title: String?,
val description: String?,
val imageUrl: Uri?,
val promoImageUrl: Uri?,
val subscription: ProductSubscription?,
)
  • productId - 产品标识符;
  • productType - 产品类型;
  • productStatus - 产品状态;
  • priceLable - 格式化的产品价格,包括[language]语言的货币符号;
  • price - 以最小单位(戈比)表示的价格;
  • currency - ISO 4217货币代码;
  • language - 使用 BCP 47 编码指定的语言;
  • title - 产品名称,以[language]语言表示;
  • description - 产品描述,以[language]语言表示;
  • imageUrl - 图片链接;
  • promoImageUrl - 促销图片链接;
  • subscription - 订阅描述,仅对于类型为 subscription 的产品返回。

务器对产品请求的响应结构:

产品结构

data class ProductSubscription(
val subscriptionPeriod: SubscriptionPeriod?,
val freeTrialPeriod: SubscriptionPeriod?,
val gracePeriod: SubscriptionPeriod?,
val introductoryPrice: String?,
val introductoryPriceAmount: String?,
val introductoryPricePeriod: SubscriptionPeriod?
)
  • meta - 请求的附加信息;
  • code - 响应代码;
  • errorMessage - 用户的错误消息;
  • errorDescription - 错误消息的详细解释;
  • errors - 请求产品的错误列表;
  • products - 请求的产品列表。

订阅结构

data class ProductSubscription(
val subscriptionPeriod: SubscriptionPeriod?,
val freeTrialPeriod: SubscriptionPeriod?,
val gracePeriod: SubscriptionPeriod?,
val introductoryPrice: String?,
val introductoryPriceAmount: String?,
val introductoryPricePeriod: SubscriptionPeriod?
)
  • subscriptionPeriod - 订阅周期;
  • freeTrialPeriod - 订阅试用期;
  • gracePeriod - 订阅宽限期;
  • introductoryPrice - 格式化的介绍性订阅价格,包括product:language语言的货币符号;
  • introductoryPriceAmount - 以货币最小单位(戈比)表示的介绍性价格;
  • introductoryPricePeriod - 介绍性价格计算周期。

订阅周期结构

data class SubscriptionPeriod(
val years: Int,
val months: Int,
val days: Int,
)
  • years - 年数;
  • months - 月数;
  • days - 天数。

获取用户购买列表

关键词:RuStore 开发者,文档,RuStoreSDK,连接支付,购买,购买列表。要获取用户的购买列表,请使用 getPurchases 方法。getPurchases

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.purchaseProduct(
productId = productId,
orderId = UUID.randomUUID().toString(),
quantity = 1,
developerPayload = null,
).addOnSuccessListener { paymentResult: PaymentResult ->
when (paymentResult) {
// Process PaymentResult
}
}.addOnFailureListener { throwable: Throwable ->
// Process error
}

购买结构

data class Purchase(
val purchaseId: String?,
val productId: String,
val productType: ProductType?,
val invoiceId: String?,
val description: String?,
val language: String?,
val purchaseTime: Date?,
val orderId: String?,
val amountLabel: String?,
val amount: Int?,
val currency: String?,
val quantity: Int?,
val purchaseState: PurchaseState?,
val developerPayload: String?,
val subscriptionToken: String?
)
  • purchaseId - 购买标识符;

  • productId - 产品标识符;

  • productType - 产品类型;

  • invoiceId - 账单标识符;

  • description - 购买描述;

  • language - 使用 BCP 47 编码指定的语言;

  • purchaseTime - 购买时间(RFC 3339 格式);

  • orderId - 应用程序生成的唯一支付标识符(uuid);

  • amountLable - 格式化的购买价格,包括[language]语言的货币符号;

  • amount - 以最小货币单位表示的价格;

  • currency - ISO 4217货币代码;

  • quantity - 产品数量;

  • purchaseState - 购买状态:

    • 购买状态的可能值:
      • CREATED - 已创建;
      • INVOICE_CREATED - 已创建,等待支付;
      • CONFIRMED - 已确认;
      • PAID - 已支付;
      • CANCELLED - 购买已取消;
      • CONSUMED - 已确认购买消耗;
      • CLOSED - 订阅已取消。
  • developerPayload - 开发者指定的字符串,包含关于订单的附加信息;

  • subscriptionToken - 用于在服务器端验证购买的令牌。了解有关在服务器上验证购买的更多信息。

务器对购买请求的响应结构:

产品结构

data class PurchasesResponse(
override val meta: RequestMeta?,
override val code: Int,
override val errorMessage: String?,
override val errorDescription: String?,
override val errors: List<DigitalShopGeneralError>?,
val purchases: List<Purchase>?,
) : ResponseWithCode
  • meta - 请求的附加信息;
  • code - 响应代码;
  • errorMessage - 用户的错误消息;
  • errorDescription - 错误消息的详细解释;
  • errors - 请求产品的错误列表;
  • purchases - 请求的购买列表。

状态模型 (purchaseState):

订阅购买状态模型 (SUBSCRIPTIONS):

非消耗性产品购买状态模型 (NON-CONSUMABLES):

消耗性产品购买状态模型 (CONSUMABLES):

获取购买信息

关键词:RuStore,开发者,文档,RuStoreSDK,连接支付,购买,购买列表。要获取关于购买的信息,请使用 getPurchaseInfo 方法。

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.purchaseProduct(productId).addOnSuccessListener { paymentResult ->
if (paymentResult is PaymentResult.Success) {
val subscriptionToken = paymentResult.subscriptionToken
yourApi.validate(subscriptionToken)
}
}

购买结构

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.getPurchases().addOnSuccessListener { purchases ->
purchases.forEach { purchase ->
yourApi.validate(purchase.subscriptionToken)
}
  • purchaseId - 购买标识符;

  • productId - 产品标识符;

  • productType - 产品类型;

  • invoiceId - 账单标识符;

  • description - 购买描述;

  • language - 使用 BCP 47 编码指定的语言;

  • purchaseTime - 购买时间(RFC 3339 格式);

  • orderId - 应用程序生成的唯一支付标识符(uuid);

  • amountLable - 格式化的购买价格,包括[language]语言的货币符号;

  • amount - 以最小货币单位表示的价格;

  • currency - ISO 4217货币代码;

  • quantity - 产品数量;

  • purchaseState - 购买状态:

    • 购买状态的可能值:
      • CREATED - 已创建;
      • INVOICE_CREATED - 已创建,等待支付;
      • CONFIRMED - 已确认;
      • PAID - 已支付;
      • CANCELLED - 购买已取消;
      • CONSUMED - 已确认购买消耗;
      • CLOSED - 订阅已取消。
  • developerPayload - 开发者指定的字符串,包含关于订单的附加信息;

  • subscriptionToken - 用于在服务器端验证购买的令牌。了解有关在服务器上验证购买的更多信息。

状态模型 (purchaseState):

订阅购买状态模型 (SUBSCRIPTIONS):

非消耗性产品购买状态模型 (NON-CONSUMABLES):

消耗性产品购买状态模型 (CONSUMABLES):

购买产品

关键词:RuStore,开发者,文档,RuStoreSDK,连接支付,购买产品。要调用购买产品,请使用 purchaseProduct 方法:

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.confirmPurchase(purchaseId = "purchaseId", developerPayload = null)
.addOnSuccessListener {
// Process success
}.addOnFailureListener { throwable: Throwable ->
// Process error
}
  • productId: String - 产品标识符;
  • orderId: String - 订单标识符,在AnyApp端创建(可选。如果未指定,则会自动生成);
  • quantity: Int - 产品数量(可选);
  • developerPayload - AnyApp 开发者的附加信息(可选)。

购买结果的结构

public sealed interface PaymentResult {     

public data class Success(
val orderId: String?,
val purchaseId: String,
val productId: String,
val invoiceId: String,
val subscriptionToken: String? = null,
) : PaymentResult

public data class Cancelled(
val purchaseId: String,
) : PaymentResult

public data class Failure(
val purchaseId: String?,
val invoiceId: String?,
val orderId: String?,
val quantity: Int?,
val productId: String?,
val errorCode: Int?,
) : PaymentResult

public object InvalidPaymentState : PaymentResult()
}
  • Success成功完成数字产品购买的结果。
  • Failure购买数字产品时错误的结果。
  • Cancelled数字产品购买被取消的结果。
  • InvalidPaymentStateSDK支付错误。可能因deeplink回调不正确而发生。|

购买的服务器端验证

如果您需要在服务器端验证成功的购买,您可以使用 purchaseProduct 成功购买产品时返回的 PurchaseResult 中的 subscriptionToken。

SubscriptionToken由购买的invoiceId和RuStore的userId组成,用点书写:"$invoiceId.$userId"。

从购买结果中获取subscriptionToken

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.getPurchases().addOnSuccessListener { purchases ->
purchases.forEach { purchase ->
yourApi.validate(purchase.subscriptionToken)
}

还可以从 Purchase 实体中获取 subscriptionToken。可以通过使用 getPurchases() 方法来获取 Purchase 实体。

从购买结果中获取subscriptionToken

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.purchaseProduct(productId).addOnSuccessListener { paymentResult ->
if (paymentResult is PaymentResult.Success) {
val subscriptionToken = paymentResult.subscriptionToken
yourApi.validate(subscriptionToken)
}
}

购买消耗(确认)

关键词:RuStore,开发者,文档,RuStoreSDK,购买确认,消耗。RuStore 包含以下类型的产品:

  • CONSUMABLE - 消耗的(可以多次购买,例如应用中的水晶);
  • NON_CONSUMABLE - 非消耗的(只能购买一次,例如禁用应用程序中的广告);
  • SUBSCRIPTION - 订阅(可以购买一段时间,例如流媒体服务的订阅)。

有处于 PurchaseState.PAID 状态的 CONSUMABLE 类型产品需要消耗。

您可以使用 confirmPurchase 方法来消耗购买:

调用确认方法

val purchasesUseCase: PurchasesUseCase = billingClient.purchases
purchasesUseCase.confirmPurchase(purchaseId = "purchaseId", developerPayload = null)
.addOnSuccessListener {
// Process success
}.addOnFailureListener { throwable: Throwable ->
// Process error
}
  • purchaseId - 购买标识符;
  • developerPayload - 开发者指定的字符串,包含订单的附加信息(可选)。

取消购买

关键词:RuStore,开发者,文档,RuStoreSDK,购买取消。要取消购买,您可以使用 deletePurchase 方法:

val purchasesUseCase: PurchasesUseCase = billingClient.purchasespurchasesUseCase.deletePurchase(purchaseId =  \"purchaseId\" )    \`\`.addOnSuccessListener {        \`\`// Process success    \`\`}.addOnFailureListener { throwable: Throwable -\>        \`\`// Process error    \`\`}
  • purchaseId - 购买标识符。

果您有与删除购买相关的逻辑,请使用此方法。购买将在20分钟后自动取消,或者在同一客户端进行重复购买时取消。

错误处理

可能的错误:

  • RuStoreNotInstalledException() - 用户设备上未安装 RuStore。
  • RuStoreOutdatedException() - 用户设备上安装的 RuStore 不支持支付。
  • RuStoreUserUnauthorizedException() - 用户未在 RuStore 中授权。
  • RuStoreApplicationBannedException() - 应用程序在 RuStore 中被禁止。
  • RuStoreUserBannedException() - 用户在 RuStore 中被禁止。
  • RuStoreException(message: String) - RuStore的基本错误,其他错误均继承自此。

用 RuStoreBillingClient.purchases.purchaseProduct() 方法时,错误会自动处理。

要向用户显示错误对话框,您可以使用 resolveForBilling 方法:

错误处理

public fun RuStoreException.resolveForBilling(context: Context)

购买消耗和取消的流程

关键词:RuStore,开发者,文档,RuStoreSDK,消耗,取消,购买确认。未完成的支付处理应由 AnyApp 的开发者执行。

如果出现以下情况,应使用取消购买方法(deletePurchase):

1。获取购买列表方法(getPurchases)返回了具有以下状态的购买:

  1. PurchaseState.CREATED.
  2. PurchaseState.INVOICE_CREATED.

某些情况下,通过银行应用程序(如快速支付系统(SBP)、SberPay、T-Pay等)支付后,当用户随后返回到 AnyApp 应用程序时,购买的状态可能仍然是 PurchaseState.INVOICE_CREATED。这与银行端处理购买的时间有关。因此,开发者需要正确地将获取购买列表的逻辑与屏幕生命周期相结合。

解决这个问题的另一种方法是,只有在用户与应用程序交互时,才取消处于 PurchaseState.INVOICE_CREATED 状态的购买。例如,可以将这个逻辑放到一个单独的按钮中。

购买方法 (purchaseProduct) 返回 PaymentResult.Cancelled

购买方法 (purchaseProduct) 返回 PaymentResult.Failure

如果获取购买列表的方法 (getPurchases) 返回了类型为 CONSUMABLE 且状态为 PurchaseState.PAID 的购买,则需要使用产品消耗方法 (confirmPurchase)。

事件日志记录

关键词:RuStore,开发者,文档,RuStoreSDK,连接支付,事件日志记录。如果您想在支付库中日志记录事件,可以在调用 RuStoreBillingClientFactory.create() 时添加 externalPaymentLoggerFactory 和 debugLogs 参数(这些参数对初始化是可选的):

val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
externalPaymentLoggerFactory = { tag -> PaymentLogger(tag) },
debugLogs = true
)

class PaymentLogger(private val tag: String) : ExternalPaymentLogger {
override fun d(e: Throwable?, message: () -> String) {
Log.d(tag, message.invoke(), e)
}

override fun e(e: Throwable?, message: () -> String) {
Log.e(tag, message.invoke(), e)
}

override fun i(e: Throwable?, message: () -> String) {
Log.i(tag, message.invoke(), e)
}

override fun v(e: Throwable?, message: () -> String) {
Log.v(tag, message.invoke(), e)
}

override fun w(e: Throwable?, message: () -> String) {
Log.w(tag, message.invoke(), e)
}
}

用日志记录的参数:

  • externalPaymentLoggerFactory - 允许创建将库日志传递到宿主应用的记录器的接口。
  • debugLogs - 启用日志记录(Release 构建会自动关闭日志)

中,PaymentLogger - 是支付事件日志记录实现的一个示例。

更换主题

SDK 支持通过 BillingClientThemeProvider 提供者接口的动态主题更换:

val billingClient: RuStoreBillingClient = RuStoreBillingClientFactory.create(
context = app,
consoleApplicationId = "111111",
deeplinkScheme = "yourappscheme",
themeProvider? = BillingClientThemeProviderImpl(),
)

class BillingClientThemeProviderImpl: BillingClientThemeProvider {

override fun provide(): BillingClientTheme {
val darkTheme = ....
if(darkTheme){
BillingClientTheme.Dark
} else {
BillingClientTheme.Light
}
}
}}
}

错误处理

关键词:RuStore,开发者,文档,RuStoreSDK,连接支付,错误,处理错误。可能的错误

  • RuStoreNotInstalledException() - 用户设备上未安装 RuStore;
  • RuStoreOutdatedException() - 用户设备上安装的 RuStore 不支持支付;
  • RuStoreUserUnauthorizedException() - 用户未在 RuStore 中授权;
  • RuStoreApplicationBannedException() - 应用程序在 RuStore 中被禁止;
  • RuStoreUserBannedException() - 用户在 RuStore 中被禁止;
  • RuStoreException(message: String) - RuStore的基本错误,其他错误均继承自此。

用 RuStoreBillingClient.purchases.purchaseProduct() 方法时,错误会自动处理。

要向用户显示错误对话框,您可以使用 resolveForBilling 方法:

public fun RuStoreException.resolveForBilling(context: Context)

支付SDK错误代码

在"ResponseWithCode"接口的"code"字段中,可能出现的错误描述:

http codecode描述
2000请求成功
40040001请求参数错误 - 缺少必填参数/参数格式错误
40040003应用未找到
40040004应用状态为"inactive"
40040005产品未找到
40040006产品状态为"inactive"
40040007产品类型无效。支持的类型包括:"consumable","non-consumable","subscription"
40040008已存在具有该"order_id"的购买
40040009当前客户已有此产品的购买记录,状态为"invoice_created"。需要提示客户支付/取消购买
40040010对于"consumable"类型产品。当前客户已有此产品的购买记录,状态为"paid"。首先需要在设备上确认购买消耗,然后才能发送购买此产品的下一个请求。
40040011对于"non-consumable"类型产品。当前客户已有此产品的购买记录,状态为"pre_confirmed"/"confirmed"。该产品已被购买。产品不能多次销售
40040012对于"subscription"类型产品。当前客户已有此产品的购买记录,状态为"pre_confirmed"/"confirmed"。该产品已被购买。产品不能多次销售
40040013对于"subscription"类型产品。在请求订阅服务的产品列表"GET/products"("serviceId","user_id")时,数据未被接收。
40040014请求中缺少必要属性
40040015在更新购买时无法更改状态(禁止转换)
40040016购买非消耗产品的订阅时,指定的 quantity > 1
40040017产品已删除,新的购买不可用
40040018无法消耗类型为"产品类型"的产品
40140101无效的令牌
40140102令牌的有效期已过
40340301请求的资源访问被拒绝(未授权)
40340302当前令牌不授权当前调用(方法被禁止)
40340303请求中的应用程序标识符与令牌不匹配
40340305令牌类型错误
40440401未找到
40840801请求中指定的通知等待时间已过
50050***支付服务的内部错误