Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
1289 lines (948 sloc) 44.2 KB

API Documentation

(generated from source files using make doc-api)


The store API is mostly events based. As a user of this plugin, you will have to register listeners to changes happening to the products you register.

The core of the listening mechanism is the when() method. It allows you to be notified of changes to one or a set of products using a query mechanism:

    store.when("full version").owned(unlockApp);
    store.when("downloadable content").downloaded(showContent);

The updated event is fired whenever one of the fields of a product is changed (its owned status for instance).

This event provides a generic way to track the statuses of your purchases, to unlock features when needed and to refresh your views accordingly.

Registering products

The store needs to know the type and identifiers of your products before you can use them in your code.

Use store.register() before your first call to store.refresh().

Once registered, you can use store.get() to retrieve the product object from the store.

      id: "cc.fovea.purchase.consumable1",
      alias: "100 coins",
      type: store.CONSUMABLE
    var p = store.get("100 coins");
    // or
    var p = store.get("cc.fovea.purchase.consumable1");

The product id and type have to match products defined in your Apple and Google developer consoles.

Learn how to do that in HOWTO: Create New Products.

Displaying products

Right after you registered your products, nothing much is known about them except their id, type and an optional alias.

When you perform the initial refresh() call, the store's server will be contacted to load informations about the registered products: human readable title and description, price, etc.

This isn't an optional step as some despotic store owners (like Apple) require you to display information about a product as retrieved from their server: no hard-coding of price and title allowed! This is also convenient for you as you can change the price of your items knowing that it'll be reflected instantly on your clients' devices.

However, the information may not be available when the first view that needs them appears on screen. For you, the best option is to have your view monitor changes made to the product.

monitor changes

