Мы наконец-то доделали большое обновление для Бурить-Копать!, в котором в том числе появились внутриигровые покупки.
В Яндекс Игры добавить покупки было довольно легко. Товары заводятся в консоли, без модерации. В SDK присутствуют 4 метода - необходимый минимум для покупок:
1) Получить список продуктов -
ysdk.payments.getCatalog()2) Купить -
ysdk.payments.purchase()3) Списать покупку -
ysdk.payments.consumePurchase()4) Проверить несписанные покупки -
ysdk.payments.getPurchases()Я ожидал, что в Играх ВКонтакте будет примерно такой же набор методов и такая же логика работы. Но я очень ошибался.
В админке ВКонтакте нельзя указать список товаров. Вместо этого, когда клиент игры инициирует покупку, ВКонтакте отправляет на твой сервер запрос, чтобы получить информацию о товаре: его цену, название и т. д.
Окей, вроде не сложно. Сервер для лидербордов у нас уже имеется, завожу табличку с товарами, буду отвечать на запросы ВК. Ну и пусть клиент тоже тогда берёт список покупок тоже с сервера, чтобы был единый источник данных.
Инициируем запрос на покупку (VKWebAppShowOrderBox), информация о товаре показывается, всё круто. У метода даже есть callback на случай успешной покупки. Ура. Готово. Но не работает.
Так, оказывается покупку нужно подтвердить на сервере. Ну ладно, допустим. Пишу простенький endpoint, который подтверждает покупки. Всё, всё работает, покупка проходит, товары начисляются.
Так, стоп, а как быть, если оплата прошла, но дальше у клиента пропал интернет и мы не смогли начислить награду и сохранить данные? Где этот механизм
getPurchases/consume?А нет его. Его нужно реализовать самостоятельно, на своем сервере, на основе уведомлений. Оказывается, это не просто подтверждения, а прям callback для того, чтобы ты на своем сервере реализовал логику заказов.
Стискиваю зубы, курю API, который шлёт данные в не особо приятном формате, создаю табличку с покупками, заполняю её на основе уведомлений от ВК, пишу endpoint'ы получения и списания покупок, пишу к ним клиентскую часть.
В какой-то момент, приходит осознание, что
consume endpoint должен быть защищен. Чтобы злоумышленник не мог списать чужие покупки. Стискиваю зубы ещё сильнее, нахожу механизм проверки подлинности данных, добавляю подпись к моим consume запросам, матерюсь, потому что из-за JS-кода всё приходится делать на callback'ах. Начинаю писать серверную валидацию подписи - не работает. Матерюсь - не работает. Подключаю нейросети - не работает. Забиваю на java, копирую их пример на PHP, запускаю - не работает. Сто раз перепроверяю все входные данные для подписи - не работает. Методом тыка, выясняю, что ВК не умеет подписывать любую строку, в частности JSON. Заворачиваю мой JSON в base64 - работает.
Приводу код в порядок. Дохожу до кода, который начисляет внутриигровую валюту и списывает покупку. В каком порядке вызывать методы? Яндекс предлагает сначала начислить, сохранить данные, и только потом списывать покупку. Ок, но у Яндекса uptime серверов явно побольше моего. В случае отказа на стороне моего сервера, игрок получит ресурсы, а покупка не будет списана, и в следующий раз он ещё раз получит ресурсы. Ситуация редкая, может забить?
Не могу забить. Добавляю в сохранение поле, в которое буду записывать начисленные, но не списанные покупки. Так появляется какая-никакая атомарность, и второй раз за одну и ту же покупку награда не начислится.
Всё, теперь всё. Проверяю, всё работает. Обновление уже доступно ВК. По времени заняло около 12-15 человеко-часов. Стал бы я этим заниматься, если бы знал, сколько всплывёт нюансов? Наверное, да, мое решение гибкое, с возможностью расширения под другие площадки.
Но вам я такого не посоветую. Тем более, что
У Game Push это давно реализовано
P. S. А ещё ВКонтакте может прислать уведомление о refund'e, т. е. возврате покупки, и ты должен забрать у пользователя то, что ты ему начислил. Я забил на это, ничего забирать не буду. Помечаю покупку как refunded и всё. Если будет много возвратов, вернусь к этому вопросу.
#лидерборды #инапы #программирование #самый_крутяк