CTO at Unnyhog, Backend/Game-dev (Java/C#/Nodejs)
0,1
рейтинг
23 ноября 2013 в 16:53

Разработка → Android In-app purchasing: платное отключение рекламы в своём приложении recovery mode

Много раз уже просили написать статью о том, как в приложении реализовать платное отключение рекламы. По In-app уже были статьи на хабре. Правда, они старую версию API рассматривали. В принципе, новая версия не особо то и отличается от старой. Была похожая статья, но там больше именно про отображение рекламы рассказывалось, а второй части статьи мы так и не увидели. Как оказалось, многим до сих пор интересен этот вопрос, решил написать как это реализовать в своём приложении.

In-App Purchase представляет собой сервис покупки виртуальных товаров внутри приложения (например игровой валюты, новых уровней и т.д.). Применяется он в основном в играх, в тех случаях, когда встает вопрос о необходимости заработка на своем творении.

В данной статье рассмотрю как можно использовать In-App Purchase для отключения рекламы в своём приложении.

Реклама в приложении


В принципе, можно взять любую площадку. Возьмём, к примеру AdMob. Я для удобства обычно подобные вещи в обёртки запихиваю, чтобы при смене площадки, если потребуется, почти ничего не пришлось менять. Обёртки для рекламной площадки должны реализовывать интерфейс:
public interface AdsControllerBase {
        
        public void createView( RelativeLayout layout);
        public void show(boolean show);
        public void onStart();
        public void onDestroy();
        public void onResume();
        public void onStop();
}


Тогда обёртка для AdMob будет выглядеть примерно так:
Обёртка для AdMob
public class AdMobController implements AdsControllerBase, AdListener {
	private static final String ADMOB_ID = "ваш_идентификатор_из_AdMob";
	private static final int REQUEST_TIMEOUT = 30000;
	private AdView adView;
	private Context c;
	private long last;

	public AdMobController(Context activity, RelativeLayout layout) {
		this.c = activity;
		createView(layout);
		last = System.currentTimeMillis() - REQUEST_TIMEOUT;
	}

	public void createView(RelativeLayout layout) {
		if(PreferencesHelper.isAdsDisabled()) return;
		adView = new AdView((Activity) c, AdSize.BANNER, ADMOB_ID);
		RelativeLayout.LayoutParams adParams = new RelativeLayout.LayoutParams(
				RelativeLayout.LayoutParams.WRAP_CONTENT,
				RelativeLayout.LayoutParams.WRAP_CONTENT);
		adParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		adParams.addRule(RelativeLayout.CENTER_HORIZONTAL);

		adView.setAdListener(this);

		layout.addView(adView, adParams);

		adView.loadAd(new AdRequest()); 
	}
	
	// обновляем рекламу не чаще, чем раз в 30 секунд
	public void show(boolean show) {
		if(adView == null) return;	
		adView.setVisibility((show) ? View.VISIBLE : View.GONE);
		if (show && (System.currentTimeMillis() - last > REQUEST_TIMEOUT)) {
			last = System.currentTimeMillis();
			adView.loadAd(new AdRequest());
		}
	}

	@Override
	public void onReceiveAd(Ad ad) {}

	@Override
	public void onFailedToReceiveAd(Ad ad, AdRequest.ErrorCode error) {}

	@Override
	public void onPresentScreen(Ad ad) {}

	@Override
	public void onDismissScreen(Ad ad) {}

	@Override
	public void onLeaveApplication(Ad ad) {}

	@Override
	public void onStart() {}

	@Override
	public void onDestroy() {}

	@Override
	public void onResume() {}

	@Override
	public void onStop() {}

}



Тогда инициализация рекламы будет такой:
AdsControllerBase ads = new AdMobController(this, layout);


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

In-app purchasing или внутренние платежи в приложениях


Для того, чтобы работать с системой покупок необходим файл IMarketBillingService.aidl. Лежит он в /user/android-sdk-linux/extras/google/play_billing директории с SDK. Положить файлик надо в com.android.vending.billing пакет вашего приложения.

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

Для работы необходимо добавить разрешение в AndroidManifest.xml:
<uses-permission android:name=«com.android.vending.BILLING»/>

Очень помогает официальная документация и пример из SDK.

Необходимо определить ключик в приложении – PublicKey, полученный при регистрации аккаунта на Android Market
Определяем IabHelper и инициализируем. Если удачно, то пытаемся восстановить покупки.

// id вашей покупки из админки в Google Play
static final String SKU_ADS_DISABLE = "com.ads.disable";
IabHelper mHelper;
private void billingInit() {
		mHelper = new IabHelper(this, BASE64_PUBLIC_KEY);

		// включаем дебагинг (в релизной версии ОБЯЗАТЕЛЬНО выставьте в false)
		mHelper.enableDebugLogging(true);

		// инициализируем; запрос асинхронен 
		// будет вызван, когда инициализация завершится
		mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
			public void onIabSetupFinished(IabResult result) {
				if (!result.isSuccess()) {
					return;
				}

				// чекаем уже купленное
				mHelper. queryInventoryAsync(mGotInventoryListener);
			}
		});
	}



