6.0.0
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.
All operations with the client are also accessible from Blueprints
. Below is an initialization example.
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 user and the app are not banned in RuStore.
- In-app purchases for the app are enabled in RuStore Console.
- Unreal Engine 4.26 and later.
The service has some restrictions to work outside of Russia.
Getting started
- Copy the contents of the
Plugins
folder from the official RuStore repository on GitFlic into thePlugins
folder of your project. - Restart Unreal Engine.
- In the plugins list (Edit > Plugins > Project > Mobile) select the RuStoreBilling and RuStoreCore plugins.
- Connect the
RuStoreCore
andRuStoreBilling
modules in thePublicDependencyModuleNames
list of theYourProject.Build.cs
file. - In the project settings (Edit > Project Settings > Android) set the Minimum SDK Version parameter at least to 24 and the Target SDK Version parameter to 31 or later.
Deeplink handling
For payments via third party apps (SBP, SberPay и др.) to work, implement deeplink processing correctly. The RuStore Billing plugin will automatically add an additional activity
with the required intent-filter
to AndroidManifest.xml
(see below). You can change this behavior in the RuStoreBilling_UPL_Android.xml
file.
<activity android:name="com.Plugins.RuStoreBilling.RuStoreIntentFilterActivity" android:exported="true" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Set your appscheme -->
<data android:scheme="@string/rustore_app_scheme" />
</intent-filter>
</activity>
String resource @string/rustore_app_scheme
needs to be implemented in your project.
@string/rustore_app_scheme
— your deeplink scheme. This scheme must match the deeplink scheme specified during the initialization of the billing client.
Initialization
Initialize the library before calling its methods.
FURuStoreBillingClientConfig config;
config.consoleApplicationId = "123456";
config.deeplinkScheme = "yourscheme";
config.allowNativeErrorHandling = false;
config.enableLogs = false;
URuStoreBillingClient::Instance()->Init(config);
All operations with the client are also accessible from Blueprints
. Below is an initialization example.
-
consoleApplicationId
— application code from the RuStore console (example:https://console.rustore.ru/apps/123456
).To get the app ID, copy digits from the URL address of the app page in the RuStore Console that appear after
apps/
. -
deeplinkSheme
— 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.
The Init()
call ties the object to the scene root, and, If no further work with the object is needed, execute the Dispose()
method to free memory.
Deinitialization
URuStoreBillingClient::Instance()->Dispose();
Initialization check
If you need to check whether the library is initialized, use the GetIsInitialized()
method. The method returns true
if the library is initialized and false
if Init
hasn't been called yet.
bool isInitialized = URuStoreBillingClient::Instance()->GetIsIninialized();
Before you start
Payments availability check
During the purchases availability check, the following conditions are verified.
- The current version of RuStore is installed on the user's device.
- RuStore app supports payments.
- 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.
To check payments availability, use the CheckPurchasesAvailability
method.
Every asynchronous request gets a requestId
that is unique within a single app launch. Each event returns requestId
of the request that triggered this event.
long requestId = URuStoreBillingClient::Instance()->CheckPurchasesAvailability(
[]( long requestId, TSharedPtr<FURuStoreFeatureAvailabilityResult, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
The Success
callback returns the FURuStoreFeatureAvailabilityResult
structure in the Response
parameters (see below).
USTRUCT(BlueprintType)
struct RUSTORECORE_API FURuStoreFeatureAvailabilityResult
{
GENERATED_USTRUCT_BODY()
FURuStoreFeatureAvailabilityResult()
{
isAvailable = false;
}
UPROPERTY(BlueprintReadWrite)
bool isAvailable;
UPROPERTY(BlueprintReadWrite)
FURuStoreError cause;
};
-
isAvailable
— compliance with the payments availability conditions (true
/false
). -
cause
— error information.
All possible cause
errors are described in Failure
.
The Failure
callback returns the FURuStoreError
structure with the error information in the Error
parameter. The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Getting started 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.
long requestId = URuStoreBillingClient::Instance()->GetProducts(
productIds,
[]( long requestId, TSharedPtr<FURuStoreProductsResponse, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
TArray<FString> 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 Success
callback returns the FURuStoreFeatureAvailabilityResult
structure in the Response
parameter (see below).
GetProducts
response
USTRUCT(BlueprintType)
struct FURuStoreProductsResponse
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
TArray<FURuStoreProduct> products;
};
products
— products list
Product structure
USTRUCT(BlueprintType)
struct FURuStoreProduct
{
GENERATED_USTRUCT_BODY()
FURuStoreProduct()
{
productId = "";
productType = EURuStoreProductType::NON_CONSUMABLE;
productStatus = EURuStoreProductStatus::INACTIVE;
priceLabel = "";
price = 0;
currency = "";
language = "";
title = "";
description = "";
imageUrl = "";
promoImageUrl = "";
}
UPROPERTY(BlueprintReadOnly)
FString productId;
UPROPERTY(BlueprintReadOnly)
EURuStoreProductType productType;
UPROPERTY(BlueprintReadOnly)
EURuStoreProductStatus productStatus;
UPROPERTY(BlueprintReadOnly)
FString priceLabel;
UPROPERTY(BlueprintReadOnly)
int price;
UPROPERTY(BlueprintReadOnly)
FString currency;
UPROPERTY(BlueprintReadOnly)
FString language;
UPROPERTY(BlueprintReadOnly)
FString title;
UPROPERTY(BlueprintReadOnly)
FString description;
UPROPERTY(BlueprintReadOnly)
FString imageUrl;
UPROPERTY(BlueprintReadOnly)
FString promoImageUrl;
UPROPERTY(BlueprintReadOnly)
FURuStoreProductSubscription 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.
Product type
UENUM(BlueprintType)
enum class EURuStoreProductType : uint8
{
NON_CONSUMABLE UMETA(DisplayName = "NON_CONSUMABLE"),
CONSUMABLE UMETA(DisplayName = "CONSUMABLE"),
SUBSCRIPTION UMETA(DisplayName = "SUBSCRIPTION")
};
Product status
UENUM(BlueprintType)
enum class EURuStoreProductStatus : uint8
{
ACTIVE UMETA(DisplayName = "ACTIVE"),
INACTIVE UMETA(DisplayName = "INACTIVE")
};
Subscription structure
USTRUCT(BlueprintType)
struct FURuStoreProductSubscription
{
GENERATED_USTRUCT_BODY()
FURuStoreProductSubscription()
{
introductoryPrice = "";
introductoryPriceAmount = "";
}
UPROPERTY(BlueprintReadOnly)
FURuStoreSubscriptionPeriod subscriptionPeriod;
UPROPERTY(BlueprintReadOnly)
FURuStoreSubscriptionPeriod freeTrialPeriod;
UPROPERTY(BlueprintReadOnly)
FURuStoreSubscriptionPeriod gracePeriod;
UPROPERTY(BlueprintReadOnly)
FString introductoryPrice;
UPROPERTY(BlueprintReadOnly)
FString introductoryPriceAmount;
UPROPERTY(BlueprintReadOnly)
FURuStoreSubscriptionPeriod 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.
Subscription period structure
USTRUCT(BlueprintType)
struct FURuStoreSubscriptionPeriod
{
GENERATED_USTRUCT_BODY()
FURuStoreSubscriptionPeriod()
{
years = 1970;
months = 1;
days = 1;
}
UPROPERTY(BlueprintReadOnly)
int years;
UPROPERTY(BlueprintReadOnly)
int months;
UPROPERTY(BlueprintReadOnly)
int days;
};
years
— amount of years.months
— amount of months.days
— amount of days.
The Failure
callback returns the FURuStoreError
structure with the error information in the Error
parameter.
The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Product purchase
To purchase product, use the PurchaseProduct()
method.
Purchase product request
long requestId = URuStoreBillingClient::Instance()->PurchaseProduct(
productId,
orderId,
quantity,
developerPayload,
[]( long requestId, TShardPtr<FURuStorePaymentResult, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
productId
— product ID assigned to product in RuStore Console (mandatory).orderId: String
— payment ID generated by the app (optional). If you specify this parameter in your system, you will receive it via our API. If not specified, will be generated automatically (uuid).quantity
— product amount (optional).developerPayload
— string with additional order information, that you can specify on purchase initialization.
The Success
callback returns a smart pointer to the inherited object (ESPMode::ThreadSafe
) of the FURuStorePaymentResult
structure in the Response
parameter.
You can retrieve the inherited object type using the GetTypeName()
method. Typecast can be done with StaticCastSharedPtr<>
.
// TShardPtr<FURuStorePaymentResult, ESPMode::ThreadSafe> response
FString typeName = response->GetTypeName();
if (typeName == "FURuStoreSuccess")
{
auto success = *StaticCastSharedPtr<FURuStoreSuccess>(response);
}
Possible type values:
FURuStoreSuccess
- successful purchase result.FURuStoreFailure
- there was a problem during sending payment request or receiving payment status, purchase status unknown.FURuStoreCancelled
— payment request sent, although, the user closed the payment screen on their app, thus, the payment result is unknown.FURuStoreInvalidPaymentState
— SDK payments error. May occur due to an incorrect return deeplink.
In the blueprint implementation, the Success
callback returns inherited class object URuStorePaymentResultClass
in the Response
parameter. Typecast can be done with calls Cast To
.
Purchase result structure
UCLASS(BlueprintType)
class RUSTOREBILLING_API URuStorePaymentResultClass : public UObject
{
GENERATED_BODY()
};
USTRUCT(BlueprintType)
struct RUSTOREBILLING_API FURuStorePaymentResult
{
GENERATED_USTRUCT_BODY()
virtual ~FURuStorePaymentResult() {}
virtual FString GetTypeName() { return "FURuStorePaymentResult"; }
};
Success
purchase result structure
UCLASS(BlueprintType)
class RUSTOREBILLING_API URuStoreSuccess : public URuStorePaymentResultClass
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
FURuStoreSuccess value;
};
USTRUCT(BlueprintType)
struct RUSTOREBILLING_API FURuStoreSuccess : public FURuStorePaymentResult
{
GENERATED_USTRUCT_BODY()
FURuStoreSuccess()
{
orderId = "";
purchaseId = "";
productId = "";
invoiceId = "";
subscriptionToken = "";
sandbox = false;
}
UPROPERTY(BlueprintReadOnly)
FString orderId;
UPROPERTY(BlueprintReadOnly)
FString purchaseId;
UPROPERTY(BlueprintReadOnly)
FString productId;
UPROPERTY(BlueprintReadOnly)
FString invoiceId;
UPROPERTY(BlueprintReadOnly)
FString subscriptionToken;
UPROPERTY(BlueprintReadOnly)
bool sandbox;
virtual FString GetTypeName() override { return "FURuStoreSuccess"; }
};
Cancelled
purchase result structure
UCLASS(BlueprintType)
class RUSTOREBILLING_API URuStoreCancelled : public URuStorePaymentResultClass
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
FURuStoreCancelled value;
};
USTRUCT(BlueprintType)
struct RUSTOREBILLING_API FURuStoreCancelled : public FURuStorePaymentResult
{
GENERATED_USTRUCT_BODY()
FURuStoreCancelled()
{
purchaseId = "";
sandbox = false;
}
UPROPERTY(BlueprintReadOnly)
FString purchaseId;
UPROPERTY(BlueprintReadOnly)
bool sandbox;
virtual FString GetTypeName() override { return "FURuStoreCancelled"; }
};
Failure
purchase result structure
UCLASS(BlueprintType)
class RUSTOREBILLING_API URuStoreFailure : public URuStorePaymentResultClass
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
FURuStoreFailure value;
};
USTRUCT(BlueprintType)
struct RUSTOREBILLING_API FURuStoreFailure : public FURuStorePaymentResult
{
GENERATED_USTRUCT_BODY()
public:
FURuStoreFailure()
{
purchaseId = "";
invoiceId = "";
orderId = "";
quantity = 0;
productId = "";
errorCode = 0;
sandbox = false;
}
UPROPERTY(BlueprintReadOnly)
FString purchaseId;
UPROPERTY(BlueprintReadOnly)
FString invoiceId;
UPROPERTY(BlueprintReadOnly)
FString orderId;
UPROPERTY(BlueprintReadOnly)
int quantity;
UPROPERTY(BlueprintReadOnly)
FString productId;
UPROPERTY(BlueprintReadOnly)
int errorCode;
UPROPERTY(BlueprintReadOnly)
bool sandbox;
virtual FString GetTypeName() override { return "FURuStoreFailure"; }
};
The sandbox
parameter defines whether a payment is a test payment. Available values: true
or false
,
where true
means a test payment and false
— an actual payment.
InvalidPaymentState
purchase result structure
UCLASS(BlueprintType)
class RUSTOREBILLING_API URuStoreInvalidPaymentState : public URuStorePaymentResultBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly)
FURuStoreInvalidPaymentState value;
};
USTRUCT(BlueprintType)
struct RUSTOREBILLING_API FURuStoreInvalidPaymentState : public FURuStorePaymentResult
{
GENERATED_USTRUCT_BODY()
virtual FString GetTypeName() override { return "FURuStoreInvalidPaymentState"; }
};
The Failure
callback returns the FURuStoreError
structure with the error information in the Error
parameter. The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Getting products list
This method returns purchases with the following statuses. For more informations on possible purchase states see Getting purchase info.
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.
long requestId = URuStoreBillingClient::Instance()->GetPurchases(
[]( long requestId, TSharedPtr<FURuStorePurchasesResponse, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreRuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
The Success
callback returns the FURuStorePurchasesResponse
structure in the Response
parameter (see below).
GetPurchases
response
USTRUCT(BlueprintType)
struct FURuStorePurchasesResponse: public FURuStoreResponseWithCode
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly)
TArray<FURuStorePurchase> purchases;
};
purchases
— list of requested purchases
Purchase structure
USTRUCT(BlueprintType)
struct FURuStorePurchase
{
GENERATED_USTRUCT_BODY()
FURuStorePurchase()
{
purchaseId = "";
productId = "";
invoiceId = "";
language = "";
purchaseTime = FDateTime(0);
orderId = "";
amountLabel = "";
amount = 0;
currency = "";
quantity = 0;
purchaseState = EURuStorePurchaseState::CANCELLED;
developerPayload = "";
subscriptionToken = "";
}
UPROPERTY(BlueprintReadOnly)
FString purchaseId;
UPROPERTY(BlueprintReadOnly)
FString productId;
UPROPERTY(BlueprintReadOnly)
FString invoiceId;
UPROPERTY(BlueprintReadOnly)
FString language;
UPROPERTY(BlueprintReadOnly)
FDateTime purchaseTime;
UPROPERTY(BlueprintReadOnly)
FString purchaseTimeLabel;
UPROPERTY(BlueprintReadOnly)
FString orderId;
UPROPERTY(BlueprintReadOnly)
FString amountLabel;
UPROPERTY(BlueprintReadOnly)
int amount;
UPROPERTY(BlueprintReadOnly)
FString currency;
UPROPERTY(BlueprintReadOnly)
int quantity;
UPROPERTY(BlueprintReadOnly)
EURuStorePurchaseState purchaseState;
UPROPERTY(BlueprintReadOnly)
FString developerPayload;
UPROPERTY(BlueprintReadOnly)
FString subscriptionToken;
};
Purchase state
UENUM(BlueprintType)
enum class EURuStorePurchaseState : uint8
{
CREATED UMETA(DisplayName = "CREATED"),
INVOICE_CREATED UMETA(DisplayName = "INVOICE_CREATED"),
CONFIRMED UMETA(DisplayName = "CONFIRMED"),
PAID UMETA(DisplayName = "PAID UMETA"),
CANCELLED UMETA(DisplayName = "CANCELLED"),
CONSUMED UMETA(DisplayName = "CONSUMED"),
PAUSED UMETA(DisplayName = "PAUSED"),
CLOSED UMETA(DisplayName = "CLOSED")
};
-
purchaseId
— product ID. -
productId
— product ID assigned to product in RuStore Console (mandatory). -
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. If not specified, will be generated automatically (uuid). -
amountLable
— formatted purchase price, including currency symbol. -
amount
— price in minimum currency units. -
currency
— ISO 4217 currency code. -
quantity
— product amount (optional). -
purchaseState
— purchase state:CREATED
— purchase created;INVOICE_CREATED
— purchase invoiced is created and awaiting payment;PAID
— only for consumable products — intermediate status, the funds on the user's account are put on hold. The purchase is awaiting confirmation from the developer;CONFIRMED
— payment for non-consumable product successful;CONSUMED
— payment for consumable product successful;CANCELLED
— purchase canceled — there was no payment or the payment was refunded (if a purchase is a subscription it doesn't change its state toCANCELLED
);PAUSED
— for subscriptions — the purchase is in HOLD period;TERMINATED
— subscription terminated.
-
developerPayload
— string with additional order information, that you can specify on purchase initialization. -
subscriptionToken
— purchase token for server validation .
The Failure
callback returns the FURuStoreError
structure with error information in the Error
parameter.
The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Getting purchase information
Go get purchase information, use theGetPurchaseInfo
method.
long requestId = URuStoreBillingClient::Instance()->GetPurchaseInfo(
purchaseId,
[]( long requestId, TSharedPtr<FURuStorePurchaseInfoResponse, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
purchaseId
— product ID
The Success
callback returns the FURuStorePurchase
structure in the Response
parameter.
The Failure
callback returns the FURuStoreError
structure with the error information. The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
State model (purchaseState
)
CONSUMABLES
- purchase state model
NON-CONSUMABLES
- purchase state model
(SUBSCRIPTIONS
) - purchase state model
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
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
.
long requestId = RuStoreBillingClient::Instance()->ConfirmPurchase(
purchaseId,
[]( long requestId, TSharedPtr<FURuStoreError, ESPMode::ThreadSafe> error) {
// Process error
},
[]( long requestId, TSharedPtr<FURuStoreConfirmPurchaseResponse, ESPMode::ThreadSafe> response) {
// Process response
}
);
purchaseId
— product ID.
The Failure
callback returns the FURuStoreError
structure with error information in the Error
parameter. The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Purchase cancellation
To cancel a purchase, use the DeletePurchase
method.
Purchase cancellation request
long requestId = URuStoreBillingClient::Instance()->DeletePurchase(
purchaseId,
[]( long requestId, TSharedPtr<FURuStoreDeletePurchaseResponse, ESPMode::ThreadSafe> response) {
// Process response
},
[]( long requestId, TSharedPtr<FURuStoreRuStoreError, ESPMode::ThreadSafe> error) {
// Process error
}
);
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.
The Failure
callback returns the FURuStoreError
structure with error information in the Error
parameter. The structure of the FURuStoreError
error is described in [Error Handling].(#handlingerrors).
Handle pending payments
Handling of unfinished payments is done by the developer.
To confirm a purchase of CONSUMABLE
product in the PAID
state, call the [purchase confirmation
method].(#confirm) (see Retrieve purchase details).
When dealing with purchase cancellation using payment processing methods take into account your business logic. Some developers implement additional checks before purchase confirmation or cancellation. In that case, make a separate purchase state request.
For example, if the user paid for the product that you cannot deliver for some reason, call the payment cancellation method for the purchase in the PAID
status to cancel the purchase.
If the get purchases list method returns a purchase in the INVOICE_CREATED
state, you can us the purchase cancellation method. For instance, if you don't want to see a purchase in that state in the purchase list. This is optional as RuStore handles cancellation of such purchases on its side.
Sometimes, after paying with a bank app (SBP, SberPay, T-Pay etc.) and returning to the app, the status is still INVOICE_CREATED
, and the payment status is unsuccessful. This is caused by the purchase processing time by the bank. In this case, you need to adapt your screen lifecycle to the product list retrieval logic.
Alternatively, you can implement cancellation of purchases in the INVOICE_CREATED
state only through user action in your app. For example, create a dedicated button for this purpose.
Event logging
If you need to log the events of the payment library, set enableLogs = true
in the FURuStoreBillingClientConfig
structure on calling the Init
method.
FURuStoreBillingClientConfig config;
config.consoleApplicationId = "123456";
config.deeplinkScheme = "yourscheme";
config.allowNativeErrorHandling = false;
config.enableLogs = true;
URuStoreBillingClient::Instance()->Init(config);
The BillingClientLogger
object will be used that implements messages output into Logcat
class BillingClientLogger(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)
}
}
Change interface theme
To dynamically change the theme, use the SetTheme
method.
EURuStoreTheme theme = EURuStoreTheme::DARK;
URuStoreBillingClient::Instance()->SetTheme(theme);
theme
— theme type fromEURuStoreTheme
.
Theme type
UENUM(BlueprintType)
enum class EURuStoreTheme : uint8
{
DARK UMETA(DisplayName = "DARK"),
LIGHT UMETA(DisplayName = "LIGHT")
};
DARK
— dark theme.LIGHT
— light theme.
You can retrieve the information about the set theme using the GetTheme
method.
EURuStoreTheme theme = URuStoreBillingClient::Instance()->GetTheme();
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.
Error structure
USTRUCT(BlueprintType)
struct RUSTORECORE_API FURuStoreRuStoreError
{
GENERATED_USTRUCT_BODY()
FURuStoreRuStoreError()
{
name = "" ;
description = "" ;
}
UPROPERTY(BlueprintReadOnly)
FString name;
UPROPERTY(BlueprintReadOnly)
FString 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.
You can change this behavior any time using the SetAllowNativeErrorHandling()
method.
RuStoreBillingClient::Instance()->SetAllowNativeErrorHandling(false);
true
— display dialog.false
— do not display dialog.
Error codes
Below is the list of possible errors that can be received errorCode
field.
HTTP code | Error code | Description |
---|---|---|
400 | 40001 | Incorrect request parameters: mandatory parameters are not filled in/incorrect parameters format. |
400 | 40003 | App not found. |
400 | 40004 | App status: inactive . |
400 | 40005 | Product not found. |
400 | 40006 | Product status: inactive . |
400 | 40007 | Invalid product type. Supported types: consumable , non-consumable , subscription . |
400 | 40008 | An order with this order_id already exists. |
400 | 40009 | This client already has an active order for this product in the invoice_created state. Prompt the client to either complete their payment or cancel the purchase. |
400 | 40010 | For consumable products. This customer has and order for this product in the paid state. First, the order needs to be consumed (confirmed) in the app, then, you can send another purchase request. |
400 | 40011 | For non-consumable products. This client already has an order of this product in the pre_confirmed /confirmed state. Such product has already been purchased. This product cannot be sold more than once. |
400 | 40012 | For subscription products. This client already has an order of this product in the pre_confirmed /confirmed state. Such product has already been purchased. This product cannot be sold more than once. |
400 | 40013 | For subscription products. The data was not received in the GET/products (serviceId , user_id ) response. |
400 | 40014 | One or more mandatory attribute was not received in the request. |
400 | 40015 | Failed to change the order status during the purchase update (not allowed). |
400 | 40016 | quantity > 1 is specified for a non-consumable product purchase. |
400 | 40017 | Product deleted, new purchases not available. |
400 | 40018 | Cannot consume products with product type . |
401 | 40101 | Invalid token. |
401 | 40102 | Token lifetime has expired. |
403 | 40301 | Access to the requested resource is denied (unauthorized). |
403 | 40302 | This call is not authorized for the current token (method not allowed). |
403 | 40303 | The app ID in the request does not match the token. |
403 | 40305 | Incorrect token type. |
404 | 40401 | Not found. |
408 | 40801 | The notification timeout period specified in the request has expired. |
500 | 50*** | Payment service internal error. |