I've been working on IAB V3, and have been having trouble, when trying to get the sku details off the store. After cheking for result failure of the inventory query, I try to get the details with inv.getSkuDetails(MODULE).getPrice(); This always returns null, and when debugging I can see that the inventory object is empty.
However These requests always return null.
I am aware that google requires you to upload an apk to the alpha release channel (and release it) and have the InAppPurchase active (I also gave the store a few days to take over the changes). To test I used the same APK as uploaded to the alpha channel.
The IabHelper obviously managed to connect to the right app, as a test purchase was possible, and could be queried from the store.
Code:
`mHelper = new IabHelper(MainActivity.this, getString(getString(R.string.key1));
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener(){
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(TAG, "Problem Setting up Billing" + result);
return;
}
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
if (result.isFailure()) {
Toast.makeText(getApplicationContext(), R.string.connectionFailed, Toast.LENGTH_SHORT).show();
Log.d(TAG, result.getMessage() + result.getResponse() + inv.toString());
return;
}
String modulePrice = inv.getSkuDetails(MODULE).getPrice();
String moduleTitle = inv.getSkuDetails(MODULE).getTitle();
String moduleDescription=inv.getSkuDetails(MODULE).getDescription();
}
}
ArrayList<String> skuList=new ArrayList<String>();
skuList.add(MODULE);
try {
mHelper.queryInventoryAsync(true, skuList, mQueryFinishedListener);
} catch (IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
}
});`
Ignore the wrong indentation and missing brackets, as i cut out some code.
I greatly appreciate any ideas as to what could be causing this problem.
Thanks
Related
I have a new android app in which I am adding in-app billing and I am tearing my hair out with frustration.
I have uploaded a signed APK and published to alpha. I created a set of in-app products and activated them all. I have created a new gmail account and defined them as a tester for the app on the app apk page.
I have factory reset my android phone and initialised it with the new gmail account. I have entered the /apps/testing link in to chrome and signed up as a tester. I then downloaded and installed my app. Inside my app I asked for the in app products that were available and was shown the set i created above. I selected one to buy and went through the following purchase process.
1. Screen shows product to be purchased and price and requests press continue which i do
2. Screen shows payment methods and I select redeem code
3. Screen shows redeem your code and I enter one of the promotion codes I set up in the developer console earlier (not mentioned above - sorry) and press redeem
4. Screen shows product again, this time with price crossed out and offers option to add item which I select (very strange being asked to add again buy hey ho)
5. Screen shows item added
6. After a fews seconds screen shows Error you already own this item.
How can this be, this user did not exist before ten minutes ago and has only used this app once as described above.
I have seen many questions in stack overflow and elsewhere similar to this and tried everything, clearing google play store cache, clearing google play store data etc. This sequence described above is my latest attempt with a completely clean user on a completely clean phone.
I could upload my app code used but that misses the point, which is how can this gmail account already own an item when this gmail account have never purchased anything before from anyone. Surely this is a bug.
All clues very welcome as to how to proceed. Code now added, note this is a hybrid android app, with the user purchase decisions code in javascript/html and the in app actions in the wrapper code below
private void processCommand(JSONObject commandJSON) throws JSONException
{
String command = commandJSON.getString("method");
if ("GetInAppProducts".equals(command))
{
Log.d(TAG, "Querying Inventory");
InAppPurchaseSkuString = null ; // clear the purchased sku. Note this is tested in mConsumeFinishedListener
mHelper.queryInventoryAsync(true, itemSkus, new IabHelper.QueryInventoryFinishedListener()
{
#Override
public void onQueryInventoryFinished(IabResult iabResult, Inventory inventory)
{
InventoryRecord = inventory ;
if (iabResult.isFailure())
{
Log.d(TAG, "Query inventory failed");
SendEndItemsToApp ();
}
else
{
Log.d(TAG, "Query inventory was successful.");
InventoryCheckCount = 0 ; // seems that we cannot just fire off a whole lot of these checks at the same time, so do them in sequence
if (itemSkus.size()>0) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
}
}
});
}
else if ("BuyInAppProduct".equals(command))
{
JSONArray params = commandJSON.getJSONArray("parameters");
InAppPurchaseSkuString = params.getString(0);
Log.d(TAG, "User decision to purchase " + InAppPurchaseSkuString);
mHelper.launchPurchaseFlow( MainActivity.this, InAppPurchaseSkuString, InAppPurchaseActivityCode, mPurchaseFinishedListener, "mypurchasetoken"); // consider putting the user email address in the last field - need to get from app
};
}//end of ProcessCommand
public void CheckForOwnedItems ()
{
Log.d(TAG, "Pre Purchase Inventory Processing Started");
String sku = itemSkus.get(InventoryCheckCount);
if (InventoryRecord.getSkuDetails(sku) != null)
{
if (InventoryRecord.hasPurchase(sku))
{
consumeItem ();
}
else
{
SendItemToApp ();
InventoryCheckCount++;
if (InventoryCheckCount < itemSkus.size()) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
};
};
}//end of CheckForOwnedItems
public void SendItemToApp ()
{
String sku = itemSkus.get(InventoryCheckCount);
String priceString = InventoryRecord.getSkuDetails(sku).getPrice().replaceAll("[^\\d.]+", ""); // RegExp removes all characters except digits and periods
String infoString = "InAppProductDetails('" + sku + "','" + "dummy" + "','" + priceString + "');"; // dummy is a placeholder for product description which is not (yet?) used in the app
Log.d(TAG, infoString);
mWebView.evaluateJavascript (infoString, new ValueCallback<String>()
{
#Override
public void onReceiveValue(String s)
{
//Log.d(TAG,"Returned from InAppProductDetails:");
}
}
);
}
public void SendEndItemsToApp ()
{
String endString = "InAppProductsEnd();"; // name is a placeholder for now
Log.d(TAG, endString);
mWebView.evaluateJavascript(endString, new ValueCallback<String>()
{
#Override
public void onReceiveValue(String s)
{
//Log.d(TAG,"Returned from InAppProductsEnd:");
}
}
);
}
public void consumeItem()
{
Log.d(TAG,"Pre Purchase Inventory Query Started");
String sku = itemSkus.get(InventoryCheckCount);
mHelper.consumeAsync(InventoryRecord.getPurchase(sku), mConsumeFinishedListener);
}
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener()
{
public void onConsumeFinished (Purchase purchase, IabResult result)
{
if (result.isSuccess())
{
Log.d(TAG, "Pre Purchase Consume Item Completed");
SendItemToApp ();
InventoryCheckCount++;
if (InventoryCheckCount < itemSkus.size()) { CheckForOwnedItems (); } else { SendEndItemsToApp (); }
}
else
{
Log.d(TAG,"Pre Purchase Consume Item Failed");
}
}
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener()
{
public void onIabPurchaseFinished (IabResult result, Purchase purchase)
{
if (result.isFailure())
{
Log.d(TAG,"Purchase Scenario Failed");
}
else if (purchase.getSku().equals(InAppPurchaseSkuString))
{
Log.d(TAG,"Purchase Scenario Completed");
String evalString = "InAppProductPurchased('" + InAppPurchaseSkuString + "');";
Log.d(TAG, evalString);
mWebView.evaluateJavascript (evalString, new ValueCallback<String>()
{
#Override
public void onReceiveValue(String s)
{
Log.d(TAG, "Returned from InAppProductPurchased:");
}
}
);
}
}
};
I have found that this error does not occur when using paypal (i.e. real money) to make the purchase, so I believe that this "Error you already own this item" message is in some way connected to using a promotion code for the test. And (so far) my paypal account has not been charged (as I am a resgistered tester for the app).
Thanks for visiting my page.
Few days ago, I've developed simple android game with in app-billing.
Now I am going to implement restore purchase function but I don't know how can I dot it.
I've made few days of googling and found many links to help it but they didn't work me now.
Please let me know how to do it programmatically.
Where can i find sample of restore purchase ?
I've implemented in app purchase already but not restore purchase.
I used Android Studio 1.5.1.
I've refered http://www.techotopia.com/index.php/An_Android_Studio_Google_Play_In-app_Billing_Tutorial to implement in app purchase.
Please help me :(
Thanks in advance.
If you are implemented the InApp Purchase using v3 you need not worry about the restore Purchase implementation. You can query the inventory and catch the existing Purchase information. Please check the implementation.
What I did here is I have already a purchase module. While I complete the purchase, I will send the information to our server. After relog in or come back to the application, the server will send the current user purchase info whether he is Purchased or not. if the server gives a negative result, I will check the query inventory that is there any existing purchase over there. For that, I am using the following code in the MainActivity onCreate().
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.enableDebugLogging(true);
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
Log.e(TAG, "In App Set UP error:: Please check gmail account settings/ Credit Card Info etc");
return;
}
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(MainActivity.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
Log.d(TAG, "Setup successful. Querying inventory.");
if (mSupplier.getmSubscriptionStatus() == 0) { // This is the Status given from Local Server 0- UnScubscribed User, 1- Subscribed User
mHelper.queryInventoryAsync(mGotInventoryListenerForPurchase);
}
}
});
In the Result, You can Identify the existing purchase information.
IabHelper.QueryInventoryFinishedListener mGotInventoryListenerForPurchase = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
try {
Log.d(TAG, "Query inventory For Purchase finished.");
String payload = // Your developer payload
if (mHelper == null) return;
if (result.isFailure()) {
Log.v(TAG, "PURCHSE RESULT ERROR- NO PURCHASE AVAILABLE");
return;
}
Log.v(TAG, "Query inventory For Purchase was successful.");
if (mSkuDetailList == null || mSkuDetailList.isEmpty()) {
Log.v(TAG, "SKU INFO FROM LOCAL SERVER- UNABLE TO PURCHASE");
return;
}
Purchase premiumPurchase = null;
for (IabSkuDetailModel data : mSkuDetailList) {
// Filter the purchase info using SKU:::
premiumPurchase = inventory.getPurchase(data.getmPackageName());
if (premiumPurchase != null) {
break;
}
}
if (premiumPurchase == null) {
Log.v(TAG, "NO Available Purchase for this user");
return;
}
if (verifyDeveloperPayload(premiumPurchase)) {
Log.v(TAG, "Purchase is there already ::: developer payload is Matching:: This need to update Local Server: No need to purchase agian");
if (premiumPurchase.getSku().equalsIgnoreCase(mSelectedSku)) {
IabPurchaseUpdateReq request = new IabPurchaseUpdateReq();
request.setmPurchaseToken(premiumPurchase.getToken());
request.setmUserId("" + mSupplier.getmUserId());
request.setmPublicKey(IabConstants.IAB_RSA_PUBLIC_KEY);
request.setmSignature(premiumPurchase.getSignature());
request.setmSubscriptionId(premiumPurchase.getSku());
request.setmPurchaseObj(premiumPurchase.getOriginalJson());
//Update "result to local Server"
} else {
Log.v(TAG, "SKU mismatch ::: ");
}
} else {
Log.v(TAG, "Developer payload error:: Wrong Purchase");
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
I am trying to implement the In-App Purchase using the wrapper of Trivial Gas tutorial. After completion of successful purchase The app crashed, since then the app is getting crashed at the beginning.
java.lang.IllegalArgumentException: java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D07209B:asn1 encoding routines:ASN1_get_object:too long
at com.nightowl.memory.Security.generatePublicKey(Security.java:85)
at com.nightowl.memory.Security.verifyPurchase(Security.java:65)
at com.nightowl.memory.IabHelper.queryPurchases(IabHelper.java:875)
at com.nightowl.memory.IabHelper.queryInventory(IabHelper.java:550)
at com.nightowl.memory.IabHelper.queryInventory(IabHelper.java:528)
at com.nightowl.memory.IabHelper$2.run(IabHelper.java:623)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D07209B:asn1 encoding routines:ASN1_get_object:too long
at com.android.org.conscrypt.OpenSSLKey.getPublicKey(OpenSSLKey.java:101)
at com.android.org.conscrypt.OpenSSLRSAKeyFactory.engineGeneratePublic(OpenSSLRSAKeyFactory.java:47)
at java.security.KeyFactory.generatePublic(KeyFactory.java:171)
at com.nightowl.memory.Security.generatePublicKey(Security.java:80)
... 6 more
Caused by: java.lang.RuntimeException: error:0D07209B:asn1 encoding routines:ASN1_get_object:too long
at com.android.org.conscrypt.NativeCrypto.d2i_PUBKEY(Native Method)
at com.android.org.conscrypt.OpenSSLKey.getPublicKey(OpenSSLKey.java:99)
... 9 more
I have used the following code to call it:
String base64EncodedPublicKey;
base64EncodedPublicKey= String.valueOf(R.string.myPubKey);
additionalSkuList = new ArrayList<String>();
for(int i=0;i<3;i++)
{
for(int j=0;j<4;j++)
{
if(i==0&&j>1)
break;
additionalSkuList.add(id[i][j]);
}
}
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d("main", "m here");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.d("main", "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
isIAB = true;
mHelper.queryInventoryAsync(true, additionalSkuList, mQueryFinishedListener);
Log.d("main", "" + isIAB + " " + isLoad);
}
});
IabHelper.QueryInventoryFinishedListener
mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
Log.d("main", "m here too");
if (result.isFailure()) {
// handle error
return;
}
for(int i=0;i<3;i++)
{
for(int j=0;j<4;j++)
{
if(i==0&&j>1)
break;
price[i][j]=inventory.getSkuDetails(id[i][j]).getPrice();
}
}
isLoad=true;
data.setPrice(price);
data.setDataLoad(true);
// update the UI
}
};
public void onPurchaseCall()
{
int loc[] = data.getItem();
mHelper.launchPurchaseFlow(this, id[loc[0]][loc[1]], 100*loc[0]+loc[1],mPurchaseFinishedListener, id[loc[0]][loc[1]]);
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log.d("main", "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(id[0][0])) {
data.setNoads(1);
mAdView.setVisibility(View.INVISIBLE);
data.setNotificationState(1);
data.setNoOfnotifications(2);
data.setNotificationMsg(0, "Purchase Complete");
data.setNotificationMsg(1, PurchaseMsg[0]);
// consume the gas and update the UI
}
}
};
Can anyone help me to solve it? What I guessed from the log is that something problem with the Public Key I provide. But the first time it worked (It was working till I initiated a successful purchase). And I was also getting the price list of all my in-app products.
I tried to test the purchase with a testing account. On successful completion of the purchase this error came and since then the app is not running for that account.
Don't use String.valueOf(R.string.myPubKey), it will give you a wrong value. Instead use getResources().getString(R.string.myPubKey_data)) to get your public key string.
Although the question is old, but i write answer for anyone will have this problem.
The problem is from public key length. It's not valid and is longer than it should be. Make sure you entered it correctly.
I am trying to add Googles In-App Billing to my Android 4+ app. I have set up everything as described in "Preparing Your In-app Billing Application". Now I have uploaded the app to the Alpha testing channel in the Developer console.
Additionally I have set up a test account (described here) to be able purchase the items without triggering a real payment.
After installing the alpha version from the Play Store on my test device (using the test account of course) there a two problems:
No product information is fetched from the Play Store. Thus I cannot show any price information, etc.
When I start a purchase there is absolutly no hint, that this will be a free test purchase. Everything looks exactly like a real purchase.
This is the code I use:
String publicKey = MyApp.getPublicKey(); // de-code and get the public key
final IabHelper googlePlayHelper = new IabHelper(context, publicKey);
Log.d("TAG", "IabHelber Init");
googlePlayHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d("TAG", "IabHelber Init - Non Success: " + result);
} else {
Log.d("TAG", "IabHelber Init - SUCCESS");
try {
googlePlayHelper.queryInventoryAsync(true, new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
Log.d("TAG", "query Inventory - Non Success: " + result);
} else {
Log.d("TAG", "query Inventory - SUCCESS");
if (inventory.hasDetails(2my.product.id")) {
Log.d("TAG", "NO DETAILS");
} else {
Log.d("TAG", "Has Details");
}
}
}
}
} catch (Exception e) {
Log.d("TAG", "EXCEPTION: " + e.getMessage());
}
}
}
});
The Log shows the following:
D/TAG (25995): IabHelber Init
D/TAG (25995): IabHelber Init - SUCCESS
D/TAG (25995): query Inventory - SUCCESS
D/TAG (25995): NO DETAILS
What could be the reason that now details are fetched?
The docs that that there should be an hint when performing a test purchase. Why am I runnig a "real" purchase instead?
I have not been able to find out why purchases by test users are not handled as test purchases. But the problem with the missing product details is solved:
I used the following call to query the inventory:
googlePlayHelper.queryInventoryAsync(true, new IabHelper.QueryInventoryFinishedListener() { ... });
This is totally valid code and the first parameters (true in this example) states, that the query should fetch the product details. But it seems, that this parameter does not have any effect until a further parameter is given: One has to explicitly specify the IDs of the product one would like to fetch:
List<String> productIDs = new ArrayList<String>();
productIDs.add(IAP_ID_1);
productIDs.add(IAP_ID_2);
productIDs.add(IAP_ID_3);
googlePlayHelper.queryInventoryAsync(true, productIDs, new IabHelper.QueryInventoryFinishedListener() { ... });
Hi i'm trying to add in app purchases to my app i have set up my in app purchases on the developer console which are set to active i have then queried them which yesterday was working perfectly i retrieved all the details but today its coming back as null. the only thing that has changed is that i had to uninstall the app and re-run it. I have checked my skus both in the app and on the developer console which match exactly when i run IabHelper start setup i get a result of ok. And then i call IabHelper.QueryInventoryFinishedListener and that results back as being ok but when i try access anything from the inventory it comes back as null. does anyone know why? or if i'm doing some wrong in my code?
in my on Create();
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log.v("Menu", "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
Log.v("Menu", "INAPP BILLING SETUP COMPLETE: " + result);
ArrayList<String> skuList = new ArrayList<String> ();
skuList.add("myapp.consumable.inapppurchase_id_1");
skuList.add("myapp.consumable.inapppurchase_id_2");
skuList.add("myapp.consumable.inapppurchase_id_3");
skuList.add("myapp.permanant.inapppurchase_id_6");
skuArray = new JSONArray(skuList);
mHelper.queryInventoryAsync(true, skuList, mQueryFinishedListener);
}
});
Then i heres my code for the QueryListener
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure()) {
Log.v("Menu", "RESULT FALIURE");
return;
}
Log.v("Menu", "this +" + skuArray);
Log.v("Menu", "Inventory +" + inventory);
for(int i = 0; i < skuArray.length(); i++){
try {
String SKU = skuArray.getString(i);
if(inventory.getSkuDetails(SKU) != null){
Log.v("Menu", "SKU = " + SKU);
Log.v("Menu", "SKU" + SKU + "= " + inventory.getSkuDetails(SKU));
updateProductData("price",inventory.getSkuDetails(SKU).getPrice(),i);
updateProductData("id",inventory.getSkuDetails(SKU).getSku(),i);
}else{
Log.v("Menu", "SKU RETURNED NULL" + SKU);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
Ok i spoke to Google about this issue. And they say they have made changes which requires the apk to be published before adding in app purchases they recommend uploading the apk to alpha testing channel and published (not in draft mode).
Ill give it a try and feed back if it works
After I published my alpha apk to google play the billing started to work again.