Skip to main content

2.1.1

With RuStore you can integrate payments in your mobile app.

tip

If you are in doubt read the instruction in the usage scenarios.

Implementation example

Look at the example app to learn how to integrate our SDK.

Prerequisites

  • App uploaded to RuStore Console.
  • App passed moderation (you don't have to publish the app).
Important
  • Test build signature (for example: debug) of the app must match the signature of the app build that was uploaded to the console and passed moderation (for example, release).
  • The current version of RuStore is installed on the user's device.
  • User is authorized in RuStore.
  • The user and the app are not banned in RuStore.
  • In-app purchases for the app are enabled in RuStore Console.
caution

The service has some restrictions to work outside of Russia.

Getting started

Download RuStore Billing SDK and import it to the project (Assets > Import Package > Custom Package). Dependencies are connected automatically with the External Dependency Manager (included in the SDK).

tip

If you use macOS, change Archive Utility settings. Uncheck Keep expanding if possible. Otherwise, the project archive will not be downloaded correctly.

You can also clone the code using Git.

For the SDK dependencies to work correctly, set the following settings.

  • Edit > Project Settings > Player Settings > Publishing Settings, enable Custom Main Gradle Template and Custom Gradle Properties Template.
  • Assets > External Dependencies Manager > Android Resolver > Settings, enable Use Jetifier, Patch mainTemplate.gradle, Patch gradleTemplate.properties.

Also, make sure to do Assets > External Dependencies Manager > Android Resolver > Force Resolve.

Minimum API level must be set to at least 24. Application minification (ProGuard/R8) is not currently supported; it must be disabled in the project settings (File > Build Settings > Player Settings > Publishing Settings > Minify).

RuStore SDK uses deeplink to handle third-party payment applications. This makes it easier to pay with third-party apps and return to your app.

To configure deeplinks functionality in your app and RuStore SDK, define deeplinkScheme in your AndroidManifest file and redefine the onNewIntent method of your activity.

AndroidManifest.xml
<activity
android:name="ru.rustore.unitysdk.RuStoreUnityActivity" android:theme ="@style/UnityThemeSelector" android:exported ="true">
<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>

Replace yourappscheme in the example above with the name of your scheme. For example, ru.package.name.rustore.scheme.

Then, extend the UnityPlayerActivity class and add incoming intent processing in onNewIntent.

RuStoreUnityActivity class example
package ru.rustore.unitysdk;

import android.os.Bundle;
import android.content.Intent;
import ru.rustore.unitysdk.billingclient.RuStoreUnityBillingClient;
import com.unity3d.player.UnityPlayerActivity;

public class RuStoreUnityActivity extends UnityPlayerActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
RuStoreUnityBillingClient.onNewIntent(getIntent());
}
}
@Override protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
RuStoreUnityBillingClient.onNewIntent(intent);
}
}

Put the Java file with the UnityPlayerActivity extension code in the project folder Assets. If you already have your own UnityPlayerActivity extension, include the code of onCreate and onNewIntent functions into it.

Initialization

Initialize the library before calling its methods.

In the editor menu select Window > RuStoreSDK > Settings > Billing Client.

RuStoreBillingClient.Instance.Init();

Once you’re required other settings, you can pass them directly from the code.

RuStoreBillingClient initialization
var config =  new RuStoreBillingClientConfig() {
consoleApplicationId = "123456" ,
deeplinkPrefix = "yourappscheme" ,
allowNativeErrorHandling = true,
enableLogs = true
};

RuStoreBillingClient.Instance.Init(config);
  • consoleApplicationId — идентификатор приложения из консоли RuStore.

  • deeplinkPrefix — deeplink URL address. You can use any unique name (for example: yourappscheme).
  • allowNativeErrorHandling — allow handling errors using native SDK (see more in Error handling).
  • enableLogs — enable logging.
note
Tye deeplink scheme that is passed in deeplinkPrefix must match the scheme specified in AndroidManifest.xml (see Deeplink processing).

If you need to check that the library is initialized, use RuStoreBillingClient.isInitialized, which returns true if the library is initialized and false if the init function has not been called yet.

var isInitialized = RuStoreBillingClient.Instance.IsInitialized;

Before you start

Payment appRuStore Server RuStore_Billing_ClientYour serverYour appUserPayment appRuStore Server RuStore_Billing_ClientYour serverYour appUserPayments availability check[Optional]Server validation[Mandatory]Purchasing consumable product[Mandatory]Deeplink processing for paying with SBP, SberPay, etc.Product purchaseStarts your app checkPurchasesAvailabilityResult getProductsProducts list of your appDisplaying list of available purchasesPurchasing product purchaseProductRequest payment methodMaking paymentPayment methodPayment resultPayment infoServer validation (public API)Reliable purchase informationDeliver product to the userValidation result confirmPurchaseConsumption result purchaseProductRequest payment methodSpecified SBP/SberPay/T-PayStart payment processPayment scenarioPurchase paymentReturn to app OnNewIntentPayment resultDisplaying payment screen with the result

