Skip to main content

6.0.2

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 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

To connect payments to your project, execute the following command.

flutter pub add flutter_rustore_billing

This command will add the following string to the pubspec.yaml file.

dependencies: flutter_rustore_billing: ^6.0.2

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=".sample.MainActivity">
<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.

info

The scheme defined in the Androidmanifest file must match the scheme you specify in the create method of the RuStore SDK billing.

Initialization

Initialize the library before calling its methods.

For initialization, call the RustoreBillingClient.initialize() method.
RustoreBillingClient.initialize(
"123456", //consolApplicationId
"yourappscheme", //deeplinkScheme
).then((value) {
print("initialize success: $value");
}, onError: (err) {
print("initialize err: $err");
}
);
  • 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/.

note

applicationId specified in build.gradle must match applicationId of the APK file you published in the RuStore Console.

  • deeplinkScheme — deeplink scheme that i sused to return to your app after paying with a third party payment app (for example, SberPay or SBP). SDK generates its own host to this scheme.
note
Tye deeplink scheme that is passed in deeplinkScheme must match the scheme specified in AndroidManifest.xml (see Deeplink processing).

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

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 RustoreBillingClient.available() method.If all above conditions are met, true is returned.

RustoreBillingClient.available().then((value) { 
print("available success $value");
}, onError: (err) {
print("available err: $err");
}
);

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 RustoreBillingClient.products(ids) method to request the information about products added to your app in RuStore Console.

RustoreBillingClient.products(ids).then((response) {
for (final product in response.products) {
print(product?.productId);
}
}, onError: (err) {
print("products err: $err");
}
);

ids: List<String?> — 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 ProductsResponse

class ProductsResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<Product?> products;
List<DigitalShopGeneralError?> errors;
}
  • code — response code.
  • errorMessage — error message for the user.
  • errorDescription — detailed error message.
  • traceId — error ID.
  • errors — errors list for requested products.
  • products — products list.

DigitalShopGeneralError error structure

class DigitalShopGeneralError {
String? name;
int ? code;
String? description
}
  • name – error name.
  • code — error code.
  • description – error description.

Product structure

class Product {
String productId;
String? productType;
String productStatus;
String? priceLabel;
int ? price;
String? currency;
String? language;
String? title;
String? description;
String? imageUrl;
String? promoImageUrl;
Subscription? 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

class Subscription {
SubscriptionPeriod? subscriptionPeriod;
SubscriptionPeriod? freeTrialPeriod;
SubscriptionPeriod? gracePeriod;
String? introductoryPrice;
String? introductoryPriceAmount;
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.

SubscriptionPeriod structure

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

Purchasing product

To purchase product, use the RustoreBillingClient.purchase(id) method.

RustoreBillingClient.purchase(id).then((response) {
print("purchase success: $response");
}, onError: (err) {
print("purchase err: $err");
}
);

id — product ID assigned to product in RuStore Console (mandatory)

PaymentResult structure

class PaymentResult {
SuccessInvoice? successInvoice;
InvalidInvoice? invalidInvoice;
SuccessPurchase? successPurchase;
InvalidPurchase? invalidPurchase;
}

SuccessInvoice structure

class SuccessInvoice {
String invoiceId;
String finishCode;
}

InvalidInvoice structure

class InvalidInvoice {
String? invoiceId;
}

SuccessPurchase structure

class SuccessPurchase {
String finishCode;
String? orderId;
String purchaseId;
String productId;
String? invoiceId;
String? subscriptionToken;
bool? sandbox;
}

InvalidPurchase structure

class InvalidPurchase {
String? purchaseId;
String? invoiceId;
String? orderId;
int? quantity;
String? productId;
int? errorCode;
bool? sandbox;
}
  • SuccessInvoice — payments completed with a result.
  • InvalidInvoice — payments completed without invoice id. They might have been started with an invalid invoice number (for example: an empty line).
  • SuccessPurchase — successful product purchase result.
  • InvalidPurchase — payments completed with error.

Possible finishCode statuses

  • SUCCESSFUL_PAYMENT — payment successful.
  • CLOSED_BY_USER — canceled by the user.
  • UNHANDLED_FORM_ERROR — unknown error.
  • PAYMENT_TIMEOUT — timeout payment error.
  • DECLINED_BY_SERVER — declined by server.
  • RESULT_UNKNOWN — unknown payment status.

Getting products list

This method returns purchases with the following statuses. For more informations on possible purchase states see Getting purchase info.

Type/StatusINVOICE_CREATEDCONFIRMEDPAIDPAUSED
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 RustoreBillingClient.purchases method.

RustoreBillingClient.purchases().then((response) {
for (final product in response.purchases) {
print(product?.purchaseId);
}
}, onError: (err) {
print("purchases err: $err" );
}
);

The method returns PurchasesResponse (see below).

class PurchasesResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<Purchase?> purchases;
List<DigitalShopGeneralError?> errors;
}
  • code — response code.
  • errorMessage — error message for the user.
  • errorDescription — detailed error message.
  • errors — errors list for requested products.
  • purchases — list of requested purchases.

DigitalShopGeneralError structure (see below).

class DigitalShopGeneralError {
String? name;
int ? code;
String? description;
}
  • name – error name.
  • code — error code.
  • description – error description.
class Purchase {
String? purchaseId;
String? productId;
String? productType;
String? language;
String? purchaseTime;
String? orderId;
String? amountLabel;
int? amount;
String? currency;
int? quantity;
String? purchaseState;
String? developerPayload;
String? invoiceId;
String? subscriptionToken;
}

Purchase structure (see below).

