Как реализовать биллинг в приложении для Android?


кажется, что это довольно сложно реализовать в приложении биллинга в Android приложение. Как я мог это сделать? Пример приложения из SDK имеет только одно действие, что упрощает его для такого приложения, как mine, которое имеет несколько действий.

6 60

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) ... Хорошо, как первый "привет Биллинг"..

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

для биллинга в приложении 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();
    }
}

Тестирование В Приложении Биллинг Покупки

  1. создайте учетную запись Google+ (не используйте учетную запись main)
  2. добавьте пользователей, которые будут тестировать приложение в вашей группе или сообществе.

ошибки, с которыми вы можете столкнуться во время тестирования покупки в приложении

товар, который вы запросили, недоступен для покупки

решение по словам Андрейбогдана в Stackoverflow,

все Inducesmile за его учебник

блог разработчика Android также рекомендует учебный класс по продаже продуктов в приложении. Чтобы увидеть полную реализацию и узнать, как протестировать приложение, пожалуйста, проверьте этот учебник: продажа продуктов в приложении

ладно, это одна из тех вещей, которые не очень много документации, доступной в интернете, поэтому я собираюсь сделать все возможное, чтобы объяснить все шаг за шагом. Взято из моего блога, который является более подробной версией этого (со скриншотами),здесь на Миллибит. Без дальнейших церемоний,

Шаг Первый: разрешения Это самый простой шаг. Перейдите к своему манифесту.xml-файл и добавить следующую строку под вашим тег:

<uses-permission android:name="com.android.vending.BILLING" />

это даст вашему приложению разрешения на доступ к биллингу в приложении. Если вы нацелены на версии выше API 22, Вам нужно будет убедиться, что это разрешение предоставляется во время выполнения.

Шаг Второй: Play Консоль Теперь вам нужно загрузить приложение на консоль Google Play. Мы еще не публикуем наше приложение для общественности (не волнуйтесь), мы просто загружаем его в раздел бета-версии, который позволит нам тестировать покупки в приложении. Причина, по которой нам нужно это сделать, заключается в том, что Google должен иметь некоторую версию вашего APK, загруженную для процессов выставления счетов, чтобы фактически работать.

  1. перейти к https://play.google.com/apps/publish/

  2. создать приложение

  3. выполните следующие действия, чтобы настроить приложение

  4. перейти к выпускам приложений

  5. перейдите к Бета

  6. создайте APK вашего приложения в Android studio и загрузите его в бета-версию в Play Console

(перед выпуском убедитесь, что вы уже заполнили список магазинов ,рейтинг контента и цены и распределение)

  1. нажмите волшебную кнопку (Опубликовать!)

Шаг Третий: Проект Установки Хорошо это часть, где вы должны скопировать и вставить куча файлов.

во-первых, захватите этой файл, загрузите его и поместите под src/mainон должен встроиться в папку Далее, хватай все это util папка и вставьте его в src/java folder. затем перестроить проект для устранения ошибок. Папка Util Содержит Следующие Классы:

  • IabBroadcastReceiver
  • IabException
  • IabHelper
  • IabResult
  • инвентаризации
  • купить
  • безопасность
  • SkuDetails

Шаг Четвертый: Создать Продукты

  1. Создать Управляемый Продукт

  2. Нажмите кнопку Сохранить и сделать "шаблон цены"

здесь вы выберете цену этого продукта. Вы можете выбрать цену для разных стран или настроить ее автоматически, если вы просто выберете все страны по вашей цене:

  1. убедитесь, что продукт в приложении активирован и связан с правильным приложением в консоли в последний раз.

наконец, обратите внимание на ID вашего продукта. Мы будем использовать этот идентификатор в следующих нескольких шагах.

  1. получить 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();
    }
}

вот это! Теперь вы можете начать зарабатывать деньги. Это действительно так просто!

опять же, если вы хотите более подробную версию этого учебника, со скриншотами и картинками, посетите оригинальный пост здесь. Дайте мне знать в комментариях, если у вас есть еще вопросы.