Payments availability check

To check payments availability, use the CheckPurchasesAvailability method. If all above conditions are met, FeatureAvailabilityResult.isAvailable == true is returned. Otherwise, FeatureAvailabilityResult.isAvailable == false is returned, where FeatureAvailabilityResult.cause is an error of a failed condition.

All possible errors RuStoreException are described in Error handling. Other errors are returned in onFailure. (See Task API).

Calling CheckPurchasesAvailability
RuStoreBillingClient.Instance.CheckPurchasesAvailability( 
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
if (response.isAvailable) {
// Process purchases available
} else {
// Process purchases unavailable
}
}
);

Working with SDK

Retrieving products list

You verified that payments are available and the users are able to make purchases. Now you can request products list. Use the GetProducts method to request the information about products added to your app in RuStore Console.

Calling GetProducts
RuStoreBillingClient.Instance.GetProducts(productIds,
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
// Process response
}
);

string[] productIds — the list of product IDs. Must not exceed 100 entries.

To specify id of the products needed for the method, do the following.

  1. Open RuStore Console.
  2. Navigate to the Applications tab.
  3. Select the necessary app.
  4. In the left side menu select Monetization.
  5. Select product type: Subscriptions or In-App purchases.
  6. Copy the IDs of the required products. These are product ids.

The method returns products list List<Product>.

Product structure

Product class
public class Product {
public enum ProductStatus {
ACTIVE,
INACTIVE
}
public enum ProductType {
NON_CONSUMABLE,
CONSUMABLE,
SUBSCRIPTION
}
public string productId;
public ProductType productType;
public ProductStatus productStatus;
public string priceLabel;
public int price;
public string currency;
public string language;
public string title;
public string description;
public string imageUrl;
public string promoImageUrl;
public ProductSubscription subscription;
}
  • productId — product ID assigned to product in RuStore Console (mandatory).
  • productType — product type: CONSUMABLE/NON-CONSUMABE/SUBSCRIPTION.
  • productStatus — product status.
  • priceLable — formatted product price, including currency symbol in language.
  • price — price in minimum currency units.
  • currency — ISO 4217 currency code.
  • language — language specified with BCP 47 code.
  • title — product name in language.
  • description — descriptions in language.
  • imageUrl — image URL.
  • promoImageUrl — promo image URL.
  • subscription — subscription description, returns only for subscription products.

Subscription structure

ProductSubscription class
public class ProductSubscription {
public SubscriptionPeriod subscriptionPeriod;
public SubscriptionPeriod freeTrialPeriod;
public SubscriptionPeriod gracePeriod;
public string introductoryPrice;
public string introductoryPriceAmount;
public SubscriptionPeriod introductoryPricePeriod;
}
  • subscriptionPeriod — subscription period.
  • freeTrialPeriod — subscription trial period.
  • gracePeriod — subscription grace period.
  • introductoryPrice — formated introductory price with the currency symbol in the product:language language.
  • introductoryPriceAmount — introductory price in minimum currency units.
  • introductoryPricePeriod — introductory price invoice period.

Structure of the subscription period

SubscriptionPeriod class
public class SubscriptionPeriod {
public int years;
public int months;
public int days;
}
  • years — amount of years.
  • months — amount of months.
  • days — amount of days.

Getting products list

This method returns purchases with the following statuses.

Type/StatusINVOICE_CREATEDCONFIRMEDPAID
CONSUMABLE++
NON-CONSUMABLE++
SUBSCRIPTION++
note

This method returns incomplete purchases that require attention. Also, it shows confirmed subscriptions and consumable products that cannot be purchased more than once.

Go get the user's purchases list, use the GetPurchases method.

RuStoreBillingClient.Instance.GetPurchases(
onFailure: (error) => {
// Process error
},
onSuccess: (response) => {
// Process response
}
);

The method returns List<Purchase> response — purchases list.

Purchase structure

public class Purchase {
public enum PurchaseState
{
CREATED,
INVOICE_CREATED,
CONFIRMED,
PAID,
CANCELLED,
CONSUMED,
CLOSED
}
public string purchaseId;
public string productId;
public string description;
public string invoiceId;
public string language;
public DateTime purchaseTime;
public string orderId;
public string amountLabel;
public int amount;
public string currency;
public int quantity;
public PurchaseState purchaseState;
public string developerPayload;
public string subscriptionToken;
}
  • purchaseId — product ID.
  • productId — product ID assigned to product in RuStore Console (mandatory).
  • description — descriptions in language.
  • invoiceId — invoice ID.
  • language — language specified with BCP 47 code.
  • purchaseTime — purchase time.
  • orderId — payment ID generated by the app (optional). If you specify this parameter in your system, you will receive it via our API. Если не укажете, он будет сгенерирован автоматически (uuid). Максимальная длина 150 символов.
  • amountLable — formatted purchase price, including currency symbol.
  • amount — price in minimum currency units.
  • currency — ISO 4217 currency code.
  • quantity — количество продукта (необязательный параметр — если не указывать, будет подставлено значение 1).
  • purchaseState — purchase state:
  • developerPayload — string with additional order information, that you can specify on purchase initialization.
  • subscriptionToken — purchase token for server validation .

