Встроенные покупки и подписки: как сделать правильно
Встроенные покупки и подписки в мобильном приложении: правила магазинов, проверка покупок на сервере, восстановление и частые ошибки.
Встроенные покупки — это оплата цифрового внутри мобильного приложения через систему магазина: StoreKit у Apple и Google Play Billing у Google. Деньги списывает магазин, он же отвечает за возвраты и отмену подписок, а твоя задача — правильно понять, что человек купил, и выдать ему доступ.
На демо покупка выглядит просто: нажал кнопку, увидел системное окно, получил премиум. В жизни подписки продлеваются, не продлеваются, возвращаются, переходят на другой телефон, и всё это должно попасть в твою базу.
Что продаётся через встроенные покупки
Правило в общих чертах: всё цифровое, что потребляется внутри приложения, идёт через магазин. Подписки, премиум-функции, кредиты, внутриигровая валюта, контент. Физические товары и услуги в реальном мире — доставка, такси, билеты на концерт — оплачиваются обычным платёжным провайдером.
Видов покупок три:
- расходуемые — кредиты, монеты: купил, потратил, можно купить снова;
- нерасходуемые — разблокировка навсегда: покупается один раз;
- подписки с автопродлением — доступ на период, который продлевается сам.
Цены задаются в консоли магазина, а не в коде. На экране покупки показывай цену, которую вернул магазин: она уже в валюте и формате страны пользователя. Для подписки рядом должны быть срок, цена и честная фраза о том, что она продлевается автоматически. Отменить подписку человек может только в настройках магазина — ты сделать это за него не можешь, но можешь показать, где это делается.
Проверка покупки на сервере
Главное правило: приложение не решает, что человек премиум. Это решает сервер.
После покупки приложение получает от магазина подтверждение — подписанную транзакцию у Apple, токен покупки у Google. Оно отправляет его на твой сервер, а сервер проверяет его у самого магазина через серверный API: настоящая ли покупка, для какого продукта, до какого числа действует. Только после этого сервер записывает доступ.
Хранить доступ удобно в одной таблице, независимо от того, откуда пришли деньги:
create table entitlements (
user_id bigint not null,
product text not null,
source text not null, -- apple, google, web
expires_at timestamptz,
original_txn text not null unique
);
Уникальный идентификатор исходной транзакции защищает от двойного начисления, когда одну и ту же покупку прислали и приложение, и уведомление магазина.
Серверные уведомления магазинов
Продление, отмена, возврат денег, проблема с картой — всё это происходит без участия приложения, иногда когда оно не открывалось неделями. Узнать об этом можно только из уведомлений, которые магазин присылает на твой сервер: App Store Server Notifications у Apple и Real-time developer notifications у Google.
Подключи их с первого дня. Без них подписка, за которую вернули деньги, останется активной навсегда, а продлённая будет выглядеть истёкшей, пока человек не откроет приложение. Учти и льготный период: если карта не прошла, магазин какое-то время пытается списать снова, и обрывать доступ в первую же минуту не стоит.
Восстановление покупок
Кнопка «Восстановить покупки» обязательна — без неё Apple отклонит приложение. Она нужна человеку, который сменил телефон или переустановил приложение: магазин отдаёт его прошлые покупки, а ты снова выдаёшь доступ.
Заранее реши, что делать, если покупка сделана с одного аккаунта в приложении, а восстанавливают её из-под другого. Перенести доступ на текущий аккаунт или сказать «эта подписка привязана к другому входу» — оба варианта нормальны, плохо не решить никак.
Частые ошибки
- Доступ выдаётся по флагу из приложения, без проверки на сервере.
- Доступ выдаётся в момент нажатия, а не после подтверждения покупки.
- На iOS транзакция не завершается после выдачи доступа, и магазин присылает её снова при каждом запуске.
- На Android покупка не подтверждается (acknowledge) в течение нескольких дней, и Google автоматически возвращает деньги.
- Возвраты не обрабатываются, и доступ остаётся у тех, кто получил деньги назад.
- Цена зашита в интерфейс текстом и расходится с реальной.
- Продукты не отправлены на проверку вместе со сборкой, и проверяющий не может ничего купить.
Тестируй в песочнице: у Apple есть тестовые аккаунты, у Google — лицензионные тестировщики, и в обоих случаях подписки продлеваются ускоренно, так что месяц проходит за минуты.
Можно не писать всё это самому — есть сервисы, которые берут на себя проверку покупок и уведомления. Это экономит время, но добавляет зависимость, так что таблица доступов на твоей стороне всё равно нужна.
Коротко
- Цифровое в приложении продаётся через встроенные покупки, физическое — обычной оплатой.
- Доступ выдаёт сервер после проверки покупки у магазина, а не приложение.
- Серверные уведомления магазинов нужны с первого дня: продления, отмены, возвраты.
- Кнопка восстановления покупок обязательна, и правило для смены аккаунта решается заранее.
- Завершай транзакции на iOS и подтверждай покупки на Android.
SUBXCAT может собрать покупки и подписки так, чтобы доступ в базе совпадал с тем, за что люди заплатили.