Как подтвердить покупку для приложения Android на стороне сервера (Google Play в биллинге приложений v3)


97

У меня есть простое приложение (требуется вход в систему с учетной записью). Я предоставляю несколько дополнительных функций для платных пользователей, например больше новостей.

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

Я проверил официальный образец Trivialdrive, предоставленный Google, он не предоставляет образец кода для проверки на стороне сервера, вот мои вопросы.

  1. Я обнаружил, что образец использует открытый ключ моего приложения внутри для проверки покупки, это выглядит не очень хорошо, я думаю, что могу просто перенести процесс проверки на свой сервер в сочетании с учетными данными пользователя, чтобы увидеть, завершена ли покупка пользователя, а затем обновить базу данных.
  2. Также есть API покупки, который я могу использовать для запроса, мне нужно передать покупной токен пользователя на сервер.

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

И я боюсь, что может возникнуть ситуация, если пользователь купил этот предмет в Google Play, но по какой-то причине, как раз в то время, когда мое приложение запускало проверку на моем сервере, сетевое соединение не работает или мой собственный сервер не работает , пользователь только что заплатил деньги в Google Play, но я не записал покупку на моем сервере? Что мне делать, как я могу справиться с этой ситуацией.


Вероятно, вам следует удалить флаг ios из этого вопроса.
— Густаво Гевара

Ответы:


161

Похоже, то, что вы ищете, - это способ проверить, включены ли у пользователя премиум-функции в его учетной записи, поэтому я бы начал с этого;

Убедитесь, что в вашей базе данных есть какой-то флаг, указывающий, есть ли у пользователя расширенные функции, и включите его в полезные данные ответа API при запросе информации об учетной записи. Этот флаг будет вашим основным авторитетом для «премиум-функций».

Когда пользователь совершает покупку в приложении, кэшируйте данные (токен, идентификатор заказа и идентификатор продукта) локально на клиенте (т. Е. В приложении), а затем отправьте их в свой API.

Затем ваш API должен отправить его purchaseTokenв API разработчика Google Play для проверки.

Отсюда может произойти несколько вещей:

  1. Квитанция действительна, ваш API отвечает клиенту кодом состояния 200 Ok.
  2. Квитанция недействительна, ваш API отвечает клиенту с кодом состояния 400 Bad Request.
  3. API Google Play не работает, ваш API отвечает кодом состояния 502 Bad Gateway

В случае 1. или 2. (коды состояния 2xx или 4xx) ваш клиент очищает кеш с деталями покупки, потому что они ему больше не нужны, поскольку API указал, что они были получены.

После успешной проверки (случай 1.) вы должны установить premiumдля пользователя значение true.

В случае 3. (код состояния 5xx) или тайм-аута сети клиент должен продолжать попытки, пока он не получит код состояния 2xx или 4xx от вашего API.

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

Такой подход должен учитывать тайм-ауты сети, недоступность серверов и т. Д.

Теперь вам нужно рассмотреть несколько вопросов:

Что должно произойти сразу после покупки? Следует ли приложению ждать, пока проверка будет успешной, прежде чем предоставлять премиум-контент, или оно должно предварительно предоставить доступ и забрать его, если проверка не удалась?

Предоставление предварительного доступа к премиум-функциям упрощает процесс для большинства ваших пользователей, но вы также будете предоставлять доступ ряду мошеннических пользователей, пока ваш API проверяет purchaseToken.

Другими словами: покупка действительна до тех пор, пока не будет доказана ее подделка; мошеннические, пока не будет доказана достоверность?

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

Если expiryTimeMillisэто в прошлом, вы можете установить premiumфлаг в значение false. Если это в будущем, перепланируйте его снова на новое expiryTimeMillis.

Наконец, чтобы убедиться, что у пользователя есть премиум-доступ (или нет), ваше приложение должно запрашивать у вашего API сведения о пользователях при запуске приложения или когда он выходит из фона.


