I am setting up the Inn-App billing from Google play in my LibGDX based Android app.
Scenario : I have bought the test product "android.test.purchased" successfully and I have not Consumed it. I have not consumed it because it can be bought only once and I have disabled the buy button for that test product. Till this everything is fine.
Issue : Now when I re open the again for 2nd time. I got an alert saying that "Failed to query inventory: IabResult: Error refreshing inventory (querying owned items). (response: -1003:Purchase signature verification failed".
Question :
I expect my product can be bought only once and hence I have not consumed it. Will that be a problem while querying for the inventory in onCreate().
mHelper.queryInventoryAsync(mGotInventoryListener);
Code
private void iabSetup() {
// load game data
loadData();
String base64EncodedPublicKey = "Key";
Log.d(TAG, "Creating IAB helper.");
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()) {
complain("Problem setting up in-app billing: " + result);
return;
}
if (mHelper == null) return;
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
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("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the additional 1 life?
Purchase life1Purchase = inventory.getPurchase(SKU_1_Life);
life1Purchased = (life1Purchase != null);
Log.d(TAG, "User life1Purchased : " + life1Purchased);
};
public void onAdd1lifeButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for infinite gas subscription.");
if (busy) {
Log.d(TAG, "Can't buy item. So busy yet");
return;
}
try {
new Thread(new Runnable() {
#Override
public void run() {
busy = true;
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_1_Life, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
// 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("Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_1_Life)) {
Log.d(TAG, "Purchase is SKU_1_Life.");
busy = false;
}
}
};
Related
I am new to the coding and i mostly copy and paste from the google in app billing
Can anyone guide me in what is wrong in the code. Refer this
Android check if in app purchase was bought before
Here is my purchaseActivity
public class PurchaseActivity extends CustomMenu {
tk.myessentialoils.ideasapp.util.IabHelper mHelper;
Button buyButton;
String product1SKU;
Boolean VIP=false;
//String product1SKU = "vip_member";//getResources().getString(R.string.product1SKU);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
buyButton = findViewById(R.id.buyButton);
product1SKU = getString(R.string.product1SKU);
mHelper = new tk.myessentialoils.ideasapp.util.IabHelper(this, getString(R.string.billing64basecode));
mHelper.startSetup(new tk.myessentialoils.ideasapp.util.IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(tk.myessentialoils.ideasapp.util.IabResult result) {
if (!result.isSuccess()) {
// Oh no, there was a problem.
Log.d("TAG", "Problem setting up In-app Billing: " + result);
}
// Hooray, IAB is fully set up!
}
});
/*
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d("TAG","Error querying inventory. Another async operation in progress.");
}
SharedPreferences.Editor editor = this.getSharedPreferences("Name", MODE_PRIVATE).edit();
Log.d("TAG", "checkVIPStatus: " +VIP);
editor.putBoolean("VIP",VIP);
editor.apply();
*/
}
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) try {
mHelper.dispose();
} catch (tk.myessentialoils.ideasapp.util.IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
}
mHelper = null;
}
public void buyClick(View view) {
try {
mHelper.launchPurchaseFlow(this, product1SKU, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
} catch (tk.myessentialoils.ideasapp.util.IabHelper.IabAsyncInProgressException e) {
e.printStackTrace();
Toast.makeText(this, "Please set up your google account", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
tk.myessentialoils.ideasapp.util.IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new tk.myessentialoils.ideasapp.util.IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(tk.myessentialoils.ideasapp.util.IabResult result,
tk.myessentialoils.ideasapp.util.Purchase purchase)
{
if (mHelper == null) return;
if (result.isFailure()) {
// Handle error
Log.d("TAG", "onQueryInventoryFinished: Failed");
}
else if (purchase.getSku().equals(product1SKU)) {
buyButton.setEnabled(false);
VIP=true;
storeVIP();
}
}
};
tk.myessentialoils.ideasapp.util.IabHelper.QueryInventoryFinishedListener mGotInventoryListener
= new tk.myessentialoils.ideasapp.util.IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(tk.myessentialoils.ideasapp.util.IabResult result,
tk.myessentialoils.ideasapp.util.Inventory inventory) {
if (result.isFailure()) {
// handle error here
Log.d("TAG", "onQueryInventoryFinished: Failed");
}
else {
// does the user have the premium upgrade?
boolean mIsPremium = inventory.hasPurchase(product1SKU);
buyButton.setEnabled(false);// update UI accordingly
VIP=true;
storeVIP();
}
}
};
private void storeVIP(){
SharedPreferences.Editor editor = getSharedPreferences("Name", MODE_PRIVATE).edit();
Log.d("TAG", "checkVIPStatus: " +VIP);
editor.putBoolean("VIP",VIP);
editor.apply();
}
}
Question 1
My purchase activity crashed when I enable the commented code.
mHelper.queryInventoryAsync(mGotInventoryListener);
With the error IAB is not set up.
Can anyone show me what is wrong with this?
Question 2
Should I use queryInventoryAsync or use the method as described in here?
Android: in-app: check if an item has been purchased
Which method is easier or more efficient?
Question 1 answer
Place inside the IAB
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
buyButton = findViewById(R.id.buyButton);
product1SKU = getString(R.string.product1SKU);
mHelper = new tk.myessentialoils.ideasapp.util.IabHelper(this, getString(R.string.billing64basecode));
mHelper.startSetup(new tk.myessentialoils.ideasapp.util.IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(tk.myessentialoils.ideasapp.util.IabResult result) {
if (result.isSuccess()) {
Log.d("TAG", "onIabSetupFinished: ");
// Hooray, IAB is fully set up!
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d("TAG","Error querying inventory. Another async operation in progress.");
}
}
else {
// Oh no, there was a problem.
Log.d("TAG", "Problem setting up In-app Billing: " + result);
}
}
});
}
Question 2 answer
It depend of type of item we are selling.
For one time transactions, and the item cannot be bought further, i used queryInventoryAsync.
I could manage in my code to perform an in-app-billing. However, if I install the app, and redo the purchase, it does purchase again.
Where is the problem?
Here is the code:
case R.id.action_disable_ads:
try {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
return true;
}
return super.onOptionsItemSelected(item);
}
//functions special for in App Billing
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener
= new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result,
Purchase purchase)
{
if (result.isFailure()) {
// Handle error
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
consumeItem();
//buyButton.setEnabled(false);
}
}
};
public void consumeItem() {
try {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener
= new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
try {
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU),
mConsumeFinishedListener);
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
//first save purchase state
SharedPreferences settings = getPreferences(0);
SharedPreferences.Editor settingsEditor = settings.edit();
settingsEditor.putString("adfree", "purchased");
settingsEditor.apply();
//then, hide the disable ads button
MenuItem disableAds = mainMenu.findItem(R.id.action_disable_ads);
disableAds.setVisible(false);
//then display a message that the transaction is successful
Toast.makeText(getApplicationContext(), "AdFree bundle purchased successfully!", Toast.LENGTH_SHORT).show();
//then change the state of ad-free bundle for current session:
adFreeBundle = "purchased";
//then disable ads
interstitialAd = null;
mAdView.setVisibility(View.GONE);
} else {
//display an error message that the transaction was not successful
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
builder.setTitle("TRANSACTION FAILED!");
builder.setMessage("Unfortunately, the transaction was unsuccessful!");
builder.setPositiveButton("CLOSE", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Do nothing but close the dialog
dialog.dismiss();
}
});
final AlertDialog alert = builder.create();
alert.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface arg0) {
alert.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.parseColor("#000000"));
}
});
alert.show();
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) try {
mHelper.dispose();
} catch (IabHelper.IabAsyncInProgressException e) {
//e.printStackTrace();
}
mHelper = null;
}
The process of in-app-billing functioned just fine. However, I would be happy to try to get the purchased item upon reinstalling the app.
This is because you are consuming the purchase, and setting a flag in sharedPreferences.
Don't do this anyone can change the sharedPreferences using the right tools.
When you uninstall your app your sharedPreferences will reset and you will lose your 'purchase'.
Because you consume the item, in the 2nd try your inventory won't have the right SKU available, this is why you are prompt to buy again.
If you make the buy button always visible you will be asked to pay every click.
To fix this
Don't consume the item, and the most important thing don't set any flag in sharedPreferences. this also apply's to sql databases.
Consuming is not meant to be used in a non-consumable item, this is your case since you are charging the user to remove the ads, if you try to consume this your users won't be very happy
When your app starts check your inventory if your inventory has the right SKU then give the user the product.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
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.");
try {
mHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
handleInventory(result, inv);
}
});
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error querying inventory. Another async operation in progress.");
e.printStackTrace();
}
}
});
public void handleInventory(IabResult result, Inventory inventory) {
if (mHelper == null) return;
// Is it a failure?
if (result.isFailure()) {
return;
}
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inventory.getPurchase(YOUR SKU);
//verify Developer Payload here, it's recommended but not mandatory.
//mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
mIsPremium = premiumPurchase != null;
if (mIsPremium) {
//Enable feature
}else{
//Disable feature
}
}
I'm having some serious problems trying to implement in-app purchases on my otherwise completed app. I have a class, BaseActivity, that all my other classes extend. This is where my toolbars, nav drawers, and IabHelper are set up.
BaseActivity.java:
public abstract class BaseActivity extends AppCompatActivity implements IabBroadcastReceiver.IabBroadcastListener{
public boolean m30paid = false;
public boolean m31paid = false;
public boolean m32paid = false;
public boolean m37paid = false;
static final String ITEM_SKU = "android.test.purchased";
IabHelper mHelper;
IabBroadcastReceiver mBroadcastReceiver;
private final String LOG = BaseActivity.class.getSimpleName();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String base64EncodedPublicKey = "********KEY HERE*********";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
Log.d(LOG, "Problem setting up in app billing: " + result);
}
else Log.d(LOG, "set up correctly!");
if (mHelper == null) return;
}
});
mBroadcastReceiver = new IabBroadcastReceiver(this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
//IAB fully set up, query inventory
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
#Override
public void onDestroy() {
super.onDestroy();
// very important:
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
// very important:
Log.d(LOG, "Destroying helper.");
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
#Override
public void receivedBroadcast() {
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(LOG, "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("Failed to query inventory: " + result);
return;
}
Log.d(LOG, "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().
*/
}};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(LOG, "Purchase finished: " + result + ", purchase: " + purchase);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
return;
} else if (purchase.getSku().equalsIgnoreCase("workout_30")) {
m30paid = true;
}
Log.d(LOG, "Purchase successful.");
}
};
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(LOG, "Showing alert dialog: " + message);
bld.create().show();
}
public void complain(String message) {
Log.e(LOG, "**** TrivialDrive Error: "+ message);
alert("Error: " + message);
}
From there, I try to set launchPurchaseFlow on button click in another class.
Purchases.java:
public class purchases extends BaseActivity {
IabHelper mHelper;
static final String ITEM_SKU = "android.test.purchased";
IabBroadcastReceiver mBroadcastReceiver;
Context mContext;
Activity mActivity = this;
static final int RC_REQUEST = 10001;
private Button btn30;
static final String SKU_W30 = "workout_30";
String payload = "";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.purchases);
mToolBar = activateToolbar();
setUpNavigationDrawer();
getSupportActionBar().setTitle("Purchases");
btn30 = (Button) findViewById(R.id.btn30);
Button btn31 = (Button) findViewById(R.id.btn31);
Button btn32 = (Button) findViewById(R.id.btn32);
Button btn37 = (Button) findViewById(R.id.btn37);
btn30.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
mHelper.launchPurchaseFlow(purchases.this, SKU_W30, RC_REQUEST, mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
complain("Error");
}
}
});
}
}
The error I'm getting when I click the button is:
FATAL EXCEPTION: main
Process: com.bestworkouts.sheikoworkout, PID: 4118
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.bestworkouts.sheikoworkout.util.IabHelper.launchPurchaseFlow(android.app.Activity, java.lang.String, int, com.bestworkouts.sheikoworkout.util.IabHelper$OnIabPurchaseFinishedListener, java.lang.String)' on a null object reference
at com.bestworkouts.sheikoworkout.purchases$1.onClick(purchases.java:41)
at android.view.View.performClick(View.java:5697)
at android.widget.TextView.performClick(TextView.java:10814)
at android.view.View$PerformClick.run(View.java:22526)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7229)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Basically what I want it to do is in the onIabPurchaseFinished, change a boolean value to true. I have an if statement on my expLV so if the boolean is false it says you need to purchase this, if it's true, it gives access. This part is working good so far, but I can't seem to get past this error to see if it properly changes the boolean value and unlocks the data.
I'm not sure what I'm missing, and I've been messing it and dealing with multiple errors for 2 days now.. any help greatly appreciated!
Subclass variable mHelper isn't initialised. Try removing IabHelper mHelper; from purchase class which extends BaseActivity where mHelper is initialised.
Declaring mHelper in subclass will hide superclass mHelper variable.
In my Android-libGDX game, I have the GameHelper and the IabHelper. I need to have the onActivityResult method setup for both the classes. Below mentioned are the scenarios in which I have issues. Could you please assist me to resolve this and have the onActivityResult menthod setup for both GameHelper and IabHelper.
Scenario 1
When I setup the onActivityResult exclusively for GameHelper like the below one. The GameHelper related sign-in / sign-out works perfect but no the IabHelper. The IabHelper related process like purchase fails on 2nd product stating that there is already a asynch process is going on.
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
Log.i("AndroidLauncher", "onActivityResult - response :" + response);
gameHelper.onActivityResult(request, response, data);
}
Scenario 2When I setup the onActivityResult exclusively for IabHelper. The above mentioned issue in Scenario 1 is not happening for IabHelper. But the sign-in / sign-out related functions are not working in GameHelper.
#Override
public void onActivityResult(int request, int response, Intent data) {
if (!mHelper.handleActivityResult(request, response, 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(request, response, data);
}
}
Additional Code Details
private void iabSetup() {
// load game data
loadData();
String base64EncodedPublicKey ="KeyKeyKey";
Log.d(TAG, "Creating IAB helper.");
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()) {
complain("Problem setting up in-app billing: " + result);
return;
}
if (mHelper == null) return;
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
// 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("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the additional 1 life?
Purchase life1Purchase = inventory.getPurchase(SKU_1_Life);
life1Purchased = (life1Purchase != null);
Log.d(TAG, "User life1Purchased : " + life1Purchased);
// Do we have the additional 2 life?
Purchase life2Purchase = inventory.getPurchase(SKU_2_Life);
life2Purchased = (life2Purchase != null);
Log.d(TAG, "User life2Purchased : " + life2Purchased);
// Do we have the basket Speed Purchased ?
Purchase basketSpeedPurchase = inventory.getPurchase(SKU_Increase_Bucket_Speed);
basketSpeedPurchased = (basketSpeedPurchase != null);
Log.d(TAG, "User basketSpeedPurchased : " + basketSpeedPurchased);
// Do we have the basket Speed Purchased ?
Purchase basketSizePurchase = inventory.getPurchase(SKU_Increase_Bucket_Size);
basketSizePurchased = (basketSizePurchase != null);
Log.d(TAG, "User basketSizePurchased : " + basketSizePurchased);
// Do we have the basket Speed Purchased ?
Purchase adFreeGamePurchase = inventory.getPurchase(SKU_Ads_Free_Game);
adFreeGamePurchased = (adFreeGamePurchase != null);
Log.d(TAG, "User adFreeGamePurchased : " + adFreeGamePurchased);
}
};
public void onAdd1lifeButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Add 1 life.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_1_Life, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onAdd2livesButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Add 2 lives.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_2_Life, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onIncreaseBucketSpeedButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for increasing bucket speed.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Increase_Bucket_Speed, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onIncreaseBucketSizeButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for increasing bucket speed.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Increase_Bucket_Size, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
public void onAdsFreeGameButtonClicked() {
if (!mHelper.subscriptionsSupported()) {
complain("Subscriptions not supported on your device yet. Sorry!");
return;
}
Log.d(TAG, "Launching purchase flow for Ads Free Game.");
try {
new Thread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(AndroidLauncher.this, SKU_Ads_Free_Game, RC_REQUEST, mPurchaseFinishedListener, payloadString);
}
}).start();
} catch (Exception e) {
Log.d(TAG, "buyItem Exception: " + e);
}
}
// 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("Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_1_Life)) {
Log.d(TAG, "Purchase is SKU_1_Life.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_2_Life.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Increase_Bucket_Speed.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Increase_Bucket_Size.");
}
if (purchase.getSku().equals(SKU_2_Life)) {
Log.d(TAG, "Purchase is SKU_Ads_Free_Game.");
}
}
};
I'm going to implement the Android In-App Billing V3 using IabHelper (trivialdrivesample).
I made the game with cocos2d-x.
In the google developer console there are already ten in-app products.
They almost work well, But some of the items don't work. There is no shown the In-App bundle activity.
I don't know why some work well, some don't work.
It is very strange.
Here is some my code block.
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()) {
JSONObject prmsToSend = null;
String jsonStr = "{\"Message\":\"Failed 1\"}";
try {
prmsToSend = new JSONObject(jsonStr);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
AndroidNDKHelper.SendMessageWithParameters("failTrans", prmsToSend);
return;
}
Log.d(TAG, "Purchase successful.");
// bought 1/4 tank of gas. So consume it.
Log.d(TAG, "Purchase is gas. Starting gas consumption.");
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
};
// 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.");
}
else {
}
AndroidNDKHelper.SendMessageWithParameters("completeTrans", null);
Log.d(TAG, "End consumption flow.");
}
};
// 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()) {
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().
*/
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
myAndroidContext = EasyAsPi.getContext();
//
AndroidNDKHelper.SetNDKReciever(this);
// Try to get your hash key
try {
PackageInfo info = getPackageManager().getPackageInfo("co.easyaspi.piguys", PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.e("MY KEY HASH:", Base64.encodeToString(md.digest(), Base64.DEFAULT));
System.out.println("MY KEY HASH:"+ Base64.encodeToString(md.digest(), Base64.DEFAULT));
}
}
catch (NameNotFoundException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
// Initialize EziSocial Plugin Manager for Android.
// ToDo Make sure you add the Facebook App ID in strings.xml file.
EziSocialManager.initWithActivity(this,
this.getApplicationContext().getString(R.string.app_id),
false, // Set to see ezisocial plugin logs
savedInstanceState
);
String base64EncodedPublicKey =
"My 64bit key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Oh noes, there was a problem.
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);
}
});
}
#Override
protected void onResume()
{
super.onResume();
EziSocialManager.applicationResumed();
}
#Override
public void onPause()
{
super.onPause();
EziSocialManager.applicationPaused();
}
#Override
public void onDestroy()
{
super.onDestroy();
EziSocialManager.applicationDestroyed();
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
public void requestProductData(final JSONObject prms)
{
Log.v("requestProductData", "Passed params are : " + prms.toString());
String param = null;
try {
param = prms.getString("item");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String item = null;
if(param.equals("1"))
{
item = strItem1;
}
else if(param.equals("2"))
{
item = strItem2;
}
else if(param.equals("3"))
{
item = strItem3;
}
else if(param.equals("4"))
{
item = strItem4;
}
else if(param.equals("5"))
{
item = strItem5;
}
else if(param.equals("6"))
{
item = strItem6;
}
else if(param.equals("7"))
{
item = strItem7;
}
else if(param.equals("8"))
{
item = strItem8;
}
else if(param.equals("9"))
{
item = strItem9;
}
else if(param.equals("10"))
{
item = strItem10;
}
else if(param.equals("11"))
{
item = strItem11;
}
else if(param.equals("12"))
{
item = strItem12;
}
Log.d(TAG, "Buy gas button clicked.");
Log.d(TAG, "Launching purchase flow for gas.");
strItem = item;
mHelper.launchPurchaseFlow(this, item, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
EziSocialManager.onActivityResult(requestCode, resultCode, 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.");
}
}
As I have suffered from in-app billing version 3. It looks like complicated solution provided by google if you want to create test app for version 3.
I have found best library on github.
https://github.com/anjlab/android-inapp-billing-v3
You can achieve simplicity using it.