InApp

In-application items are defined by the developer in the FreeFactory Console. Item fields are:

  • akey (unique identifier string),

  • description (item description string),

  • type (unlockable or consumable or subscription),

  • priceCents (price in cents).

The unlockable type describes an item that is buyable only once, e.g: unlock a game.

The consumable type describes an item that is buyable many times, e.g: virtual money.

The subscription type describes an item that is billed every month. This is reserved to our predeclared partners only. In this case, the field mGratis gives the number of free months before the actual billing. Please note that in case of such a subscription, the « free of charge » period can only happen once. If the user subscribes again later, this period will not be taken into account.

If the item is not a subscription, there is no field mGratis.

Transaction describes an item transaction. It contains the following fields:

  • akey item’s identifier,

  • when date field of the transaction (in string format YYYY-MM-DD hh:mm:ss),

  • type of item (unlockable or consumable),

  • transactionId unique transaction identifier (integer).

The InApp singleton object contains the following member functions:

InApp.items()

Retrieve the list of the items available to buy or subscribe in the current application.

Returns

a new Deferred.Deferred() instance with an item array.

Example of use:

import fbx.inapp 1.0
...

ListModel {
   id: items

   function reload() {
      items.clear();
      return InApp.items().then(function(ret) {
         for (var i = 0; i < ret.length; i++)
            items.append(ret[i]);
      }, function (err) {
         console.error("items listing failed:", err.message);
      });
   }

   Component.onCompleted: reload();
}

In the above code snippet, each item (ret[i]) is in the form (for example):

{
    "description": "an item to buy once",
    "priceCents": 499,
    "type": "unlockable",
    "akey": "UNLOCK_1",
}

Where the items are as listed on the Factory application page:

  • description is the description

  • priceCents is the price in cents

  • type can be one of “unlockable”, “consumable” or “subscription”

  • akey is the key field

InApp.transactions()

List the transactions done by the user in this application. Subscribed items are not listed in the transaction list. Only unlockable and consumable are listed.

Returns

a new Deferred.Deferred() instance with a transaction array.

Example of use:

import fbx.inapp 1.0
...

ListModel {
   id: trns

   function reload() {
      trns.clear();
      return InApp.transactions().then(function(ret) {
         for (var i = 0; i < ret.length; i++)
            trns.append(ret[i]);
      }, function (err) {
         console.error("transactions listing failed:", err.message);
      });
   }

   Component.onCompleted: reload();
}

In the above code snippet, each transaction (ret[i]) is in the form (for example):

{
    "akey": "UNLOCK_1",
    "when": "2014-10-01 13:37:42",
    "type": "unlockable",
    "transactionId": 2620
}

Where the transaction’s fields are:

  • akey is the key field

  • when is the date and time (local time)

  • type can be one of “unlockable” or “consumable”

  • transactionId is the ID to keep a reference about this transaction

InApp.buyItem(item)

Starts a new transaction to buy a new item. User interaction is handled automatically (a purchase code popup is spawned). The provided item must be an item as returned from the InApp.items() call. The price and the description will be checked against the predeclared item (matched by the akey parameter) and refused if not exactly the same. It is possible to change the price of an item, on the Factory side, but be aware that this change is immediate thus calls made afterwards will get a priceChanged return code if the calling application does not reflect the server side change.

Returns

a new Deferred.Deferred() instance with a transactionId on success.

Example of use:

import fbx.inapp 1.0
...

Button {
   property bool inhibitBuy: false

   enabled: !inhibitBuy

   onClicked: {
      inhibitBuy = true;
      InApp.buyItem(my_item).then(function (ret) {
             console.log("ok, transaction id is:", ret.transactionId);
      }, function (err) {
         console.error("Cannot buy item: " + JSON.stringify(err));
      }).both(function () {
         inhibitBuy = false;
      });
   }
}

In the above code snippet, a transaction (ret) is in the form (for example):

{
    "ok": true,
    "transactionId": 2627
}

Where the transaction’s fields are:

  • ok is true for successful transaction

  • transactionId is the ID to keep a reference about this transaction

For predeclared partners only:

On request, it is possible to add a flag freeItem: true to the item. This flag means that the item will not be checked against the predeclared list of items, and will be used and billed as-is. In that case, the akey parameter (which must be a string of maximum 30 alphanumeric characters, underscores authorized), will be kept as an internal reference in our DB in case of later reclamation or check.

InApp.subscribeItem(item)

Subscribe to an item. This call is reserved for predeclared partners only.

Returns

a new Deferred.Deferred() instance with a transactionId and a wsId.

Example of use:

import fbx.inapp 1.0
...

Button {
    onClicked: {
        var my_item = {
            "description": "a subscribing item",
            "priceCents": 499,
            "akey": "SUB_1"
        };

        InApp.subscribeItem(my_item).then(function (ret) {
            console.log(ret.transactionId, ret.wsId, ret.subId);
        });
    }
}

In the above code snippet, a subscription transaction (ret) is in the form (for example):

{
    "ok": true,
    "wsId": 1542,
    "transactionId": 2628,
    "subId": 1169,
    "mGratisFlag": true
}

