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 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.
Try publishing your app in alpha or beta. They have changed the way of doing tests and now this is required.
Cheers