Consume (confirm) purchase

Products that require confirmation

Please, keep in mind the purchase type. Confirmation is only needed if your product is CONSUMABLE and can be purchased more than once.

To correctly deliver such products, confirm purchases with the confirmPurchase method. On delivering a product, use server validation. Deliver your product only after the payment (invoice) is in the CONFIRMED status. Use the addOnSuccessListener callback of the confirmPurchase method to deliver products.

Attention!

The PAID status is intermediate and meas that the user's money is put on hold on their card, so you need to confirm the purchase.

The exceptions are SBP or mobile payment — see the explanation below.

When paying for CONSUMABLE products with SBP or mobile payment, one-stage payment is used, although the payment model is as in two-stage payments. It means that what a payment is in the PAID status, when paying with SBP or mobile payment, the money is withdrawn from the buyer's account, as well as the developer's fee is withdrawn from their's. In this case, if a purchase is canceled from the PAID status the refund is made, not reverse. The developer's fee will not be returned. At the same time, to complete the purchase you still have to confirm purhchase — see the table below.

Payment methodPayment typePayment in the PAID status
  • bank cards;
  • Sber ID;
  • SberPay;
  • T-Pay;
  • VK Pay.
Two-stage
  • Money is on hold on the buyer's account.
  • Developer's fee is not applied.
  • Payment cancellation (reverse) is available.
  • SBP;
  • mobile payment.
One-stage
  • Money is withdrawn from the buyer's account.
  • The developer's fee is applied.
  • If a purchase is canceled form the PAID status the transaction is refunded, not reverse reversed. The developer's fee will not be returned.

ConfirmPurchase request

Us the confirmPurchase method to confirm a purchase. Purchase confirmation request must be accompanied by the delivery of the product. After calling the confirmation method the purchase changes its state to CONSUMED.

Calling ConfirmPurchase
RuStoreBillingClient.Instance.ConfirmPurchase(
purchaseId: "purchaseId" ,
onFailure: (error) => {
// Process error
},
onSuccess: () => {
// Process success
}
);
  • purchaseId — product ID.

Purchase cancellation

To cancel a purchase, use the DeletePurchase method.

Calling DeletePurchase
RuStoreBillingClient.Instance.DeletePurchase(
purchaseId: "purchaseId" ,
onFailure: (error) => {
// Process error
},
onSuccess: () => {
// Process success
}
);
  • purchaseId — product ID.
info

Use this method if your app logic is related to purchase cancellation. The purchase is canceled automatically after a 20-min timeout, or upon a second purchase from the same customer.

Error handling

Possible errors

  • RuStoreNotInstalledException — RuStore is not installed on the user's device;
  • RuStoreOutdatedException — RuStore version installed on the user's device does not support this SDK;
  • RuStoreUserUnauthorizedException — user is not authorized in RuStore;
  • RuStoreRequestLimitReached — not enough time has passed since the process was last shown;
  • RuStoreReviewExists — this user has already rated your app;
  • RuStoreInvalidReviewInfo — problems with ReviewInfo;
  • RuStoreException — basic RuStore error from which other errors are inherited.

Errors that may occur are processed by the onFailure handler.

Error structure

RuStoreError class
public class RuStoreError {
public string name;
public string description;
}
  • name – error name.
  • description – error description.

Automatic error handling

On calling the PurchaseProduct method, errors are handled automatically.

If allowNativeErrorHandling == true is passed the during SDK initialization, both the Failure handler is called and the user is shown the error dialog.

Kotlin
public fun RuStoreException.resolveForBilling(context: Context)

You can change this behavior after the initialization by setting the AllowNativeErrorHandling property.

C#
RuStoreBillingClient.Instance.AllowNativeErrorHandling = false;

Purchase confirmation and cancellation scenario

Due to the change of the product purchase model result, the business logic of the purchase confirmation and cancellation was also changed.

Use the deletePurchase method in the following cases:

  • the getPurchases method returned a purchase with one of the following statuses:

    • PurchaseState.CREATED;
    • PurchaseState.INVOICE_CREATED;
  • the purchaseProduct method returned:

    • PaymentResult.Cancelled;
    • PaymentResult.Failure.

Use the (confirmPurchase) method if the (getPurchases) method returned a CONSUMABLE purchase with the PurchaseState.PAID status.