Для платного приложения, как я получу квитанцию ​​от Google?
— Мербин Джо

2
Здравствуй! Нет возможности получить доступ к истории подписок в гугле? Как предотвратить потерю факта использования уже купленной подписки в случае, если приложение вылетает в момент сохранения PurchaToken?
— scythargon 06

2
У меня аналогичная проблема ... вместо того, чтобы позволить приложению отправлять токен на api, не было бы более надежным дать указание серверу разработчика Google сделать это с помощью push-уведомления прямо на мой api?
— Джанлука Геттини

Для подписок, которые были отменены, Google Play Developer API по-прежнему будет возвращать 200 после отмены, если для проверки используется тот же старый токен покупки.
— Сезар Кобуз,

Итак, для подписки вы предлагаете, чтобы после первого вызова на сервере мы сохранили токен покупки и идентификатор продукта и запланировали другой проверочный вызов (повторно запустить тот же запрос), когда произойдет expiryTimeMillis? Так мы должны проверять действительность подписки? Есть ли какие-нибудь рекомендации от Android, как это сделать? У Apple есть видео на WWDC об этом, в котором довольно ясно объясняется хорошая практика, но мало что можно найти о Play Store.
— schankam

28

Документация по этому поводу сбивает с толку и странно многословна с вещами, которые почти не имеют значения, в то время как действительно важная документация почти не связана и ее очень трудно найти. Это должно отлично работать на самой популярной серверной платформе, которая может запускать клиентские библиотеки API Google, включая Java, Python, .Net и NodeJS, среди прочих. Примечание. Я тестировал только клиент API Python, как показано ниже.

Необходимые шаги:

  1. Создайте проект API по ссылке API Access в консоли Google Play.

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

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

  4. Получите клиентскую библиотеку API Google для своей серверной платформы по адресу https://developers.google.com/api-client-library.

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

Вам не нужно беспокоиться об областях авторизации, выполнении пользовательских запросов, обновлении токенов доступа и т. Д., Клиентская библиотека api позаботится обо всем. Вот пример использования библиотеки Python для проверки подписки:

Сначала установите клиент API Google в свой pipenv следующим образом:

$ pipenv install google-api-python-client

Затем вы можете настроить учетные данные клиента api, используя json-файл закрытого ключа для аутентификации учетной записи службы.

credentials = service_account.Credentials.from_service_account_file("service_account.json")

Теперь вы можете подтверждать покупки подписки или продуктов напрямую с помощью библиотеки.

#Build the "service" interface to the API you want
service = googleapiclient.discovery.build("androidpublisher", "v3", credentials=credentials)

#Use the token your API got from the app to verify the purchase
result = service.purchases().subscriptions().get(packageName="your.app.package.id", subscriptionId="sku.name", token="token-from-app").execute()
#result is a python object that looks like this ->
# {'kind': 'androidpublisher#subscriptionPurchase', 'startTimeMillis': '1534326259450', 'expiryTimeMillis': '1534328356187', 'autoRenewing': False, 'priceCurrencyCode': 'INR', 'priceAmountMicros': '70000000', 'countryCode': 'IN', 'developerPayload': '', 'cancelReason': 1, 'orderId': 'GPA.1234-4567-1234-1234..5', 'purchaseType': 0}

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

Python | Java | .NET | PHP | NodeJS (Github TS) | Перейти (Github JSON)


5
Согласитесь, документация ужасна ... Есть идеи, как это сделать с помощью функций Firebase (Firestore) и Cloud в качестве бэкэнда?
— Джефф Пэджетт

Если ваши облачные функции находятся в NodeJS, вы могли бы использовать ссылку NodeJS выше, чтобы заставить работать клиентскую библиотеку API?
— Дхирадж Гупта

17