  • purchaseId — product ID.
  • productId — product ID assigned to product in RuStore Console (mandatory).
  • description — descriptions in language.
  • 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.
  • developerPayload — string with additional order information, that you can specify on purchase initialization.

Getting purchase information

Go get purchase information, use the purchaseInfo method.
RustoreBillingClient.purchaseInfo(purchaseId).then((purchase) {
print(purchase);
}, onError: (err) {
print("Error getPurchaseInfo: $err");
}
);

Purchase structure (see below).

class Purchase {
String? purchaseId;
String? productId;
String? productType;
String? language;
String? purchaseTime;
String? orderId;
String? amountLabel;
int? amount;
String? currency;
int? quantity;
String? purchaseState;
String? developerPayload;
String? invoiceId;
String? subscriptionToken;
}
  • purchaseId — product ID.
  • productId — product ID assigned to product in RuStore Console (mandatory).
  • description — descriptions in language.
  • 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.
  • developerPayload — string with additional order information, that you can specify on purchase initialization.

State model (purchaseState)

CONSUMABLES - purchase state model

Purchase created, however, the invoice was not createdNo payment was made. This purchase can be ignored.The user has an unfinished purchase. Invoice was created but not paid. You need to inform the user.Purchase canceledFor consumable products. Purchase paid, but not consumed. Any App need to send an immediate request for purchase confirmation.For consumable products. Purchase consumption confirmed.CREATEDINVOICE_CREATEDCANCELLEDInvoice createdInvoice paidConsumptionPAIDCONSUMEDInvoice canceledPayment returnInvoice was paid and received an errorPurchase canceled

NON-CONSUMABLES - purchase state model

Purchase created, however, the invoice was not created. There was no payment. This purchase can be ignored.The user has an unfinished purchase. Invoice was created but not paid. You need to inform the user.Purchase canceledPayment confirmation status for non-consumable or subscription products. This status means that the product is purchased, further purchase attempts will lead errors.CREATEDINVOICE_CREATEDCANCELLEDInvoice creationPurchase confirmedCONFIRMEDInvoice canceledPayment returnPurchase cancellation

(SUBSCRIPTIONS) - purchase state model

img

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 RustoreBillingClient.confirm(id) 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.confirm(id).then((response) {
print( "confirm success: $response" );
}, onError: (err) {
print( "confirm err: $err" );
}
);
  • id — product ID.

The method returns ConfirmPurchaseResponse (see below).

class ConfirmPurchaseResponse {
int code;
String? errorMessage;
String? errorDescription;
String? traceId;
List<DigitalShopGeneralError?> errors;
}
  • code — response code.
  • errorMessage — error message for the user.
  • errorDescription — detailed error message.
  • traceId — error ID.
  • errors — errors list for requested products.

DigitalShopGeneralError structure (see below).

class DigitalShopGeneralError {
String? name;
int ? code;
String? description;
}
  • name – error name.
  • code — error code.
  • description – error description.

Purchase cancellation

To cancel a purchase, use the deletePurchase method.

RustoreBillingClient.deletePurchase(purchaseId = "purchaseId").then(() {
print("Delete success");
}, onError: (err) {
print("Error getPurchaseInfo: $err");
}
);
  • 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.

Server validation

If you need to validate a successful app using API RuStore methods, you can use subscriptionToken in PurchaseResult returned by purchaseProduct on successful purchase.

SubscriptionToken consists of invoiceId and userId of the purchase separated by period: $invoiceId.$userId.

You can also get a subscriptionToken from the Purchase entity. You can retrieve the Purchase entity using the purchases() method.

Event logging

By default, logging in flutter sdk is enabled. You can view log entries using the RuStoreFlutterBillingPlugin tag.

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;
  • RuStoreApplicationBannedException — app is banned in RuStore RuStore;
  • RuStoreUserBannedException — user is blocked in RuStore;
  • RuStoreException — basic RuStore error from which other errors are inherited.

By default, native error handling is enabled in the plugin, in case of an error the following actions are performed:

  • A corresponding error handler is called.

  • The error is passed to the resolveForBilling method of the native SDK. This is used to show the error dialogue to the user.

resolveForBilling
fun RuStoreException.resolveForBilling(context: Context)

To prevent passing the error to the native SDK, call the offNativeErrorHandling function.

Disabling native error handling
RustoreBillingClient.offNativeErrorHandling();