mGotInventoryListener – слушатель для восстановления покупок.
// Слушатель для востановителя покупок.
	IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
		private static final String TAG = "QueryInventoryFinishedListener";

		public void onQueryInventoryFinished(IabResult result,
				Inventory inventory) {
			LOG.d(TAG, "Query inventory finished.");
			if (result.isFailure()) {
				LOG.d(TAG, "Failed to query inventory: " + result);
				return;
			}

			LOG.d(TAG, "Query inventory was successful.");

			/*
			 * Проверяются покупки. 
			 * Обратите внимание, что надо проверить каждую покупку, чтобы убедиться, что всё норм! 
			 * см. verifyDeveloperPayload().
			 */
			
			Purchase purchase = inventory.getPurchase(SKU_ADS_DISABLE);
			PreferencesHelper.savePurchase(
							context,
					PreferencesHelper.Purchase.DISABLE_ADS,
							purchase != null && verifyDeveloperPayload(purchase));
			ads.show(!PreferencesHelper.isAdsDisabled());

		}
	};


Теперь надо, собственно, саму покупку реализовать:

private void buy(){
		if(!PreferencesHelper.isAdsDisabled()){
			/* для безопасности сгенерьте payload для верификации. В данном примере просто пустая строка юзается.
			 * Но в реальном приложение подходить к этому шагу с умом. */
		    String payload = ""; 
			mHelper.launchPurchaseFlow(this, SKU_ADS_DISABLE, RC_REQUEST, 
		               mPurchaseFinishedListener, payload);
		}
	}


SKU_ADS_DISABLE – идентификатор товара, который вы создали в адмике Google Play. mPurchaseFinishedListener – слушатель:
// слушатель завершения покупки
	IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
		public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
			if (result.isFailure()) {
				return;
			}
			if (!verifyDeveloperPayload(purchase)) {
				return;
			}

			if (purchase.getSku().equals(SKU_ADS_DISABLE)) {
				Toast.makeText(getApplicationContext(), "Purchase for disabling ads done.", Toast.LENGTH_SHORT);
				// сохраняем в настройках, что отключили рекламу
				PreferencesHelper.savePurchase(
								context,
								PreferencesHelper.Purchase.DISABLE_ADS,
								true);
				// отключаем рекламу
				ads.show(!PreferencesHelper.isAdsDisabled());
			}

		}
	};


Стоит отдельно поговорить о методе по верификации:
boolean verifyDeveloperPayload(Purchase p) {
		String payload = p.getDeveloperPayload();
		/*
		 * TODO: здесь необходимо свою верификацию реализовать
		 * Хорошо бы ещё с использованием собственного стороннего сервера.
		 */

		return true;
	}


Сейчас нет никакой проверки покупок, но в реальном приложении вы должны сверять полученные данные с той сгенерированой строкой, что вы отправили в запросе на покупку. Проверять это надо на своём стороннем сервере. Для обычно приложения или офф-лайн игры это может и не критично, но для он-лайн игры это очень важно.

В принципе всё, теперь при запуске приложения просиходит проверка настроек (куда мы сохранили, что отключили рекламу):
PreferencesHelper.loadSettings(this);

После чего реклама уже не будет показываться.

Тестирование покупок


Сейчас довольно удобно тестировать своё приложение. Можно залить .apk как альфа/бета версию и опубликовать. При этом можно назначить группу в Google+, которая будет иметь возможность к тестированию. Если вы публикуете альфа или бета версию приложения, то в маркете она не появится, иметь доступ будет только эта группа.


Тестеры смогут осуществлять покупки. Деньги будут списываться без комиссии и будут возвращены после 15 минут после покупки. Так что, не беспокойтесь. Вот только у вас не получится протестировать приложение, если ваш аккаунт на устройстве и аккаунт издателя один и тот же =/

