Як монетизувати додаток за допомогою віртуальної валюти

TL;DR:
- Віртуальна валюта перетворює покупку на баланс, який витрачає ваш додаток. Нарахування, списання та облік.
- Покупки в App Store і Google Play нараховують кредити автоматично. Усе інше, включно з вебплатежами, винагородами та промо, ви нараховуєте через API.
- Списання сьогодні відбувається з вашого сервера, а методи SDK ще в розробці.
- Куплені кредити та кредити, нараховані підпискою, зберігаються окремо — саме тому згоряти можуть лише другі.
Кредити доречні тоді, коли щось у вашому додатку коштує вам грошей щоразу, коли цим користуються. Генерація, рендер, експорт, епізод. Якщо ваші витрати не змінюються незалежно від того, скільки люди користуються продуктом, тариф підписки — простіша відповідь, і на цьому можна зупинитися.
Для решти налаштування валюти в дашборді займає півдня. Довше триває все, що після: домогтися схвалення consumable від Apple ще до того, як з'явиться інтерфейс кредитів, вирішити, що стається з балансом при перевстановленні, і з'ясувати, яким кредитам взагалі дозволено згоряти.
Одна річ, яку варто знати перед стартом. Кредити нараховуються автоматично, коли хтось купує прив'язаний продукт в App Store або Google Play. Якщо ви продаєте через Stripe або Paddle, жодне нарахування само не спрацює, тож ваш бекенд викликає API нарахування після того, як власний вебхук підтвердить платіж.
Якщо ви ще не створили валюту, швидкий старт охоплює частину з дашбордом. Ця стаття — про все інше.
Що таке віртуальна валюта в додатку?
Віртуальна валюта — це баланс, який ваш додаток нараховує та витрачає від імені користувача. Хтось купує 500 монет за $4.99, потім витрачає 50, щоб відкрити епізод, 10, щоб згенерувати зображення, і 200, щоб придбати курс. Кредити з'являються й без покупки за ними: реферальний бонус, щоденний безкоштовний ліміт, жест доброї волі після того, як щось пішло не так. У будь-якому разі число рухає ваш додаток.

Назви змінюються залежно від категорії. AI-додатки кажуть «кредити» або «токени», драма-додатки — «монети», ігри — «геми» чи «золото», мовні додатки — «сердечка». Механіка під ними та сама, і в обох сторах вона продається як розхідні внутрішні покупки.
Працювати це змушують три частини.
Нарахування додає до балансу — або автоматично, коли хтось купує прив'язаний продукт чи продовжує підписку, або через виклик API, коли ви хочете видати кредити з будь-якої іншої причини.
Списання знімає кредити за дію у вашому додатку.
Облік фіксує кожну зміну, щоб ви могли відповісти на тікет у підтримку про те, що кредити зникли.
Покупки та продовження в сторах нараховують самі. Усе на боці списання — це код, який пишете ви.
Чи потрібен вам бекенд?
Для списання — сьогодні так.
Ваш додаток постачається з публічним API-ключем, і витягти його з бінарника — справа кількох хвилин. ID користувачів знайти так само легко. Якби клієнт міг списувати кредити лише цими двома значеннями, хтось міг би спорожнити баланс будь-якого користувача взагалі ззовні вашого додатка, і жодна кількість коду на боці додатка цього б не спинила.
Секретний ключ ніколи не залишає ваш сервер, тож саме там відбувається списання. Методи SDK для читання та списання балансу ще в розробці, і саме модель безпеки пояснює, чому вони займають стільки часу.

