I have implemented In-App Billing in my Activity following the google GitHub example
this is my code:
public class Premium extends Activity implements IabBroadcastReceiver.IabBroadcastListener {
Button premium;
//toast
Context context = getBaseContext();
int duration = Toast.LENGTH_SHORT;
//debug tag
static final String TAG = "CHORDS";
//does the user have premium?
boolean mIsPremium = false;
//SKUs
static final String SKU_PREMIUM = "chords_premium";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001;
// The helper object
IabHelper mHelper;
// Provides purchase notification while this app is running
IabBroadcastReceiver mBroadcastReceiver;
public void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.premium_layout);
premium = (Button) findViewById(R.id.premium);
String base64EncodedPublicKey = "Key"
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(this, base64EncodedPublicKey);
// 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.
Log.d(TAG, "Problem setting up in-app billing: " + result);
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mHelper == null) return;
mBroadcastReceiver = new IabBroadcastReceiver(Premium.this);
IntentFilter broadcastFilter = new IntentFilter(IabBroadcastReceiver.ACTION);
registerReceiver(mBroadcastReceiver, broadcastFilter);
Log.d(TAG, "Setup successful. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d(TAG, "Error querying inventory. Another async operation in progress.");
}
}
});
}
// 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()) {
Log.d(TAG, "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"));
premium.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mIsPremium = true)
premium.setEnabled(false);
else {
}
}
});
};
};
#Override
public void receivedBroadcast() {
// Received a broadcast notification that the inventory of items has changed
Log.d(TAG, "Received broadcast notification. Querying inventory.");
try {
mHelper.queryInventoryAsync(mGotInventoryListener);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d(TAG, "Error querying inventory. Another async operation in progress.");
}
}
// User clicked the "Upgrade to Premium" button.
public void onUpgradeAppButtonClicked(View arg0) {
Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
/* 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 = "";
try {
mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST,
mPurchaseFinishedListener, payload);
} catch (IabHelper.IabAsyncInProgressException e) {
Log.d(TAG,"Error launching purchase flow. Another async operation in progress.");
}
}
#Override
protected 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)) {
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
/** Verifies the developer payload of a purchase. */
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
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()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
if (!verifyDeveloperPayload(purchase)) {
Log.d(TAG, "Error purchasing. Authenticity verification failed.");
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
Toast toast = Toast.makeText(context, R.string.premium_bought, duration);
toast.show();
mIsPremium = true;
updateUI();
}
}
};
// We're being destroyed. It's important to dispose of the helper here!
#Override
public void onDestroy() {
super.onDestroy();
// very important:
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
public void updateUI() {
//TODO: elimina pubblicita
}
}
As I wrote above in te title, I need to start the google purchase
when my premium button is pressed. The android documentation is not very clear on how to do this stuff in particular.
What should I put in my OnClick() method to start the purchase flow?
You should call
mHelper.startSetup
on the button click to start the purchase flow.
Related
I just completed setting up inapp billing using google play billing with aidl.
On successful purchase the premium feature is activated through a boolean. But after the app is closed and relaunched, premium feature disappears. i.e the boolean reverts to false. I would like to know how to ensure the boolean stays as true after app launch as long as premium has been purchased.
On MainActivity
public class MainActivity extends AppCompatActivity {
public static boolean proFeature = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
On InAppBilling Activity
public class InAppBilling extends Activity implements IabBroadcastReceiver.IabBroadcastListener {
private static final String TAG = ".InAppBilling";
IabHelper mHelper;
boolean premiumFeature = false;
static final String SKU_PREMIUM = "android.test.purchased";
static final int RC_REQUEST = 10001;
IabBroadcastReceiver mBroadcastReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_lay);
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
Log.d(TAG, "Query inventory finished.");
if (mHelper == null) return;
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
Purchase premiumPurchase = inv.getPurchase(SKU_PREMIUM);
premiumFeature = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
Log.d(TAG, "User is " + (premiumFeature ? "PREMIUM" : "NOT PREMIUM"));
updateUi();
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
#Override
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert("Thank you for upgrading to premium!");
premiumFeature = true;
updateUi();
setWaitScreen(false);
}
}
};
public void updateUi(){
button.setVisibility(premiumFeature ? View.GONE : View.VISIBLE);
if (premiumFeature){
MainActivity.proFeature = true;
}else{
MainActivity.proFeature = false;
}
}
In your purchase finish listener, modify below code to store value into shared preferance.
if (purchase.getSku().equals(SKU_PREMIUM)) {
// bought the premium upgrade!
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
alert("Thank you for upgrading to premium!");
premiumFeature = true;
SharedPreferences sharedPref = context.getSharedPreferences(
"my_sp", Context.MODE_PRIVATE);
sharedPref.edit().putBoolean("isPremium, premiumFeature).commit();
updateUi();
setWaitScreen(false);
}
And on app relaunch get this value from shared preference again.
SharedPreferences sharedPref = context.getSharedPreferences(
"my_sp", Context.MODE_PRIVATE);
premiumFeature = sharedPref.getBoolean("isPremium, false);
Update(19 Feb, 2022):
As #Shazniq said, It's always good to store those details on the server for safety, along with user profile data. So on every launch of the app, you can verify the details. You have to apply your own logic to verify it whenever you need it.
You can save it to sharedpreferences or on your server and query the status in beginning(splash screen).
or you can do a inventory query in splash screen to check the status in the splash screen.
trying to use in app billing in Android at the moment.
Everything works fine and I can purchase items and query existing ones but my OnIabPurchaseFinishedListener listener is not being called during a successful purchase. It is called when there is an issue but not on a completed purchase.
I have my main activity, with fragments and a navigation drawer. (fragments are not touched here. I have a helper class that all the billing happens in.
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, MyBilling.Update {\
MyBilling bill;
OnCreate(){
bill = new MyBilling(this, this);
bill.onCreate();
}
private void RemoveAdsClick()
{
bill.purchaseRemoveAds();
}
public void NewPurchaseUpdate(){
tinydb.putBoolean("Premium", true);
nav_Menu.findItem(R.id.remove_ad_button).setVisible(false);
displaySelectedScreen(R.id.distance_check); // Reload screen
}
}
public class MyBilling extends Activity {
////Interface
public interface Update {
void NewPurchaseUpdate();
}
// Debug tag, for logging
static final String TAG = "XXXXXX";
static final String SKU_REMOVE_ADS = "remove_ads";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10111;
// Activity
Activity activity;
// The helper object
IabHelper mHelper;
String base64EncodedPublicKey = "xxxxxxxx";
String payload = "xxxxx";
public boolean isAdsDisabled = false;
public boolean AdCheck = false;
////Instance of interface
Update myActivity;
public MyBilling(Activity launcher,Update activity) {
this.activity = launcher;
myActivity = activity;
}
// User clicked the "Remove Ads" button.
public void purchaseRemoveAds() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
mHelper.launchPurchaseFlow(activity, SKU_REMOVE_ADS,
RC_REQUEST, mPurchaseFinishedListener, payload);
}
});
}
#Override
protected 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.");
}
}
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_REMOVE_ADS)) {
// bought the premium upgrade!
myActivity.NewPurchaseUpdate();
Log.d(TAG, "New Purchase Update Method was called");
}
}
};
}
some googling showed that it could be an issue with onActivityResult not being in the helper class. So I made billing class extend activity and then added onActivityResult and #Override but this also is not called with any breakpoints.
As mentioned above, OnIabPurchaseFinishedListener is called for a failed purchase (already own item) but not for a successful one.
Any help here would be great, I have tried to keep the code as light as possible to help with reading. If I'm missing anything please let me know.
I have fixed this issue by ditching my helper class for now and moving the billing tasks into the main activity.
I am trying to make a login into my android app. I have generated the google-services.json with the same package name of the class and copied into the app folder of my application. On attempting to login it shows a dim screeen and thereafter shows a white screen of the the page I am trying to login with.
This is the code snippet from my activity class
// Button click listeners
btnSignIn.setOnClickListener(this);
btnSignOut.setOnClickListener(this);
btnRevokeAccess.setOnClickListener(this);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN).build();
Log.e(TAG, "onCreate End!");
}
protected void onStart() {
Log.e(TAG, "onStart!");
super.onStart();
mGoogleApiClient.connect();
Log.e(TAG, "onStart end!");
}
protected void onStop() {
Log.e(TAG, "onStop!");
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
super.onStop();
}
/**
* Method to resolve any signin errors
* */
private void resolveSignInError() {
PendingIntent signInIntent = mConnectionResult.getResolution();
if (signInIntent != null) {
if (mConnectionResult.hasResolution()) {
try {
mIntentInProgress = true;
mConnectionResult.startResolutionForResult(this, RC_SIGN_IN);
} catch (SendIntentException e) {
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
} else {
showDialog(DIALOG_PLAY_SERVICES_ERROR);
}
}
#Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case DIALOG_PLAY_SERVICES_ERROR:
if (GooglePlayServicesUtil.isUserRecoverableError(mConnectionResult.getErrorCode())) {
return GooglePlayServicesUtil.getErrorDialog(
mConnectionResult.getErrorCode(),
this,
RC_SIGN_IN,
new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
Log.e(TAG, "Google Play services resolution cancelled");
}
});
} else {
return new AlertDialog.Builder(this)
.setMessage(R.string.play_services_error)
.setPositiveButton(R.string.close,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.e(TAG, "Google Play services error could not be "
+ "resolved: " + mConnectionResult.getErrorCode());
}
}).create();
}
default:
return super.onCreateDialog(id);
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "onConnectionFailed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
Log.i(TAG, "See error types in http://developer.android.com/reference/com/google/android/gms/common/ConnectionResult.html");
if (!result.hasResolution()) {
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this,
0).show();
return;
}
if (result.getErrorCode() == ConnectionResult.SIGN_IN_REQUIRED) {
Log.i(TAG, "Error type: SIGN_IN_REQUIRED");
}
if (result.getErrorCode() == ConnectionResult.NETWORK_ERROR ) {
Log.i(TAG, "Error type: NETWORK_ERROR ");
}
if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) {
// An API requested for GoogleApiClient is not available. The device's current
// configuration might not be supported with the requested API or a required component
// may not be installed, such as the Android Wear application. You may need to use a
// second GoogleApiClient to manage the application's optional APIs.
} else if (!mIntentInProgress) {
Log.i(TAG, "not IntentInProgress!");
// Store the ConnectionResult for later usage
mConnectionResult = result;
if (mSignInClicked) {
Log.i(TAG, "signInAlreadyClicked! Resolving sign error...");
// The user has already clicked 'sign-in' so we attempt to
// resolve all
// errors until the user is signed in, or they cancel.
resolveSignInError();
}
}
Log.i(TAG, "onConnectionFailed end!");
//TODO: Sign OUT?
}
#Override
protected void onActivityResult(int requestCode, int responseCode,
Intent intent) {
if (requestCode == RC_SIGN_IN) {
if (responseCode != RESULT_OK) {
mSignInClicked = false;
}
mIntentInProgress = false;
if (!mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
}
}
#Override
public void onConnected(Bundle arg0) {
mSignInClicked = false;
Toast.makeText(this, "User is connected!", Toast.LENGTH_LONG).show();
// Get user's information
getProfileInformation();
// Update the UI after signin
updateUI(true);
}
/**
* Updating the UI, showing/hiding buttons and profile layout
* */
private void updateUI(boolean isSignedIn) {
if (isSignedIn) {
btnSignIn.setVisibility(View.GONE);
btnSignOut.setVisibility(View.VISIBLE);
btnRevokeAccess.setVisibility(View.VISIBLE);
llProfileLayout.setVisibility(View.VISIBLE);
} else {
btnSignIn.setVisibility(View.VISIBLE);
btnSignOut.setVisibility(View.GONE);
btnRevokeAccess.setVisibility(View.GONE);
llProfileLayout.setVisibility(View.GONE);
}
}
/**
* Fetching user's information name, email, profile pic
* */
private void getProfileInformation() {
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
Person currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
Log.i(TAG, "Name: " + personName + ", plusProfile: "
+ personGooglePlusProfile + ", email: " + email
+ ", Image: " + personPhotoUrl);
txtName.setText(personName);
txtEmail.setText(email);
// by default the profile url gives 50x50 px image only
// we can replace the value with whatever dimension we want by
// replacing sz=X
personPhotoUrl = personPhotoUrl.substring(0,
personPhotoUrl.length() - 2)
+ PROFILE_PIC_SIZE;
new LoadProfileImage(imgProfilePic).execute(personPhotoUrl);
} else {
Toast.makeText(getApplicationContext(),
"Person information is null", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onConnectionSuspended(int arg0) {
mGoogleApiClient.connect();
updateUI(false);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
/**
* Button on click listener
* */
#Override
public void onClick(View v) {
if (!mGoogleApiClient.isConnecting()) {
switch (v.getId()) {
case R.id.btn_sign_in:
// Signin button clicked
signInWithGplus();
break;
case R.id.btn_sign_out:
// Signout button clicked
signOutFromGplus();
break;
case R.id.btn_revoke_access:
// Revoke access button clicked
revokeGplusAccess();
break;
}
}
}
/**
* Sign-in into google
* */
private void signInWithGplus() {
if (!mGoogleApiClient.isConnecting()) {
// We only process button clicks when GoogleApiClient is not transitioning
// between connected and not connected.
mSignInClicked = true;
resolveSignInError();
}
}
/**
* Sign-out from google
* */
private void signOutFromGplus() {
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
updateUI(false);
}
}
/**
* Revoking access from google
* */
private void revokeGplusAccess() {
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status arg0) {
Log.e(TAG, "User access revoked!");
mGoogleApiClient.disconnect();
mGoogleApiClient.connect();
updateUI(false);
}
});
}
}
/**
* Background Async task to load user profile picture from url
* */
private class LoadProfileImage extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public LoadProfileImage(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
this is the login button from my page xml file calling the login function to authenticate with google plus
<com.google.android.gms.common.SignInButton
android:id="#+id/btn_sign_in"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"/>
Please why is my app not authenticating with google plus sign. Please assist
I downloaded the sample app
Got my key from the developer console
Put the proper permissions in my manifest
Nothing helps: I got always product not found...
Thanx - Ralf
here is the code, Java, Android Studio
// Does the user have the professional upgrade? should be an In-App product
private static Boolean _PROFESSIONAL = false;
// Debug tag, for logging
private static final String TAG_BILLING = "BillingService";
// (arbitrary) request code for the purchase flow
static final int RC_REQUEST = 10001; // 10002 does not help either...
// The helper object
IabHelper mHelper = null;
static final String SKU_PROFESSIONAL = "com...";
private String base64EncodedPublicKey = "KSRWbpF........... from Google play Developer Console
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// Create the helper, passing it our context and the public key to verify signatures with
mHelper = new IabHelper(this, 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.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
#Override
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh no, 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.
mHelper.queryInventoryAsync(true, null, mGotInventoryListener);
}
});
}
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG_BILLING, "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;
}
// Do we have the premium upgrade?
Purchase premiumPurchase = inventory.getPurchase(SKU_PROFESSIONAL);
_PROFESSIONAL = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
}
};
// User clicked the menu upgrade professional
private void Upgrade() {
String payload = sMyPurchaseToken;
// tested, but no help
// define IabHelper.flagEndAsync() as public, and add iabHelper.flagEndAsync() before iabHelper.launchPurchaseFlow(...)
// mHelper.flagEndAsync();
// could be tested: this, "android.test.purchased", 10002
/////////// here the program shows the Google Play message that the programm does not exist... or is not configured for payment...
mHelper.launchPurchaseFlow(MyActivity.this, SKU_PROFESSIONAL, RC_REQUEST, mPurchaseFinishedListener, payload);
//mHelper.launchPurchaseFlow(this, "android.test.purchased", 10002, is ok
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG_BILLING, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
if (mHelper == null) return;
// Pass on the activity result to the helper for handling
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
}
else {
}
}
boolean verifyDeveloperPayload(Purchase p) {
String payload = p.getDeveloperPayload();
return true;
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
return;
}
if (purchase.getSku().equals(SKU_PROFESSIONAL)) {
_PROFESSIONAL = true;
}
}
};
// Called when consumption is complete
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG_BILLING, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// if we were disposed of in the meantime, quit.
if (mHelper == null) return;
if (result.isSuccess()) {
}
else {
complain("Error while consuming: " + result);
}
}
};
Im trying to add in app billing that will allow the user to make a purchase as many times as they like (no limit). Therefore it must be consumed. The first time I run the code on a new device it works fine and the test purchase is made successfully. However it fails to be consumed and doesnt let me make another purchase. The problem seems to be in this method, as it always ends up with result.isFailure() thus, the purchase is not consumed (and can only be made once).
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Toast.makeText(getApplicationContext(), "Failed to make purchase.", Toast.LENGTH_LONG).show();
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
consumeItem();
}
}
};
Does anyone know how I can fix the problem?
Here is the rest of the code:
Preference removeAds = (Preference) findPreference("inAppBilling");
removeAds.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
mHelper.launchPurchaseFlow(About.this, ITEM_SKU, 10001, mPurchaseFinishedListener, "mypurchasetoken");
return true;
}
});
#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()) {
Toast.makeText(getApplicationContext(), "Failed to make purchase.", Toast.LENGTH_LONG).show();
return;
}
else if (purchase.getSku().equals(ITEM_SKU)) {
consumeItem();
}
}
};
public void consumeItem() {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
}
IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
// Handle failure
} else {
mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU), mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
if (result.isSuccess()) {
CustomAlerts.showBasicAlert("Thanks", "We appreciate your support.", About.this);
} else {
// handle error
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
You need to query the users inventory on app start. If there are any purchased but not yet redeemed products in it, you can just redeem them at this point. Here's the docu for retrieving it.