Как реализовать Expo In App Purchases and Subscriptions для iOS и Android
Внутриигровые покупки и подписки в 2021 году: время перемен?
После более чем года использования react-native-iap
в качестве решения для покупки в приложении React Nativ e, я недавно решил перейти на expo-in-app-purchases
, чтобы решить ряд проблем, с которыми я столкнулся с предыдущим пакетом.
Из моего пути разработки очевидно, что expo-in-app-purchases
- лучший пакет для принятия сейчас в 2021 году. Я задокументирую причины этого в этой статье вместе с полным пошаговым руководством по реализации expo-in-app-purchases
. Что будет охвачено:
- Сначала будут объяснены преимущества
expo-in-app-purchases
и многочисленные проблемы, с которыми я столкнулся сreact-native-iap
. - Установка
expo-in-app-purchases
будет рассмотрена, прежде чем углубляться в интеграцию пакета в проекты React Native. - Будут рассмотрены передовые методы тестирования покупок и подписок из приложения, а также распространенные проблемы, с которыми вы можете столкнуться при реализации покупок в приложении для своих приложений. Эти проблемы основаны на моем собственном опыте работы с пакетами для устройств Android и iOS.
- Был предоставлен GitHub Gist, содержащий пример реализации
expo-in-app-purchases
, который будет объяснен ниже.
Expo In App Purchases vs React Native IAP
Сравнение react-native-iap
и expo-in-app-purchases
будет сначала рассмотрено вместе с трудностями, с которыми я столкнулся при работе с прежним пакетом.
Номера установки для двух пакетов могут рассказать довольно вводящую в заблуждение историю: react-native-iap
в настоящее время имеет более 20 000 загрузок в неделю на момент написания и expo-in-app-purchases
с задержкой по сравнению с немногим более 1 500 загрузок в неделю.
Оба пакета предлагают очень похожую структуру API, основанную на асинхронном подключении к соответствующему магазину (App Store или Google Play) и прослушивателю событий для обработки запросов на покупку как для разовых покупок продукта, так и для запросов на подписку.
Сходство в структуре упрощает миграцию, поскольку ваш код во многом будет следовать идентичной логике. Фактически, самая большая разница в том, что метод expo-in-app-purchases
getProductsAsync()
возвращает другую структуру данных, чем метод react-native-iap
с тем же именем.
Хотя это изменение влияет на ваши структуры данных и компоненты, ориентированные на IAP, поправки довольно тривиальны и могут быть быстро исправлены.
Expo In App Purchases работает с любым проектом React Native
expo-in-app-purchases
не является эксклюзивным только для Bare Expo Workflows - он работает с любым проектом React Native при условии, что зависимость react-native-unimodules
также установлена вместе с expo-in-app-purchases
.
Если вы не используете Expo, react-native-unimodules
инструкции по установке для iOS и Android можно найти здесь на веб-сайте Expo.
Почему я перешел на Expo In App Purchases
Основные преимущества использования expo-in-app-purchases
двоякие:
- Никакой настройки от Android Studio не требуется, кроме обеспечения
BILLING
разрешения, включенного в ваш файл манифеста, и наличия установленных юнимодулей React Native. iOS просто требует стандартной установки модуля и добавления в проект возможности Xcode для покупок из приложения. - Все API-интерфейсы надежны и работают без проблем, с исчерпывающим справочником по API, размещенным в едином согласованном документе GitHub.
Такая надежность и ясная документация делают expo-in-app-purchases
быструю и легкую интеграцию.
react-native-iap
, с другой стороны, имеет множество проблем, которые варьируются от несовместимой документации, спорадических циклов выпуска и неработающих API, которые в конечном итоге побудили меня к поиску альтернативных решений IAP.
Вот некоторые из проблем, с которыми я лично столкнулся react-native-iap
:
- Документация очень несовместима с циклами выпуска и не удобна для разработчиков. Очень сложно найти рабочие примеры последних API и отладить проблемы, которые часто трудно выявить из-за плохой документации.
react-native-iap
теперь находится в версии 6 без каких-либо заметных изменений по сравнению с версией 4 - версией, которую я использовал до обновления до 6. Выпуск4.4.x
не позволил мне выполнить обновление с4.3.0
из-за того, что запросы на подписку просто не работали, для которой не было найдено решение на GitHub.- В документации есть инструкции по установке Android, но, следуя этим инструкциям в версии 6 пакета, получение подписок просто не работало для Android - без явных ошибок сборки или несоответствий.
- На момент написания статьи на GitHub имеется 101 нерешенная проблема, многие из которых не решены в течение нескольких месяцев. Многие из этих проблем относятся к критически важным функциям, которые абсолютно необходимы для работы IAP.
Очевидно, есть несколько веских причин потратить пару часов на миграцию с react-native-iap
. Есть ли лучшее решение с expo-in-app-purchases
? Да, и разработчики могут быть уверены, что пакет будет надежно поддерживаться в соответствии с остальной частью пакета Expo.
Если вы столкнулись с аналогичными проблемами при реализации IAP или впервые внедряете IAP в React Native, в следующих разделах процесс реализации будет ускорен.
Expo IAP: установка iOS и Android
Установите expo-in-app-purchases
с помощью npm или yarn в качестве зависимости проекта:
yarn add expo-in-app-purchases
Если вы используете Expo, убедитесь, что вы используете Bare Workflow. Покупки в приложении в настоящее время работают только в Bare Workflows из-за встроенной конфигурации, необходимой в XCode и Android Studio.
Если вы не работаете с проектом Expo, на данном этапе рекомендуется установить react-native-unimodules
. Прежде чем продолжить, ознакомьтесь с этапами установки, чтобы убедиться, что он правильно настроен в ваших собственных проектах.
Установка для iOS
Для iOS просто запустите pod install:
npx pod-install #or cd ios && pod install
Для настройки Xcode убедитесь, что в ваш проект добавлена возможность Покупки в приложении и что ваши SKU настроены в App Store Connect.
Подробные инструкции App Store Connect по настройке покупок в приложении на стороне iOS см. В разделе Настройка покупок в приложении iOS в моей опубликованной статье: React Native: подписки с Покупки в приложении . Читателю предлагается обратиться к подробным инструкциям из этой статьи, чтобы не повторяться здесь.
В этой статье также будет рассмотрена настройка Android и Google Play; об этом будет подробнее рассказано далее.
Установка под Android
Убедитесь, что у вас есть BILLING
разрешение, включенное в ваш AndroidManifest.xml
файл. Если его нет, просто вставьте следующий тег в файл под основным тегом <manifest>
:
<uses-permission android:name="android.permission.BILLING" />
Также потребуется дополнительная настройка в вашей Google Play Console. Чтобы настроить покупки в приложении, выполните следующие действия:
Настройка консоли Google Play
Для регистрации в качестве разработчика в Google Play требуется единовременная плата в размере 25 долларов США. После оплаты вы получаете мгновенный доступ к своей учетной записи. Эта модель единовременной оплаты намного экономичнее, чем годовая абонентская плата Apple в размере 99 долларов.
- Чтобы выполнить следующие шаги, убедитесь, что вы уже настроили свою учетную запись разработчика Google Play в Консоль Google Play.
Все, начиная со страницы вашего магазина, информации о компании, выставления счетов и покупок в приложении, тестирования приложений и т. Д., Осуществляется из консоли Google Play. Теперь вы можете создать свое приложение и настроить свои IAP.
- Перед настройкой приложения настройте свой платежный профиль, чтобы можно было получать доход от продаж в приложении. На странице Все приложения (самая верхняя страница панели управления консоли) разверните пункт меню Настройка и нажмите Платежный профиль, чтобы сделать это. . Существует период ожидания в пару дней, пока на ваш банковский счет будет зачислена небольшая сумма для подтверждения вашей учетной записи.
- На странице Все приложения нажмите Создать приложение и введите необходимые данные. Все ваши IAP размещаются в вашей записи приложения.
- Перейдите в панель управления своего приложения и найдите в главном меню меню Монетизация. Вас заинтересуют разделы Контент для продажи и Подписки. Идите вперед и создайте необходимые SKU вместе с их ценой, расчетным периодом и другими настройками, такими как условия бесплатной пробной версии.
Метаданные, такие как статус бесплатной пробной версии, код валюты и другие важные данные, относящиеся к покупке в приложении или подписке, возвращаются при вызове getProductsAsync()
или purchaseItemAsync()
из expo-in-app-purchases
.
Фоновое чтение в Google Play IAP
На веб-сайте Android Developer есть полезная официальная документация для дальнейшего ознакомления с IAP. Особенно интересны статьи, относящиеся к этой статье: Продавайте цифровые покупки с помощью платежной системы Google Play и Продавайте подписки с помощью платежной системы Google Play.
И разовые покупки, и подписки поддерживаются expo-in-app-purchases
API, причем оба типа покупок используют унифицированные вызовы API для обоих типов.
Теперь, когда ваши IAP и / или подписки настроены, мы можем переключить наше внимание на React Native. Но перед этим есть одна деталь тестирования приложения, которая влияет на то, как вы тестируете покупки в приложении:
Покупки в приложении можно тестировать только в закрытых или открытых тестовых треках.
Если вы новичок в разработке Android, тестовые треки могут быть новой концепцией.
Здесь есть несколько полезных официальных документов, которые четко объясняют различия между этими тремя треками и содержат гораздо более подробные сведения, чем то, что мы здесь сосредоточили на IAP.
Конкретно релизы можно разделить на 3 категории или «тестовые треки»:
- Внутреннее тестирование позволяет вам создать приложение и поделиться ссылкой на тестирование внутри с членами вашей команды. Эти сборки приложения не проверяются, но тестировщики получают мгновенный доступ к приложению для немедленного тестирования. Обратной стороной внутреннего тестирования является то, что покупки в приложении нельзя тестировать. Артикулы будут возвращены в React Native, но запросы на покупку не будут выполнены.
- С другой стороны, закрытое и открытое тестирование требует проверки со стороны Google, которая может занять до 7 дней для новых разработчиков и 1–3 дней для ранее проверенных приложений (эти типичное время цитируется из Stack Overflow). Обычно я получаю очень быстрые обзоры обновлений приложений, обычно за несколько часов. После успешной проверки ваше приложение будет доступно для загрузки подходящим тестировщикам по ссылке, указанной в консоли Google Play. Из этих сборок будут доступны покупки в приложении для тестирования, и это то, что мы хотим.
Это ограничение вызывает трение в вашем цикле разработки, но его нельзя избежать. Имейте в виду, что:
- Мы не можем быстро протестировать встроенные покупки в Google Play в режиме разработки с такими предметами роскоши, как горячая перезагрузка и мгновенные обновления. Мы должны загрузить сборку релиза приложения в Google Play и позволить Google проверить приложение, прежде чем мы сможем тестировать IAP на устройстве.
- Следовательно, и, возможно, очевидно, что покупки в приложении можно тестировать только на физическом устройстве Android, а не в эмуляторе Android. После того, как запросы IAP будут протестированы и будут работать в соответствии с вашими требованиями, дальнейшие обновления вашего пользовательского интерфейса могут быть выполнены в режиме разработки или внутреннего тестирования.
С учетом всего сказанного и теперь ваши SKU настроены как для iOS, так и для Android, давайте перейдем к фактической реализации React Native.
Реализация Expo In App Purchases в React Native
Реализовать IAP с expo-in-app-purchases
довольно просто. В документации перечислены необходимые методы в порядке их выполнения. Официальная документация включает полезные фрагменты кода для всех методов, поэтому в этой части мы сосредоточимся на их интеграции в компонент React Native, а не на повторении примеров.
Сказав это, вот обзор необходимых методов - методов, которые мы должны реализовать, - чтобы заставить работать покупки в приложении:
connectAsync()
: подключается к магазину App Store или Google Play.getProductsAsync([skus])
: извлекает сведения о продукте для предоставленных артикулов из выбранного магазина.getPurchaseListener(purchase => { /* callback function */ })
: прослушиватель событий, обрабатывающий входящие запросы на покупку. Мы обсудим, как это реализовать дальше.purchaseItemAsync(sku, ?currentSku)
: ключевая функция для инициирования покупки. ПараметрcurrentSku
необходим только для Android, если у пользователя уже есть активная подписка. Вызов этой функции вызовет собственный пользовательский интерфейс устройства для покупки, который обычно включает только аутентификацию и подтверждение покупки. ВашpurchaseEventListener
обратный вызов обрабатывает и после этого завершает транзакцию.finishTransactionAsync(purchase, consume)
: эта функция должна быть вызвана для завершения транзакции; он отмечает транзакцию как завершенную на стороне магазина. Параметрpurchase
будет предоставлен через вашу функцию обратного вызова. флаги потребления, будь то разовая покупка или повторяющаяся покупка (подписка).
Это 5 функций, которые мы не можем позволить себе упустить для работы IAP. Как и connectAsync()
, у нас также есть disconnectAsync()
, который завершает соединение с магазином. Это может быть вызвано, когда пользователи больше не имеют доступа к покупкам в приложении, например, когда они вышли из системы и им необходимо пройти аутентификацию для покупки товаров.
Если вы хотите отображать историю покупок, получая ее прямо из Магазина, вы можете использовать getPurchaseHistoryAsync()
. Это может быть полезно, если вы не сохраняете покупки в своей базе данных, но мы настоятельно рекомендуем вам это сделать! Чтобы также получить последний код ответа из покупательской активности пользователя, используйте getBillingResponseCodeAsync()
.
Если вы работаете с react-native-iap
, вы заметите сходство между двумя API, которые делают миграцию очень простой.
Интеграция методов IAP в компонент React Native
Теперь давайте посмотрим, как именно эти методы могут быть интегрированы в React Native. Я задокументирую свой предпочтительный метод; это хорошо работает для меня, но может быть не единственным эффективным решением. Полностью прокомментированный GitHub Gist будет представлен после обзора решения.
По моему опыту, наиболее эффективный способ реализовать вышеуказанные методы в React Native - это сделать следующее:
- Создайте компонент, который подключается к Store и размещает ваш прослушиватель событий покупки и функцию обратного вызова, и оберните ваше дерево компонентов, для которого требуется доступ к API
expo-in-app-purchases
, этим компонентом. Этот компонент может называться, например,<IAPManager>
. Эта статья будет придерживаться этого названия. - В этом компоненте определите некоторое локальное состояние, чтобы отметить, обрабатывается ли покупка в приложении. Это может быть просто пара
[processing, setProcessing]
сuseState
. Одновременно может обрабатываться только одна покупка в приложении, и вы не хотите, чтобы пользователи неоднократно запрашивали покупки, когда другая находится в процессе обработки. - Определите контекст в том же файле для доступа к состоянию
<IAPManager />
processing
, а также к другим полезным функциям, таким какgetProductsAsync()
. Крючок контекста, например,useIap()
, может быть определен для функциональных компонентов, что в наши дни является стандартной практикой. - Теперь в
<IAPManager />
определите логику покупки в функцииpurchaseItemAsync()
, а также любую обработку, которая необходима на вашем сервере.finishTransactionAsync()
следует вызывать после ваших собственных шагов обработки.
После настройки этого шаблона дочерние компоненты, <IAPManager />
полагающиеся на функциональность IAP, могут использовать ваш контекст и вызывать purchaseItemAsync()
. Поскольку это дочерние компоненты, компонент <IAPManager />
connectAsync()
уже был бы вызван, и будет установлено соединение для обработки покупок.
purchaseItemAsync()
можно запустить простым onPress()
из компонента <Button />
или <TouchableOpacity />
, чтобы начать процесс покупки.
Полный компонент IAPManager
Ниже представлен GitHub Gist, демонстрирующий, как можно определить IAPManager. Я прокомментировал это как можно больше, чтобы дать читателю как можно больше ориентиров в исходном коде:
Общие проблемы и советы по реализации
В этом разделе описаны некоторые советы по реализации и общие проблемы, которые я обнаружил при работе с IAP.
Форматирование цен
Помните о структурах данных как возвращаемых продуктов для продажи, так и покупок. Если вы из react-native-iap
, IAPItemDetails
сильно отличается.
Имейте в виду, что priceAmountMicros
можно преобразовать в стандартный формат цены с помощью следующих параметров:
// priceAmountMicros to priceAmount let priceAmount: number = parseInt(item.priceAmountMicros) / 1000000;
priceAmountMicros
можно комбинировать с priceCurrencyCode
, если вам нужно манипулировать ценой (например, рассчитывать ежемесячные исходящие сообщения в течение 12-месячного периода подписки). Такая манипуляция не так проста с использованием строкового свойства price
, которое объединяет код цены и валюты.
Установите флаг IGNORE_IAP
Как мы выяснили ранее, IAP можно тестировать только на физических устройствах на Android, где к ним имеют доступ только сборки из закрытого и открытого трека тестирования.
На стороне iOS артикулы продуктов не загружаются в Симулятор - по крайней мере, на момент написания этой статьи. Итак, если у вас есть getProductsAsync()
при запуске вашего приложения - ну, ваше приложение будет зависать, поскольку метод не будет разрешен или истечет время ожидания без настраиваемой логики.
Однако мы не можем просто определить логику, которая полностью игнорирует IAP, если вы находитесь в режиме разработки, с чем-то вроде process.env.NODE_ENV
, потому что мы все еще можем получать продукты и тестировать пользовательский интерфейс, относящийся к IAP в режиме разработки, даже если только на физическом устройстве.
Вместо среды, возможно, стоит установить константу IGNORE_IAP
, если вы знаете, что у вас есть доступ только к симуляторам и эмуляторам. Задайте это логическое значение в файле констант и импортируйте его, где необходимо, и переключите значение в зависимости от ваших возможностей тестирования.
Проверка, подключены ли вы уже к магазину
Если вы хотите получать продукты за пределами <IAPManager />
, например, когда ваше приложение запускается для немедленного получения и сохранения IAP, целесообразно проверить, подключены ли вы уже к магазину.
Вы можете определить connectAsync()
в блоке try catch, чтобы просто игнорировать неудачные попытки подключения, если хранилище уже подключено:
// connect to store if not done so already try { await InAppPurchases.connectAsync(); } catch (e) { /* already connected, check `e` to verify this */ }
Восстановить состояние обработки, если покупка не состоялась (но не завершилась ошибкой)
В официальном примере setPurchaseListener
оператор if рассматривает диапазон IAPResponseCode
значений, например, когда пользователь отменяет или откладывает покупку, и это может быть соблазнительным местом для размещения setProcessing(false)
для обновления пользовательского интерфейса после неудачной покупки.
Но на самом деле существует крайний случай, когда IAPResponseCode
возвращает OK
, но ваш массив покупок будет пуст. Я испытал это на Android, когда я уже был подписан, но пытался повторно подписаться с тем же SKU. Ответ был OK
, но покупки не возвращались. Android UX отображал уже подписанное сообщение, прежде чем я закрыл модальное окно IAP.
Следовательно, логика results.forEach()
не была выполнена, как и никакая другая обработка кода ответа.
Чтобы избежать этих крайних случаев, вы можете просто поставить setProcessing(false)
после этого большого оператора if, обрабатывающего каждый код ошибки. Именно это и было сделано в GitHub Gist.
Проверка, подписался ли пользователь на другой платформе
Это крайний случай, но пользователи устройств iOS и Android могут захотеть использовать ваше приложение на обоих из них. Если пользователь уже подписался на одной платформе, вы не хотите позволять ему повторять попытку подписаться на другой. Вместо этого вы должны отобразить сообщение, уведомляющее их о том, что они подписались в другом месте, и либо управлять своей подпиской на этой платформе, либо обращаться в службу поддержки, чтобы отменить подписку.
Чтобы проверить этот сценарий, сохраните в своей базе данных платформу, которая использовалась при подписке пользователя. Просто используйте Platform.OS
React Native, чтобы получить это.
Теперь в ваших UX-компонентах вы можете проверить, соответствует ли эта платформа текущей активной платформе:
// get platform user subscribed from (your database) const subscribedOn = user.subscription.platform; // check whether user is now on different platform const subscribedOnDifferentPlatform = planPlatform !== Platform.OS);
Выпуск JDK Runtime 16 (только для Android)
В качестве дополнительной проблемы, связанной с вашим проектом Android Bare Workflow Expo (не связанным с IAP), стоит упомянуть, что JDK 16.0.1
в настоящее время приводит к сбою сборки приложения.
Если обновления JDK не работают нормально, просто измените свой android
run
скрипт в вашем package.json
. Следующее использовало JDK 15.0.2
вместо 16.0.1
, которое я обновил в своей системе:
# package.json "scripts": { "android": "export JAVA_HOME=$(/usr/libexec/java_home -v 15.0.2); react-native run-android", ... } ...
Это может быть исправлено к тому моменту, когда читатель это увидит, но стоит отметить, что вернуться к предыдущей версии JDK легко.
В итоге
На этом мы завершаем обсуждение реализации покупок в приложении с помощью expo-in-app-purchases
. В этой статье обсуждались проблемы с react-native-iap
и причины перехода на expo-in-app-purchases
в качестве решения React Native IAP.
Благодаря схожим API, согласованной документации и надежности expo-in-app-purchases
- это идеальный пакет React Native IAP в 2021 году, а миграция - это простой и стоящий процесс.