Where the transaction’s fields are:

  • ok is true for successful transaction

  • wsId is the unique ID of the subscriber (it represents an accountId / profileName pair)

  • transactionId is the ID to keep a reference about this transaction

  • subId is the ID to keep a reference about this subscription. It is returned by other calls to identify uniquely the subscription

  • mGratisFlag is a flag to indicate that the (re-)subscription is in a free-month period (depending on the parameter for that subscription key on the Factory)

InApp.checkSubscription(akey)

Check a subscription for akey. This call is for predeclared partners only.

Returns

a new Deferred.Deferred() instance with an object containing these fields:

  • subscribed (either true or false for the current connected profile)

  • wsId (integer unique id)

  • endDate (null or YYYY-MM-DD)

  • subId (integer unique subscription id)

  • subscribedProfiles (array of profiles subscribed to this item, maybe including the current profile).

Each subscribed profile contains the profileName field too.

If no profile is subscribed, the array of subscribedProfiles is empty.

Example response for this call, when only the current user profile is subscribed:

{
    "ok": true,
    "subscribed": true,
    "wsId": 1542,
    "endDate": null,
    "subId": 1169,
    "subscribedProfiles": [
        {
            "wsId": 1542,
            "profileName": "me",
            "subId": 1169,
            "endDate": null
        },
    ]
}

Where the response’s fields are:

  • ok is true for successful transaction

  • subscribed is true for a valid subscription

  • wsId is the unique ID of the subscriber (it represents an accountId / profileName pair)

  • endDate is either null (which means the subscription has not been ended) or the date when the subscription will effectively end (in case it has been terminated)

  • subId is the ID to keep a reference about this subscription. It is returned by other calls to identify uniquely the subscription

  • subscribedProfiles is an array of all the currently subscribed profiles, whether the current one is subscribed or not. The field meaning of each element of this array is the same as above

Example response for this call, when the current logged-in profile is not subscribed, but others are:

{
    "ok": true,
    "subscribed": false,
    "subscribedProfiles": [
        {
            "wsId": 1543,
            "profileName": "Dad",
            "subId": 1164,
            "endDate": null
        },
        {
            "wsId": 1544,
            "profileName": "Mom",
            "subId": 1185,
            "endDate": null
        }
    ]
}
InApp.unsubscribeItem(akey)

Unsubscribe an item (of type subscription). This call is reserved for predeclared partners only.

Returns

a new Deferred.Deferred() instance containing an object with these fields:

  • ok (boolean) whether the call was acknowledged or not (if an error occured),

  • transactionId (integer) transaction id,

  • subId (integer unique subscription id, the same that was returned at subscription)

  • endDate in the following format: YYYY-MM-DD.

The endDate field gives the actual end of the subscription. It is equal to the next day of the month that matches the start of the subscription. This can be in the current month or the next one if the current day of month is greater than the start day. For shorter months, the endDate will be equal to the last day of the month.

Example response for this call:

{
    "ok": true,
    "transactionId": 3023,
    "subId": 1169,
    "endDate": "2016-09-25"
}

Server to Server API

The Server to Server JSON API allows the merchant to retrieve and process transactions or subscriptions through HTTPS. This API is not part of the application, thus has no implementation in libfbxqml.

The base URL to call for all these services is https://factory.free.fr/merchant/.

The merchant server must use a certificate provided by the extranet. It is used for identification, so there is no need for a merchantId parameter, only the wsId (which is checked to belong to an application of the merchant).

Each POST call must send JSON data and all responses (to GET or POST calls) are JSON encoded. In each response, a field ok is set to true when everything is OK or false when something is wrong. In the latter case a field error contains the error. There is a list of possible errors at the end of this section.

The identifier used to reference a subscription is called a subId. It is guaranteed to be an integer unique on the platform. The identifier used to reference a user is called a wsId. It is guaranteed to be an integer unique on the platform, and depends on the accountId and the profileName.

API calls in alphabetical order:

  • GET https://factory.free.fr/merchant/check_subscription.pl?wsId=<WSID>&akey=<AKEY>

    Check if there is a valid subscription from wsId for akey. For predeclared partners only.

Example query:

https://factory.free.fr/merchant/check_subscription.pl?wsId=4242&akey=SUB_1

Example response:

{ "ok": true, "endDate": null, "subId": 5542 }

Note that the ok field set to true indicates the user is subscribed. In case it is not, an error no_such_wsid is returned. See the InApp.checkSubscription() section to know the meaning of the other response fields.

  • GET https://factory.free.fr/merchant/get_subscriptions.pl

    Get all current subscriptions (at the date of the call, for all your applications). For predeclared partners only.

Example query:

https://factory.free.fr/merchant/get_subscriptions.pl

Example response:

