Android In-app Purchase V3 Error: Authentication is required -
i implementing google in-app purchase v3
, followed steps stated on here in official documentation here. have uploaded app in google playstore alpha testing
, have downloaded playstore url real device giving me error
error
authentication required. need sign google account.
my code in-app purchase here:
public class buypointsfragment extends fragment //in app billing variable start // debug tag, logging static final string tag = "com.myapp"; // user have premium upgrade? boolean mispremium = false; // user have active subscription infinite gas plan? boolean msubscribedtoinfinitegas = false; // skus our products: premium upgrade (non-consumable) , gas // (consumable) static final string sku_premium = "premium"; static final string sku_gas = "gas"; // sku our subscription (infinite gas) static final string sku_infinite_gas = "infinite_gas"; // (arbitrary) request code purchase flow static final int rc_request = 10001; // graphics gas gauge static int[] tank_res_ids = {}; // how many units (1/4 tank our unit) fill in tank. static final int tank_max = 4; // current amount of gas in tank, in units int mtank; // 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 publisher account"; // sanity checks see if developer (that's you!) // followed // instructions run sample (don't put these checks on app!) if (base64encodedpublickey.contains("construct_your")) { throw new runtimeexception( "please put app's public key in mainactivity.java. see readme."); } if (getactivity().getpackagename().startswith("com.myapp.activity")) { throw new runtimeexception( "please change sample's package name! see readme."); } // create helper, passing our context , public key // verify signatures log.d(tag, "creating iab helper."); mhelper = new iabhelper(getactivity(), base64encodedpublickey); // enable debug logging (for production application, should set // false). mhelper.enabledebuglogging(true); // start setup. asynchronous , specified listener // 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 problem. complain(getstring(r.string.problem_setting_inapp_billing) + result); return; } // have been disposed of in meantime? if so, quit. if (mhelper == null) return; // iab set up. now, let's inventory of stuff // 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 gas."); /* * todo: security, generate payload here verification. see * comments on verifydeveloperpayload() more info. since * sample, use empty string, on production app * should generate this. */ string payload = ""; mhelper.launchpurchaseflow(getactivity(), sku_gas, rc_request, mpurchasefinishedlistener, payload); } // updates ui reflect model public void updateui() { // update car color reflect premium status or lack thereof // ((imageview)findviewbyid(r.id.free_or_premium)).setimageresource(mispremium // ? r.drawable.premium : r.drawable.free); // "upgrade" button visible if user not premium // findviewbyid(r.id.upgrade_button).setvisibility(mispremium ? // view.gone : view.visible); // "get infinite gas" button visible if user not // subscribed yet // (r.id.infinite_gas_button).setvisibility(msubscribedtoinfinitegas ? // view.gone : view.visible); // update gas gauge 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 "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 real application, recommend save data in * secure way prevent tampering. simplicity in sample, * store data using 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 dispose of helper here! @override public void ondestroy() { super.ondestroy(); // important: log.d(tag, "destroying helper."); if (mhelper != null) { mhelper.dispose(); mhelper = null; } } // listener that's called when finish querying items , // subscriptions own iabhelper.queryinventoryfinishedlistener mgotinventorylistener = new iabhelper.queryinventoryfinishedlistener() { public void onqueryinventoryfinished(iabresult result, inventory inventory) { log.d(tag, "query inventory finished."); // have been disposed of in meantime? if so, quit. if (mhelper == null) return; // failure? if (result.isfailure()) { complain(getstring(r.string.failed_to_query_inventory) + result); return; } log.d(tag, "query inventory successful."); /* * check items own. notice each purchase, check * developer payload see if it's correct! see * verifydeveloperpayload(). */ // have premium upgrade? purchase premiumpurchase = inventory.getpurchase(sku_premium); mispremium = (premiumpurchase != null && verifydeveloperpayload(premiumpurchase)); log.d(tag, "user " + (mispremium ? "premium" : "not premium")); // have 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 gas delivery -- if own gas, should fill // tank 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 developer payload of purchase. */ boolean verifydeveloperpayload(purchase p) { string payload = p.getdeveloperpayload(); /* * todo: verify developer payload of purchase correct. * same 1 sent when initiating purchase. * * warning: locally generating random string when starting purchase * , verifying here might seem approach, * fail in case user purchases item on 1 device , * uses app on different device, because on other device * not have access random string * generated. * * developer payload has these characteristics: * * 1. if 2 different users purchase item, payload different * between them, 1 user's purchase can't replayed * user. * * 2. payload must such can verify when app * wasn't 1 initiated purchase flow (so items * purchased user on 1 device work on other devices owned * user). * * using own server store , verify developer payloads across * app installations recommended. */ return true; } // callback when purchase finished iabhelper.oniabpurchasefinishedlistener mpurchasefinishedlistener = new iabhelper.oniabpurchasefinishedlistener() { public void oniabpurchasefinished(iabresult result, purchase purchase) { log.d(tag, "purchase finished: " + result + ", purchase: " + purchase); // if disposed of in 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. consume it. log.d(tag, "purchase gas. starting gas consumption."); mhelper.consumeasync(purchase, mconsumefinishedlistener); } else if (purchase.getsku().equals(sku_premium)) { // bought premium upgrade! log.d(tag, "purchase 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 infinite gas subscription log.d(tag, "infinite gas subscription purchased."); alert("thank subscribing infinite gas!"); msubscribedtoinfinitegas = true; mtank = tank_max; updateui(); setwaitscreen(false); } } }; // called when consumption complete iabhelper.onconsumefinishedlistener mconsumefinishedlistener = new iabhelper.onconsumefinishedlistener() { public void onconsumefinished(purchase purchase, iabresult result) { log.d(tag, "consumption finished. purchase: " + purchase + ", result: " + result); // if disposed of in meantime, quit. if (mhelper == null) return; // know "gas" sku because it's 1 // consume, // don't check sku consumed. if have more // 1 // sku, should check... if (result.issuccess()) { // consumed, apply effects of item in // our // game world's logic, in our case means filling gas // tank bit log.d(tag, "consumption successful. provisioning."); mtank = mtank == tank_max ? tank_max : mtank + 1; savedata(); alert("you filled 1/4 tank. tank " + 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 activity result helper handling if (!mhelper.handleactivityresult(requestcode, resultcode, data)) { // not handled, handle ourselves (here's you'd // perform handling of activity results not related in-app // billing... super.onactivityresult(requestcode, resultcode, data); } else { log.d(tag, "onactivityresult handled iabutil."); } } //in app billing method end here
my products managed products
in developer account in-app products
edit:
when use android.test.purchased
sku
works fine , change sku
product_id
giving me error authentication required. need sign google account.
please make sure product id on playstore in app purchase "test_product" should use same sku in code. , if change sku application, possible sku names must exists on playstore in-app products. once run problem , reason sku item not exists on google playstore in-app products add on playstore , resolved.