Эссе о разработке игр, мышлении и книгах

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

Типы покупок в Google продумывал либо гений (и я не понимаю всей глубины задумки) либо программист уровня middle (и тогда он допустил прямо классическую middle-овскую ошибку).

Итак, у Google есть типы покупок: inapp-покупки (in application) и подписки. А вот у inapp покупки есть ФЛАГ, который указывает «потреблена» покупка или нет. Флаг устанавливает клиентское приложение. При каждом запуске приложение получает список непотреблённых покупок и должно решать что с ними делать. Те покупки, которые мы считаем non-purchasable, мы не потребляем и получаем их каждый раз при запуске приложения.

Вот когда я это осознал, у меня в голове сразу штук 5 прикольных косяков нарисовалось, которые могут угробить небольшого размера контору. Начиная с того, что из-за ошибки приложение внезапно «потребит» весь вечный контент и при следующем запуске заблокирует всем пользователям доступ к нему.

Собственно об ошибке. Про классичность — это, конечно, сугубо моё нескромное мнение, основанное на моём же опыте. Суть косяка заключается примерно в следующем:

  1. Необходимо поддерживать сложную систему сущностей и отношений между ними.
  2. В какой-то момент, появляется необходимость внести изменения в эту систему, например, добавить большой кусок новой логики.
  3. Правильный путь — поддерживать максимально плоскую систему типов, минимизировать количество уровней иерархии и связей между ними. Поскольку это несёт благо и процветание.
  4. Но в мозгу внезапно начинает свербить. Можно же сделать проще и архитектура от этого станет только «красивее» (иерархические схемки всегда круче смотрятся, правда?). Достаточно разбить один тип на несколько подтипов (а в случае с Google даже это не сделано явно).
  5. Мы экономим своё время, красиво перерисовываем схемки и всячески радуемся себе.
  6. Мы не думаем о том, что тем самым увеличили страдания всех пользователей нашей системы.
  7. Страдающие пользователи портят карму. До скончания времён!
  8. Отрицательная карма всегда настигает… Обычно, когда через год приходится добавлять ещё одно сложное поведение в ту же систему.

Мораль

Когда делаешь что-то сложное, не @#$@!$@$ выпендривайся и делай просто.