I'm implementing in-app billing where the user shall be able to buy access to premium content. This is typical non-consumable items. (Let's say the premium content is extra questions or categories in an question-app)
I have used this tutorial to create the first version. The problem is how I shall implement the non-consumable part..
How do I know that the user has bought the premium content? One solution I'm think of is to have a column in my question-table that is initially "0". When the user buy premium access, the this column is set to "1".
Is this a way to go?
Where in my code do I get the message from the billing API that the content is already bought? (If it is..)
My code.. (From tutorial, "buy the possibility to click a button")
public class BuyExtras extends Activity {
private static final String TAG = "inAppBilling";
static final String ITEM_SKU = "android.test.purchased";
IabHelper mHelper;
private Button clickButton;
private Button buyButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate CALLED");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buy_extras);
buyButton = (Button)findViewById(R.id.buyButton);
clickButton = (Button)findViewById(R.id.clickButton);
clickButton.setEnabled(false);
String base64EncodedPublicKey =
"<myKey>";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess()) {
Log.d(TAG, "In-app Billing setup failed: " +
result);
} else {
Log.d(TAG, "In-app Billing is set up OK");
}
}
});
}
public void buyClick(View view) {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001, mPurchaseFinishedListener, "mypurchasetoken");
}
#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() {
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()) {
clickButton.setEnabled(true);
} else {
// handle error
}
}
};
public void buttonClicked (View view)
{
clickButton.setEnabled(false);
buyButton.setEnabled(true);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
}
I read this question indicating that I not should call the
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
But is just removing it ok? Where should I handle the case that the user tries a second buy?
If you don't want consume, then don't use consumeAsync.
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inv)
{
if (result.isFailure())
{
Log.e(TAG, "In-app Billing query failed: " + result);
return;
} else
{
boolean hasPurchased_ITEM_SKU_PURCHASE_1 = inv.hasPurchase(ITEM_SKU_PURCHASE_1);
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = sharedPref.edit();
editor.putBoolean(KEY_PREF_PURCHASE_1_AVAILABLE, !hasPurchased_ITEM_SKU_PURCHASE_1);
editor.commit();
// You can update your UI here, ie. Buy buttons.
}
}
You can use the sharedpref to store the purchase info, and also check every onCreate of the activity and update the sharedpref accordingly.
The key part on how to check if a SKU is purchased is:
boolean hasPurchased_ITEM_SKU_PURCHASE_1 = inv.hasPurchase(ITEM_SKU_PURCHASE_1);
Do your query sync in your IAP setup and update your UI accordingly.
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess()) {
Log.d(TAG, "In-app Billing setup failed: " +
result);
} else {
mHelper.queryInventoryAsync(mReceivedInventoryListener);
Log.d(TAG, "In-app Billing is set up OK");
}
}
});
Related
The problem is that the app doesn't allow the users to purchase more than once. I need to make my item consumable, and the answer is all explained here.
What I need to know is EXACTLY WHERE I have to put this part:
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
in my code?
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
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() {
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()) {
clickButton.setEnabled(true);
} else {
// handle error
}
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_in_app_billing);
buyButton = (Button)findViewById(R.id.buyButton);
clickButton = (Button)findViewById(R.id.clickButton);
String base64EncodedPublicKey =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3F8aC2kUFQHf/X3xnfgulD0UpgrQifcjZ66zzhUPhQ/TcrONl22V74Q/Uj57rCwSUfdzz7wbUPxuPayGKBozzoH+2vhMGSetgCFZLcrNbRpBBbihOZrj//GTXMa6VkpUPTAqthEF0oI1M/bW9vF75xZI3u2KAS/AYDfqLTRZ6mh+xh6n/3i0ntSZT+UwzguwyHfS9JwuGGg5AKSutaWhnvOTNeQjsxTskc483h9DfvvRiwdiQPlv7wJRSSIc3RHVwDHleEJ8rsRa8JTypBJuL5oRZSGePUlejWhJvs23tgy5xrvGsMgsICssGzIem2XXSUWm/NDjeO0v2Eh+quQKVQIDAQAB";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess())
{
Log.d(TAG, "In-app Billing setup failed: " + result);
} else
{
Log.d(TAG, "In-app Billing is set up OK");
}
}
});
}
}
Because in the other answer Haxis says "you have to call the consume function just after the purchase."
Could anyone help me please?
As you can check in the google sample -
https://github.com/googlesamples/android-play-billing/blob/master/TrivialDrive/app/src/main/java/com/example/android/trivialdrivesample/MainActivity.java
consumeAsync() is called twice:
-When the purchase is complete, in OnIabPurchaseFinishedListener
-When the inventory is queried at the start of the activity, in QueryInventoryFinishedListener
You also need to call it when the purchase was successful as well as when you query the inventory and find a consumable item in there (because if the item is consumed, then it won't appear in the inventory)
I want to use In-App purchase using Google API and JNI in C++ is there sample or tutorial that use JNI for In-app Billing ?
item in my Game is consumable.
i can use IAP in java and this is main activity class But how i call it From JNI
public class IAPActivity extends Activity {
public static final String RSA= "********";
// Debug tag, for logging
private static final String TAG = "LOG";
// SKUs for our products: the premium upgrade (non-consumable)
private static final String SKU_PREMIUM = "gp";
// Does the user have the premium upgrade?
private boolean mIsPremium = false;
// (arbitrary) request code for the purchase flow
//static final int RC_REQUEST = ;
// The helper object
private IabHelper mHelper;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// You can find it in your Bazaar console, in the Dealers section.
// It is recommended to add more security than just pasting it in your source code;
mHelper = new IabHelper(this, RSA);
final IabHelper.QueryInventoryFinishedListener onQueryInventoryFinished = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
//kharid nakarde
if (result.isFailure()) {
Log.d(TAG, "Failed to query inventory: " + result);
return;
}
else {
Log.d(TAG, "Query inventory was successful.");
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
// update UI accordingly
Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
}
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
final IabHelper.OnIabPurchaseFinishedListener onIabPurchaseFinished = new OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
// TODO Auto-generated method stub
if (result.isFailure()) {
Log.d(TAG, "Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals(SKU_PREMIUM)) {
// give user access to premium content and update the UI
}
}
};
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);
}
// Hooray, IAB is fully set up!
mHelper.queryInventoryAsync(onQueryInventoryFinished);
}
});
Button btnPurchase=(Button) findViewById(R.id.btnPurchaseGP);
btnPurchase .setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
// TODO Auto-generated method stub
mHelper.launchPurchaseFlow(IAPActivity.this, SKU_PREMIUM, 0, onIabPurchaseFinished, "payload-string");
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + 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.");
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
}
Unfortunately JNI is very complex and i don't any idea for use it please Help me
I'm working on one of my first android apps which has some in-app billing features. I've spent the last 3 days researching how to add these in-app purchases after purchasing my developer's licence. I stumbled across a guide that lead me to success on a test program :
http://www.techotopia.com/index.php/An_Android_Studio_Google_Play_In-app_Billing_Tutorial
The test program works great. But when i applied this guide to my NavigationDrawerFragment Program, I've ran into some issues. Like i said in the title, i'm using android.test.purchased as the SKU and it seems to only let me use the test purchase every now and then.
Other times i get "In-app billing error: Unable to buy item, Error response: 7:Item Already Owned"
I have added the billing permission in the manifest file, I have also made this alteration to the Security.java File (following the guide):
public static boolean verifyPurchase(String base64PublicKey,
String signedData, String signature) {
if (TextUtils.isEmpty(signedData) ||
TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
if (BuildConfig.DEBUG) {
return true;
}
return false;
}
MainActivity.java - OnCreate - Log.d code is // on purpose
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String base64EncodedPublicKey =
"My key that i almost pasted in here :P";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new
IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess()) {
//Log.d(TAG, "In-app Billing setup failed: " +
//result);
} else {
//Log.d(TAG, "In-app Billing is set up OK");
}
}
});
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
Rest of MainActivity.java that doesn't have to do with the drawerFragment:
private static final String TAG = "com.example.inappbilling";
static final String ITEM_SKU = "android.test.purchased";
public void buyClick(View view) {
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,
mPurchaseFinishedListener, "mypurchasetoken");
}
#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();
}
}
};
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()) {
//clickButton.setEnabled(true);
Context.MODE_PRIVATE);
//Never had this code actually run before, could contain errors
//but those aren't the issue to my knowledge.
SharedPreferences pref = MainActivity.this.getSharedPreferences("MyPref",
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("BoughtTabard",true);
editor.commit();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, TabardGuideNeedToBuy.newInstance(8))
.commit();
} else {
// handle error
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
Not sure if I'm just overlooking a minor issue or something, but any ideas/solutions are greatly appreciated!
And I have indeed added the IInAppBillingService.aidl to the correct spot, along with all 9 of the util files(Base64,Secruity,SkuDeatils ect...)
if for some reason android.test.purchased isnt consumed when it's supposed to be, add the consumeItem(); call from in the if (result.isFailure()), run the app, relaunch the purchase flow, it will then crash and consume android.test.purchased, then go back to the code and remove the consumeItem(); call from within the if (result.isFailure()). Re-run the app once again, and android.test.purchased should now be able to be consumed again.
I implement an in-app for Google Play. I use the test item: android.test.purchased and described the item as NONconsumable (not consuming it anywhere in the code with mHelper.consumeAsync). While running the app, after some "restart"s and "force close"s, "going online to offline" and "offline to online" somewhere in the middle, the item becomes consumable again and I can purchase the item. It is consumable sometimes and non consumable other times! I appreciate any suggestion. My code is below:
...
public class Home extends Activity {
...
static final String ITEM_SKU = "android.test.purchased";
IabHelper mHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
}
#Override
protected void onStart() {
super.onStart();
mHelper = new IabHelper(this, key);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener(){
public void onIabSetupFinished(IabResult result){
if (!result.isSuccess()){
Log.d("setup", "NOT OK");
}
else{
Log.d("setup", "OK");
List SKU_List = new ArrayList();
SKU_List.add(ITEM_SKU);
mHelper.queryInventoryAsync(false, SKU_List, mQueryFinishedListener);
}
}
});
}
IabHelper.QueryInventoryFinishedListener mQueryFinishedListener = new IabHelper.QueryInventoryFinishedListener(){
#Override
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
return;
}
try {
if (inventory.hasPurchase(ITEM_SKU)) {
buttonsInPremiumMode();
} else {
buttonsNOTPremiumMode();
String itemPrice = inventory.getSkuDetails(ITEM_SKU).getPrice();
buyPremiumButton.setText(getResources().getString(R.string.buyPremiumButton) + itemPrice);
}
}
catch(Exception e){
}
}
};
private void purchase(){
if (mHelper != null){
mHelper.flagEndAsync();
mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001, mPurchaseFinishedListener, "payloadercode");
}
}
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener(){
public void onIabPurchaseFinished(IabResult result, Purchase purchase){
if (result.isFailure()){
}
else if (purchase.getSku().equals(ITEM_SKU)){
buttonsInPremiumMode();
}
else {
}
}
};
#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();
if (mHelper != null) {
mHelper.dispose();
}
mHelper=null;
}
private void buttonsNOTPremiumMode(){
...
}
private void buttonsInPremiumMode(){
...
}
}
Putting
mHelper.flagEndAsync()
just before
mHelper.launchPurchaseFlow
fixed my problem.
I added In-app billing v3 in my app and when I try to buy an item, the transfer is successful and money is taken from credit card, but the item doesn't add in app. What could be wrong?
//EDIT
public class GetcoinsActivity extends MainActivity {
SharedPreferences prefs_coins;
static final String ITEM_SKU_1 = "coins_1";
static final int RC_REQUEST = 10001;
private static final String TAG = "com.chess.black";
IabHelper mHelper;
Button btn1;
int score_get = 100;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_getcoins);
prefs_coins = PreferenceManager.getDefaultSharedPreferences(GetcoinsActivity.this);
if (prefs_coins.contains(activity_play_normal.APP_PREFERENCES_score))
{
score_get = prefs_coins.getInt(activity_play_normal.APP_PREFERENCES_score, 0);
}
btn1 = (Button) findViewById(R.id.buttonBuy30);
String base64EncodedPublicKey = "here's my key";
mHelper = new IabHelper(this, base64EncodedPublicKey);
mHelper.startSetup(new
IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess()) {
Log.d(TAG, "In-app Billing setup failed: " +
result);
} else {
Log.d(TAG, "In-app Billing is set up OK");
}
}
});
}
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_1)) {
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_1),
mConsumeFinishedListener);
}
}
};
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener =
new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase,
IabResult result) {
if (result.isSuccess()) {
score_get = score_get + 500;
Editor editor = prefs_coins.edit();
editor.putInt(activity_play_normal.APP_PREFERENCES_score, score_get);
editor.commit();
} else {
// handle error
}
}
};
public void OnClickBuy30(View v)
{
mHelper.launchPurchaseFlow(GetcoinsActivity.this, ITEM_SKU_1, RC_REQUEST,
mPurchaseFinishedListener);
}
protected void OnDestroy()
{
super.onDestroy();
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
protected void OnActivityResult(int requestCode, int resultCode, Intent data)
{
if (!mHelper.handleActivityResult(requestCode,
resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
}
ok first please put Logs in your code ... (Log.d(..))
maybe your score will be not written in the preferences because the billing process is in another process .. try this
SharedPreferences yourPrefs= ctx.getSharedPreferences(ctx.PREFS_NAME,ctx.MODE_MULTI_PROCESS);