Comment implémenter la facturation In-App dans une application Android?

Il semble assez compliqué d’implémenter la facturation In-App dans une application Android. Comment pourrais-je faire ça? L’exemple d’application du SDK ne comporte qu’une seule activité, ce qui le simplifie en quelque sorte pour une application comme la mienne qui a plusieurs activités.

Eh bien, je vais essayer d’expliquer ce que j’ai vécu. Je ne me considère pas comme un expert à ce sujet mais je me suis cassé la tête plusieurs jours.

Pour commencer, j’ai eu beaucoup de mal à comprendre le stream de travail de l’exemple et de l’application. J’ai pensé qu’il serait préférable de commencer avec un exemple simple, mais il est très difficile de séparer le code en petits morceaux et de ne pas savoir si vous cassez quelque chose. Je vais vous dire ce que j’ai et ce que j’ai changé de l’exemple pour le faire fonctionner.

J’ai une seule activité d’où viennent tous mes achats. C’est ce qu’on appelle Pro.

Tout d’abord, vous devez mettre à jour la variable base64EncodedPublicKey dans votre classe Security avec votre clé de développeur Market publique ou vous verrez une exception intéressante.

Eh bien, je lie mon activité à mon service de facturation comme ceci:

public class Pro extends TrackedActivity implements OnItemClickListener { private BillingService mBillingService; private BillingPurchaseObserver mBillingPurchaseObserver; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pro); //Do my stuff mBillingService = new BillingService(); mBillingService.setContext(getApplicationContext()); mHandler = new Handler(); mBillingPurchaseObserver = new BillingPurchaseObserver(mHandler); } } @Override protected void onStart() { //Register the observer to the service super.onStart(); ResponseHandler.register(mBillingPurchaseObserver); } @Override protected void onStop() { //Unregister the observer since you dont need anymore super.onStop(); ResponseHandler.unregister(mBillingPurchaseObserver); } @Override protected void onDestroy() { //Unbind the service super.onDestroy(); mBillingService.unbind(); } 

De cette façon, tous les achats communiquent avec ce service, qui enverra ensuite les demandes JSON au marché. Vous pourriez penser que les achats sont effectués au même instant mais non. Vous envoyez la demande et l’achat peut arriver quelques minutes ou quelques heures plus tard. Je pense que cela concerne principalement la surcharge du serveur et l’approbation des cartes de crédit.

