Анализируйте отказы от покупки товаров в Play Billing

1. Введение

В этой лабораторной работе вы сосредоточитесь на создании одноразового продукта, интеграции своего приложения с Play Billing Library (PBL) и анализе причин прекращения покупок.

Аудитория

Эта лабораторная работа предназначена для разработчиков приложений для Android, которые используют библиотеку Play Billing Library (PBL) или хотят использовать PBL для монетизации своих одноразовых продуктов.

Чему вы научитесь...

  • Как создавать одноразовые продукты в Google Play Console.
  • Как интегрировать ваше приложение с PBL.
  • Как обрабатывать единовременные закупки расходных и нерасходных товаров в PBL.
  • Как проанализировать отказы от покупок.

Что вам понадобится...

2. Создайте пример приложения

Пример приложения разработан как полнофункциональное приложение для Android, имеющее полный исходный код, демонстрирующий следующие аспекты:

  • Интеграция приложения с PBL
  • Получить одноразовые продукты
  • Запуск потоков закупок для одноразовых продуктов
  • Сценарии покупки, которые приводят к следующим ответам по счетам:
    • BILLING_UNAVAILABLE
    • USER_CANCELLED
    • OK
    • ITEM_ALREADY_OWNED

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

Предварительные условия

Перед сборкой и развертыванием примера приложения выполните следующие действия:

Строить

Целью этого этапа сборки является создание подписанного файла пакета приложения Android для примера приложения.

Чтобы создать пакет приложения Android, выполните следующие действия:

  1. Загрузите пример приложения с GitHub .
  2. Соберите пример приложения . Перед сборкой измените имя пакета примера приложения, а затем выполните сборку. Если в Play Console есть пакеты других приложений, убедитесь, что имя пакета, указанное для примера приложения, уникально.

    Примечание : при сборке примера приложения создаётся только APK-файл, который можно использовать для локального тестирования. Однако при запуске приложения не будут загружены товары и цены, поскольку они не настроены в Play Console, что мы сделаем далее в этой практической работе.
  3. Создайте подписанный пакет приложений для Android.
    1. Сгенерируйте ключ загрузки и хранилище ключей
    2. Подпишите свое приложение с помощью ключа загрузки
    3. Настроить подписание приложения Play

Следующий шаг — загрузить пакет приложения Android в Google Play Console.

3. Создайте одноразовый продукт в Play Console.

Для создания одноразовых продуктов в Google Play Console необходимо иметь приложение в Play Console. Создайте приложение в Play Console, а затем загрузите ранее созданный подписанный пакет приложений.

Создать приложение

Чтобы создать приложение:

  1. Войдите в Google Play Console , используя свою учетную запись разработчика.
  2. Нажмите «Создать приложение» . Откроется страница «Создать приложение» .
  3. Введите название приложения, выберите язык по умолчанию и другие сведения, связанные с приложением.
  4. Нажмите «Создать приложение» . Это создаст приложение в Google Play Console.

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

Загрузите подписанный пакет приложения

  1. Загрузите подписанный комплект приложений во внутреннюю тестовую версию Google Play Console . Только после загрузки вы сможете настроить функции монетизации в Play Console.
  2. Нажмите Тестирование и выпуск > Тестирование > Внутренний выпуск > Создать новый выпуск .
  3. Введите название релиза и загрузите подписанный файл пакета приложения.
  4. Нажмите кнопку «Далее» , а затем нажмите кнопку «Сохранить и опубликовать» .

Теперь вы можете создавать свои одноразовые продукты.

Создать одноразовый продукт