Полностью рабочий пример можете посмотреть на гитхабе.
CTO at Unnyhog, Backend/Game-dev (Java/C#/Nodejs)
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (18)

  • +1
    Люди скорее будут бесплатно отключать интернет, чем платить за отключение рекламы.
    • 0
      Вы ошибаетесь, у многих хороших приложений существуют 2 версии в Google Play: бесплатная с рекламой и платная — без. Приблизительная тенденция — один процент установок платной версии по отношению к бесплатной, что весьма неплохо, если у вас аудитория около 100к.

      Пруф:
      iCube+
      iCube
      • 0
        Я бы сказал, что «очень даже неплохо», так как платная версия iCube принесла больше, чем бесплатная.
        Да, реклама в бесплатной версии может в перспективе принести больше, но пока заметно меньше приносит дохода.

        К тому же, в платной версии больший процент In-App, чем в бесплатной)
        • +2
          Платная + IAP? Да это же обдираловка :) Обычно люди не готовы покупать платные версии, но готовы сами себя обманывать, массово делая IAP покупки на более мелкие суммы, которые в итоге получаются на гораздо большую сумму.
          • 0
            Я сам такого же мнение. Считаю, что если я купил игру, то уже всё должно быть включено.
            Но у нас In-App необязательны. В кубике, к примеру, In-app два: на разблок всех уровней, на отмену времени на использование помощника.
            И как показала практика, люди купившие игру, готовы покупать и In-app, по сравнение с теми, кто играет в бесплатную версию.

            Поэтому, для теста следующую игру планируем запустить только в виде платной версии.
            • 0
              Тут есть опасность слива кармы приложения возмущенными игроками «как?! я купил, так она еще и денег требует?!». Такое существует сплошь и рядом, даже на бесплатных приложениях с покупкой монеток через IAP. Как пример (стоит почитать комментарии): play.google.com/store/apps/details?id=com.preferansgame
              • 0
                Да, есть такое. И что забавно, 90%+ из них — это русскоязычные пользователи.
                Так что, если у вас игра/приложение нацелены именно на эту группу пользователей, то можете получить слив.

                Но всё равно, если игра хорошая, то на общем фоне эти сливы сглаживаются.
                • +2
                  Могу даже сказать больше — рунетовская аудитория apple store еще хуже в этом плане. Они там считают, что раз ябблы, то все должно быть сделано просто идеально, включая все бесплатное. Если игроку не понравился звук появления бонуса, он смело ставит «1», если показалось, что что-то выбивается из его видения — ставит ту же оценку. Платежеспособность гораздо хуже аудитории гуглоплея — на рекламу у них нет дешевого интернета, а на покупки — привязанной карты, ибо много кому железо было просто подарено как статусная игрушка. Рунет стоит или обходить стороной или включать его в распространение как вторичный рынок, как бы это непатриотично ни звучало.
      • 0
        Вообще-то разговор был про отключение интернета и автоматическое отключение рекламы без необходимости что-то делать дополнительно, причем тут соотношение «платная / бесплатная»?
        • –1
          Люди скорее будут бесплатно отключать интернет, чем платить за отключение рекламы.


          В том, что люди думают иначе.
          И подтверждение этому — корневой коммент.
          • +1
            Наверное, вы живете в параллельной вселенной, где мобильный интернет дешевый и работает везде. Если есть 2 слота под симки и есть вторая симка со спецтарифом под интернет — замечательно. Можно сказать, что да, есть wifi и т.п, что превращает мобильную железку в еще один стационар, но не важно. На просторах рунета очень сильно распространена тема замены стоковых прошивок на «кастомы» (тот же цианоген, либо его порты под конкретные железки), так вот там уже с ходу патченный hosts с прошитыми рекламными сетями на localhost. Да, существуют приложения, которые проверяют доступность и нелокальность банерных сетей и отказываются работать в противном случае. Что в итоге наступает? Совершенно верно — слив кармы приложения.
    • 0
      Если преподнести это как помощь разработчику, то почему нет?
      • +1
        Донат в принципе нежизнеспособная идея в условиях рунета, к сожалению. К тому же часто выплевывалась реклама с вирусней, по которой много не накликаешь безнаказанно — только неделю назад можно стало мониторить показ контента, причем бан нужно делать вручную.
        • 0
          Недавние изменения в AdMob конечно радуют, но фильтровать это всё вручную то ещё занятие…
          • 0
            Ну выбор не велик — либо полное выпиливание банеров, либо фильтрование. Но зловредов не так много, всего около 15-20, через какое-то время показы становятся чистыми.
        • 0
          Я тут на одном своём русскоязычном приложении(игре) поэксперементировал в плане доната. На мой взгляд — работает.
          • 0
            А можете привести примерное соотношение проданативших игроков к общей массе? Если игроков пару сотен тысяч и если один процент будет донатить, то это замечательно, если тот же 1 процент на 100 игроках, то не очень.
            • 0
              Нет, процент значительно ниже) Скорее он 0,1%. Но я считаю, что для нашего региона это нормальный показатель.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.