Let's demonstrate this with an example:

    // method called when the screen showing your purchase is made visible
    function show() {

    function render() {

        // Get the product from the pool.
        var product = store.get("cc.fovea.test1");

        if (!product) {
        else if (product.state === store.REGISTERED) {
            $el.html("<div class=\"loading\" />");
        else if (product.state === store.INVALID) {
        else {
            // Good! Product loaded and valid.
                  "<div class=\"title\">"       + product.title       + "</div>"
                + "<div class=\"description\">" + product.description + "</div>"
                + "<div class=\"price\">"       + product.price       + "</div>"

            // Is this product owned? Give him a special class.
            if (product.owned)

            // Is an order for this product in progress? Can't be ordered right now?
            if (product.canPurchase)

    // method called when the view is hidden
    function hide() {
        // stop monitoring the product;

In this example, render redraw the purchase element whatever happens to the product. When the view is hidden, we stop listening to changes (


initiate a purchase

Purchases are initiated using the store.order() method.

The store will manage the internal purchase flow that'll end:

  • with an approved event. The product enters the APPROVED state.
  • with a cancelled event. The product gets back to the VALID state.
  • with an error event. The product gets back to the VALID state.

See product life-cycle for details about product states.

finish a purchase

Once the transaction is approved, the product still isn't owned: the store needs confirmation that the purchase was delivered before closing the transaction.

To confirm delivery, you'll use the product.finish() method.

example usage

During initialization:

store.when("extra chapter").approved(function(product) {
    // download the feature
    app.downloadExtraChapter().then(function() {

When the purchase button is clicked:

store.order("full version");

un-finished purchases

If your app wasn't able to deliver the content, product.finish() won't be called.

Don't worry: the approved event will be re-triggered the next time you call store.refresh(), which can very well be the next time the application starts. Pending transactions are persistant.

simple case

In the most simple case, where:

  • delivery of purchases is only local ;
  • you don't want to implement receipt validation ;

you may just want to finish all purchases automatically. You can do it this way:

store.when("product").approved(function(p) {

NOTE: the "product" query will match any purchases (see here to learn more details about queries).

Receipt validation

Some unthoughtful users will try to use fake "purchases" to access features they should normally pay for. If that's a concern, you should implement receipt validation, ideally server side validation.

When a purchase has been approved by the store, it's enriched with transaction information (product.transaction attribute).

To verfify a purchase you'll have to do three things:

  • configure the validator.
  • call product.verify() from the approved event, before finishing the transaction.
  • finish the transaction when transaction is verified.

example using a validation URL

store.validator = "";

store.when("my stuff").approved(function(product) {

store.when("my stuff").verified(function(product) {

For an example using a validation callback instead, see the documentation of the validator method.


For subscription, you MUST implement remote receipt validation.

If the validator returns a store.PURCHASE_EXPIRED error code, the subscription will automatically loose its owned status.

Typically, you'll enable and disable access to your content this way.

store.when("cc.fovea.subcription").updated(function(product) {
    if (product.owned)

store object

store is the global object exported by the purchase plugin.

As with any other plugin, this object shouldn't be used before the "deviceready" event is fired. Check cordova's documentation for more details if needed.

Find below all public attributes and methods you can use.


The verbosity property defines how much you want store.js to write on the console. Set to:

  • store.QUIET or 0 to disable all logging (default)
  • store.ERROR or 1 to show only error messages
  • store.WARNING or 2 to show warnings and errors
  • store.INFO or 3 to also show information messages
  • store.DEBUG or 4 to enable internal debugging messages.

See the logging levels constants.


The sandbox property defines if you want to invoke the platform purchase sandbox

  • Windows will use the IAP simulator if true (see Windows docs)
  • Android: NOT IN USE


product types

store.FREE_SUBSCRIPTION         = "free subscription";
store.PAID_SUBSCRIPTION         = "paid subscription";
store.NON_RENEWING_SUBSCRIPTION = "non renewing subscription";
store.CONSUMABLE                = "consumable";
store.NON_CONSUMABLE            = "non consumable";

error codes

store.ERR_SETUP               = ERROR_CODES_BASE + 1; //
store.ERR_LOAD                = ERROR_CODES_BASE + 2; //
store.ERR_PURCHASE            = ERROR_CODES_BASE + 3; //
store.ERR_PAYMENT_CANCELLED   = ERROR_CODES_BASE + 6; // Purchase has been cancelled by user.
store.ERR_PAYMENT_INVALID     = ERROR_CODES_BASE + 7; // Something suspicious about a purchase.
store.ERR_UNKNOWN             = ERROR_CODES_BASE + 10; //
store.ERR_FINISH              = ERROR_CODES_BASE + 13;
store.ERR_COMMUNICATION       = ERROR_CODES_BASE + 14; // Error while communicating with the server.
store.ERR_SUBSCRIPTIONS_NOT_AVAILABLE = ERROR_CODES_BASE + 15; // Subscriptions are not available.
store.ERR_MISSING_TOKEN       = ERROR_CODES_BASE + 16; // Purchase information is missing token.
store.ERR_VERIFICATION_FAILED = ERROR_CODES_BASE + 17; // Verification of store data failed.
store.ERR_BAD_RESPONSE        = ERROR_CODES_BASE + 18; // Verification of store data failed.
store.ERR_REFRESH             = ERROR_CODES_BASE + 19; // Failed to refresh the store.
store.ERR_DOWNLOAD            = ERROR_CODES_BASE + 21;
store.ERR_PRODUCT_NOT_AVAILABLE = ERROR_CODES_BASE + 23; // Error code indicating that the requested product is not available in the store.
store.ERR_CLOUD_SERVICE_PERMISSION_DENIED = ERROR_CODES_BASE + 24; // Error code indicating that the user has not allowed access to Cloud service information.
store.ERR_CLOUD_SERVICE_NETWORK_CONNECTION_FAILED = ERROR_CODES_BASE + 25; // Error code indicating that the device could not connect to the network.
store.ERR_CLOUD_SERVICE_REVOKED = ERROR_CODES_BASE + 26; // Error code indicating that the user has revoked permission to use this cloud service.
store.ERR_PRIVACY_ACKNOWLEDGEMENT_REQUIRED = ERROR_CODES_BASE + 27; // Error code indicating that the user has not yet acknowledged Apple’s privacy policy for Apple Music.
store.ERR_UNAUTHORIZED_REQUEST_DATA = ERROR_CODES_BASE + 28; // Error code indicating that the app is attempting to use a property for which it does not have the required entitlement.
store.ERR_INVALID_OFFER_IDENTIFIER = ERROR_CODES_BASE + 29; // Error code indicating that the offer identifier is invalid.
store.ERR_INVALID_OFFER_PRICE = ERROR_CODES_BASE + 30; // Error code indicating that the price you specified in App Store Connect is no longer valid.
store.ERR_INVALID_SIGNATURE = ERROR_CODES_BASE + 31; // Error code indicating that the signature in a payment discount is not valid.
store.ERR_MISSING_OFFER_PARAMS = ERROR_CODES_BASE + 32; // Error code indicating that parameters are missing in a payment discount.

product states

store.REGISTERED = 'registered';
store.INVALID    = 'invalid';
store.VALID      = 'valid';
store.REQUESTED  = 'requested';
store.INITIATED  = 'initiated';
store.APPROVED   = 'approved';
store.FINISHED   = 'finished';
store.OWNED      = 'owned';
store.DOWNLOADING = 'downloading';
store.DOWNLOADED = 'downloaded';

logging levels

store.QUIET   = 0;
store.ERROR   = 1;
store.WARNING = 2;
store.INFO    = 3;
store.DEBUG   = 4;

validation error codes

store.INVALID_PAYLOAD   = 6778001;
store.CONNECTION_FAILED = 6778002;
store.PURCHASE_EXPIRED  = 6778003;
store.PURCHASE_CONSUMED = 6778004;
store.INTERNAL_ERROR    = 6778005;
store.NEED_MORE_DATA    = 6778006;

special purpose

store.APPLICATION = "application";

store.Product object

Most events methods give you access to a product object.

Products object have the following fields and methods.

store.Product public attributes

  • - Identifier of the product on the store
  • product.alias - Alias that can be used for more explicit queries
  • product.type - Family of product, should be one of the defined product types.
  • - Name of the group your subscription product is a member of (default to "default"). If you don't set anything, all subscription will be members of the same group.
  • product.state - Current state the product is in (see life-cycle below). Should be one of the defined product states
  • product.title - Localized name or short description
  • product.description - Localized longer description
  • product.priceMicros - Price in micro-units (divide by 1000000 to get numeric price)
  • product.price - Localized price, with currency symbol
  • product.currency - Currency code (optionaly)
  • product.countryCode - Country code. Available only on iOS
  • product.loaded - Product has been loaded from server, however it can still be either valid or not
  • product.valid - Product has been loaded and is a valid product
    • when product definitions can't be loaded from the store, you should display instead a warning like: "You cannot make purchases at this stage. Try again in a moment. Make sure you didn't enable In-App-Purchases restrictions on your phone."
  • product.canPurchase - Product is in a state where it can be purchased
  • product.owned - Product is owned
  • product.deferred - Purchase has been initiated but is waiting for external action (for example, Ask to Buy on iOS)
  • product.introPrice - Localized introductory price, with currency symbol
  • product.introPriceMicros - Introductory price in micro-units (divide by 1000000 to get numeric price)
  • product.introPricePeriod - Duration the introductory price is available (in period-unit)
  • product.introPricePeriodUnit - Period for the introductory price ("Day", "Week", "Month" or "Year")
  • product.introPricePaymentMode - Payment mode for the introductory price ("PayAsYouGo", "UpFront", or "FreeTrial")
  • product.ineligibleForIntroPrice - True when a trial or introductory price has been applied to a subscription. Only available after receipt validation. Available only on iOS
  • product.discounts - Array of discounts available for the product. Each discount exposes the following fields:
    • id - The discount identifier
    • price - Localized price, with currency symbol
    • priceMicros - Price in micro-units (divide by 1000000 to get numeric price)
    • period - Number of subscription periods
    • periodUnit - Unit of the subcription period ("Day", "Week", "Month" or "Year")
    • paymentMode - "PayAsYouGo", "UpFront", or "FreeTrial"
    • eligible - True if the user is deemed eligible for this discount by the platform
  • product.downloading - Product is downloading non-consumable content
  • product.downloaded - Non-consumable content has been successfully downloaded for this product
  • product.additionalData - additional data possibly required for product purchase
  • product.transaction - Latest transaction data for this product (see transactions).
  • product.expiryDate - Latest known expiry date for a subscription (a javascript Date)
  • product.lastRenewalDate - Latest date a subscription was renewed (a javascript Date)
  • product.billingPeriod - Duration of the billing period for a subscription, in the units specified by the billingPeriodUnit property. (not available on iOS < 11.2)
  • product.billingPeriodUnit - Units of the billing period for a subscription. Possible values: Minute, Hour, Day, Week, Month, Year. (not available on iOS < 11.2)
  • product.trialPeriod - Duration of the trial period for the subscription, in the units specified by the trialPeriodUnit property (windows only)
  • product.trialPeriodUnit - Units of the trial period for a subscription (windows only)

store.Product public methods


Call product.finish() to confirm to the store that an approved order has been delivered. This will change the product state from APPROVED to FINISHED (see life-cycle).

As long as you keep the product in state APPROVED:

  • the money may not be in your account (i.e. user isn't charged)
  • you will receive the approved event each time the application starts, where you should try again to finish the pending transaction.
example use
    // synchronous
    // asynchronous
    app.downloadFeature(function() {


Initiate purchase validation as defined by the store.validator.

return value

A Promise with the following methods:

  • done(function(product){})
    • called whether verification failed or succeeded.
  • expired(function(product){})
    • called if the purchase expired.
  • success(function(product, purchaseData){})
    • called if the purchase is valid and verified.
    • purchaseData is the device dependent transaction details returned by the validator, which you can most probably ignore.
  • error(function(err){})
    • validation failed, either because of expiry or communication failure.
    • err is a store.Error object, with a code expected to be store.ERR_PAYMENT_EXPIRED or store.ERR_VERIFICATION_FAILED.


A product will change state during the application execution.

Find below a diagram of the different states a product can pass by.

           +--> VALID +--> REQUESTED +--> INITIATED +-+
                ^      +------------------------------+
                |      |
                |      |             +--> DOWNLOADING +--> DOWNLOADED +
                |      |             |                                |
                |      +--> APPROVED +--------------------------------+--> FINISHED +--> OWNED
                |                                                             |

states definitions

  • REGISTERED: right after being declared to the store using store.register()
  • INVALID: the server didn't recognize this product, it cannot be used.
  • VALID: the server sent extra information about the product (title, price and such).
  • REQUESTED: order (purchase) requested by the user
  • INITIATED: order transmitted to the server
  • APPROVED: purchase approved by server
  • FINISHED: purchase delivered by the app (see Finish a Purchase)
  • OWNED: purchase is owned (only for non-consumable and subscriptions)
  • DOWNLOADING purchased content is downloading (only for non-consumable)
  • DOWNLOADED purchased content is downloaded (only for non-consumable)


  • When finished, a consumable product will get back to the VALID state, while other will enter the OWNED state.
  • Any error in the purchase process will bring a product back to the VALID state.
  • During application startup, products may go instantly from REGISTERED to APPROVED or OWNED, for example if they are purchased non-consumables or non-expired subscriptions.
  • Non-Renewing Subscriptions are iOS products only. Please see the iOS Non Renewing Subscriptions documentation for a detailed explanation.

state changes

Each time the product changes state, appropriate events is triggered.

Learn more about events here and about listening to events here.

store.Error object

All error callbacks takes an error object as parameter.

Errors have the following fields:

  • error.code - An integer error code. See the error codes section for more details.
  • error.message - Human readable message string, useful for debugging.


Register an error handler.

callback is a function taking an error as argument.

example use:

    console.log("ERROR " + e.code + ": " + e.message);

alternative usage

  • store.error(code, callback)
    • only call the callback for errors with the given error code.
    • example: store.error(store.ERR_SETUP, function() { ... });

unregister the error callback

To unregister the callback, you will use

var handler = store.error(function() { ... } );


Add (or register) a product into the store.

A product can't be used unless registered first!

Product is an object with fields :

  • id
  • type
  • alias (optional)

See documentation for the product object for more information.

example usage
    id: "cc.fovea.inapp1",
    alias: "full version",
    type: store.NON_CONSUMABLE

Reserved keywords

Some reserved keywords can't be used in the product id and alias:

  • product
  • order
  • registered
  • valid
  • invalid
  • requested
  • initiated
  • approved
  • owned
  • finished
  • downloading
  • downloaded
  • refreshed


Retrieve a product from its id or alias.

example usage
    var product = store.get("cc.fovea.product1");


Register a callback for a product-related event.

return value

Return a Promise with methods to register callbacks for product events defined below.


  • loaded(product)
    • Called when product data is loaded from the store.
  • updated(product)
    • Called when any change occured to a product.
  • error(err)
  • approved(product)
    • Called when a product order is approved.
  • owned(product)
    • Called when a non-consumable product or subscription is owned.
  • cancelled(product)
    • Called when a product order is cancelled by the user.
  • refunded(product)
    • Called when an order is refunded by the user.
  • Actually, all other product states have their promise
    • registered, valid, invalid, requested, initiated and finished
  • verified(product)
    • Called when receipt validation successful
  • unverified(product)
    • Called when receipt verification failed
  • expired(product)
    • Called when validation find a subscription to be expired
  • downloading(product, progress, time_remaining)
    • Called when content download is started
  • downloaded(product)
    • Called when content download has successfully completed

alternative usage

  • store.when(query, action, callback)
    • Register a callback using its action name. Beware that this is more error prone, as there are not gonna be any error in case of typos.
store.when("cc.fovea.inapp1", "approved", function(product) { ... });

unregister a callback

To unregister a callback, use


The when and once methods take a query parameter. Those queries allow to select part of the products (or orders) registered into the store and get notified of events related to those products.

No filters:

  • "product" or "order" - for all products.

Filter by product types:

  • "consumable" - all consumable products.
  • "non consumable" - all non consumable products.
  • "subscription" - all subscriptions.
  • "free subscription" - all free subscriptions.
  • "paid subscription" - all paid subscriptions.

Filter by product state:

  • "valid" - all products in the VALID state.
  • "invalid" - all products in the INVALID state.
  • "owned" - all products in the OWNED state.
  • etc. (see here for all product states).

Filter individual products:

  • "PRODUCT_ID" - product with the given product id (replace by your own product id)
  • "ALIAS" - product with the given alias

Notice that you can add the "product" and "order" keywords anywhere in your query, it won't change anything but may seem nicer to read.


  • "consumable order" - all consumable products
  • "full version" - the alias of a registered product
  • "order cc.fovea.inapp1" - the id of a registered product
    • equivalent to just "cc.fovea.inapp1"
  • "invalid product" - an invalid product
    • equivalent to just "invalid"


Identical to store.when, but the callback will be called only once. After being called, the callback will be unregistered.

alternative usage

  • store.once(query, action, callback)
    • Same remarks as store.when(query, action, callback)

store.order(product, additionalData)

Initiate the purchase of a product.

The product argument can be either:

  • the store.Product object
  • the product id
  • the product alias

The additionalData argument can be either:

  • null
  • object with attributes:
    • oldSku, a string with the old subscription to upgrade/downgrade on Android. Note: if another subscription product is already owned that is member of the same group, oldSku will be set automatically for you (see
    • prorationMode, a string that describe the proration mode to apply when upgrading/downgrading a subscription (with oldSku) on Android. See Possible values:
      • DEFERRED - Replacement takes effect when the old plan expires, and the new price will be charged at the same time.
      • IMMEDIATE_AND_CHARGE_PRORATED_PRICE - Replacement takes effect immediately, and the billing cycle remains the same.
      • IMMEDIATE_WITHOUT_PRORATION - Replacement takes effect immediately, and the new price will be charged on next recurrence time.
      • IMMEDIATE_WITH_TIME_PRORATION - Replacement takes effect immediately, and the remaining time will be prorated and credited to the user.
    • discount, a object that describes the discount to apply with the purchase (iOS only):
      • id, discount identifier
      • key, key identifier
      • nonce, uuid value for the nonce
      • timestamp, time at which the signature was generated (in milliseconds since epoch)
      • signature, cryptographic signature that unlock the discount

See the "Purchasing section" to learn more about the purchase process.

See "Subscriptions Offer Best Practices" for more details on subscription offers.

return value

store.order() returns a Promise with the following methods:

  • then - called when the order was successfully initiated
  • error - called if the order couldn't be initiated


Register the callback to be called when the store is ready to be used.

If the store is already ready, callback is executed immediately.

store.ready() without arguments will return the ready status.

alternate usage (internal)

store.ready(true) will set the ready status to true, and call the registered callbacks.

Unregister a callback. Works for callbacks registered with ready, when, once and error.

Example use:

    var fun = function(product) {
        // Product loaded while the store screen is visible.
        // Refresh some stuff.



Set this attribute to either:

validation URL example

store.validator = ""; // if you want to use Fovea **
  • URL


  • Method:


  • Data Params

    The product object will be added as a json string.

    Example body:

      additionalData : null
      alias : "monthly1"
      currency : "USD"
      description : "Monthly subscription"
      id : "subscription.monthly"
      loaded : true
      price : "$12.99"
      priceMicros : 12990000
      state : "approved"
      title : "The Monthly Subscription Title"
      transaction : { // Additional fields based on store type (see "transactions" below)  }
      type : "paid subscription"
      valid : true

    The transaction parameter is an object, see transactions.

  • Success Response:

    • Code: 200
          ok : true,
          data : {
              transaction : { // Additional fields based on store type (see "transactions" below) }
      The transaction parameter is an object, see transactions. Optional. Will replace the product's transaction field with this.
  • Error Response:

    • Code: 200 (for validation error codes)
          ok : false,
          data : {
              code : 6778003 // Int. Corresponds to a validation error code, click above for options.
          error : { // (optional)
              message : "The subscription is expired."


    • Code: non-200
      The response's status and statusText will be displayed in an formatted error string.

** Fovea's receipt validator is available here.

validation callback example

store.validator = function(product, callback) {

    // Here, you will typically want to contact your own webservice
    // where you check transaction receipts with either Apple or
    // Google servers.
    callback(true, { ... transaction details ... }); // success!
    callback(true, { transaction: "your custom details" }); // success!
        // your custom details will be merged into the product's transaction field

    // OR
    callback(false, {
        code: store.PURCHASE_EXPIRED, // **Validation error code
        error: {
            message: "XYZ"

    // OR
    callback(false, "Impossible to proceed with validation");

    // Here, you will typically want to contact your own webservice
    // where you check transaction receipts with either Apple or
    // Google servers.

** Validation error codes are documented here.


A purchased product will contain transaction information that can be sent to a remote server for validation. This information is stored in the product.transaction data. This field is an object with a different format depending on the store type.

The product.transaction field has the following format:

  • type: "ios-appstore" or "android-playstore"
  • store specific data

store specific data - iOS

Refer to this documentation for iOS.

Transaction Fields (Subscription)

    id : "idString"
    "type": "ios-appstore"

store specific data - Android

Start here for Android.

developerPayload : undefined
id : "idString"
purchaseToken : "purchaseTokenString"
receipt : '{ // NOTE: receipt's value is string and will need to be parsed
signature : "signatureString",
"type": "android-playstore"


Another option is to use Fovea's validation service that implements all the best practices to enhance your subscriptions and secure your transactions.


Refresh the historical state of purchases and price of items. This is required to know if a user is eligible for promotions like introductory offers or subscription discount.

It is recommended to call this method right before entering your in-app purchases or subscriptions page.

You can of update() as a light version of refresh() that won't ask for the user password. Note that this method is called automatically for you on a few useful occasions, like when a subscription expires.


After you're done registering your store's product and events handlers, time to call store.refresh().

This will initiate all the complex behind-the-scene work, to load product data from the servers and restore whatever already have been purchased by the user.

Note that you can call this method again later during the application execution to re-trigger all that hard-work. It's kind of expensive in term of processing, so you'd better consider it twice.

One good way of doing it is to add a "Refresh Purchases" button in your applications settings. This way, if delivery of a purchase failed or if a user wants to restore purchases he made from another device, he'll have a way to do just that.

NOTE: It is a required by the Apple AppStore that a "Refresh Purchases" button be visible in the UI.

return value

This method returns a promise-like object with the following functions:

  • .cancelled(fn) - Calls fn when the user cancelled the refresh request.
  • .failed(fn) - Calls fn when restoring purchases failed.
  • .completed(fn) - Calls fn when the queue of previous purchases have been processed. At this point, all previously owned products should be in the approved state.
  • .finished(fn) - Calls fn when the restore is finished, i.e. it has failed, been cancelled, or all purchased in the approved state have been finished or expired.

In the case of the restore purchases call, you will want to hide any progress bar when the finished callback is called.

example usage
   // ...
   // register products and events handlers here
   // ...
   // then and only then, call refresh.
restore purchases example usage

Add a "Refresh Purchases" button to call the store.refresh() method, like:

<button onclick="restorePurchases()">Restore Purchases</button>
function restorePurchases() {

To make the restore purchases work as expected, please make sure that the "approved" event listener had be registered properly and, in the callback, product.finish() is called after handling.


Opens the Manage Subscription page (AppStore, Play, Microsoft, ...), where the user can change his/her subscription settings or unsubscribe.

example usage


Opens the Manage Billing page (AppStore, Play, Microsoft, ...), where the user can update his/her payment methods.

example usage


Redeems a promotional offer from within the app.

  • On iOS, calling store.redeem() will open the Code Redemption Sheet.
  • This call does nothing on Android and Microsoft UWP.
example usage

store.log object


Logs an error message, only if store.verbosity >= store.ERROR


Logs a warning message, only if store.verbosity >= store.WARNING

Logs an info message, only if store.verbosity >= store.INFO


Logs a debug message, only if store.verbosity >= store.DEBUG


An optional developer-specified string to attach to new orders, to provide supplemental information if required.

When it's a string, it contains the direct value to use. Example:

store.developerPayload = "some-value";

When it's a function, the payload will be the returned value. The function takes a product as argument and returns a string.


store.developerPayload = function(product) {
  return getInternalId(;


An optional string that is uniquely associated with the user's account in your app.

This value can be used for payment risk evaluation, or to link a purchase with a user on a backend server.

When it's a string, it contains the direct value to use. Example:

store.applicationUsername = "user_id_1234567";

When it's a function, the applicationUsername will be the returned value.


store.applicationUsername = function() {
  return state.get(["session", "user_id"]);


Evaluate and return the value from store.applicationUsername.

When its a string, the value is returned right away.

When its a function, the return value of the function is returned.




An optional string of developer profile name. This value can be used for payment risk evaluation.

Do not use the user account ID for this field.


store.developerName = "";


Return all products member of a given subscription group.

Random Tips

  • Sometimes during development, the queue of pending transactions fills up on your devices. Before doing anything else you can set store.autoFinishTransactions to true to clean up the queue. Beware: this is not meant for production.
  • The plugin will auto refresh the status of user's purchases every 24h. You can change this interval by setting store.autoRefreshIntervalMillis to another interval (before calling store.init()). (this isn't implemented on iOS since it isn't necessary). Set to 0 to disable auto-refreshing.

internal APIs


store.products array

Array of all registered products




Acts like the Array push method, but also adds the product to the byId and byAlias objects.

store.products.byId dictionary

Registered products indexed by their ID



store.products.byAlias dictionary

Registered products indexed by their alias


store.products.byAlias["full version"]```

aliases to store methods, added for convenience.

store._queries object

The queries object handles the callbacks registered for any given couple of query and action.

Internally, the magic is found within the triggerWhenProduct method, which generates for a given product the list of all possible queries that describe the product.

Queries are generated using the id, alias, type or validity of the product.


Transform a human readable query string into a unique string by filtering out reserved keywords:

  • order
  • product

store._queries.callbacks object

Callbacks registered organized by query strings

store._queries.callbacks.byQuery dictionary

Dictionary of:

  • key: a string equals to query + " " + action
  • value: array of callbacks

Each callback have the following attributes:

  • cb: callback function
  • once: true iff the callback should be called only once, then removed from the dictionary.

store._queries.callbacks.add(query, action, callback, once)

Simplify the query with uniqueQuery(), then add it to the dictionary.

action is concatenated to the query string to create the key.

store._queries.triggerAction(action, args)

Trigger the callbacks registered when a given action (string) happens, unrelated to a product.

args are passed as arguments to the registered callbacks.

  • Call the callbacks
  • Remove callbacks that needed to be called only once

store._queries.triggerWhenProduct(product, action, args)

Trigger the callbacks registered when a given action (string) happens to a given product.

args are passed as arguments to the registered callbacks.

The method generates all possible queries for the given product and action.

  • + " " + action
  • product.alias + " " + action
  • product.type + " " + action
  • "subscription " + action (if type is a subscription)
  • "valid " + action (if product is valid)
  • "invalid " + action (if product is invalid)
  • action

Then, for each query:

  • Call the callbacks
  • Remove callbacks that needed to be called only once

Note: All events also trigger the updated event

store.trigger(product, action, args)

For internal use, trigger an event so listeners are notified.

It's a conveniance method, that adds flexibility to _queries.triggerWhenProduct by:

  • allowing to trigger events unrelated to products
    • by doing store.trigger("refreshed") for example.
  • allowing the product argument to be either:
    • a product
    • a product id
    • a product alias
  • converting the args argument to an array if it's not one
  • adding the product itself as an argument to the event if none were passed

store.error.callbacks array

Array of user registered error callbacks.


Execute all error callbacks with the given error argument.


Remove all error callbacks.


store.utils.logError(context, error)

Add warning logs on a console describing an exceptions.

This method is mostly used when execting user registered callbacks.

  • context is a string describing why the method was called
  • error is a javascript Error object thrown by a exception

store.utils.callExternal(context, callback, ...)

Calls an user-registered callback. Won't throw exceptions, only logs errors.

  • name is a short string describing the callback
  • callback is the callback to call (won't fail if undefined)

example usage

store.utils.callExternal("ajax.error", options.error, 404, "Not found");


Simplified version of jQuery's ajax method based on XMLHttpRequest. Only supports JSON requests.


  • url:
  • method: HTTP method to use (GET, POST, ...)
  • success: callback(data)
  • error: callback(statusCode, statusText)
  • data: body of your request


Returns an UUID v4. Uses window.crypto internally to generate random values.


Returns the MD5 hash-value of the passed string.