Non-consumable purchases
На этой неделе столкнулся с замечательным примером того, что даже умные люди способны реализовать одну и туже функциональность в одном и том же случае тремя разными способами (наверняка даже большим количеством, но нам пока только три потребовалось).
Речь пойдёт о non-consumable покупках/товарах в мобильных магазинах Apple, Google и Amazon. Non-consumable — это разовые покупки, которые нет необходимости повторять. Например: наборы уровней, вечный VIP, отключение рекламы. Consumable, наоборот — это расходники — золото, энергия, сундуки и прочий f2p шлак.
Из типов покупок ещё есть разные подписки и некоторые специфичные для конкретных магазинов типы (тоже, кстати, странное явление), но в данном контексте они нам не нужны.
Ситуация, как видите, одна и та же: в магазине мобильных приложений нужно сделать конкретный тип покупок и способ взаимодействия разработчиков и пользователей с ним. Теперь смотрите за руками, пойдём от менее прикольного к более интересному.
Всё в нюансы рассказывать не буду. Просто опишу конкретный use-case — определение типа покупки. На мой взгляд, уметь легко получить тип покупки — это вполне здравое желание.
Примечание: всё описанное ниже относится к server-server api.
Amazon
Сделал правильнее всех. У него вообще API выглядит существенно приятнее, чем у остальных.
Вводится тип покупки Entitlements, что в переводе на человеческий означает «факт наличия права на что-то» и полностью соответствует сути. При получении информации о покупке, разработчику возвращается её тип. Всё.
Apple
Тоже сделали для этих покупок отдельный тип. Его так и назвали non-consumable. Слегка запутано, но ладно. Однако возвращать тип они уже не хотят. Поэтому при обычной проверке определить что тебе пришло нельзя.
Но у всех покупок есть два параметра:
- Transaction Identifier - идентификатор текущей транзакции
- Original Transaction Identifier - идентификатор оригинально транзакции, если текущая транзакция восстанавливает уже купленный товар (например, пользователь удалил приложение, поставил его заново и нажал соответствующую кнопку).
В первый раз они одинаковые. А после восстановления уже разные. И завязываться на это, конечно, нельзя.
Типы покупок в Google продумывал либо гений (и я не понимаю всей глубины задумки) либо программист уровня middle (и тогда он допустил прямо классическую middle-овскую ошибку).
Итак, у Google есть типы покупок: inapp-покупки (in application) и подписки. А вот у inapp покупки есть ФЛАГ, который указывает «потреблена» покупка или нет. Флаг устанавливает клиентское приложение. При каждом запуске приложение получает список непотреблённых покупок и должно решать что с ними делать. Те покупки, которые мы считаем non-purchasable, мы не потребляем и получаем их каждый раз при запуске приложения.
Вот когда я это осознал, у меня в голове сразу штук 5 прикольных косяков нарисовалось, которые могут угробить небольшого размера контору. Начиная с того, что из-за ошибки приложение внезапно «потребит» весь вечный контент и при следующем запуске заблокирует всем пользователям доступ к нему.
Собственно об ошибке. Про классичность — это, конечно, сугубо моё нескромное мнение, основанное на моём же опыте. Суть косяка заключается примерно в следующем:
- Необходимо поддерживать сложную систему сущностей и отношений между ними.
- В какой-то момент, появляется необходимость внести изменения в эту систему, например, добавить большой кусок новой логики.
- Правильный путь — поддерживать максимально плоскую систему типов, минимизировать количество уровней иерархии и связей между ними. Поскольку это несёт благо и процветание.
- Но в мозгу внезапно начинает свербить. Можно же сделать проще и архитектура от этого станет только «красивее» (иерархические схемки всегда круче смотрятся, правда?). Достаточно разбить один тип на несколько подтипов (а в случае с Google даже это не сделано явно).
- Мы экономим своё время, красиво перерисовываем схемки и всячески радуемся себе.
- Мы не думаем о том, что тем самым увеличили страдания всех пользователей нашей системы.
- Страдающие пользователи портят карму. До скончания времён!
- Отрицательная карма всегда настигает… Обычно, когда через год приходится добавлять ещё одно сложное поведение в ту же систему.
Мораль
Когда делаешь что-то сложное, не @#$@!$@$ выпендривайся и делай просто.
Читать далее
- Считаем бизнес-план для игры в Steam
- Концепт-документ игры NoCraft
- Концепт-документ игры Space Opera Engine
- Следующий фронтир геймдизайна
- Концепт-документ «Сказки»
- Модная типизация в Python
- Концепт-документ игры Сказания
- Концепт-документ игры News Makers
- Концепт-документ ММО про исследование космоса
- Концепт-документ игры Рой