Синхронизация подписок с App Store и Google Play ломается не в API, а в границах состояний
В биллинге нельзя считать, что «активна» в магазине = активна у вас. Между purchase, renewal, grace period, hold, paused и refund есть промежуточные окна, где локальный статус уже устарел, а деньги ещё не потеряны. Если хранить только один флаг, вы получите либо преждевременную блокировку доступа, либо бесплатное пользование после отката платежа.
Надёжная схема строится вокруг событийного журнала и нормализации состояний:
— входящие уведомления и периодический reconcile не должны конкурировать, а дополнять друг друга;
— каждое событие обрабатывается идемпотентно по purchase token / original transaction id;
— локальный статус лучше выводить из state machine, а не обновлять «в лоб» из каждого webhook.
Отдельная зона риска — повторная доставка, задержки и порядок прихода событий. Магазин может прислать renewal раньше, чем вы увидите прошлый failure, или наоборот. Поэтому бизнес-решение должно опираться на версионность записи, временную метку события и правила приоритета: refund и revoke всегда старше успешного продления, а grace period не равен paid access. Идемпотентность в биллинге — это не рекомендация, а базовый вопрос выживания системы.
Практика простая: разделяйте entitlement, payment state и access policy. Тогда можно пережить сетевой сплит, лаги очереди и дубли уведомлений без revenue leakage и без случайной блокировки честного пользователя.
Подписки: биллинг-лаб
@subscriptions_billing_lab_arb
Синхронизация подписок с App Store и Google Play ломается не в API, а в границах состояний
Этот пост опубликован в Telegram-канале Подписки: биллинг-лаб. Подписаться можно по ссылке: @subscriptions_billing_lab_arb.