Полный пример использования клиентской библиотеки Google API для PHP :

  1. Настройте свой проект Google и получите доступ к Google Play для своей учетной записи службы, как описано в ответе Марка здесь https://stackoverflow.com/a/35138885/1046909 .

  2. Установите библиотеку: https://developers.google.com/api-client-library/php/start/installation .

  3. Теперь вы можете проверить квитанцию ​​следующим образом:

    $client = new \Google_Client();
    $client->setAuthConfig('/path/to/service/account/credentials.json');
    $client->addScope('https://www.googleapis.com/auth/androidpublisher');
    $service = new \Google_Service_AndroidPublisher($client);
    $purchase = $service->purchases_subscriptions->get($packageName, $productId, $token);

    После этой покупки $ будет экземпляром Google_Service_AndroidPublisher_SubscriptionPurchase

    $purchase->getAutoRenewing();
    $purchase->getCancelReason();
    ...

Это не работает, я продолжаю получать (401) Требуется вход в систему, а setAuthConfig не принимает учетные данные учетной записи службы json
— Raulnd

Этот сработал для меня putenv ('GOOGLE_APPLICATION_CREDENTIALS = credentials.json'); $ client = новый Google_Client (); $ client-> useApplicationDefaultCredentials (); $ client-> addScope (' googleapis.com/auth/androidpublisher' ); $ service = новый Google_Service_AndroidPublisher ($ client); $ Purchase = $ service-> Purchases_products-> get ($ packageName, $ productId, $ token); var_dump (покупка $);
— Raulnd

Это на случай inapp биллинга. Что, если я хочу получать orderId в своей базе данных всякий раз, когда пользователь покупает мое приложение в магазине игр, а не в приложении?
— Анкеш Кумар Джайсансария

stackoverflow.com/questions/48662787/ ... См. этот вопрос. Ищу ответ на этот вопрос. У этого также есть активная
— награда

@MingalevME Что делать, если формат токена неверен и PHP выдает фатальную ошибку, как мне отловить эту ошибку?
— alexx0186 02

12

Вы можете попробовать использовать Purchases.subscriptions: получить на стороне сервера. Он принимает в качестве параметров packageName, subscriptionId и токен и требует авторизации .

Проверяет, действительна ли покупка подписки пользователя, и возвращает время истечения срока ее действия.

В случае успеха этот метод возвращает ресурс Purchases.subscriptions в теле ответа.


9
У меня серьезные проблемы с авторизацией.

8
Шутки в сторону. В отношении того, насколько критичны покупки для некоторых приложений, поддержка и документация бессмысленны. Это то, что вам нужно сделать на сервере: github.com/google/… . Подробнее здесь: stackoverflow.com/questions/35127086/…
— пользователь

0

Я отвечаю на это беспокойство

сетевое соединение не работает или мой собственный сервер не работает, пользователь только что заплатил деньги в Google Play, но я не записал покупку на моем сервере? Что мне делать, как я могу справиться с этой ситуацией.

Ситуация такая:

Пользователь покупает элемент "abc" с помощью службы Google Play -> вернуть ОК -> не удалось подтвердить с сервером по некоторым причинам, таким как отсутствие подключения к Интернету.

Решение:

На стороне клиента, прежде чем показывать кнопку «Google Кошелек», вы проверяете, принадлежит ли уже элемент «abc».

  • если да, проверьте еще раз на сервере
  • если нет, покажите кнопку «Google Кошелек».

Покупка покупка = mInventory.getPurchase ('abc');

if (purchase != null) // Verify with server 

else // show Google Wallet button

https://developer.android.com/google/play/billing/billing_reference.html#getSkuDetails


4
Я не понимаю, почему проверка на сервере более безопасна, чем проверка в приложении. В конце концов, это приложение, которое разблокирует функции, поэтому все еще можно удалить или инвертировать код в приложении, которое проверяет, является ли ответ сервера «ОК»
— Джанлука Геттини

2
@GianlucaGhettini, потому что иногда сервер - это то, что предоставляет приобретенную услугу, а не приложение, кроме того, приложение может быть реконструировано, а затем с некоторой трудностью процесс проверки может быть взломан.
— Mohyaddin Alaoddin
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.