2.1.1
With RuStore you can integrate payments in your mobile app.
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).
- 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.
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).
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
, PatchgradleTemplate.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).
Deeplink handling
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.
<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
.
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.
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.
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
Payments availability check
To check payments availability, use theCheckPurchasesAvailability
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).
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.
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.
- Open RuStore Console.
- Navigate to the Applications tab.
- Select the necessary app.
- In the left side menu select Monetization.
- Select product type: Subscriptions or In-App purchases.
- Copy the IDs of the required products. These are product
id
s.
The method returns products list List<Product>
.
Product structure
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 inlanguage
.price
— price in minimum currency units.currency
— ISO 4217 currency code.language
— language specified with BCP 47 code.title
— product name inlanguage
.description
— descriptions inlanguage
.imageUrl
— image URL.promoImageUrl
— promo image URL.subscription
— subscription description, returns only forsubscription
products.
Subscription structure
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 theproduct:language
language.introductoryPriceAmount
— introductory price in minimum currency units.introductoryPricePeriod
— introductory price invoice period.
Structure of the subscription period
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/Status | INVOICE_CREATED | CONFIRMED | PAID |
---|---|---|---|
CONSUMABLE | + | + | |
NON-CONSUMABLE | + | + | |
SUBSCRIPTION | + | + |
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 inlanguage
.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 CONFIRMED
status.
Use the addOnSuccessListener
callback of the confirmPurchase
method to deliver products.
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 method | Payment type | Payment in the PAID status |
---|---|---|
| Two-stage |
|
| One-stage |
|
ConfirmPurchase request
Us theconfirmPurchase
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
.
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.
RuStoreBillingClient.Instance.DeletePurchase(
purchaseId: "purchaseId" ,
onFailure: (error) => {
// Process error
},
onSuccess: () => {
// Process success
}
);
purchaseId
— product ID.
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 withReviewInfo
;RuStoreException
— basic RuStore error from which other errors are inherited.
Errors that may occur are processed by the onFailure
handler.
Error structure
public class RuStoreError {
public string name;
public string description;
}
name
– error name.description
– error description.
Automatic error handling
On calling thePurchaseProduct
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.
public fun RuStoreException.resolveForBilling(context: Context)
You can change this behavior after the initialization by setting the AllowNativeErrorHandling
property.
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.