Как подтвердить покупку для приложения 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

thanhbinh84
источник
4
Я не понимаю, почему проверка на сервере более безопасна, чем проверка в приложении. В конце концов, это приложение, которое разблокирует функции, поэтому все еще можно удалить или инвертировать код в приложении, которое проверяет, является ли ответ сервера «ОК»
Джанлука Геттини
2
@GianlucaGhettini, потому что иногда сервер - это то, что предоставляет приобретенную услугу, а не приложение, кроме того, приложение может быть реконструировано, а затем с некоторой трудностью процесс проверки может быть взломан.
Mohyaddin Alaoddin