J’ai ensuite un ListView avec mes articles et j’ouvre un AlertDialog sur chacun d’eux, les invitant à acheter l’article. Quand ils cliquent sur un object, je le fais:

  private class BuyButton implements DialogInterface.OnClickListener { private BillingItem item = null; private Ssortingng developerPayload; public BuyButton(BillingItem item, Ssortingng developerPayload) { this.item = item; this.developerPayload = developerPayload; } @Override public void onClick(DialogInterface dialog, int which) { if (GeneralHelper.isOnline(getApplicationContext())){ //I track the buy here with GA SDK. mBillingService.requestPurchase(this.item.getSku(), this.developerPayload); } else { Toast.makeText(getApplicationContext(), R.ssortingng.msg_not_online, Toast.LENGTH_SHORT).show(); } } } 

Bon, vous devriez voir que le marché s’ouvre et que l’utilisateur termine ou annule l’achat.

Ce qui compte alors, c’est mon PurChaseObserver, qui gère tous les événements que le marché envoie. Ceci est une version dépouillée de celui-ci mais vous devriez obtenir le point (voir mes commentaires à travers le code):

 private class BillingPurchaseObserver extends PurchaseObserver { public BillingPurchaseObserver(Handler handler) { super(Pro.this, handler); } @Override public void onBillingSupported(boolean supported) { if (supported) { //Enable buy functions. Not required, but you can do stuff here. The market first checks if billing is supported. Maybe your country is not supported, for example. } else { Toast.makeText(getApplicationContext(), R.ssortingng.billing_not_supported, Toast.LENGTH_LONG).show(); } } @Override public void onPurchaseStateChange(PurchaseState purchaseState, Ssortingng itemId, int quantity, long purchaseTime, Ssortingng developerPayload) { //This is the method that is called when the buy is completed or refunded I believe. // Here you can do something with the developerPayload. Its basically a Tag you can use to follow your transactions. i dont use it. BillingItem item = BillingItem.getBySku(getApplicationContext(), itemId); if (purchaseState == PurchaseState.PURCHASED) { if (item != null){ //This is my own implementation that sets the item purchased in my database. BillingHelper is a class with methods I use to check if the user bought an option and update the UI. You should also check for refunded. You can see the Consts class to find what you need to check for. boolean resu = item.makePurchased(getApplicationContext()); if (resu){ Toast.makeText(getApplicationContext(), R.ssortingng.billing_item_purchased, Toast.LENGTH_LONG).show(); } } } } private void trackPurchase(BillingItem item, long purchaseTime) { //My code to track the purchase in GA } @Override public void onRequestPurchaseResponse(RequestPurchase request, ResponseCode responseCode) { //This is the callback that happens when you sent the request. It doesnt mean you bought something. Just that the Market received it. if (responseCode == ResponseCode.RESULT_OK) { Toast.makeText(getApplicationContext(), R.ssortingng.billing_item_request_sent, Toast.LENGTH_SHORT).show(); } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) { //The user canceled the item. } else { //If it got here, the Market had an unexpected problem. } } @Override public void onRestoreTransactionsResponse(RestoreTransactions request, ResponseCode responseCode) { if (responseCode == ResponseCode.RESULT_OK) { //Restore transactions should only be run once in the lifecycle of your application unless you reinstalled the app or wipe the data. SharedPreferences.Editor edit = PreferencesHelper.getInstance().getDefaultSettings(getApplicationContext()).edit(); edit.putBoolean(Consts.DB_INITIALIZED, true); edit.commit(); } else { //Something went wrong } } } 

Et je crois que vous ne devriez pas avoir à éditer autre chose. Le rest du code “fonctionne”. Vous pouvez essayer d’utiliser l’exemple de SKU dans vos propres articles “android.test.purchased”. Jusqu’à présent, j’ai testé cela et cela fonctionne, mais j’ai encore besoin de couvrir tout comme l’état remboursé. Dans ce cas, je laisse l’utilisateur conserver les fonctionnalités mais je veux m’assurer qu’il fonctionne parfaitement avant de le modifier.

J’espère que cela vous aidera, vous et les autres.

V3: voici un tutoriel pour un démarrage rapide. Il utilise les classes d’aide de l’exemple de Google (Trivial Drive).

http://www.techotopia.com/index.php/Integrating_Google_Play_In-app_Billing_into_an_Android_Application_%E2%80%93_A_Tutorial

Pour la facturation dans l’application v3, j’ai trouvé cela très utile.
http://blog.blundellapps.com/simple-inapp-billing-payment-v3/

Si vous souhaitez utiliser une bibliothèque facile à publier sur Google Play et Amazon Appstore, vous pouvez utiliser RoboBillingLibrary . Il résume les détails des deux dans une bibliothèque facile à utiliser. Les instructions détaillées sont sur la page Github.

Il y a un exemple complet d’ Android In-App Billing v3 étape par étape est donné ici avec la capture d’écran. S’il vous plaît vérifier le tutoriel: Android In-App Billing v3 en utilisant la classe ServiceConnection

J’espère que ça va aider.

Pour plus de précisions, consultez ce didacticiel: Implémentation de la facturation intégrée aux API de la version 3

Étapes à suivre pour intégrer la bibliothèque de facturation In-App dans notre projet

Mettez à jour votre fichier AndroidManifest.xml.

Créez un ServiceConnection et liez-le à IInAppBillingService.

Envoyez des demandes de facturation in-app depuis votre application vers IInAppBillingService.

Gérer les réponses de facturation In-App à partir de Google Play.

Mettre à jour AndroidManifest.xml

  

Ajoutez les permissions dans le fichier Manifest.xml

Ajout du fichier AIDL à votre projet

Construisez votre application Vous devriez voir un fichier généré nommé IInAppBillingService.java dans le répertoire / gen de votre projet.

Mettre à jour les dépendances dans le fichier build.gradle

 apply plugin: 'com.android.application' android { comstackSdkVersion 24 buildToolsVersion "24.0.0" defaultConfig { applicationId "com.inducesmile.androidinapppurchase" minSdkVersion 14 targetSdkVersion 24 versionCode 2 versionName "1.1" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { comstack fileTree(dir: 'libs', include: ['*.jar']) testComstack 'junit:junit:4.12' comstack 'com.android.support:appcompat-v7:24.1.1' comstack 'com.intuit.sdp:sdp-android:1.0.3' comstack 'com.android.support:support-annotations:24.1.1' comstack 'org.jetbrains:annotations-java5:15.0' } 

InAppPurchaseActivity.java et activity_in_app_purchase.xml

C’est là que les utilisateurs de l’application auront la possibilité d’effectuer des achats intégrés. Dans le fichier de mise en page, nous donnerons à l’utilisateur la possibilité d’effectuer des achats dans différentes dénominations.

InAppPurchaseActivity.java

Remarque: les méthodes getAllUserPurchase () et itemPurchaseAvailability () doivent être appelées dans un thread autre que l’interface utilisateur pour éviter le blocage des applications.

 public class InAppPurchaseActivity extends AppCompatActivity { private static final Ssortingng TAG = InAppPurchaseActivity.class.getSimpleName(); private IInAppBillingService mService; private CustomSharedPreference customSharedPreference; Ssortingng[] productIds = new Ssortingng[]{Helper.ITEM_ONE_ID, Helper.ITEM_TWO_ID, Helper.ITEM_THREE_ID}; private ImageView buyOneButton, buyTwoButton, buyThreeButton; private static final char[] symbols = new char[36]; static { for (int idx = 0; idx < 10; ++idx) symbols[idx] = (char) ('0' + idx); for (int idx = 10; idx < 36; ++idx) symbols[idx] = (char) ('a' + idx - 10); } private String appPackageName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_in_app_purchase); appPackageName = this.getPackageName(); Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); serviceIntent.setPackage("com.android.vending"); bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); customSharedPreference = new CustomSharedPreference(InAppPurchaseActivity.this); buyOneButton = (ImageView)findViewById(R.id.buy_one); buyOneButton.setVisibility(View.GONE); assert buyOneButton != null; buyOneButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_ONE_ID); } }); buyTwoButton = (ImageView)findViewById(R.id.buy_two); buyTwoButton.setVisibility(View.GONE); assert buyTwoButton != null; buyTwoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_TWO_ID); } }); buyThreeButton = (ImageView)findViewById(R.id.buy_three); buyThreeButton.setVisibility(View.GONE); assert buyThreeButton != null; buyThreeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_THREE_ID); } }); } ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); AvailablePurchaseAsyncTask mAsyncTask = new AvailablePurchaseAsyncTask(appPackageName); mAsyncTask.execute(); } }; private void purchaseItem(String sku){ String generatedPayload = getPayLoad(); customSharedPreference.setDeveloperPayLoad(generatedPayload); try { Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", generatedPayload); PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); try { startIntentSenderForResult(pendingIntent.getIntentSender(), Helper.RESPONSE_CODE, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); } catch (IntentSender.SendIntentException e) { e.printStackTrace(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == Helper.RESPONSE_CODE) { int responseCode = data.getIntExtra("RESPONSE_CODE", 0); String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); if (resultCode == RESULT_OK) { try { JSONObject purchaseJsonObject = new JSONObject(purchaseData); String sku = purchaseJsonObject.getString("productId"); String developerPayload = purchaseJsonObject.getString("developerPayload"); String purchaseToken = purchaseJsonObject.getString("purchaseToken"); //the developerPayload value is better stored in remote database but in this tutorial //we will use a shared preference for(int i = 0; i < productIds.length; i++){ if(productIds[i].equals(sku) && developerPayload.equals(customSharedPreference.getDeveloperPayload())){ customSharedPreference.setPurchaseToken(purchaseToken); //access to private content Intent contentIntent = new Intent(InAppPurchaseActivity.this, PrivateContentActivity.class); startActivity(contentIntent); } } } catch (JSONException e) { e.printStackTrace(); } } } } private String getPayLoad(){ RandomString randomString = new RandomString(36); String payload = randomString.nextString(); return payload; } public class RandomString { private final Random random = new Random(); private final char[] buf; public RandomString(int length) { if (length < 1) throw new IllegalArgumentException("length < 1: " + length); buf = new char[length]; } public String nextString() { for (int idx = 0; idx < buf.length; ++idx) buf[idx] = symbols[random.nextInt(symbols.length)]; return new String(buf); } } public final class SessionIdentifierGenerator { private SecureRandom random = new SecureRandom(); public String nextSessionId() { return new BigInteger(130, random).toString(32); } } private class AvailablePurchaseAsyncTask extends AsyncTask { Ssortingng packageName; public AvailablePurchaseAsyncTask(Ssortingng packageName){ this.packageName = packageName; } @Override protected Bundle doInBackground(Void... voids) { ArrayList skuList = new ArrayList(); skuList.add(Helper.ITEM_ONE_ID); skuList.add(Helper.ITEM_TWO_ID); skuList.add(Helper.ITEM_THREE_ID); Bundle query = new Bundle(); query.putSsortingngArrayList(Helper.ITEM_ID_LIST, skuList); Bundle skuDetails = null; try { skuDetails = mService.getSkuDetails(3, packageName, "inapp", query); } catch (RemoteException e) { e.printStackTrace(); } return skuDetails; } @Override protected void onPostExecute(Bundle skuDetails) { List canPurchase = new ArrayList(); int response = skuDetails.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList responseList = skuDetails.getSsortingngArrayList("DETAILS_LIST"); if(responseList != null){ for (Ssortingng thisResponse : responseList) { JSONObject object = null; try { object = new JSONObject(thisResponse); Ssortingng sku = object.getSsortingng("productId"); Ssortingng price = object.getSsortingng("price"); canPurchase.add(new AvailablePurchase(sku, price)); } catch (JSONException e) { e.printStackTrace(); } } } } if(checkIfPurchaseIsAvailable(canPurchase, productIds[0])){ buyOneButton.setVisibility(View.VISIBLE); }else{ buyOneButton.setVisibility(View.GONE); } if(checkIfPurchaseIsAvailable(canPurchase, productIds[1])){ buyTwoButton.setVisibility(View.VISIBLE); }else{ buyTwoButton.setVisibility(View.GONE); } if(checkIfPurchaseIsAvailable(canPurchase, productIds[2])){ buyThreeButton.setVisibility(View.VISIBLE); }else{ buyThreeButton.setVisibility(View.GONE); } } } @org.jetbrains.annotations.Contract("null, _ -> false") private boolean checkIfPurchaseIsAvailable(List all, Ssortingng productId){ if(all == null){ return false;} for(int i = 0; i < all.size(); i++){ if(all.get(i).getSku().equals(productId)){ return true; } } return false; } public boolean isBillingSupported(){ int response = 1; try { response = mService.isBillingSupported(3, getPackageName(), "inapp"); } catch (RemoteException e) { e.printStackTrace(); } if(response > 0){ return false; } return true; } public void consumePurchaseItem(Ssortingng purchaseToken){ try { int response = mService.consumePurchase(3, getPackageName(), purchaseToken); if(response != 0){ return; } } catch (RemoteException e) { e.printStackTrace(); } } public Bundle getAllUserPurchase(){ Bundle ownedItems = null; try { ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null); } catch (RemoteException e) { e.printStackTrace(); } return ownedItems; } public List extractAllUserPurchase(Bundle ownedItems){ List mUserItems = new ArrayList(); int response = ownedItems.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList ownedSkus = ownedItems.getSsortingngArrayList("INAPP_PURCHASE_ITEM_LIST"); ArrayList purchaseDataList = ownedItems.getSsortingngArrayList("INAPP_PURCHASE_DATA_LIST"); ArrayList signatureList = ownedItems.getSsortingngArrayList("INAPP_DATA_SIGNATURE_LIST"); Ssortingng continuationToken = ownedItems.getSsortingng("INAPP_CONTINUATION_TOKEN"); if(purchaseDataList != null){ for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); assert signatureList != null; String signature = signatureList.get(i); assert ownedSkus != null; String sku = ownedSkus.get(i); UserPurchaseItems allItems = new UserPurchaseItems(sku, purchaseData, signature); mUserItems.add(allItems); } } } return mUserItems; } @Override public void onDestroy() { super.onDestroy(); if (mService != null) { unbindService(mServiceConn); } } } 

Créer un répertoire de package d'assistance

Créez un nouveau dossier de package et nommez-le helpers. Dans le package, créez un nouveau fichier java Helper.java.

Helper.java

 public class Helper { public static final Ssortingng ITEM_ID_LIST = "ITEM_ID_LIST"; public static final Ssortingng ITEM_ONE_ID = "productone"; public static final Ssortingng ITEM_TWO_ID = "producttwo"; public static final Ssortingng ITEM_THREE_ID = "productthree"; public static final int RESPONSE_CODE = 1001; public static final Ssortingng SHARED_PREF = "shared_pref"; public static final Ssortingng DEVELOPER_PAYLOAD = "developer_payload"; public static final Ssortingng PURCHASE_TOKEN = "purchase_token"; public static void displayMessage(Context context, Ssortingng message){ Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG).show(); } } 

Test de l'achat de facturation dans l'application

  1. Créer un compte Google+ (n'utilisez pas le compte principal)
  2. Ajoutez les utilisateurs qui testeront l'application dans votre groupe ou votre communauté.

Erreurs que vous pourriez rencontrer lors du test d'achat In-App

l'article que vous avez demandé n'est pas disponible à l'achat

Solution - Selon AndreiBogdan dans Stackoverflow ,

Tout le crédit va à Inducesmile pour son tutoriel

Android Developer Blog recommande également un cours de formation sur la vente de produits intégrés aux applications. Pour voir une implémentation complète et apprendre à tester l'application, veuillez consulter ce didacticiel: Vente de produits intégrés aux applications

D’accord, c’est l’une de ces choses qui n’a pas beaucoup de documentation disponible en ligne, alors je vais faire de mon mieux pour tout expliquer étape par étape. Tiré de mon blog, qui est une version plus détaillée de cela (avec des captures d’écran), ici sur The Millibit . Sans plus tarder,

Étape 1: Autorisations C’est l’étape la plus simple. Accédez à votre fichier manifest.xml et ajoutez la ligne suivante sous votre tag:

  

Cela donnera à votre application les permissions nécessaires pour accéder à la facturation intégrée aux applications. Si vous ciblez les versions supérieures à l’API 22, vous devez vous assurer que cette autorisation est accordée lors de l’exécution.

Deuxième étape: Play Console Vous devez maintenant télécharger votre application sur Google Play Console. Nous ne publions pas encore notre application au public (ne vous inquiétez pas), nous ne faisons que la télécharger dans la section BETA RELEASE, ce qui nous permettra de tester les achats intégrés. La raison pour laquelle nous devons le faire est que Google doit télécharger une version de votre fichier APK pour que les processus de facturation fonctionnent réellement.

  1. Accédez à https://play.google.com/apps/publish/

  2. Créer l’application

  3. Suivez les étapes pour configurer votre application

  4. Aller à la version de l’application

  5. Naviguer vers la bêta

  6. Créez un fichier APK de votre application dans un studio Android et transférez-le sur la production Bêta de la console de lecture.

(avant de publier, assurez-vous d’avoir déjà rempli la liste des magasins, la classification du contenu et le prix et la dissortingbution)

  1. Appuyez sur le bouton magique (publier!)

Troisième étape: Configuration du projet OK, c’est la partie où vous devez copier et coller un tas de fichiers.

Tout d’abord, récupérez ce fichier, téléchargez-le et placez-le sous src/main Il doit se construire dans un dossier Ensuite, récupérez tout le dossier util et collez-le dans le src/java folder. Recréez ensuite votre projet pour résoudre les erreurs. Le dossier Util contient les classes suivantes:

  • IabBroadcastReceiver
  • IabException
  • IabHelper
  • IabResult
  • Inventaire
  • achat
  • Sécurité
  • SkuDetails

Quasortingème étape: créer des produits

  1. Créer un produit géré

  2. Cliquez sur Enregistrer et créez un «modèle de tarification»

Ici, vous choisirez le prix de ce produit. Vous pouvez choisir le prix pour différents pays ou l’ajuster automatiquement si vous choisissez simplement tous les pays en fonction de votre prix:

  1. Assurez-vous que le produit intégré à l’application est activé et lié avec l’application correcte dans la console une dernière fois.

Enfin, notez l’ID de votre produit. Nous allons utiliser cet identifiant dans les prochaines étapes.

  1. Obtenez votre Base64EncodedSsortingng

Rendez-vous sur «Services & APIs» et prenez votre Base64EncodedSsortingng. Copiez et collez ceci dans un bloc-notes quelque part pour y avoir access. Ne partagez pas cela avec quiconque, ils pourront faire des choses malveillantes avec elle.

Cinquième étape: enfin! Nous pouvons commencer à coder: nous allons d’abord nous connecter à la bibliothèque de facturation intégrée à l’application et demander ce que l’utilisateur a / n’a pas acheté. Ensuite, nous achèterons le produit que nous avons mis en place plus tôt.

Tout d’abord, importez tout ce que nous avons configuré précédemment:

 import util.*; 

Nous allons maintenant utiliser un object IabHelper appelé mHelper, et nous ferons tout avec cela.

 base64EncodedPublicKey = ""; //PUT YOUR BASE64KEY HERE mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(false); //set to false in real app mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { if (!result.isSuccess()) { // Oh no, there was a problem. if (result.getResponse() == 3) { new AlertDialog.Builder(MainActivity.this) .setTitle("In app billing") .setMessage("This device is not compatible with In App Billing, so" + " you may not be able to buy the premium version on your phone. ") .setPositiveButton("Okay", null) .show(); } Log.v(TAG, "Problem setting up In-app Billing: " + result); } else { Log.v(TAG, "YAY, in app billing set up! " + result); try { mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } } } }); 

Ok, laisse-moi décomposer ce qui se passe ici. Fondamentalement, nous appelons “startSetup” pour initialiser notre “IabHelper”. Si la configuration réussit, nous interrogeons les achats que l’utilisateur a déjà et stockons les réponses dans mGotInventoryListener , que nous mGotInventoryListener ensuite:

 IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { i = inventory; if (result.isFailure()) { // handle error here Log.v(TAG, "failure in checking if user has purchases"); } else { // does the user have the premium upgrade? if (inventory.hasPurchase("premium_version")) { premiumEditor.putBoolean("hasPremium", true); premiumEditor.commit(); Log.v(TAG, "Has purchase, saving in storage"); } else { premiumEditor.putBoolean("hasPremium", false); premiumEditor.commit(); Log.v(TAG, "Doesn't have purchase, saving in storage"); } } } }; 

Le code ci-dessus est assez explicite. Fondamentalement, il vérifie simplement quels achats l’utilisateur a déjà. Maintenant que nous soaps si l’utilisateur a déjà acheté notre produit, nous soaps s’il faut ou non lui demander d’acheter notre article! S’ils n’ont jamais acheté notre produit auparavant, commençons une demande d’achat:

 public void buyPremium() { try { mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener } catch (Exception e) { e.printStackTrace(); mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually new AlertDialog.Builder(MainActivity.this) .setTitle("Error") .setMessage("An error occurred in buying the premium version. Please try again.") .setPositiveButton("Okay", null) .show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); // Pass on the activity result to the helper for handling if (!mHelper.handleActivityResult(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.v(TAG, "purchase finished"); if (purchase != null) { if (purchase.getSku().equals("premium_version")) { Toast.makeText(MainActivity.this, "Purchase successful!", Toast.LENGTH_SHORT).show(); premiumEditor.putBoolean("hasPremium", true); premiumEditor.commit(); } } else { return; } if (result.isFailure()) { return; } } }; 

Ici, nous achetons l’article (avec l’ID généré précédemment dans la console de jeu) avec les éléments suivants:

  mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener 

Notez que nous avons passé mPurchaseFinishedListener dans les parameters. Cela signifie que le résultat de l’achat sera retourné à cet auditeur. Ensuite, nous vérifions simplement si l’achat est nul et, dans le cas contraire, accordons à l’utilisateur la fonctionnalité qu’il a achetée.

Ne laissez pas les auditeurs fuir! Nous devons les détruire lorsque l’application est détruite.

 @Override public void onDestroy() { super.onDestroy(); if (mHelper != null) try { mHelper.dispose(); mHelper = null; } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } } 

Enfin, si vous souhaitez consumr votre achat pour le rendre à nouveau disponible, vous pouvez le faire facilement. Un exemple de ceci est si un utilisateur a acheté du gaz pour une voiture virtuelle, et il a manqué. Ils doivent à nouveau acheter le même produit et le rendre disponible pour un deuxième achat en le consommant:

 public void consume(){ //MAKING A QUERY TO GET AN ACCURATE INVENTORY try { mHelper.flagEndAsync(); //If any async is going, make sure we have it stop eventually mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener if(i.getPurchase("gas")==null){ Toast.makeText(this, "Already consumed!", Toast.LENGTH_SHORT).show(); } } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); Toast.makeText(this, "Error, try again", Toast.LENGTH_SHORT).show(); mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually } //ACTUALLY CONSUMING try { mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually this.mHelper.consumeAsync(this.i.getPurchase("gas"), new IabHelper.OnConsumeFinishedListener() { public void onConsumeFinished(Purchase paramAnonymousPurchase, IabResult paramAnonymousIabResult) { //resell the gas to them } }); return; } catch (IabHelper.IabAsyncInProgressException localIabAsyncInProgressException) { localIabAsyncInProgressException.printStackTrace(); Toast.makeText(this, "ASYNC IN PROGRESS ALREADY!!!!" +localIabAsyncInProgressException, Toast.LENGTH_LONG).show(); Log.v("myTag", "ASYNC IN PROGRESS ALREADY!!!"); mHelper.flagEndAsync(); } } 

C’est tout! Vous pouvez maintenant commencer à gagner de l’argent. C’est vraiment aussi simple!

Encore une fois, si vous voulez une version plus détaillée de ce tutoriel, avec des captures d’écran et des images, visitez le message original ici . Faites-moi savoir dans les commentaires si vous avez d’autres questions.