Es scheint ziemlich kompliziert zu sein, In-App Billing in einer Android-App zu implementieren. Wie könnte ich das machen? Die Beispiel-App aus dem SDK verfügt nur über eine Aktivität, wodurch sie für eine Anwendung wie meine mit mehreren Aktivitäten zu stark vereinfacht wird.
73
Antworten:
Nun, ich werde versuchen zu erklären, was ich erlebt habe. Ich betrachte mich nicht als Experte, aber ich habe mir mehrere Tage den Kopf gebrochen.
Für den Anfang hatte ich eine sehr schlechte Zeit, den Workflow des Beispiels und der Anwendung zu verstehen. Ich dachte, es sollte besser sein, mit einem einfachen Beispiel zu beginnen, aber es ist sehr schwierig, den Code in kleine Teile zu trennen und nicht zu wissen, ob Sie etwas kaputt machen. Ich werde Ihnen sagen, was ich habe und was ich gegenüber dem Beispiel geändert habe, damit es funktioniert.
Ich habe eine einzige Aktivität, aus der alle meine Einkäufe stammen. Es heißt Pro.
Zuerst sollten Sie die Variable base64EncodedPublicKey in Ihrer Sicherheitsklasse mit Ihrem öffentlichen Marktentwicklerschlüssel aktualisieren, sonst wird eine nette Ausnahme angezeigt.
Nun, ich binde meine Aktivität wie folgt an meinen BillingService:
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(); }
Auf diese Weise sprechen alle Käufe mit diesem Service, der dann die JSON-Anforderungen an den Markt sendet. Sie könnten denken, dass die Einkäufe im selben Moment getätigt werden, aber nein. Sie senden die Anfrage und der Kauf kann Minuten oder Stunden später erfolgen. Ich denke, dies ist hauptsächlich auf die Serverüberlastung und die Genehmigung der Kreditkarten zurückzuführen.
Dann habe ich eine ListView mit meinen Artikeln und öffne für jeden einen AlertDialog, in dem ich sie zum Kauf des Artikels einlade. Wenn sie auf einen Gegenstand klicken, mache ich das:
private class BuyButton implements DialogInterface.OnClickListener { private BillingItem item = null; private String developerPayload; public BuyButton(BillingItem item, String 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.string.msg_not_online, Toast.LENGTH_SHORT).show(); } } }
In Ordnung, Sie sollten sehen, dass sich der Markt öffnet und der Benutzer den Kauf entweder beendet oder abbricht.
Was dann wichtig ist, ist mein PurChaseObserver, der alle Ereignisse verarbeitet, die der Markt sendet. Dies ist eine abgespeckte Version davon, aber Sie sollten den Punkt verstehen (siehe meine Kommentare durch den 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.string.billing_not_supported, Toast.LENGTH_LONG).show(); } } @Override public void onPurchaseStateChange(PurchaseState purchaseState, String itemId, int quantity, long purchaseTime, String 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.string.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.string.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 } } }
Und ich glaube, Sie sollten nichts anderes bearbeiten müssen. Der Rest des Codes "funktioniert". Sie können versuchen, die Beispiel-SKU zunächst in Ihren eigenen Artikeln "android.test.purchased" zu verwenden. Bisher habe ich dies getestet und es funktioniert, aber ich muss noch alles wie den erstatteten Zustand abdecken. In diesem Fall lasse ich den Benutzer die Funktionen behalten, möchte aber sicherstellen, dass sie perfekt funktionieren, bevor ich sie modifiziere.
Ich hoffe es hilft dir und anderen.
quelle
V3: Hier ist ein Tutorial für einen schnellen Start. Er verwendet die Helfer-Klassen aus dem Google-Beispiel (Trivial Drive). Gut wie zuerst "Hallo Abrechnung".
http://www.techotopia.com/index.php/Integrating_Google_Play_In-app_Billing_into_an_Android_Application_%E2%80%93_A_Tutorial
quelle
Es gibt ein vollständiges Beispiel für Android In-App Billing v3. Schritt für Schritt finden Sie hier einen Screenshot. Bitte überprüfen Sie das Tutorial: Android In-App Billing v3 mit ServiceConnection Class
Hoffe es wird helfen.
Weitere Informationen finden Sie in diesem Lernprogramm: Implementieren der In-App-Abrechnung in der API der Version 3
Schritte zur Integration der In-App-Abrechnungsbibliothek in unser Projekt
Aktualisieren Sie Ihre AndroidManifest.xml-Datei.
Erstellen Sie eine ServiceConnection und binden Sie sie an IInAppBillingService.
Senden Sie In-App-Abrechnungsanforderungen aus Ihrer Anwendung an IInAppBillingService.
In-App-Abrechnungsantworten von Google Play verarbeiten.
Aktualisieren Sie AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
Fügen Sie die Berechtigungen in der Datei Manifest.xml hinzu
Hinzufügen der AIDL-Datei zu Ihrem Projekt
Erstellen Sie Ihre Anwendung. Sie sollten eine generierte Datei mit dem Namen IInAppBillingService.java im Verzeichnis / gen Ihres Projekts sehen.
Aktualisieren Sie die Abhängigkeiten in der Datei build.gradle
apply plugin: 'com.android.application' android { compileSdkVersion 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 { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.1.1' compile 'com.intuit.sdp:sdp-android:1.0.3' compile 'com.android.support:support-annotations:24.1.1' compile 'org.jetbrains:annotations-java5:15.0' }
InAppPurchaseActivity.java und activity_in_app_purchase.xml
Hier bieten wir unseren App-Benutzern die Möglichkeit, In-App-Käufe zu tätigen. In der Layoutdatei geben wir dem Benutzer die Möglichkeit, in verschiedenen Stückelungen einzukaufen.
InAppPurchaseActivity.java
Hinweis: Die Methoden getAllUserPurchase () und itemPurchaseAvailability () sollten in Nicht-UI-Threads aufgerufen werden, um einen Absturz der App zu vermeiden.
public class InAppPurchaseActivity extends AppCompatActivity { private static final String TAG = InAppPurchaseActivity.class.getSimpleName(); private IInAppBillingService mService; private CustomSharedPreference customSharedPreference; String[] productIds = new String[]{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<Void, Void, Bundle> { String packageName; public AvailablePurchaseAsyncTask(String packageName){ this.packageName = packageName; } @Override protected Bundle doInBackground(Void... voids) { ArrayList<String> skuList = new ArrayList<String>(); skuList.add(Helper.ITEM_ONE_ID); skuList.add(Helper.ITEM_TWO_ID); skuList.add(Helper.ITEM_THREE_ID); Bundle query = new Bundle(); query.putStringArrayList(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<AvailablePurchase> canPurchase = new ArrayList<AvailablePurchase>(); int response = skuDetails.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); if(responseList != null){ for (String thisResponse : responseList) { JSONObject object = null; try { object = new JSONObject(thisResponse); String sku = object.getString("productId"); String price = object.getString("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<AvailablePurchase> all, String 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(String 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<UserPurchaseItems> extractAllUserPurchase(Bundle ownedItems){ List<UserPurchaseItems> mUserItems = new ArrayList<UserPurchaseItems>(); int response = ownedItems.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); String continuationToken = ownedItems.getString("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); } } }
Erstellen Sie ein Hilfspaketverzeichnis
Erstellen Sie einen neuen Paketordner und nennen Sie ihn Helfer. Erstellen Sie im Paket eine neue Java-Datei Helper.java.
Helper.java
public class Helper { public static final String ITEM_ID_LIST = "ITEM_ID_LIST"; public static final String ITEM_ONE_ID = "productone"; public static final String ITEM_TWO_ID = "producttwo"; public static final String ITEM_THREE_ID = "productthree"; public static final int RESPONSE_CODE = 1001; public static final String SHARED_PREF = "shared_pref"; public static final String DEVELOPER_PAYLOAD = "developer_payload"; public static final String PURCHASE_TOKEN = "purchase_token"; public static void displayMessage(Context context, String message){ Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG).show(); } }
Testen des In-App-Abrechnungskaufs
Fehler, die beim Testen des In-App-Kaufs auftreten können
Der von Ihnen angeforderte Artikel kann nicht gekauft werden
Lösung - Laut AndreiBogdan in Stackoverflow ,
Android Developer Blog empfiehlt außerdem eine Schulungsklasse zum Verkauf von In-App-Produkten. In diesem Tutorial finden Sie eine vollständige Implementierung und Informationen zum Testen der Anwendung: Verkauf von In-App-Produkten
quelle
customSharedPreference
Klasse?Wenn Sie eine einfache Bibliothek zum Veröffentlichen in Google Play und im Amazon Appstore verwenden möchten , können Sie sich für RoboBillingLibrary entscheiden . Es abstrahiert die Details von beiden in einer benutzerfreundlichen Bibliothek. Detaillierte Anweisungen finden Sie auf der Github-Seite.
quelle
Okay, dies ist eines der Dinge, für die online nicht viel Dokumentation verfügbar ist. Deshalb werde ich mein Bestes geben, um alles Schritt für Schritt zu erklären. Entnommen aus meinem Blog-Beitrag, der eine detailliertere Version davon (mit Screenshots) ist, hier auf The Millibit . Ohne weiteres,
Erster Schritt: Berechtigungen Dies ist der einfachste Schritt. Navigieren Sie zu Ihrer Datei manifest.xml und fügen Sie die folgende Zeile unter Ihrem Tag hinzu:
<uses-permission android:name="com.android.vending.BILLING" />
Dadurch erhält Ihre App die Berechtigung, auf die In-App-Abrechnung zuzugreifen. Wenn Sie auf Versionen über API 22 abzielen, müssen Sie sicherstellen, dass diese Berechtigung zur Laufzeit erteilt wird.
Schritt 2: Play Console Jetzt müssen Sie Ihre App auf die Google Play Console hochladen. Wir veröffentlichen unsere App noch nicht für die Öffentlichkeit (keine Sorge). Wir laden sie lediglich in den Bereich BETA RELEASE hoch, in dem wir In-App-Käufe testen können. Der Grund dafür ist, dass Google eine Version Ihrer APK hochladen muss, damit die Abrechnungsprozesse tatsächlich funktionieren.
Gehen Sie zu https://play.google.com/apps/publish/
Erstellen Sie die Anwendung
Befolgen Sie die Schritte, um Ihre App einzurichten
Gehen Sie zu App-Versionen
Navigieren Sie zu Beta
Erstellen Sie eine APK Ihrer App in Android Studio und laden Sie sie in die Beta-Produktion in der Play Console hoch
(Stellen Sie vor der Veröffentlichung sicher, dass Sie die Store-Liste, die Inhaltsbewertung sowie die Preisgestaltung und Verteilung bereits ausgefüllt haben.)
Schritt 3: Projekt einrichten Okay, dies ist der Teil, in dem Sie eine Reihe von Dateien kopieren und einfügen müssen.
Nehmen Sie zuerst diese Datei, laden Sie sie herunter und platzieren Sie sie unter.
src/main
Sie sollte sich in einen Ordner einfügen. Nehmen Sie als Nächstes den gesamten util-Ordner und fügen Sie ihn ein. Erstellen Siesrc/java folder.
dann Ihr Projekt neu, um Fehler zu beheben. Der Util-Ordner enthält die folgenden Klassen:Schritt vier: Produkte erstellen
Verwaltetes Produkt erstellen
Klicken Sie auf Speichern und erstellen Sie eine "Preisvorlage".
Hier wählen Sie den Preis für dieses Produkt aus. Sie können den Preis für verschiedene Länder auswählen oder ihn automatisch anpassen lassen, wenn Sie nur alle Länder unter Ihrem Preis auswählen:
Notieren Sie abschließend die ID Ihres Produkts. Wir werden diese ID in den nächsten Schritten verwenden.
Gehen Sie zu "Services & APIs" und holen Sie sich Ihren Base64EncodedString. Kopieren Sie diese und fügen Sie sie irgendwo in einen Notizblock ein, damit Sie darauf zugreifen können. Teilen Sie dies nicht mit anderen, sie werden in der Lage sein, böswillige Dinge damit zu tun.
Fünfter Schritt: Endlich! Wir können mit dem Codieren beginnen: Wir binden zuerst an die In-App-Abrechnungsbibliothek und fragen ab, was der Benutzer gekauft hat / nicht gekauft hat. Dann kaufen wir das Produkt, das wir zuvor eingerichtet haben.
Importieren Sie zunächst alles, was wir zuvor eingerichtet haben:
import util.*;
Jetzt werden wir ein IabHelper-Objekt namens mHelper verwenden und alles damit machen.
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(); } } } });
Okay, lassen Sie mich zusammenfassen, was hier los ist. Grundsätzlich rufen wir "startSetup" auf, um unseren "IabHelper" zu initialisieren. Wenn das Setup erfolgreich ist, fragen wir ab, welche Käufe der Benutzer bereits hat, und speichern die Antworten in
mGotInventoryListener
, die wir als Nächstes codieren: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"); } } } };
Der obige Code ist ziemlich selbsterklärend. Grundsätzlich wird nur geprüft, welche Einkäufe der Benutzer bereits hat. Jetzt, da wir wissen, ob der Benutzer unser Produkt bereits gekauft hat oder nicht, wissen wir, ob wir ihn bitten sollen, unseren Artikel zu kaufen! Wenn sie unser Produkt noch nie gekauft haben, starten wir eine Kaufanfrage:
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; } } };
Hier kaufen wir den Artikel (mit der ID, die wir zuvor in der Spielkonsole generiert haben) wie folgt:
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
Beachten Sie, dass wir
mPurchaseFinishedListener
die Parameter übergeben haben. Dies bedeutet, dass das Ergebnis des Kaufs an diesen Hörer zurückgegeben wird. Dann prüfen wir einfach, ob der Kauf null ist, und geben dem Benutzer, falls nicht, die von ihm gekaufte Funktion.Lass die Zuhörer nicht lecken! Wir müssen sie zerstören, wenn die App zerstört.
@Override public void onDestroy() { super.onDestroy(); if (mHelper != null) try { mHelper.dispose(); mHelper = null; } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } }
Wenn Sie Ihren Einkauf konsumieren und ihn wieder zum Kauf anbieten möchten, können Sie dies ganz einfach tun. Ein Beispiel hierfür ist, wenn ein Benutzer Benzin für ein virtuelles Auto gekauft hat und es ausgegangen ist. Sie müssen dasselbe Produkt erneut kaufen, und Sie können es für einen zweiten Kauf verfügbar machen, indem Sie es konsumieren:
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(); } }
Das ist es! Sie können jetzt anfangen, Geld zu verdienen. So einfach ist das wirklich!
Wenn Sie eine detailliertere Version dieses Tutorials mit Screenshots und Bildern wünschen, besuchen Sie den Originalbeitrag hier . Lassen Sie mich in den Kommentaren wissen, wenn Sie weitere Fragen haben.
quelle
error: flagEndAsync() is not public in IabHelper; cannot be accessed from outside package
.Unable to destroy activity java.lang.IllegalArgumentException: Service not registered.
Es passiert, wenn Sie lange auf die Systemschaltfläche "Switching Applications" im Emulator tippen.Weitere Informationen zur Funktionsweise der In-App-Abrechnung mithilfe der Google Play-Abrechnungsbibliothek finden Sie im folgenden Flussdiagramm:
Sie können die Integration Schritt für Schritt verfolgen, die ich in diesem Artikel erklärt habe:
https://medium.com/@surabhichoudhary/in-app-purchasing-with-google-play-billing-library-6a72e289a78e
Wenn Sie hierfür eine Demo benötigen, ist dies der Projektlink: https://github.com/surabhi6/InAppPurchaseDemo
quelle
Ich habe eine Android In App-Abrechnungsbibliothek entwickelt, die "com.android.billingclient: billing: 2.1.0" verwendet.
Hier sind seine Eigenschaften:
Bibliotheksquelle
quelle