I am implementing Google In-app Purchase V3 and followed all steps stated over here as well in official documentation here. I have uploaded my app in Google Playstore for Alpha Testing and I have downloaded that from playstore URL into my real device but it giving me error
Error
Authentication is required. You need to sign into your Google Account.
My code for In-app purchase is here:
public class BuyPointsFragment extends Fragment
//In app Billing variable start
// Debug tag, for logging
static final String TAG = "com.myApp";
// Does the user have the premium upgrade?
boolean mIsPremium = false;
// Does the user have an active subscription to the infinite gas plan?
boolean mSubscribedToInfiniteGas = false;
// SKUs for our products: the premium upgrade (non-consumable) and gas
// (consumable)
static final String SKU_PREMIUM = "premium";
static final String SKU_GAS = "gas";
// SKU for our subscription (infinite gas)
static final String SKU_INFINITE_GAS = "infinite_gas";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;
// Graphics for the gas gauge
static int[] TANK_RES_IDS = {};
// How many units (1/4 tank is our unit) fill in the tank.
static final int TANK_MAX = 4;
// Current amount of gas in tank, in units
int mTank;
// The helper object
IabHelper mHelper;
//In app billing variable end
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//inapp load game data
loadData();
String base64EncodedPublicKey = "Base64Key from publisher account";
// Some sanity checks to see if the developer (that's you!) really
// followed the
// instructions to run this sample (don't put these checks on your app!)
if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {
throw new RuntimeException(
"Please put your app's public key in MainActivity.java. See README.");
}
if (getActivity().getPackageName().startsWith("com.myApp.activity")) {
throw new RuntimeException(
"Please change the sample's package name! See README.");
}
// Create the helper, passing it our context and the public key to
// verify signatures with
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
// enable debug logging (for a production application, you should set
// this to false).
mHelper.enableDebugLogging(true);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
complain(getString(R.string.problem_setting_inapp_billing) + result);
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null)
return;
// IAB is fully set up. Now, let's get an inventory of stuff we
// own.
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
//In app billing code end here
}
//In app billing methods start here
public void inappCall(){
setWaitScreen(true);
Log.d(TAG, "Launching purchase flow for gas.");
/*
* TODO: for security, generate your payload here for verification. See
* the comments on verifyDeveloperPayload() for more info. Since this is
* a SAMPLE, we just use an empty string, but on a production app you
* should carefully generate this.
*/
String payload = "";
mHelper.launchPurchaseFlow(getActivity(), SKU_GAS, RC_REQUEST,
mPurchaseFinishedListener, payload);
}
// updates UI to reflect model
public void updateUi() {
// update the car color to reflect premium status or lack thereof
// ((ImageView)findViewById(R.id.free_or_premium)).setImageResource(mIsPremium
// ? R.drawable.premium : R.drawable.free);
// "Upgrade" button is only visible if the user is not premium
// findViewById(R.id.upgrade_button).setVisibility(mIsPremium ?
// View.GONE : View.VISIBLE);
// "Get infinite gas" button is only visible if the user is not
// subscribed yet
// (R.id.infinite_gas_button).setVisibility(mSubscribedToInfiniteGas ?
// View.GONE : View.VISIBLE);
// update gas gauge to reflect tank status
if (mSubscribedToInfiniteGas) {
// ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(R.drawable.gas_inf);
} else {
int index = mTank >= TANK_RES_IDS.length ? TANK_RES_IDS.length - 1
: mTank;
// ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(TANK_RES_IDS[index]);
}
}
// Enables or disables the "please wait" screen.
void setWaitScreen(boolean set) {
// findViewById(R.id.screen_main).setVisibility(set ? View.GONE :
// View.VISIBLE);
// findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE :
// View.GONE);
}
void complain(String message) {
Log.e(TAG, "**** TrivialDrive Error: " + message);
alert("Error: " + message);
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(getActivity());
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(TAG, "Showing alert dialog: " + message);
bld.create().show();
}
void saveData() {
/*
* WARNING: on a real application, we recommend you save data in a
* secure way to prevent tampering. For simplicity in this sample, we
* simply store the data using a SharedPreferences.
*/
SharedPreferences.Editor spe = getActivity().getPreferences(getActivity().MODE_PRIVATE).edit();
spe.putInt("tank", mTank);
spe.commit();
Log.d(TAG, "Saved data: tank = " + String.valueOf(mTank));
}
void loadData() {
SharedPreferences sp = getActivity().getPreferences(getActivity().MODE_PRIVATE);
mTank = sp.getInt("tank", 2);
Log.d(TAG, "Loaded data: tank = " + String.valueOf(mTank));
}
// We're being destroyed. It's important to dispose of the helper here!
#Override
public void onDestroy() {
super.onDestroy();
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
// Listener that's called when we finish querying the items and
// subscriptions we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null)
return;
// Is it a failure?
if (result.isFailure()) {
complain(getString(R.string.failed_to_query_inventory) + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
/*
* Check for items we own. Notice that for each purchase, we check
* the developer payload to see if it's correct! See
* verifyDeveloperPayload().
*/
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
// Do we have the infinite gas plan?
Purchase infiniteGasPurchase = inventory
.getPurchase(SKU_INFINITE_GAS);
mSubscribedToInfiniteGas = (infiniteGasPurchase != null && verifyDeveloperPayload(infiniteGasPurchase));
Log.d(TAG, "User "
+ (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")
+ " infinite gas subscription.");
if (mSubscribedToInfiniteGas)
mTank = TANK_MAX;
// Check for gas delivery -- if we own gas, we should fill up the
// tank immediately
Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
Log.d(TAG, "We have gas. Consuming it.");
mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
mConsumeFinishedListener);
return;
}
updateUi();
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
/*
* TODO: verify that the developer payload of the purchase is correct.
* It will be the same one that you sent when initiating the purchase.
*
* WARNING: Locally generating a random string when starting a purchase
* and verifying it here might seem like a good approach, but this will
* fail in the case where the user purchases an item on one device and
* then uses your app on a different device, because on the other device
* you will not have access to the random string you originally
* generated.
*
* So a good developer payload has these characteristics:
*
* 1. If two different users purchase an item, the payload is different
* between them, so that one user's purchase can't be replayed to
* another user.
*
* 2. The payload must be such that you can verify it even when the app
* wasn't the one who initiated the purchase flow (so that items
* purchased by the user on one device work on other devices owned by
* the user).
*
* Using your own server to store and verify developer payloads across
* app installations is recommended.
*/
return true;
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: "
+ purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null)
return;
if (result.isFailure()) {
complain(getString(R.string.error_purchase) + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain(getString(R.string.error_purchase_authenitcity_failed));
setWaitScreen(false);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_GAS)) {
// bought 1/4 tank of gas. So consume it.
Log.d(TAG, "Purchase is gas. Starting gas consumption.");
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
} else if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert(getString(R.string.thank_you_updgraing_premium));
mIsPremium = true;
updateUi();
setWaitScreen(false);
} else if (purchase.getSku().equals(SKU_INFINITE_GAS)) {
// bought the infinite gas subscription
Log.d(TAG, "Infinite gas subscription purchased.");
alert("Thank you for subscribing to infinite gas!");
mSubscribedToInfiniteGas = true;
mTank = TANK_MAX;
updateUi();
setWaitScreen(false);
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. Purchase: " + purchase
+ ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null)
return;
// We know this is the "gas" sku because it's the only one we
// consume,
// so we don't check which sku was consumed. If you have more than
// one
// sku, you probably should check...
if (result.isSuccess()) {
// successfully consumed, so we apply the effects of the item in
// our
// game world's logic, which in our case means filling the gas
// tank a bit
Log.d(TAG, "Consumption successful. Provisioning.");
mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
saveData();
alert("You filled 1/4 tank. Your tank is now "
+ String.valueOf(mTank) + "/4 full!");
} else {
complain("Error while consuming: " + result);
}
updateUi();
setWaitScreen(false);
Log.d(TAG, "End consumption flow.");
}
};
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + ","
+ data);
if (mHelper == null)
return;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// not handled, so handle it ourselves (here's where you'd
// perform any handling of activity results not related to in-app
// billing...
super.onActivityResult(requestCode, resultCode, data);
} else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
//In app billing method end here
My products are Managed products in developer account In-app Products
EDIT:
When I use android.test.purchased as a SKU then it works fine and as I change my sku with my product_id then it giving me Error Authentication is required. You need to sign into your Google Account.
Please make sure your product id on playstore for in app purchase which is "test_product" you should use the same sku in your code. And if you change the sku from your application, then all that possible sku names must exists on PlayStore as in-app Products. I once run into this problem and the reason was my SKU item was not exists on Google Playstore in-app products i just add it on Playstore and it was resolved.
Android IAB Error - Authentication required
It seems that you need to publish the APK. I stuck with this problem too.
It is a bit of a longshot, but your "Google Play store" app could be malfunctioning.
I have a report from a customer that he cannot make an In-App purchase and he is getting this same error. I also know that we are still getting purchases. So I don't think you necessarily configured anything wrong. IAB v3 could simply be malfunctioning with your "Google Play Store" app.
Try going to Settings open the Application manager and choose the "Google Play store" application in the list, and click on "Uninstall updates" button which appears there. Next try to do the purchase again. If your old Google Play application supports In-App billing v3 it will work. If it doesn't, try to update the "Google Play store" app again, perhaps you will get a newer/different version of the store app and you will be able to make the purchase.
As an alternative you can try and configure a second test device with a different Google account... or ask a friend to join a beta test group and make a test purchase.
I am not really happy with IAB v3. It feels a little clunky. V2 had fewer features but felt solid.
Related
Hi i am implementing InApp purchase in my android application For this i am using IInAppBillingService. Implementation is done, but now i am facing a problem when user update or
downgrade the subscription.The problem is suppose if user have a active monthly subscription now in the mid of the month if he update the subscription to yearly what i want is to use setReplaceSkusProrationMode of billing library so that the cost will manage automatically means remaning balance of 15 days will get adjusted in new yearly subscription.
How to get this when using IInAppBillingService.The code which i am using for launching purchase is as below please help me i am lost.
/**
* Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
* which will involve bringing up the Google Play screen. The calling activity will be paused
* while the user interacts with Google Play, and the result will be delivered via the
* activity's {#link android.app.Activity#onActivityResult} method, at which point you must call
* this object's {#link #handleActivityResult} method to continue the purchase flow. This method
* MUST be called from the UI thread of the Activity.
*
* #param act The calling activity.
* #param sku The sku of the item to purchase.
* #param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or
* ITEM_TYPE_SUBS)
* #param oldSkus A list of SKUs which the new SKU is replacing or null if there are none
* #param requestCode A request code (to differentiate from other responses -- as in
* {#link android.app.Activity#startActivityForResult}).
* #param listener The listener to notify when the purchase process finishes
* #param extraData Extra data (developer payload), which will be returned with the purchase
* data when the purchase completes. This extra data will be permanently bound to that
* purchase and will always be returned when the purchase is queried.
*/
public void launchPurchaseFlow(Activity act, String sku, String itemType, List<String> oldSkus,
int requestCode, OnIabPurchaseFinishedListener listener, String extraData)
throws IabAsyncInProgressException {
checkNotDisposed();
checkSetupDone("launchPurchaseFlow");
flagStartAsync("launchPurchaseFlow");
IabResult result;
if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE,
"Subscriptions are not available.");
flagEndAsync();
if (listener != null) listener.onIabPurchaseFinished(r, null);
return;
}
try {
logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
Bundle buyIntentBundle;
if (oldSkus == null || oldSkus.isEmpty()) {
// Purchasing a new item or subscription re-signup
buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType,
extraData);
} else {
// Subscription upgrade/downgrade
if (!mSubscriptionUpdateSupported) {
IabResult r = new IabResult(IABHELPER_SUBSCRIPTION_UPDATE_NOT_AVAILABLE,
"Subscription updates are not available.");
flagEndAsync();
if (listener != null) listener.onIabPurchaseFinished(r, null);
return;
}
buyIntentBundle = mService.getBuyIntentToReplaceSkus(5, mContext.getPackageName(),
oldSkus, sku, itemType, extraData);
}
int response = getResponseCodeFromBundle(buyIntentBundle);
if (response != BILLING_RESPONSE_RESULT_OK) {
logError("Unable to buy item, Error response: " + getResponseDesc(response));
flagEndAsync();
result = new IabResult(response, "Unable to buy item");
if (listener != null) listener.onIabPurchaseFinished(result, null);
return;
}
PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
mRequestCode = requestCode;
mPurchaseListener = listener;
mPurchasingItemType = itemType;
act.startIntentSenderForResult(pendingIntent.getIntentSender(),
requestCode, new Intent(),
Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
}
catch (SendIntentException e) {
logError("SendIntentException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");
if (listener != null) listener.onIabPurchaseFinished(result, null);
}
catch (RemoteException e) {
logError("RemoteException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");
if (listener != null) listener.onIabPurchaseFinished(result, null);
}
}
There are two ways to implement in app purchases in an android app:
The AIDL which is the one that provides the mService that you use.
The Google Play Billing Library, for which you have to add the following dependency in app build.gradle implementation 'com.android.billingclient:billing:2.0.3'
It seems you are trying to call a Billing Library method from the AIDL service and of course that is not possible.
The AIDL way is now deprecated and should not be used in new projects.
That is, you should modify what you have already done to use the billing library and then you can call the methods that you now find missing.
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 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() { ... });
I am new in developing app using In-app billing,I had implmented it but some
times its giving me the following error
In-app billing error: Unable to buy item, Error response: 7:Item Already Owned
give me solution on it
You need to consume an item before buying it again..
IabHelper mHelper = new IabHelper(ACTIVITY, base64EncodedPublicKey);
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
As an example project given in Android Developer site
Named TrivialDrive we can consume purchased item as follow...
// Check for gas delivery -- if we own gas, we should fill up the tank immediately
Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
Log.d(TAG, "We have gas. Consuming it.");
mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);
return;
}
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
// We know this is the "gas" sku because it's the only one we consume,
// so we don't check which sku was consumed. If you have more than one
// sku, you probably should check...
if (result.isSuccess()) {
// successfully consumed, so we apply the effects of the item in our
// game world's logic, which in our case means filling the gas tank a bit
Log.d(TAG, "Consumption successful. Provisioning.");
mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
saveData();
alert("You filled 1/4 tank. Your tank is now " + String.valueOf(mTank) + "/4 full!");
}
else {
complain("Error while consuming: " + result);
}
updateUi();
setWaitScreen(false);
Log.d(TAG, "End consumption flow.");
}
};
Hope!!! It help You. You can go through this example project.