Як підключити POS-термінал до мобільного застосунку?
Підключення мобільного застосунку до POS-термінала — це не просто «надіслати суму на термінал». Це інтеграція із закритими протоколами обладнання, які у кожного виробника свої, плюс обробка всіх можливих збоїв зв'язку в момент фінансової операції. Ми в компанії маємо 7+ років досвіду в мобільній розробці та реалізували понад 20 інтеграцій з POS-терміналами для різних еквайєрів. Наш підхід — повне покриття всіх edge-кейсів, щоб кожна транзакція була завершена коректно.
Типова ситуація: касир у супермаркеті проводить оплату через мобільний застосунок, термінал авторизує картку, але зв'язок обривається. Що робити? Наше рішення включає автоматичний запит статусу останньої транзакції та механізм повторного підключення з 5 спробами, що дає 99% успішного визначення статусу. Замовте інтеграцію під ключ — ми беремо на себе всі етапи: від аналізу протоколів до тестування на реальному обладнанні. Оцінимо ваш проект протягом двох робочих днів.
Протоколи та виробники
Більшість POS-терміналів у торговельному ритейлі спілкуються за одним із стандартних інтерфейсних протоколів:
- ECR-протокол (Electronic Cash Register) — набір команд для передачі суми з касового застосунку на термінал. У кожного банку-еквайєра або виробника — своя реалізація. Ощадбанк, ПриватБанк, Ingenico, Verifone — у всіх відрізняється синтаксис команд і поля відповіді.
- OPI (Open Payment Initiative) / OPOS (OLE for POS) — більш стандартизовані протоколи, популярні в Європі.
- Proprietary SDK: PAX A-серія, Sunmi V2, Newland — мають власні Android SDK (термінал сам працює на Android, викликаємо AIDL-інтерфейс або Intent).
Перед початком розробки уточнюємо у клієнта: який конкретний термінал, який банк-еквайєр, який протокол підтримується. Це не технічний вибір розробника — це факти про встановлене обладнання.
| Протокол |
Виробники |
Складність |
Швидкість |
Стандартизація |
| ECR |
Ощадбанк, ПриватБанк, Альфа-Банк |
Середня |
Висока |
Ні, своя реалізація |
| OPI/OPOS |
Ingenico, Verifone |
Висока |
Середня |
Є |
| Proprietary SDK |
PAX, Sunmi, Newland |
Низька |
Висока |
Ні |
Які інтерфейси використовуються?
Bluetooth (BLE + Classic). Термінал виступає GATT-сервером (BLE) або classic Bluetooth serial device. На iOS: CoreBluetooth для BLE, classic Bluetooth — тільки через ExternalAccessory framework з MFi-сертифікацією. Це важливе обмеження: більшість POS-терміналів використовують classic Bluetooth SPP-профіль, а iOS без MFi-сертифіката виробника термінала не зможе підключитися через classic BT. На Android обмежень немає — BluetoothSocket + RFCOMM.
USB. Android: UsbManager, UsbDeviceConnection. iOS: Lightning/USB-C аксесуар — знову потрібен MFi. На практиці USB-підключення зустрічається рідше, ніж Bluetooth.
TCP/IP (Wi-Fi / LAN). Термінал у локальній мережі, застосунок підключається по IP:Port і надсилає команди в текстовому або бінарному форматі. URLSession / OkHttp для HTTP-команд або CFStream / Java Socket для raw TCP. Найбільш передбачуваний інтерфейс з точки зору iOS.
Флоу платіжної операції
- Застосунок формує команду «продаж» із сумою та додатковими параметрами.
- Надсилає на термінал.
- Термінал показує користувачеві екран оплати, приймає картку.
- Повертає відповідь: статус (
approved/declined/error), код авторизації, RRN, маскований PAN.
- Застосунок обробляє відповідь та продовжує бізнес-флоу (закриває чек, оновлює замовлення).
Кроки 2–4 можуть зайняти до 60–90 секунд при повільному еквайрингу. На цей період показуємо spinner з повідомленням «Очікування відповіді термінала» та кнопку «Скасувати» (яка надсилає команду скасування на термінал, а не просто закриває екран).
Що робити при обриві зв'язку під час транзакції?
Найнеприємніший кейс: транзакція пішла на термінал, зв'язок перервався — застосунок не знає, чи пройшов платіж. Термінал авторизував картку, але відповідь не дійшла.
Правильна схема: при втраті зв'язку — пробуємо отримати статус останньої транзакції (команда «запит останньої операції»). Якщо термінал відповідає — беремо статус звідти. Якщо ні — показуємо оператору «Статус невідомий, перевірте термінал» і зберігаємо транзакцію в PENDING статусі. Автоматичне списання при невідомому статусі — неприпустимо.
Reconnect logic: при обриві Bluetooth — автоматичне перепідключення з 5 спробами, exponential backoff. Стан з'єднання — завжди видно в UI (іконка статусу).
Повернення та скасування
Скасування транзакції (reversal) — до закриття фінансового дня. Повернення (refund) — після. Обидва вимагають окремих команд на термінал з RRN вихідної транзакції. Реалізуємо обидва сценарії — оператор повинен мати можливість виправити помилку.
Порівняння платформ: Android vs iOS
| Аспект |
Android |
iOS |
| Classic BT (SPP) |
Нативно, BluetoothSocket |
Тільки MFi-пристрої |
| BLE |
BluetoothGatt |
CoreBluetooth |
| USB |
UsbManager |
Тільки MFi |
| TCP/IP |
Socket / OkHttp |
CFStream / URLSession |
Якщо замовник вимагає iOS і термінал працює тільки по classic Bluetooth без MFi — єдиний шлях: TCP/IP через проміжний адаптер або перехід на термінал з BLE/TCP підтримкою.
До речі, протокол BLE зазвичай забезпечує нижчу затримку (до 50 мс) порівняно з classic Bluetooth (100–200 мс), що прискорює транзакцію в 2–4 рази. Це важливо для сценаріїв з високою пропускною здатністю. Більше деталей в документації Apple по CoreBluetooth.
Типові помилки при інтеграції
- Неврахування MFi-сертифікації для iOS на classic Bluetooth — призводить до неможливості підключення.
- Відсутність retry logic при обриві — втрачені транзакції.
- Використання непідходящого протоколу (наприклад, ECR для термінала, який підтримує тільки OPI).
- Неправильний парсинг відповіді термінала (різні коди помилок у різних виробників).
Що входить в роботу
- Аналіз обладнання та протоколів еквайєра.
- Реалізація транспортного шару (Bluetooth/USB/TCP).
- Розробка протоколу команд (продаж, скасування, повернення, запит статусу).
- Обробка edge-кейсів (обрив зв'язку, таймаут, невідомий статус).
- Тестування на реальному терміналі в тестовому режимі еквайєра.
- Документація з інтеграції для підтримки клієнта.
- Гарантія на транспортний шар: 6 місяців безкоштовної підтримки.
Вартість інтеграції фіксується на етапі ТЗ, а окупність досягається за рахунок автоматизації ручного введення даних. Отримайте консультацію щодо вашого проекту — оцінимо складність та терміни.
Процес роботи
З'ясування моделі термінала та протоколу → вивчення документації еквайєра → реалізація транспортного шару (BT/USB/TCP) → протокол команд (продаж, скасування, повернення, запит статусу) → обробка edge-cases (обрив зв'язку, таймаут) → тестування на реальному терміналі в тестовому режимі еквайєра → бойове тестування → експлуатація.
Орієнтири за термінами
Базова інтеграція (продаж + відповідь) за конкретним протоколом з одним інтерфейсом: 3–5 днів. Повний набір операцій (продаж, скасування, повернення, запит статусу) + retry/reconnect logic + кілька інтерфейсів: 2–3 тижні.
Наша команда — мобільні розробники з 7+ років досвіду в iOS (Swift, SwiftUI, CoreBluetooth) та Android (Kotlin, Jetpack Compose, BluetoothSocket). Ми працювали з терміналами PAX, Ingenico, Verifone, Sunmi. Ми гарантуємо якість інтеграції та прозорість кожного етапу.
Замовте інтеграцію POS-термінала — і ми забезпечимо стабільну роботу платіжного модуля у вашому застосунку.
Платежі в мобільних додатках: In-App Purchase, StoreKit 2, Google Billing, Stripe, RevenueCat
У кожному нашому проєкті з монетизації додатку ми балансуємо між політиками App Store та Google Play, вимогами PCI DSS та логікою верифікації покупок на бекенді. Неправильно зроблена система платежів — це не просто баг, це фінансові втрати та можливий бан додатку. За 7 років ми розібрали понад 50 кейсів інтеграції платіжних SDK — від простої Stripe-форми до розподіленого біллінгу з власними серверними вебхуками.
Як вибрати між In-App Purchase та зовнішнім платіжним шлюзом?
Якщо додаток продає цифровий контент або підписки — Apple та Google вимагають використовувати їхні платіжні системи. Обійти це неможливо: порушення правил 3.1.1 App Store або Google Play Developer Policy призводить до видалення додатку. Фізичні товари та послуги, що надаються офлайн, — інша історія.
In-App Purchase: дві платформи, два різних API
StoreKit 2 (iOS 15+)
StoreKit 2 — повна переробка оригінального StoreKit з async/await API. Product.products(for:), product.purchase(), Transaction.currentEntitlements — читабельно та передбачувано порівняно з чергою транзакцій через SKPaymentTransactionObserver.
Найважливіша зміна: транзакції в StoreKit 2 підписані JWS (JSON Web Signature) і верифікуються локально без серверного roundtrip. Transaction.verificationResult повертає .verified(Transaction) або .unverified(Transaction, VerificationError). Це не означає, що сервер не потрібен — він потрібен для зберігання статусу підписки, але локальна верифікація прибирає затримку при старті.
StoreKit.AppTransaction — верифікація самого факту завантаження додатку з App Store. Потрібна для додатків з платним завантаженням або безстроковими покупками, не підписками.
Складне місце в StoreKit 2 — обробка renewalState для підписок: .subscribed, .expired, .inBillingRetryPeriod, .inGracePeriod, .revoked. Стан inGracePeriod означає, що Apple намагається відновити оплату (до 16 днів) — у цей час потрібно продовжувати надавати доступ. Не обробиш — втратиш лояльних користувачів, у яких тимчасово не пройшла картка. За досвідом, близько 5% підписок потрапляють у billing retry, і автоматичне відновлення доступу повертає до 80% з них.
Google Play Billing Library (v6+)
Google Billing — складніший за StoreKit за кількістю сценаріїв. BillingClient з PurchasesUpdatedListener, queryProductDetailsAsync, launchBillingFlow, queryPurchasesAsync — обов'язково викликати при кожному старті додатку, не покладатися на PurchasesUpdatedListener як єдине джерело правди.
Підтвердження покупки: acknowledgePurchase() для non-consumables та підписок, consumePurchase() для consumables. Якщо не викликати acknowledge протягом 3 днів — Google автоматично зробить повернення. Це гарантована втрата грошей, якщо забути про acknowledge на бекенді після верифікації.
ProductDetails з SubscriptionOfferDetails — в Billing v5+ структура оферт ускладнилася: один продукт може мати кілька basePlanId та offerId (пробний період, знижка для нових користувачів, retention-офер). BillingFlowParams.SubscriptionUpdateParams для апгрейду/даунгрейду підписки з prorationMode.
Чому серверна верифікація обов'язкова?
Ніколи не довіряйте лише клієнтському коду при розблокуванні платного контенту. Клієнтська верифікація обходиться модифікацією додатку.
Для IAP мінімальна схема: додаток отримує receiptData (iOS) або purchaseToken (Android), відправляє на бекенд, бекенд верифікує через Apple App Store Server API / Google Play Developer API, зберігає статус у БД, віддає відповідь клієнту. RevenueCat робить це за вас — але якщо у вас кастомний бекенд, потрібно реалізувати самостійно.
Webhook-и важливіші, ніж здається. Користувач може скасувати підписку через налаштування телефону, не через додаток — додаток не отримає про цю подію в реальному часі. Тільки webhook від Apple/Google (або RevenueCat) дозволяє своєчасно оновити статус. Ми використовуємо перевірку підпису вхідних запитів через Apple's signedPayload та Google's DeveloperNotification.
Як RevenueCat спрощує інтеграцію?
Підтримувати StoreKit 2 та Google Billing одночасно, з урахуванням промо-кодів, оферт, відновлення покупок та серверної верифікації — це кілька місяців розробки. RevenueCat закриває більшу частину цього шару.
RevenueCat — не просто SDK для платежів. Це:
- Єдиний API для iOS та Android (і Stripe для вебу)
- Серверна верифікація та зберігання статусів підписок
- Webhooks на події (покупка, відновлення, скасування, billing issue)
- Аналітика по когортам, MRR, churn
- A/B тестування оферт через Experiments
Purchases.configure(withAPIKey:) при старті, Purchases.shared.getCustomerInfo() для отримання поточних entitlements — мінімальний інтегрований шар. Purchases.shared.purchase(package:) замість прямого виклику StoreKit/Billing.
У документації RevenueCat сказано: «RevenueCat handles receipt validation on the server side, reducing client-side complexity and preventing fraudulent purchases.»
Обмеження RevenueCat: платний (безкоштовно до певного рівня MRR, далі відсоток від доходу), не підходить для дуже складних flow з кількома storefront-ами або кастомними bundle-ами. Однак для типового SaaS-додатку економія на власній розробці є значною — інтеграція окупається швидко.
Stripe в мобільних додатках
Stripe — для оплати фізичних товарів, послуг, B2B-платежів де IAP не вимагається політикою платформи.
Stripe iOS SDK та Android SDK — PaymentSheet для готового UI оплати, PaymentSheetFlowController для кастомного UI зі збереженими картками. Payment Intent створюється на сервері, client secret передається в додаток — карткові дані ніколи не проходять через ваш сервер, лише через Stripe.
Apple Pay та Google Pay через Stripe: PKPaymentRequest (iOS) та GooglePayLauncher (Android) вже інтегровані в Stripe SDK. Конверсія у Apple Pay дає в 1.3–2 рази вищий результат, ніж форми з ручним введенням картки — це цифри, які ми підтвердили на десятці проєктів.
Збережені картки через SetupIntent + Customer API — користувач платить в один тап при повторному візиті. Compliance: PCI DSS SAQ A — найлегший рівень compliance, оскільки Stripe Tokenization прибирає потребу зберігати карткові дані на своїй стороні. Згідно з PCI DSS, передача токенів звільняє від необхідності сертифікації рівня 1.
3DS2 (Strong Customer Authentication) — обов'язковий для платежів у ЄС за PSD2. Stripe обробляє автоматично через PaymentIntent.confirmPayment, але потрібно коректно обробити .requiresAction статус та повернути користувача на потрібний екран після аутентифікації.
Що входить в роботу (deliverables)
| Документація/Артефакт |
Зміст |
| Архітектурна схема біллінгу |
Діаграма потоків: клієнт → SDK → сервер → store/webhook |
| Інтеграція SDK |
Підключення та конфігурація StoreKit 2, Google Billing, RevenueCat або Stripe |
| Серверна верифікація |
Реалізація ендпоінтів та обробка webhook (Apple/Google/RevenueCat) |
| Тестовий стенд |
Sandbox Apple, License Testers Google, Stripe Test Mode |
| Документація по запуску |
Опис ключів, provisioning profiles, TestFlight |
| Навчання команди |
Сесія з підтримки платіжного модуля |
Процес та терміни
Починаємо з з'ясування бізнес-моделі: підписки, разові покупки, consumables, freemium. Від цього залежить архітектура. Тестування IAP вимагає Sandbox-акаунтів (Apple) та License Testers (Google) — це окреме налаштування середовища.
Sandbox Apple поводиться не так, як production: підписки відновлюються кожні 5 хвилин замість місяця, inGracePeriod працює по-іншому. Обов'язково тестувати сценарії: закінчення тріалу, скасування, billing retry, refund.
Як налаштувати тестове середовище для платежів?
| Сценарій |
Інструмент |
Час реалізації |
| Підписки iOS + Android |
StoreKit 2 + Google Billing + RevenueCat |
2–3 тижні |
| Підписки з кастомним бекендом |
StoreKit 2 + Google Billing + власний webhook |
4–6 тижнів |
| Оплата карткою (фізичні товари) |
Stripe PaymentSheet |
1–2 тижні |
| Apple Pay / Google Pay |
Stripe або нативні SDK |
+ 3–5 днів |
| Повний платіжний стек |
Все вищеперераховане |
6–10 тижнів |
Розгорнути типові помилки при інтеграції
- Забули викликати
acknowledgePurchase() на Android — гроші повертаються через 3 дні.
- Не обробили
inGracePeriod — лояльні користувачі блокуються без доступу.
- Поклалися лише на Pushtokens при відновленні підписок — пропускаєте state оновлення.
- Використовували production-ключі в TestFlight — спрацьовують реальні списання.
Вартість розраховується індивідуально виходячи з набору інструментів та складності серверної логіки. У середньому ми вкладаємося в бюджет, який обговорюється окремо, але економія від запобігання помилок та churn окупає ці вкладення за 2–3 місяці.
Отримайте консультацію по вашому проєкту — зв'яжіться з нами. Ми допоможемо обрати оптимальну архітектуру платежів, яка пройде рев'ю сторів та не зламається при пікових навантаженнях.