Чтобы создать одноразовый продукт:

  1. В консоли Google Play в левом навигационном меню выберите Монетизация с Play > Продукты > Разовые продукты .
  2. Нажмите Создать одноразовый продукт .
  3. Введите следующие данные о продукте:
    • ID продукта: Введите уникальный ID продукта. Введите one_time_product_01 .
    • (Необязательно) Теги: добавьте соответствующие теги.
    • Имя: Введите название продукта. Например, Product name .
    • Описание: Введите описание продукта. Например, Product description .
    • (Необязательно) Добавьте изображение значка: загрузите значок, представляющий ваш продукт.
    Примечание: для целей данной лабораторной работы вы можете пропустить настройку раздела «Налоги, соответствие и программы» .
  4. Нажмите «Далее» .
  5. Добавьте вариант покупки и настройте его региональную доступность. Для одноразового продукта требуется как минимум один вариант покупки, который определяет способ предоставления права, его цену и региональную доступность. В этой лабораторной работе мы добавим стандартный вариант покупки для продукта. В разделе «Вариант покупки» введите следующие данные:
    • Идентификатор варианта покупки: введите идентификатор варианта покупки. Например, buy .
    • Тип покупки: выберите «Купить» .
    • (Необязательно) Теги: добавьте теги, относящиеся к этому варианту покупки.
    • (Необязательно) Нажмите «Дополнительные параметры» , чтобы настроить дополнительные параметры. В рамках этой практической работы настройку дополнительных параметров можно пропустить.
  6. В разделе Доступность и цены нажмите Установить цены > Массовое редактирование цен .
  7. Выберите опцию «Страна/регион» . Будут выбраны все регионы.
  8. Нажмите «Продолжить» . Откроется диалоговое окно для ввода цены. Введите 10 долларов США и нажмите «Применить» .
  9. Нажмите «Сохранить» , а затем нажмите «Активировать» . Это создаст и активирует возможность покупки.

Для целей этой лабораторной работы создайте 3 дополнительных одноразовых продукта со следующими идентификаторами продуктов:

  • расходный_продукт_01
  • расходный_продукт_02
  • расходный_продукт_03

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

Откройте пример приложения в Google Play Console и перейдите в раздел «Монетизация с Play» > «Продукты» > «Разовые продукты» . Затем нажмите «Создать разовый продукт» и повторите шаги с 3 по 9.

Видео создания одноразового продукта

В следующем видео-примере показаны одноразовые этапы создания продукта, описанные ранее.

4. Интеграция с PBL

Теперь мы рассмотрим, как интегрировать ваше приложение с библиотекой Play Billing Library (PBL) . В этом разделе описываются основные этапы интеграции и приводятся фрагменты кода для каждого из них. Вы можете использовать эти фрагменты в качестве руководства для реализации интеграции.

Чтобы интегрировать ваше приложение с PBL, выполните следующие действия:

  1. Добавьте зависимость Play Billing Library к примеру приложения.
    dependencies {
    val billing_version = "8.0.0"
    
    implementation("com.android.billingclient:billing-ktx:$billing_version")
    }
    
  2. Инициализируйте BillingClient . BillingClient — это клиентский SDK, который находится в вашем приложении и взаимодействует с библиотекой Play Billing. В следующем фрагменте кода показано, как инициализировать клиент для выставления счетов.
    protected BillingClient createBillingClient() {
    return BillingClient.newBuilder(activity)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
        .enableAutoServiceReconnection()
        .build();
    }
    
  3. Подключитесь к Google Play. Следующий фрагмент кода показывает, как подключиться к Google Play.
    public void startBillingConnection(ImmutableList<Product> productList) {
    Log.i(TAG, "Product list sent: " + productList);
    Log.i(TAG, "Starting connection");
    billingClient.startConnection(
        new BillingClientStateListener() {
          @Override
          public void onBillingSetupFinished(BillingResult billingResult) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
              // Query product details to get the product details list.
              queryProductDetails(productList);
            } else {
              // BillingClient.enableAutoServiceReconnection() will retry the connection on
              // transient errors automatically.
              // We don't need to retry on terminal errors (e.g., BILLING_UNAVAILABLE,
              // DEVELOPER_ERROR).
              Log.e(TAG, "Billing connection failed: " + billingResult.getDebugMessage());
              Log.e(TAG, "Billing response code: " + billingResult.getResponseCode());
            }
          }
    
          @Override
          public void onBillingServiceDisconnected() {
            Log.e(TAG, "Billing Service connection lost.");
          }
        });
    }
    
  4. Получите информацию о разовом продукте. После интеграции приложения с PBL необходимо получить информацию о разовом продукте. Следующий фрагмент кода показывает, как получить информацию о разовом продукте в вашем приложении.
    private void queryProductDetails(ImmutableList<Product> productList) {
    Log.i(TAG, "Querying products for: " + productList);
    QueryProductDetailsParams queryProductDetailsParams =
        QueryProductDetailsParams.newBuilder().setProductList(productList).build();
    billingClient.queryProductDetailsAsync(
        queryProductDetailsParams,
        new ProductDetailsResponseListener() {
          @Override
          public void onProductDetailsResponse(
              BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) {
            // check billingResult
            Log.i(TAG, "Billing result after querying: " + billingResult.getResponseCode());
            // process returned productDetailsList
            Log.i(
                TAG,
                "Print unfetched products: " + productDetailsResponse.getUnfetchedProductList());
            setupProductDetailsMap(productDetailsResponse.getProductDetailsList());
            billingServiceClientListener.onProductDetailsFetched(productDetailsMap);
          }
        });
    }
    
    Извлечение ProductDetails дает вам ответ, подобный следующему:
    {
        "productId": "consumable_product_01",
        "type": "inapp",
        "title": "Shadow Coat (Yolo's Realm | Play Samples)",
        "name": "Shadow Coat",
        "description": "A sleek, obsidian coat for stealth and ambushes",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 1990000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$1.99",
                "offerIdToken": "<--offerIdToken-->",
                "purchaseOptionId": "buy",
                "offerTags": []
            }
        ]
    },
    {
        "productId": "consumable_product_02",
        "type": "inapp",
        "title": "Emperor Den (Yolo's Realm | Play Samples)",
        "name": "Emperor Den",
        "description": "A fair lair glowing with molten rock and embers",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 2990000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$2.99",
                "offerIdToken": "<--offerIdToken-->",
                "purchaseOptionId": "buy",
                "offerTags": []
            }
        ]
    }
    
  5. Запустите процесс выставления счетов.
    public void launchBillingFlow(String productId) {
    ProductDetails productDetails = productDetailsMap.get(productId);
    if (productDetails == null) {
      Log.e(
          TAG, "Cannot launch billing flow: ProductDetails not found for productId: " + productId);
      billingServiceClientListener.onBillingResponse(
          BillingResponseCode.ITEM_UNAVAILABLE,
          BillingResult.newBuilder().setResponseCode(BillingResponseCode.ITEM_UNAVAILABLE).build());
      return;
    }
    ImmutableList<ProductDetailsParams> productDetailsParamsList =
        ImmutableList.of(
            ProductDetailsParams.newBuilder().setProductDetails(productDetails).build());
    
    BillingFlowParams billingFlowParams =
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build();
    
    billingClient.launchBillingFlow(activity, billingFlowParams);
    }
    
  6. Обнаружение и обработка покупок. На этом этапе вам необходимо:
    1. Подтвердите покупку
    2. Предоставить право пользователю
    3. Уведомить пользователя
    4. Уведомить Google о процессе покупки
    Из них шаги a, b и c должны быть выполнены в бэкэнде и, следовательно, выходят за рамки этой лабораторной работы. В следующем фрагменте показано, как уведомить Google о расходном одноразовом продукте:
    private void handlePurchase(Purchase purchase) {
    // Step 1: Send the purchase to your secure backend to verify the purchase following
    // https://developer.android.com/google/play/billing/security#verify
    
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.
    
    // Step 3: Notify the user using appropriate messaging.
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
      for (String product : purchase.getProducts()) {
        Log.d(TAG, product + " purchased successfully! ");
      }
    }
    
    // Step 4: Notify Google the purchase was processed.
    // For one-time products, acknowledge the purchase.
    // This sample app (client-only) uses billingClient.acknowledgePurchase().
    // For consumable one-time products, consume the purchase
    // This sample app (client-only) uses billingClient.consumeAsync()
    // If you have a secure backend, you must acknowledge purchases on your server using the
    // server-side API.
    // See https://developer.android.com/google/play/billing/security#acknowledge
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED && !purchase.isAcknowledged()) {
    
      if (shouldConsume(purchase)) {
        ConsumeParams consumeParams =
            ConsumeParams.newBuilder().setPurchaseToken(purchase.getPurchaseToken()).build();
        billingClient.consumeAsync(consumeParams, consumeResponseListener);
    
      } else {
        AcknowledgePurchaseParams acknowledgePurchaseParams =
            AcknowledgePurchaseParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();
        billingClient.acknowledgePurchase(
            acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
      }
     }
    }
    

5. Анализируйте отказы от покупок

До сих пор в рамках практического занятия ответы Play Billing были сосредоточены на ограниченном количестве сценариев, таких как USER_CANCELLED , BILLING_UNAVAILABLE , OK и ITEM_ALREADY_OWNED . Однако Play Billing может возвращать 13 различных кодов ответов , которые могут быть вызваны различными реальными факторами.

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

Код ошибки ответа USER_CANCELED

Этот код ответа указывает на то, что пользователь отказался от пользовательского интерфейса процесса покупки, не завершив ее.

Вероятные причины

Какие действия вы можете предпринять?

  • Может указывать на пользователей с низким уровнем намерений, чувствительных к ценам.
  • Покупка находится в ожидании или платеж отклонен.

Код ошибки ответа BILLING_UNAVAILABLE

Этот код ответа означает, что покупка не может быть завершена из-за проблемы с платёжным провайдером пользователя или выбранным им способом оплаты. Например, срок действия кредитной карты пользователя истёк или пользователь находится в стране, не поддерживаемой Google Play. Этот код не указывает на ошибку в самой системе оплаты Play Billing.

Вероятные причины

Какие действия вы можете предпринять?

  • Приложение Play Store на устройстве пользователя устарело.
  • Пользователь находится в стране, где Play не поддерживается.
  • Пользователь является корпоративным пользователем, и администратор его предприятия запретил пользователям совершать покупки.
  • Google Play не может списать средства с использованием способа оплаты пользователя. Например, срок действия кредитной карты пользователя мог истёк.
  • Мониторинг тенденций возникновения системных проблем в конкретных регионах
  • Рассмотрите возможность перехода на PBL 8 , поскольку он поддерживает более детализированный код подответа PAYMENT_DECLINED_DUE_TO_INSUFFICIENT_FUNDS . Если вы получили этот код ответа, уведомите пользователей об ошибке или предложите альтернативные способы оплаты.
  • Этот код ответа предназначен для повторных попыток, позволяя вам реализовывать подходящие стратегии повторных попыток.
    Автоматические повторные попытки вряд ли помогут в этом случае. Однако ручная повторная попытка может помочь, если пользователь устранит причину, вызвавшую проблему. Например, если пользователь обновит версию Play Store до поддерживаемой, ручная повторная попытка первоначальной операции может сработать.

    Если вы получаете этот код ответа, когда пользователь не находится в сеансе, повторная попытка может быть бессмысленной. Если в результате покупки вы получаете ответ `BILLING_UNAVAILABLE`, весьма вероятно, что пользователь получил отзыв от Google Play во время покупки и знает о проблеме. В этом случае вы можете отобразить сообщение об ошибке, указывающее на то, что что-то пошло не так, и предложить кнопку ``Попробовать ещё раз``, чтобы дать пользователю возможность повторить попытку вручную после устранения проблемы.

Стратегии повторных попыток для кодов ошибок ответа

Эффективные стратегии повторных попыток для устранимых ошибок в библиотеке Play Billing Library (PBL) различаются в зависимости от контекста, например, от взаимодействия пользователя в сеансе (например, во время покупки) до фоновых операций (например, запроса покупок при возобновлении работы приложения). Важно реализовать эти стратегии, поскольку некоторые значения BillingResponseCode указывают на временные проблемы, которые можно устранить повторной попыткой, в то время как другие являются постоянными и не требуют повторных попыток.

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

Подробную информацию о конкретных кодах ответов и соответствующих им рекомендуемых стратегиях повторных попыток см. в разделе Обработка кодов ответов BillingResult .

6. Дальнейшие шаги

Справочные документы

7. Поздравляем!

Поздравляем! Вы успешно создали новый одноразовый продукт в Google Play Console, протестировали коды ответов на платежи и проанализировали отказы от покупок.

Опрос

Мы очень ценим ваши отзывы об этой лабораторной работе. Уделите несколько минут и пройдите наш опрос.