5.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 the 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.
- Defold 1.6.2 and later.
The service has some restrictions to work outside of Russia.
Connect to project
Connecting to project
- Copy the plugin and the sample app projects from the official RuStore repository on GitFlic.
- Open the Android project from the
extension_libraries
folder in your IDE. - Build the project via
gradle assemble
command. Upon successful build in thebilling_example/extension_rustore_billing/lib/android
andbilling_example/extension_rustore_core/lib/android
folders the following files will be created:RuStoreDefoldBilling.jar
;RuStoreDefoldCore.jar
.
- Copy the
billing_example/extension_rustore_billing
andbilling_example/extension_rustore_core
folders in the root of your project.
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>
<!-- RUSTORE BILLING INTENT FILTER -->
<activity android:name="ru.rustore.defold.billing.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="yourappscheme" />
</intent-filter>
</activity>
Replace yourappscheme
in the example above with the name of your scheme. For example, ru.package.name.rustore.scheme
.
The scheme defined in the Androidmanifest
file must match the scheme you specify in the create
method of the RuStore SDK billing.
App example contains a modified manifest in the billing_example/extension_rustore_billing/manifests/android/AndroidManifest.xml
file
Initialization
Initialize the library before calling its methods.
For initialization, call theinit()
method.
local APPLICATION_ID = "123456"
local DEEPLINK_SCHEME = "yourappscheme"
local DEBUG_LOGS = true
rustorebilling.init(APPLICATION_ID, DEEPLINK_SCHEME, DEBUG_LOGS)
-
123456
— 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/
. -
yourappscheme
— 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. -
debugLogs
— flag that enables/disables logging. Set it totrue
, if you want events to be logged. Otherwise, set it tofalse
.
yourappscheme
must match the scheme specified in AndroidManifest.xml
(see Deeplink processing).Before you start
Payments availability check
- 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.
You must subscribe to events once before using this method:
rustore_check_purchases_available_success
;rustore_check_purchases_available_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_check_purchases_available_success", _check_purchases_available_success)
rustorecore.connect("rustore_check_purchases_available_failure", _check_purchases_available_failure)
end
function _check_purchases_availability_success(self, channel, value)
local data = json.decode(value)
end
function _check_purchases_availability_failure(self, channel, value)
local data = json.decode(value)
end
rustorebilling.check_purchases_availability()
The rustore_check_purchases_availability_success
callback returns a JSON string with the service availability information (see below).
-
isAvailable
— compliance with the payments availability conditions (true
/false
). -
cause
— error information.
The error structure is described in Error Handling.
The rustore_check_purchases_availability_failure
callback returns a JSON string with the error information. The error structure is described in Error Handling.
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 get_products()
method to request the information about products added to your app in RuStore Console.
You must subscribe to events once before using this method:
rustore_on_get_products_success
;rustore_on_get_products_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_get_products_success", _on_get_products_success)
rustorecore.connect("rustore_on_get_products_failure", _on_get_products_failure)
end
function _on_get_products_success(self, channel, value)
local data = json.decode(value)
end
func _on_get_products_failure(self, channel, value)
local data = json.decode(value)
end
local PRODUCT_IDS = {
"non_con2",
"non_con1",
"con2",
"con1",
"sub2",
"sub1"}
rustorebilling.get_products(PRODUCT_IDS)
PRODUCT_IDS
— 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 rustore_on_get_products_success
callback returns a JSON string with the products information (see below).
currency
— ISO 4217 currency code.description
— descriptions inlanguage
.imageUrl
— image URL.language
— language specified with BCP 47 code.price
— price in minimum currency units.priceLable
— formatted product price, including currency symbol inlanguage
.productId
— product ID assigned to product in RuStore Console (mandatory).productStatus
— product type:CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.productType
— product status.promoImageUrl
— promo image URL.title
— product name inlanguage
.subscription
— subscription description, returns only forsubscription
products.
Available subscription
fields (see below).
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.
Available fields of the period object (see below).
years
— amount of years.months
— amount of months.days
— amount of days.
The rustore_on_get_products_failure
callback returns a JSON string with the error information. The error structure is described in Error Handling.
Purchasing product
To purchase product, use the purchase_product()
method.
You must subscribe to events once before using this method:
rustore_on_purchase_product_success
;rustore_on_purchase_product_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_purchase_product_success", _on_purchase_product_success)
rustorecore.connect("rustore_on_purchase_product_failure", _on_purchase_product_failure)
end
function _on_purchase_product_success(self, channel, value)
local data = json.decode(value)
end
function _on_purchase_product_failure(self, channel, value)
local data = json.decode(value)
end
local PRODUCT_ID = "example_id"
local PARAMS = "{" ..
"\"orderId\":\"example\"," ..
"\"quantity\":1," ..
"\"payload\":\"example\"" ..
"}"
rustorebilling.purchase_product(PRODUCT_ID, PARAMS)
PRODUCT_ID
— product ID assigned to product in RuStore Console (mandatory);PARAMS
— optional parameters: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);quantity
— product amount (optional);payload
— string with additional order information, that you can specify on purchase initialization.
Therustore_on_purchase_product_success
callback returns a JSON string with the purchase information. Available fields:
-
type
— request result typeSuccess
- successful purchase result;Failure
- there was a problem during sending payment request or receiving payment status, purchase status unknown;Cancelled
— payment request sent, although, the user closed the payment screen on their app, thus, the payment result is unknown;InvalidPaymentState
— SDK payments error. May occur due to an incorrect return deeplink.
-
data
— JSON string with optional fields.
The Success
object is returned if a request is successful. Available fields:
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).purchaseId
— product ID.productId
— product ID assigned to product in RuStore Console (mandatory).invoiceId
— invoice ID.subscriptionToken
— purchase token for server validation .
The Failure
object is returned if there was an error during request processing. Available fields:
purchaseId
— product ID.invoiceId
— invoice ID.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).quantity
— product amount (optional).productId
— product ID assigned to product in RuStore Console (mandatory).errorCode
— error code.
Error codes are described in section Error codes.
The Cancelled
object returns if a purchase is canceled by the user. Available fields:
purchaseId
— product ID.
The InvalidPaymentState
object is returned if there is an SDK error. For example, if there is an incorrect return deeplink.
The rustore_on_purchase_product_failure
callback returns a JSON string with the error information. The error structure is described in Error Handling.
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 get_purchases()
method.
You must subscribe to events once before using this method:
rustore_on_get_purchases_success
;rustore_on_get_purchases_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_get_purchases_success", _on_get_purchases_success)
rustorecore.connect("rustore_on_get_purchases_failure", _on_get_purchases_failure)
end
function _on_get_purchases_success(self, channel, value)
local data = json.decode(value)
for key, value in pairs(data) do
-- value.amount
end
end
function _on_get_purchases_failure(self, channel, value)
local data = json.decode(value)
end
rustorebilling.get_purchases()
The rustore_on_get_purchases_success
callback returns an object array with the information about purchases. Available fields:
amount
— price in minimum currency units.amountLable
— formatted purchase price, including currency symbol.currency
— ISO 4217 currency code.description
— descriptions inlanguage
.developerPayload
— string with additional order information, that you can specify on purchase initialization.invoiceId
— invoice ID.language
— language specified with BCP 47 code.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).productId
— product ID assigned to product in RuStore Console (mandatory).productType
— product type:CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.purchaseId
— product ID.purchaseState
— purchase state:purchaseTime
— purchase time.quantity
— product amount (optional).subscriptionToken
— purchase token for server validation .
The rustore_on_get_purchases_failure
callback returns a JSON string with the error information. The error structure is described in Error Handling.
Getting purchase information
Go get purchase information, use theget_purchase_info()
method.
You must subscribe to events once before using this method:
rustore_on_get_purchase_info_success
;rustore_on_get_purchase_info_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_get_purchase_info_success", _on_get_purchase_info_success)
rustorecore.connect("rustore_on_get_purchase_info_failure", _on_get_purchase_info_failure)
end
function _on_get_purchase_info_success(self, channel, value)
local data = json.decode(value)
end
function _on_get_purchase_info_failure(self, channel, value)
local data = json.decode(value)
end
# Your purchase information request UI implementation
function _on_get_purchase_info_pressed(purchaseId):
rustorebilling.get_purchase_info(purchaseId)
end
purchaseId
— product ID
The rustore_on_get_purchase_info_success
callbacj returns a JSON string with the purchase information. Available fields:
amount
— price in minimum currency units.amountLable
— formatted purchase price, including currency symbol.currency
— ISO 4217 currency code.description
— descriptions inlanguage
.developerPayload
— string with additional order information, that you can specify on purchase initialization.invoiceId
— invoice ID.language
— language specified with BCP 47 code.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).productId
— product ID assigned to product in RuStore Console (mandatory).productType
— product type:CONSUMABLE
/NON-CONSUMABE
/SUBSCRIPTION
.purchaseId
— product ID.purchaseState
— purchase state:purchaseTime
— purchase time.quantity
— product amount (optional).subscriptionToken
— purchase token for server validation .
The rustore_on_get_purchase_info_failure
callback returns a JSON string with the error information. The error structure is described in Error Handling.
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 request
Us theconfirm_purchase()
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
.
You must subscribe to events once before using this method:
rustore_on_confirm_purchase_success
;rustore_on_confirm_purchase_failure
.
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_confirm_purchase_success", _on_confirm_purchase_success)
rustorecore.connect("rustore_on_confirm_purchase_failure", _on_confirm_purchase_failure)
end
function _on_confirm_purchase_success(self, channel, value)
local data = json.decode(value)
end
function _on_confirm_purchase_failure(self, channel, value)
local data = json.decode(value)
end
# Your purchase confirmation UI implementation
function _on_confirm_purchase_pressed(purchaseId):
rustorebilling.confirm_purchase(purchaseId)
end
purchaseId
— product ID.
Server validation
If you need to validate a successful app using API RuStore methods, you can use the subscriptionToken
value, from the json
string received upon a successful product purchase.
SubscriptionToken
consists of invoiceId
and userId
of the purchase separated by period: $invoiceId.$userId
.
Getting subscriptionToken
from the purchase result
function _on_purchase_product_success(self, channel, value)
local data = json.decode(value)
if data.type == "Success" then
local subscriptionToken = data.data.subscriptionToken
yourApi.validate(subscriptionToken)
end
end
You can also get subscriptionToken
from the purchases list.
function _on_get_purchases_success(self, channel, value)
local data = json.decode(value)
for key, value in pairs(data) do
yourApi.validate(value.subscriptionToken)
end
end
Purchase cancellation
To cancel a purchase, use the delete_purchase
method.
You must subscribe to events once before using this method:
rustore_on_delete_purchase_success
;rustore_on_delete_purchase_failure
.
Calling cancellation method
function init(self)
# rustorebilling initialization
rustorecore.connect("rustore_on_delete_purchase_success", _on_delete_purchase_success)
rustorecore.connect("rustore_on_delete_purchase_failure", _on_delete_purchase_failure)
end
function _on_delete_purchase_success(self, channel, value)
local data = json.decode(value)
end
function _on_delete_purchase_failure(self, channel, value)
local data = json.decode(value)
end
# Your purchase cancellation UI implementation
function _on_delete_purchase_pressed(purchaseId):
rustorebilling.delete_purchase(purchaseId)
end
purchaseId
— product ID.
The rustore_on_delete_purchase_success
callback returns purchase ID:
purchaseId
— product ID.
The rustore_on_delete_purchase_failure
callback returns a JSON string with the error information. Available fields:
purchaseId
— product ID.-
cause
— error information.
The error structure is described in Error Handling.
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.
Handling 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 payment library events, add optional debugLogs
in the init
call and subscribe to the events:
rustore_on_payment_logger_debug
;rustore_on_payment_logger_error
;rustore_on_payment_logger_info
;rustore_on_payment_logger_verbose
;rustore_on_payment_logger_warning
.
local APPLICATION_ID = "123456"
local DEEPLINK_SCHEME = "yourappscheme"
local DEBUG_LOGS = true
local LOG_TAG = "yourtag"
function init(self)
rustorecore.connect("rustore_on_payment_logger_debug", _on_payment_logger_debug)
rustorecore.connect("rustore_on_payment_logger_error", _on_payment_logger_error)
rustorecore.connect("rustore_on_payment_logger_info", _on_payment_logger_info)
rustorecore.connect("rustore_on_payment_logger_verbose", _on_payment_logger_verbose)
rustorecore.connect("rustore_on_payment_logger_warning", _on_payment_logger_warning)
rustorebilling.init(APPLICATION_ID, DEEPLINK_SCHEME, DEBUG_LOGS)
end
function _on_payment_logger_debug(self, channel, value)
rustorecore.log_debug(LOG_TAG, value)
end
function _on_payment_logger_error(self, channel, value)
rustorecore.log_error(LOG_TAG, value)
end
function _on_payment_logger_info(self, channel, value)
rustorecore.log_info(LOG_TAG, value)
end
function _on_payment_logger_verbose(self, channel, value)
rustorecore.log_verbose(LOG_TAG, value)
end
function _on_payment_logger_warning(self, channel, value)
rustorecore.log_warning(LOG_TAG, value)
end
Logging enabling parameter:
DEBUG_LOGS
— enable logging (logging will be automatically disabled for Release builds).
All logging callbacks return a JSON string. Available fields:
e
— error information ornull
. The error structure is described in Error Handling;message
– error description.
Changing interface theme
To dynamically change the theme, use theset_theme
method.
setTheme
usabe example
rustorebilling.set_theme(0)
0
- dark theme;1
- light theme.
Error handling
Error structure
function _on_failure(self, channel, value)
local data = json.decode(value)
local message = data.detailMessage
end
detailMessage
– error description.
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.
purchase_product
method, errors are handled automatically.
Use the set_error_handling
method to display the error dialog to the user.
function init(self)
rustorebilling.set_error_handling(true)
# rustorebilling initialization
end
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. |