Как реализовать биллинг в приложении для Android?
кажется, что это довольно сложно реализовать в приложении биллинга в Android приложение. Как я мог это сделать? Пример приложения из SDK имеет только одно действие, что упрощает его для такого приложения, как mine, которое имеет несколько действий.
6 ответов:
Ну, я попытаюсь объяснить, что я испытал. Я не считаю себя экспертом в этом, но я сломал голову несколько дней.
для начала, у меня было очень плохое время, пытаясь понять рабочий процесс примера и приложения. Я подумал, что лучше начать с простого примера, однако его очень трудно разделить код на мелкие кусочки и не зная, если вы что-то нарушаете. Я расскажу вам, что у меня есть и что я изменил из примера, чтобы сделать это работает.
у меня есть один вид деятельности, где все мои покупки пришли. Это называется Pro.
во-первых, вы должны обновить переменную base64EncodedPublicKey в своем классе безопасности с помощью ключа разработчика public Market, или вы увидите хорошее исключение.
Ну, я привязываю свою деятельность к моему 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(); }
таким образом, все покупки говорят с этой службой, которая затем отправит запросы JSON на рынок. Можно подумать что покупки совершаются в одно и то же время, но нет. Вы отправляете запрос, и покупка может прийти через несколько минут или часов. Я думаю, что это в основном для перегрузки сервера и одобрения кредитных карт.
затем у меня есть ListView с моими товарами, и я открываю AlertDialog на каждом из них, приглашая их купить товар. Когда они нажимают на элемент, я делаю это:
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(); } } }
хорошо, вы должны увидеть, что рынок открывается и пользователь завершает или отменяет покупку.
что тогда важно-это мой PurChaseObserver, который обрабатывает все события, которые отправляет рынок. Это урезанная версия, но вы должны получить точку (см. мои комментарии через код):
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 } } }
и я считаю, что вам не нужно ничего редактировать. Остальная часть кода "работает". Вы можете попробовать использовать образец SKU сначала в своих собственных элементах "android.тест.приобретенный." До сих пор я проверил это, и это работает, однако мне все еще нужно охватить все, как возвращенное состояние. В этом случае я позволяю пользователю сохранить функции, но я хочу убедиться, что он работает идеально, прежде чем изменять его.
Я надеюсь, это поможет вам и другим.
V3: вот учебник для быстрого начала.. Он использует вспомогательные классы из примера google (Trivial Drive) ... Хорошо, как первый "привет Биллинг"..
для биллинга в приложении v3, я нашел это действительно helpful.
http://blog.blundellapps.com/simple-inapp-billing-payment-v3/
Если вы хотите использовать легкую библиотеку для публикации в Google Play и Amazon Appstore, вы можете пойти с RoboBillingLibrary. Он абстрагирует детали обоих в одну простую в использовании библиотеку. Подробные инструкции находятся на странице Github.
есть полный пример Android в приложении Биллинг v3 шаг за шагом с скриншотом. Пожалуйста, проверьте учебник: Android в приложении Биллинг v3 с помощью класса ServiceConnection
надеюсь, что это поможет.
для получения дополнительных разъяснений, пройдите через этот учебник:реализация биллинга в приложении в версии 3 API
шаги, чтобы следовать, чтобы интегрировать библиотеку биллинга в приложении в нашем проекте
обновление ваш AndroidManifest.XML-файл.
создать ServiceConnection и привязать его к IInAppBillingService.
отправка запросов на выставление счетов в приложении из вашего приложения в IInAppBillingService.
обрабатывать ответы в приложении биллинга от Google Play.
Обновление AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
добавить разрешения в Манифесте.xml-файл
добавление файла AIDL в ваш проект
создать приложение. Вы должны увидеть сгенерированный файл с именем IInAppBillingService.java в каталоге /gen вашего проекта.
обновить зависимости в сборке.gradle file
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 и activity_in_app_purchase.xml
Это где предложит нашим пользователям приложение возможность сделать покупку в приложении. В файле макета, мы будем дайте пользователю возможность совершать покупки в разных номиналах.
InAppPurchaseActivity.java
Примечание: getAllUserPurchase () и itempurchaseavailability () методы должны вызываться в потоке без пользовательского интерфейса, чтобы избежать сбоя приложения.
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); } } }
Создать Каталог Вспомогательных Пакетов
создайте новую папку пакета и назовите ее помощники. Внутри пакета создайте новый файл java Помощник.Ява.
помощником.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(); } }
Тестирование В Приложении Биллинг Покупки
- создайте учетную запись Google+ (не используйте учетную запись main)
- добавьте пользователей, которые будут тестировать приложение в вашей группе или сообществе.
ошибки, с которыми вы можете столкнуться во время тестирования покупки в приложении
товар, который вы запросили, недоступен для покупки
решение по словам Андрейбогдана в Stackoverflow,
все Inducesmile за его учебник
блог разработчика Android также рекомендует учебный класс по продаже продуктов в приложении. Чтобы увидеть полную реализацию и узнать, как протестировать приложение, пожалуйста, проверьте этот учебник: продажа продуктов в приложении
ладно, это одна из тех вещей, которые не очень много документации, доступной в интернете, поэтому я собираюсь сделать все возможное, чтобы объяснить все шаг за шагом. Взято из моего блога, который является более подробной версией этого (со скриншотами),здесь на Миллибит. Без дальнейших церемоний,
Шаг Первый: разрешения Это самый простой шаг. Перейдите к своему манифесту.xml-файл и добавить следующую строку под вашим тег:
<uses-permission android:name="com.android.vending.BILLING" />
это даст вашему приложению разрешения на доступ к биллингу в приложении. Если вы нацелены на версии выше API 22, Вам нужно будет убедиться, что это разрешение предоставляется во время выполнения.
Шаг Второй: Play Консоль Теперь вам нужно загрузить приложение на консоль Google Play. Мы еще не публикуем наше приложение для общественности (не волнуйтесь), мы просто загружаем его в раздел бета-версии, который позволит нам тестировать покупки в приложении. Причина, по которой нам нужно это сделать, заключается в том, что Google должен иметь некоторую версию вашего APK, загруженную для процессов выставления счетов, чтобы фактически работать.
перейти к https://play.google.com/apps/publish/
создать приложение
выполните следующие действия, чтобы настроить приложение
перейти к выпускам приложений
перейдите к Бета
создайте APK вашего приложения в Android studio и загрузите его в бета-версию в Play Console
(перед выпуском убедитесь, что вы уже заполнили список магазинов ,рейтинг контента и цены и распределение)
- нажмите волшебную кнопку (Опубликовать!)
Шаг Третий: Проект Установки Хорошо это часть, где вы должны скопировать и вставить куча файлов.
во-первых, захватите этой файл, загрузите его и поместите под
src/main
он должен встроиться в папку Далее, хватай все это util папка и вставьте его вsrc/java folder.
затем перестроить проект для устранения ошибок. Папка Util Содержит Следующие Классы:
- IabBroadcastReceiver
- IabException
- IabHelper
- IabResult
- инвентаризации
- купить
- безопасность
- SkuDetails
Шаг Четвертый: Создать Продукты
Создать Управляемый Продукт
Нажмите кнопку Сохранить и сделать "шаблон цены"
здесь вы выберете цену этого продукта. Вы можете выбрать цену для разных стран или настроить ее автоматически, если вы просто выберете все страны по вашей цене:
- убедитесь, что продукт в приложении активирован и связан с правильным приложением в консоли в последний раз.
наконец, обратите внимание на ID вашего продукта. Мы будем использовать этот идентификатор в следующих нескольких шагах.
- получить Base64EncodedString
перейдите в раздел "службы и API" и возьмите Base64EncodedString. Скопируйте и вставьте это в блокнот где-нибудь, чтобы у вас был доступ к нему. Не делитесь этим ни с кем, они смогут делать с ним злые вещи.
Шаг Пятый: наконец-то! Мы можем начать кодирование: Сначала мы свяжемся с библиотекой биллинга в приложении и запросим, что пользователь купил/не купил. Затем мы купим продукт, который мы создали ранее.
во-первых, импортировать все, что мы создали ранее:
import util.*;
теперь мы будем использовать объект IabHelper под названием mHelper, и мы будем делать все с помощью этот.
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(); } } } });
хорошо, позвольте мне разобрать, что здесь происходит. В основном, мы называем "запуска программы установки" проинициализировать наш "IabHelper". Если установка прошла успешно, мы запрашиваем, какие покупки у пользователя уже есть, и сохраняем ответы в
mGotInventoryListener
, который мы будем кодировать следующим образом: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"); } } } };
приведенный выше код довольно понятен. В принципе, он просто проверяет, какие покупки у пользователя уже есть. Теперь, когда мы знаем, приобрел ли пользователь уже наш продукт, мы знаем, стоит ли просить их приобрести наш товар! Если они никогда не покупали наш продукт раньше, давайте начнем запрос на покупку:
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; } } };
здесь мы покупаем товар (с идентификатором, который мы создали в игровой консоли ранее) со следующим:
mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener
обратите внимание, что мы передали
mPurchaseFinishedListener
в параметры. Это означает, что результат покупки будет возвращен этому слушателю. Затем мы просто проверяем, является ли покупка нулевой, а если нет, присудите пользователю с любой функцией, которую они купили.не позволяйте слушателям течь! Мы должны уничтожить их, когда приложение уничтожает.
@Override public void onDestroy() { super.onDestroy(); if (mHelper != null) try { mHelper.dispose(); mHelper = null; } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } }
наконец, если вы хотите использовать свою покупку, сделав ее доступной для покупки снова, вы можете сделать это легко. Например, если пользователь купил бензин для виртуального автомобиля, и он закончился. Они должны приобрести тот же продукт снова, и вы можете сделать его доступным для второй покупки, потребляя его:
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(); } }
вот это! Теперь вы можете начать зарабатывать деньги. Это действительно так просто!
опять же, если вы хотите более подробную версию этого учебника, со скриншотами и картинками, посетите оригинальный пост здесь. Дайте мне знать в комментариях, если у вас есть еще вопросы.