Search code examples
androidtitanium-mobilein-app-billingtitanium-modulesgoogle-play

Can't access In-App Products in Play Store


I'm working on an Android app developed on Titanium SDK 3.2.0.GA and I'm using the In-App Billing module by Appcelerator.

My implementation of the module it's as follows (omitting functions which only purpose is to display log info):

var IdentifierProduct = '';
var InAppBilling = require('ti.inappbilling');
var publicKey = Alloy.Globals.Android.publicKey_1 + Alloy.Globals.Android.publicKey_2 + Alloy.Globals.Android.publicKey_3 + Alloy.Globals.Android.publicKey_4;
InAppBilling.setPublicKey(publicKey);

function initializeBilling()
{           
    var synchronousResponse = InAppBilling.checkBillingSupported();
    displaySynchronousResponseCodes(synchronousResponse);
}

function requestPurchase(identifier, item_type)
{

    // Check if billing for current product type is supported before sending purchase request
    var checkBillingResponse = InAppBilling.checkBillingSupported(item_type);
    if (checkBillingResponse.responseCode == InAppBilling.RESULT_OK)
    {
        Ti.API.info('Current product type supported, continuing with request');

        var tmpArgs = {
            productId: identifier,
            productType: item_type,
            developerPayload: 'devPayload'
        };

        Ti.API.info('args for product request\n' + JSON.stringify(tmpArgs));
        var synchronousResponse = InAppBilling.requestPurchase({
            // productId: 'android.test.purchased',
            // productId: 'android.test.canceled',
            // productId: 'android.test.refunded',
            // productId: 'android.test.item_unavailable',
            productId: identifier,
            productType: item_type,
            developerPayload: 'devPayload'
        });
        displaySynchronousResponseCodes(synchronousResponse);
    } 
    else 
    {
        Ti.API.info('Current product type not supported, aborting request');
        displaySynchronousResponseCodes(checkBillingResponse);
    }
}

function ON_BIND_EVENT(e)
{
    if (e.result == InAppBilling.SUCCESS) {
        NotifyMe('Billing Service Bound');
        enableInAppPurchases(true);


        //Call 
    } else {
        NotifyMe('Billing Service Bind Failed');
        enableInAppPurchases(false);
    }
}
InAppBilling.addEventListener(InAppBilling.ON_BIND_EVENT, ON_BIND_EVENT);

function ON_CONNECT_EVENT(e)
{
    NotifyMe('CONNECT CALLED');
    if(Ti.App.Properties.getBool('transactionsRestores') === null)
    {
        Ti.API.info('fresh install, lets restore the transactions');
        try
        {
            InAppBilling.restoreTransactions();
        }
        catch(err)
        {
            Ti.API.info('Error');
            Ti.API.info(JSON.stringify(err));
        }
    }
}
InAppBilling.addEventListener(InAppBilling.ON_CONNECT_EVENT, ON_CONNECT_EVENT);

function RESPONSE_EVENT(e)
{
    // Events with (e.sync == true) are deprecated and will be removed. Use the event object that the methods return.
    if(!e.sync){
        NotifyMe('RESPONSE CALLED ' + e.requestId + ' ' + e.responseCode);
        Ti.API.info('RESPONSE CALLED \n' + 'Request Id:\n' + e.requestId + ' ' + '\nResponse Code:' + ResponseString(e.responseCode));
        if(e.responseCode === InAppBilling.RESULT_ERROR)
        {                       
            // Error in request
            Ti.API.info('Response event error');
            Ti.API.info(JSON.stringify(e));

        }
        else if(e.responseCode === InAppBilling.RESULT_ITEM_UNAVAILABLE)
        {

            // Item unavailable in request
            Ti.API.info('Response event item unavailable');
            Ti.API.info(JSON.stringify(e));
        }

    }
}
InAppBilling.addEventListener(InAppBilling.RESPONSE_EVENT, RESPONSE_EVENT);


function PURCHASE_STATE_CHANGED_EVENT(e)
{
    Ti.API.info('PURCHASE_STATE_CHANGED_EVENT Parameters:\n' + JSON.stringify(e) );
    NotifyMe('PURCHASE STATE CHANGED CALLED ' + e.signedData + ' ' + e.signature+'\n'+ 'SECURITY RESULT ' + e.result);

    Ti.API.info('PURCHASE STATE CHANGED CALLED');
    Ti.API.info('Signature Verification Result:\n' + VerificationString(e.result));
    Ti.API.info('Signed Data:\n' + e.signedData);

    if (e.signedData != null) {
        var response = JSON.parse(e.signedData);
        /*
         * We are not guaranteed to have any orders returned so 
         * we need to make sure that this one exists before using it.
         * 
         * If there is no notificationId then there is no need to confirmNotifications().
         * This happens when restoreTransactions() triggers a PURCHASE_STATE_CHANGED_EVENT.
         */
        for(var i = 0; i < response.orders.length; i++)
        {
            if(typeof response.orders[i] !== 'undefined')
            {
                setPurchaseFlag(response.orders[i].productId);
            }
            if (response.orders[i] && response.orders[i].notificationId) 
            {

                Ti.API.info('confirming notification for order ' + JSON.stringify(response.orders[i]));
                var synchronousResponse = InAppBilling.confirmNotifications({
                    notificationIds: [response.orders[i].notificationId]
                });
                displaySynchronousResponseCodes(synchronousResponse);
            }
        }
    }
}
InAppBilling.addEventListener(InAppBilling.PURCHASE_STATE_CHANGED_EVENT, PURCHASE_STATE_CHANGED_EVENT);

function NOTIFY_EVENT(e)
{
    Ti.API.info('NOTIFY CALLED \n' + 'Notify Id:\n' + e.notifyId);

    var synchronousResponse = InAppBilling.getPurchaseInformation({
        notificationIds: [e.notifyId]
    });
    displaySynchronousResponseCodes(synchronousResponse);
}
InAppBilling.addEventListener(InAppBilling.NOTIFY_EVENT, NOTIFY_EVENT);

InAppBilling.startBillingService();

The following is a list of the steps I've reviewed to look for errors:

  • I've checked that my package name is the same as the one in my application draft in Play Store
  • I checked that my license key is correct
  • all of my In-App products are in active state
  • the request sent to the Play Store are with product ids that correspond to my In-App products.
  • My device account is in registered as a test account so it should let me make a purchase.

I added the In-App Products around two weeks ago and every time I try to request a purchase, all of them are return a not found response, Play Store couldn't take this long to activate the, right?

And yet, I get as a response "the item you are attempting to purchase could not be found".

The response code I get is 2, which I thought it meant RESULT_SERVICE_UNAVAILABLE, but the dialog that appears clearly says the item was not found, am I interpreting the response code wrongly? Or should I look into why the service is unavailable?

I followed the guide to test In-App purchases and I'm getting this error with no clear cause.

EDIT

I've been searching around and found a few questions (here and here) were developers get RESULT_SERVICE_UNAVAILABLE when making too many restoreTransactions() calls. I checked my code and saw that, indeed, I was making a restoreTransactions() call every time I launched the app.

Now I have added a validation on the ON_CONNECT_EVENT to make sure if there were transactions to restore at all, in case it doesn't then I set a flag that prevents the app from making a restoreTransactions() call the next it launches.

The problem, though, still remains, can I contact Google in some way to ask for my In-App services to be enabled again? How long do I have to wait? The users on those questions mention it could take up to 3 weeks, can we accelerate this process?

Unfortunately without In-App Purchases we cannot release our app.


Solution

  • Try publishing your app in alpha or beta. They have changed the way of doing tests and now this is required.

    Cheers