ℹ️Надсилайте запит на списання до того, як запускати дорогу роботу, а не після. Тоді саме списання є вашою перевіркою, і вам залишається тільки повернути кредит, коли генерація впала. Про це повернення — нижче, бо там є нюанс.
Що робить баланс кредитів надійним?
Баланс кредитів — це гроші, які ви вже отримали, але ще не заробили, що робить його ближчим до банківського рахунку, ніж до очок у грі. Банківські рахунки мають властивості, яких немає в числа в базі даних, і саме тут розсипається більшість саморобних реалізацій.
Почніть з історії. Кожне нарахування, списання, згоряння та повернення потрапляє в профіль користувача разом із причиною, тож коли хтось пише, що його монети зникли, ви можете піти й подивитися. Саморобні баланси зазвичай зберігають поточне число і більше нічого — і на такий тікет відповіді немає.
Далі — що відбувається, коли два списання приходять одночасно. Списання блокує баланс цього користувача, доки виконується, тож другий запит чекає своєї черги, бачить, що балансу бракує, і отримує помилку замість того, щоб загнати баланс у мінус. Повтори покриті окремо: надішліть ключ ідемпотентності, і повторний запит поверне початковий результат, а не спише вдруге.
Що людей дивує — кредити пам'ятають, звідки вони взялися. Ті, що користувач купив, лежать окремо від тих, які нарахувала підписка, і саме цей поділ дозволяє спалити ліміт, не чіпаючи пакети. Кредити, що згоряють, також витрачаються першими, тож користувач спалює ліміт цього місяця, перш ніж торкнутися оплаченого пакета.
Усе це лежить на серверах Adapty, кожна транзакція зашифрована, є сертифікація SOC2 Type II і відповідність GDPR — це і є відповідь, коли ваша служба безпеки питає, хто тримає баланс.
Що робити, коли генерація впала вже після списання?
Ви повертаєте кредит через API — і повернений кредит стає безстроковим.
Кредити, нараховані через API, не мають правила згоряння, оскільки термін дії налаштовується на зв'язку між валютою та продуктом, а за нарахуванням через API продукту немає. Тож кожна повернена генерація перетворює частину ліміту користувача, що згоряє, на баланс, який ніколи не обнуляється. Додаток із 5% збоїв тихо накопичує безстрокові кредити у своїх найактивніших користувачів — рівно протилежне до того, для чого існує ліміт.
Є два способи з цим жити. Відстежуйте повернені кредити на своєму боці й враховуйте їх, коли визначаєте розмір наступного ліміту. Або притримуйте списання, доки генерація не завершиться, тобто тримайте запит на списання, доки не отримаєте результат, і приймайте ризик конкурентних запитів у цей проміжок. Повноцінна операція hold-and-confirm є в роадмапі і зробить це чистішим.
У будь-якому разі логуйте повернення разом із причиною. Коли користувач запитає, чому змінився його баланс, вам знадобиться відповідь.
Як протестувати флоу покупки до того, як Apple схвалить ваш consumable?
Використайте файл конфігурації StoreKit у Xcode.
У цю пастку потрапляє багато команд. Список ваших продуктів повертається порожнім, доки Apple не схвалить consumable. Apple не схвалить consumable, доки ваш додаток не продемонструє ту саму реалізацію кредитів, яку вони перевіряють. Тож ви будуєте всю економіку, жодного разу не побачивши відповіді на покупку, а якщо клієнт хоче доказ, що флоу працює, ще до того, як ви його побудуєте, показати нічого.
Файл конфігурації StoreKit розриває це коло. Оголосіть продукти локально в Xcode, і StoreKit віддаватиме їх вашому додатку без участі App Store Connect. З'являється діалог покупки, спрацьовує ваш обробник, і ви можете показати повний флоу на продуктах, яких ще не існує. Sandbox-акаунти перевіряють справжній шлях через стор пізніше, коли продукт схвалять.
Розробляйте на локальному файлі конфігурації, подавайте на розгляд, коли інтерфейс кредитів готовий, і перевіряйте в sandbox. Та сама послідовність для iOS, React Native та Flutter.
Чи можуть кредити згоряти?
Ліміти за підпискою — так. Куплені кредити — ні.
Пункт 3.1.1 керівництва Apple каже, що кредити або внутрішньоігрові валюти, придбані через внутрішню покупку, не можуть згоряти, і що ви повинні мати механізм відновлення для відновлюваних покупок. Це стосується пакетів монет і наборів кредитів. За них хтось заплатив живі гроші, тож вони залишаються.
Ліміт за підпискою — інша річ. Пункт 3.1.2(a) дозволяє підпискам включати розхідні кредити, геми та внутрішньоігрові валюти, і щомісячне нарахування є перевагою поточного платіжного періоду, а не покупкою кредитів. Оскільки два пули обліковуються окремо, згоряння застосовується лише до нарахованого.
Спалювати ліміт варто. Підписник, який місяць за місяцем відкладає кредити, врешті накопичує достатньо, щоб скасувати підписку й далі користуватися продуктом, а ваш звіт про відтік звинуватить щось інше. Обнуляйте ліміт при продовженні — і невитрачений баланс перестає бути виходом, а активні користувачі купують пакети замість того, щоб проїдати запас. Списання кредитів, що згоряють, першими це підтримує: користувач з обома пулами витрачає ліміт, перш ніж торкнутися купленого, тож саме ліміт залишається тим, за продовження чого він платить.
Рев'юери читають описи продуктів буквально. В одному відхиленні написали, що кредити, які надає IAP-продукт розробника, мають термін дії; насправді їхня валюта не згоряла — згоряло 30-денне вікно доступу, вшите в consumable. Описуйте пакети як безстрокові, а ліміти — як частину періоду підписки, і не змішуйте їх в одному продукті.
ℹ️Два суміжні правила: Apple вимагає, щоб валюта, куплена через IAP, споживалася всередині додатка, а не витрачалася на товари реального світу, а Google обмежує внутрішні віртуальні валюти тим додатком, у якому їх придбали.
Що стається з балансом, коли користувач виходить з акаунта або перевстановлює додаток?
Баланс належить профілю, тож усе зводиться до того, чи є у вас стійкий ID користувача в момент нарахування кредитів.
Ламається все на анонімних користувачах. Хтось купує 500 монет, перевстановлює додаток — і баланс зник, бо нове встановлення створило новий анонімний профіль. Документація Adapty каже це прямо: анонімний користувач зберігає баланс на тому пристрої, де його отримав.
Відновлення покупок теж не рятує. Відновлення відтворює покупки; воно не відбудовує баланс, який користувач уже витратив, і не повинно.
Ідентифікуйте до нарахування. Якщо користувач може купити кредити раніше, ніж у вас з'явиться стійкий ID для нього, баланс уже під ризиком, і кожен наступний крайній випадок стає складніше осмислити.
А що з покупками, зробленими до того, як ви додали кредити?
Заднім числом нічого не нараховується. Кредити починають накопичуватися з моменту, коли валюту прив'язано до продукту, тож користувач, який купив пакет минулого місяця, на момент запуску має нульовий баланс.
Плануйте це заздалегідь, бо саме такий тікет ви отримуватимете найчастіше в перший тиждень. Пройдіться по наявних клієнтах і нарахуйте їхні баланси через API до запуску або повідомте дату старту в додатку й дайте підписникам отримати перший ліміт при наступному продовженні.
Підсумок
Робота в дашборді — це півдня. Дорого коштують тихі рішення: нарахування кредитів анонімному профілю, опис пакета у формулюванні, яке провалює перевірку в App Store, запуск без попередження наявних користувачів, що їхній баланс стартує з нуля.
Adapty нараховує кредити при покупці та продовженні, тримає баланс на рівні профілю з блокуванням на кожне списання, відокремлює куплені кредити від нарахованих і логує кожну зміну. Замовте демо або почніть зі швидкого старту.