{
    "ok": true,
    "subs": [
        {
            "appliId": 599,
            "endDate": "2016-02-13",
            "subId": 10,
            "wsId":2106367,
            "mGratisFlag": false
        },
        {
            "appliId": 599,
            "endDate":null,
            "subId": 9,
            "wsId": 2106368,
            "mGratisFlag": true
        },
        {
            "appliId": 599,
            "endDate": null,
            "subId": 6,
            "wsId":4474355,
            "mGratisFlag": false
        }
    ]
}
  • GET https://factory.free.fr/merchant/txns.pl[?month=YYYYMM]

    Get the current (or the one given in parameter) month transactions, along with other info about the merchant applications.

    This call is still beta, and its response is subject to change.

    The structure of the response is, for the moment and for example:

{
    "apps": [
        {
            "downloads": 1238237,
            "inAppTransactions": [],
            "name": "MyApp1",
            "score": "3.47",
            "InAppSubscriptions": [],
            "subscriptions": [],
            "downloadsLast14Days": 1602,
            "transactions": [],
            "id": 212
        },
        {
            "downloads": 513605,
            "inAppTransactions": [],
            "name": "MyApp2",
            "score": "3.46",
            "InAppSubscriptions": [],
            "subscriptions": [],
            "downloadsLast14Days": 704,
            "transactions": [
                {
                    "when": "2017-01-13 07:42:32",
                    "accountId": "8Z91wfaIXMs",
                    "transactionId": 37975716,
                    "priceCents": 99,
                    "label": "Store - MyApp2"
                },
                {
                    "when": "2017-01-17 13:58:42",
                    "accountId": "6Qu5iAxESja",
                    "transactionId": 38017691,
                    "priceCents": 99,
                    "label": "Store - MyApp2"
                },
                {
                    "when": "2017-01-23 23:40:56",
                    "accountId": "eyFLIqDPuGD",
                    "transactionId": 38075786,
                    "priceCents": 99,
                    "label": "Store - MyApp2"
                }
            ],
            "id": 414
        },
        {
            "downloads": 2547,
            "inAppTransactions": [],
            "name": "MyApp3",
            "score": "3.12",
            "InAppSubscriptions": [],
            "subscriptions": [
                {
                    "when": "2016-12-03 00:05:38",
                    "accountId": "KN7hB5FM0Pp",
                    "transactionId": 30455412,
                    "priceCents": 79,
                    "label": "Store - MyApp3 - Abo 201701",
                    "subscriptionId": 42,
                    "mGratisFlag": false
                },
                {
                    "when": "2017-01-13 00:05:38",
                    "accountId": "WxIT6bU72UU",
                    "transactionId": 30456270,
                    "priceCents": 79,
                    "label": "Store - MyApp3 - Abo 201701",
                    "subscriptionId": 43,
                    "mGratisFlag": true
                }
            ],
            "downloadsLast14Days": 141,
            "transactions": [],
            "id": 442
        },
        {
            "downloads": 22217,
            "inAppTransactions": [],
            "name": "MyApp4",
            "score": "3.51",
            "InAppSubscriptions": [
                {
                    "when": "2016-12-11 01:23:34",
                    "wsId": 5236,
                    "transactionId": 30456270,
                    "priceCents": 99,
                    "label": "Store - MyApp4 - Sub1 - Abo 201701",
                    "subscriptionId": 64,
                    "akey": "MY_SUB_1",
                    "mGratisFlag": false
                },
                {
                    "when": "2017-01-22 14:15:31",
                    "wsId": 1838,
                    "transactionId": 30456524,
                    "priceCents": 129,
                    "label": "Store - MyApp4 - Sub2 - Abo 201701",
                    "subscriptionId": 74,
                    "akey": "MY_SUB_2",
                    "mGratisFlag": true
                }
            ],
            "subscriptions": [],
            "downloadsLast14Days": 110,
            "transactions": [],
            "id": 448
        }
    ]
}
  • POST https://factory.free.fr/merchant/unsubscribe.pl

    Unsubscribe a wsId for an akey. For predeclared partners only.

Example POST query:

{ "wsId": 4242, akey: "SUB_1" }

Example response:

{ "ok": true, ,"endDate": "2016-09-05", "transactionId": 1755, "subId": 5542 }

Error Codes:

In case something is wrong, or just because a wsId is not subscribed to any appliId of the given merchant, ok is set to false and an error field contains an error code. See the InApp.unsubscribe() section to know the meaning of the other response fields.

The possible error codes are, in alphabetical order:

  • already_subscribed: The subscription already exists (redundant call to subscribeItem())

  • already_unsubscribed: The subscription exists but has already been ended (redundant call to unsubscribeItem())

  • blacklisted: The merchant has been blacklisted or suspended for some serious reason. Please contact us

  • internal_error: A non specified temporary error has occured. Please retry later

  • invalid_params: Something is wrong with the API parameters

  • item_disabled: This item exists but is disabled

  • no_in_app: The application has not been set up to allow in-app purchase, or you are using a call reserved for predeclared partners

  • no_such_key: The given akey does not exist in the application item list

  • no_such_wsid: The given wsId does not exist on the platform or is not subscribed to an application of the merchant

  • not_subscribed: The subscription does not exist (in a call to unsubscribeItem())

  • price_changed: The item’s price has changed in between (typically you changed it on the Factory application page). Redo the transaction with the new price