Як я став першим, хто підключив LiqPay до застосунку на Flutter
Усім привіт, це Олег Новосад, я IT-архітектор, викладач, а тут я виступаю ще й як автор гейміфікованого мобільного застосунку «Давай займемось текстом», який ми робимо для того, щоб українською мовою охоче почали говорити щонайменше 40 мільйонів людей.
Перший реліз відбувся 25 листопада 2022 — і з того часу вдалося досягти непоганих результатів: ми в десятці найкращих застосунків України на App Store, побували на першому місці в категорії «Освіта», перемогли в конкурсі стартапів та здобули нагороду за соціальний внесок.
Також завдяки нашій дещо нестандартній моделі монетизації застосунок водночас доступний абсолютно для всіх користувачів незалежно від рівня їхніх доходів, а також заробляє сам на себе. Чому і як я підключав платіжну систему LiqPay до Flutter, а також про те, що з цього вийшло, ви дізнаєтеся з цієї статті.
Модель монетизації
Призначення застосунку в нас незвичне: він закохує в українську мову. Іншими словами, він:- руйнує стереотипи щодо мови в зросійщених українців та спонукає їх переходити на українську;
- відкриває очі українськомовним українцям на те, скільки мовних скарбів у нас є і як ними можна користуватися собі на радість та користь.
Отже, ми робимо його для всіх мільйонів дорослих українців, що живуть вдома або за кордоном. Як бачите, у нашого стартапу не тільки бізнесові цілі, але ще й соціальні.
До того ж ми звичайні люди, застосунок — це наша ініціатива, нас ніхто не фінансує. Тому ми від самого початку виходили з того, що він мусить, по-перше, бути доступним для всіх, по-друге, окупити витрати на розробку, підтримку та розвиток.
Після міркувань, ми сформували модель монетизації наближену до Freemium, але з певними відмінностями. Дохід у нас формується з чотирьох джерел:
- Реклама.
- Підписки, що позбавляють від реклами та додають комфорту користування.
- Внутрішні покупки (ігрова валюта та додатковий контент).
- Донейти.
Ми з співавторкою застосунку Наталею Місюк маємо домовленість про те, доходи з яких джерел покривають які саме статті розходів, тож нам було потрібно, щоб донейти та гроші від додаткового контенту потрапляли на один рахунок, а гроші від реклами та підписок — на інший. Ми почали шукати платіжну систему, яка була б зручною як для користувачів, так і дозволяла б реалізувати все те, що ми придумали.
До речі, чому застосунок саме на Flutter, я розповідав ось тут.
Вибір платіжної системи
Задача підібрати підхожу платіжну систему виявилася не дуже простою. Ми розглядали Stripe, але він не працював з Україною, тож ми від нього відмовилися. Був варіант підключити portmone, але в Наталі з ними неприємно склалося спілкування: вони не вміли працювати з донейтами, а також наговорили зайвого. Хоча вони потім були змушені вибачитись, неприємний осад усе ж залишився, і працювати з ними ми не хотіли.
У поле зору потрапили також Fondy та LiqPay. Перший Наталя обрала, щоби підключити донейти до сайту на Wix. Було не дуже зрозуміло, чи вдасться застосувати для застосунку теж. А от LiqPay на перший погляд обіцяв легку інтеграцію, хорошу службу підтримки та широкий спектр варіантів оплати. Також LiqPay добре знайомий користувачам, бо вони часто його бачать на різних сайтах, тож знають його інтерфейс та довіряють йому.
Були ще й інші варіанти, але вони значно поступалися привабливістю LiqPay. Принаймні, на той час та за тими даними, що були в нашому розпорядженні.
Варіант підключення перший
Перший варіант підключення, про який я подумав та побачив за їхніми відкритими API, це оплата через введення даних картки. Тобто коли користувач хотів би придбати навчальний курс, йому потрібно було б ввести номер картки, ім’я, термін дії,
LiqPay надавав тестові картки для перевірки. Я спробував підключити такий варіант — і ніби вийшло. Я робив запит на оплату, мені приходила відповідь — усе куплено, усе працює, усе добре. Настільки добре, що аж підозріло. Я до того вже стикався з платіжними системами, щоправда з іншими, і знав, що тут часто виникають складнощі, тому мені не вірилось, що все дійсно може бути так легко.
Я прочитав купу статей, блогів, форумів про те, як зробити оплату за принципом наглого введення даних картки, та був уже навіть почав писати користувацький інтерфейс, для того щоб користувачу не треба було щоразу заповнювати поля, а дані якимось чином зберігалися. Також знайшов у LiqPay можливість авторизувати певну картку з отриманням спеціального токену для подальших покупок та потім побачив, як дрібненьким шрифтом на сайті LiqPay було написано, що для такого типу інтеграції мені треба мати спеціальні сертифікації, підтвердження і все в такому дусі.
І тут я зрозумів, що в нашому випадку це шлях у нікуди. Якщо я почну займатися питаннями сертифікацій, є ризик, що реліз застосунку сильно затягнеться і взагалі навряд чи колись відбудеться. Тож від такого способу підключення платіжної системи довелося відмовитися. Проте, я наводжу тут шматочок коду, щоби проілюструвати саму можливість інтеграції такого типу. Якщо хтось із вас уже має необхідні підтвердження, то такий варіант може стати в пригоді:
final liqPay = LiqPay("public_liqpay_key", "private_liqpay_key"); final number = "4242424242424242"; final expirationMonth = "12"; final expirationYear = "99"; final cvv = "000"; final card = LiqPayCard(number, expirationMonth, expirationDate, cvv); final order = LiqPayOrder(const Uuid().v4(), 1, 'Test', card: card, action: LiqPayAction.pay); await liqPay.purchase(order);
Варіант підключення другий
LiqPay популярний. Якщо ви хочете щось замовити в інтернеті — їжу в ресторані, доставку питної води, ще щось, — то дуже ймовірно, що в багатьох випадках оплата буде проходити саме через нього. Фактично, користувач заходить на сайт, звідки потрапляє у віконечко LiqPay — безпечне середовище платіжної системи — де вводить дані картки. Це звично, у людей не виникає питань «хто це тут просить дані моєї картки?», усе добре та надійно.
Крім того, ще додається можливість оплати через Apple Pay або Google Pay чи Privat24, що дуже зручно й чого не зробиш у першому варіанті підключення, про який я розповів вище.
Як реалізувати підключення через таке віконечко? У принципі, технічно це не дуже складно. Всередині мобільного застосунку можна відкривати вебсторінки — і, власне, саме це ми й робимо. Щоразу, коли користувач у нас хоче придбати курс або зробити донейт, він бачить насправді вебсторінку LiqPay і все, що стосується оплати, відбувається в ній.
Щоправда, є тонкий момент: крім проведення безпосередньо оплати, ще потрібно якимось чином передати додаткові дані: який саме курс було куплено, яким був розмір донату, чи користувач написав щось у коментарі до нього тощо.
Усе це критично важливо як для обліку та аналітики, так і для того, щоб усе коректно працювало: куплені курси ставали доступними для користувачів, а якщо оплата не пройшла з якоїсь причини, то не діставалися користувачам на халяву. Як це реалізувати?
Робота напряму означала б написання дуже великої кількості коду, тому я шукав бібліотеки для роботи з LiqPay — і не знаходив. Тоді я вирішив створити свою, яку й застосував для застосунку. Вона вже знаходиться у публічному доступі на pub.dev, вона open-source, тому при бажанні ви також можете долучитись та покращувати її, і це поки що перша та єдина така на Dart/ Flutter.
final order = LiqPayOrder( const Uuid().v4(), amount, commentController.text.isEmpty ? "Донат" : commentController.text, serverUrl: '$serverUrl/OnDonate?user_id=${user?.id}', currency: LiqPayCurrency.uah, language: LiqPayLanguage.uk); final url = await getIt<LiqPay>().checkout(order); if (mounted) { Navigator.of(context).push( MaterialPageRoute( builder: (context) => BrowserScreen(url: url, title: "Донат") ) ); }
Отже, я знайшов та запровадив рішення — і це добре. Проте, тішитися було рано: з досвіду роботи я знав, що Apple в принципі не любить, коли застосунки запускають браузери. Це проблема, адже деякі частини застосунку (наприклад, Політика конфіденційності, Правила використання тощо) у нас відкриваються саме в такий спосіб. Чи дозволить Apple оплату через віконечко LiqPay, я не був впевнений. Але дізнатися напевно можна було тільки якщо спробувати.
Почався процес інтеграції. Служба підтримки LiqPay не могла дати чіткої відповіді, яким чином і куди я маю отримати відповідь про те, що оплата пройшла. Чи ця відповідь мала приходити в сам застосунок, наприклад, callback`ом, чи кудись у небо, де я мав її впіймати — не вдавалося з’ясувати.
Мені довелося далі копирсатися в документації, де в результаті я і знайшов, знову дрібненьким шрифом, що мені потрібно самому вказати посиланням, куди я хочу отримати відповідь. Іншими словами, це мав бути якийсь сервер, куди сервер LiqPay буде надсилати запит «ось, тобі прийшла оплата». Далі в документації довго описувалися система кодування відповіді та запиту.
Мені довелося написати Firebase-функції, які в нас використовуються для курсів та донатів. Нижче наведено приклад такої для донатів:
/** * Function to react on donate. * Should be triggered by LiqPay server with purchase result. * * Query param should contain `user_id`. * * @param {functions.https.Request} request * @param {express.Response} response */ export async function onDonate( request: functions.https.Request, response: express.Response ) { const data = request.body["data"]; const signature = request.body["signature"]; functions.logger.debug(`Data: ${data}`); functions.logger.debug(`Signature: ${signature}`); const verified = Verifier.verifySignature(data, signature); if (!verified) { functions.logger.error("Signatures do not match."); response.status(403).send("Forbidden"); return; } const userId = request.query["user_id"] as string; functions.logger.info(`User id: ${userId}`); const userRef = admin.firestore().collection("users").doc(userId); const user = (await userRef.get()).data() as User; const jsonString = Buffer.from(data, "base64").toString(); functions.logger.debug(`Data JSON: ${jsonString}`); const json = JSON.parse(jsonString); let notificationData; if (json["status"] == "success") { user.stats.donates += 1; await userRef.update({"stats.donates": user.stats.donates}); notificationData = { result: "success", }; } else { notificationData = { result: "error", errorCode: json["err_code"], errorDescription: json["err_description"], }; } const payload: MessagingPayload = { notification: notificationData.result == "success" ? { title: "Дякуємо за донат!", body: "Ви допомагаєте нам ставати кращими! 💛", } : { title: "Щось сі всрало :(", body: "Ваш донат не пройшов успішно. Ми вже працюємо над цим! Дякуємо за розуміння! 💛", }, data: { type: NotificationType.DONATE, data: JSON.stringify(notificationData), }, }; await Sender.sendSinglePushNotification(payload, user); response.status(200).send("OK"); return; }
У випадку з курсами, вони стають доступними користувачу, коли і якщо оплата успішно пройшла. Якщо ж на карті, скажімо, бракує коштів або збіг термін її дії, то оплата не проходить, відповідно, курс залишається закритим.
Я написав цей серверний код, оплати почали працювати, інтеграція пройшла нібито успішно. З екрану донатів ми передавали розмір донату, коментар, усе це запаковувалося за допомогою бібліотеки в запит на LiqPay.
Після оплати LiqPay присилає підтвердження на цей сервер. Тобто все готово, усе працює, усе класно. Ми всією командою ретельно це протестували та заспамили ФОП Наталі пів сотнею оплат по 1 гривні: таку ціну для курсів я поставив для зручності на етапі тестування, тому що оплату в нуль гривень LiqPay встановити не дозволяв. Хоча все вийшло добре, радіти я все ще не поспішав, адже попереду на нас чекала перевірка від Apple.
Складнощі розгляду
Google в плані перевірки застосунків набагато менш прискіпливий, ніж Apple. Він спокійно дозволяє використання браузерів, тому з проходженням перевірки на Google Play не виникло проблем. Усе відбулося легко та швидко.
А ось Apple не був до нас таким добрим: хоча вони не були проти оплати курсів через LiqPay, та сказали нам, що донати так робити ніяк не можна, натомість запропонували скористатися їхньої системою та провести грошові подарунки як upsales всередині застосунку. З’ясування цього в нас зайняло цілий місяць часу, про це детальніше ми розповідали тут.
Я почав дивитися, як зробити донати в Apple, тому що ніколи раніше мені не доводилося про таке чути. Виявилося, що донати як віконечко, у яке користувач може ввести будь-яку суму на власний розсуд, можна було зробити тільки для благодійних організацій. Оскільки в нас йшлося про ФОП, то Apple залишав можливість оформити донейти як внутрішні покупки фіксованих розмірів у доларах.
Оскільки дата релізу вже була затягнута так, що далі нікуди, мені довелося на шаленій швидкості переробляти віконечко та закидати застосунок на повторний розгляд. І він пройшов успішно! Ми змогли запустити застосунок!
До речі, передбачаю ваше питання: так, за українським законодавством ФОПи на третій групі можуть отримувати гроші в подарунок, чим фактично і є донати. З них платиться податок. Це, якщо дуже коротко, щодо деталей раджу консультуватись із юристами та бухгалтерами, якщо хочете запровадити таке й собі.
Висновки
Цікаво, що я раніше ніде не бачив і не чув, не знайшов в інтернеті чужого досвіду з інтеграції Flutter та LiqPay, так що я можу з упевненістю сказати, що зробив це першим.
Також хочу відзначити, що документація LiqPay хоч і детальна та якісно зроблена, проте питання з інтеграцією виявилося для служби підтримки задуже складним. У мене склалося враження, що вони навіть не розуміли суть мого запиту, відповідно, нічого толком не могли підказати. У найкращому випадку просто скеровували мене читати документацію.
У попередніх публікаціях я вже писав, що ідея застосунку в нас цікава: спонукати зросійщених українців переходити на мову, а також показати безкрайні її можливості тим, хто вже говорить українською.
Напевно, цілком логічно, що на шляху до втілення такої нестандартно ідеї ми весь час шукаємо — і треба сказати, успішно знаходимо — нестандартні рішення. На момент, коли я пишу цю статтю, пройшло вже пів року з моменту першого релізу. Із застосунком усе добре, він росте та розвивається, оплати проходять. Хоча було нелегко, але мені вдалося зробити те, що мене не робив ніхто, і я пишаюся цим.
Також мені подобаються широкі можливості, які пропонують як Flutter, так і українська платіжна система.
Запрошую вас переконатися особисто в тому, що всі оплати, зокрема, донати в застосунку працюють. Цим ви також підтримаєте нас на шляху до мети: мінімум 40 мільйонів людей, які із задоволенням говорять українською. Завантажуйте застосунок тут.
23 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів