Покупка в очікуванні виглядає як продаж, але гроші ще не надійшли, і якщо надати доступ зарано, ви віддаєте продукт задарма
І в App Store, і в Google Play є стан покупки в очікуванні, тобто замовлення, яке магазин прийняв, але ще не списав кошти. Розблокуйте його до того, як платіж пройде, і кожне таке замовлення, що зірветься, буде чистим збитком. Ось як покупки в очікуванні працюють у кожному магазині, у скільки обходиться помилково наданий доступ і як з ними поводитися без втрат.

Головне
- Покупка в очікуванні це справжнє замовлення, яке магазин прийняв, але ще не списав кошти. Ваш код бачить нову покупку, але гроші не надійшли, і вони можуть не надійти ніколи.
- Надавайте право доступу лише тоді, коли стан PURCHASED у Google Play або коли транзакція завершена в Apple. Ніколи не розблоковуйте при стані PENDING чи при очікуваному результаті Apple.
- У Google Play готівкові платежі в магазині, банківські перекази та деякі платежі через рахунок мобільного оператора розраховуються поза основним потоком, тож покупка повертається як PENDING, а не PURCHASED, доки клієнт справді не заплатить.
- В Apple покупка в очікуванні це зазвичай Ask to Buy, коли організатор родини має її схвалити. Схвалення може зайняти години або дні, а завершена транзакція надходить пізніше через Transaction.updates.
- Якщо ви розблокуєте замовлення в очікуванні, яке потім скасовується, ви витратили обчислення, виклики API, сховище або виплату за витратний товар на списання, яке так і не надійшло. На відміну від повернення коштів, повертати нічого, бо нічого й не було стягнуто.
- Магазин повідомляє вас, коли замовлення в очікуванні зривається. Google надсилає ONE_TIME_PRODUCT_CANCELED, тип 2, або SUBSCRIPTION_PENDING_PURCHASE_CANCELED, тип 20. Apple просто ніколи не доставляє завершену транзакцію.
- Замовлення в очікуванні може перетворитися на справжній продаж, поки ваш застосунок закритий, тож перевіряйте знову після повернення: викликайте queryPurchasesAsync() в onResume() у Google Play і продовжуйте слухати Transaction.updates в Apple.
Хтось торкається кнопки купити у вашому застосунку. Ваші логи показують нове замовлення, ваш слухач білінгу спрацьовує, і ви віддаєте товар. Для більшості покупок це саме те, що потрібно. Для покупки в очікуванні це помилка, бо замовлення існує, а грошей немає. Клієнт обрав спосіб оплати, що розраховується пізніше, магазин досі чекає на оплату, а ви щойно надали платну функцію за списання, яке може не надійти ніколи. Це тихий родич повернення коштів. Тут нічого не скасовується, бо нічого й не було стягнуто. Ви просто віддали продукт задарма.
Покупка в очікуванні це справжнє замовлення в стані ще-не-оплачено, і воно є і в App Store, і в Google Play. Обидва магазини прямо кажуть вам зачекати. Пастка в тому, що замовлення в очікуванні у вашому коді виглядає майже так само, як завершене, тож інтеграція, яка вважає кожну нову покупку продажем, відвантажує товар за замовленнями, які магазин ще намагається стягнути. Зробіть це правильно, і ви нічого не втратите. Зробіть неправильно, і кожен повільний платіж, що зривається, буде чистим збитком, наданим за ваш рахунок.
Що таке покупка в очікуванні насправді
Покупка в очікуванні це замовлення, яке магазин зафіксував, але ще не списав кошти. Покупець розпочав процес, магазин його прийняв, а розрахунок відбувається десь, де ваш застосунок не бачить. Google Play називає це станом PENDING. Apple називає це транзакцією в очікуванні або відкладеною. Різні назви, той самий факт: магазин тримає замовлення відкритим, доки чекає на оплату, і сказав вам не вважати це замовлення грошима.
Google Play: платіж відбувається деінде
Деякі способи оплати розраховуються поза основним потоком. Готівка у фізичному магазині, банківські перекази та деякі платежі через рахунок мобільного оператора потребують додаткових кроків між дотиком і списанням. Коли клієнт обирає один із них, Google повертає покупку в стані PENDING замість PURCHASED. Для готівкового платежу клієнт отримує код через сповіщення та електронною поштою, несе його до магазину-учасника й платить на касі. Доки це не станеться, Google нічого не стягнув, і ви також. Правило Google це один рядок: використовуйте getPurchaseState() і надавайте право доступу лише тоді, коли стан PURCHASED. Воно також каже вам не підтверджувати покупку, доки вона PENDING, бо підтвердження належить оплаченому замовленню, а не обіцяному.
Apple: покупка чекає на чийсь дотик
Стан очікування Apple означає, що транзакція потребує зовнішньої дії, перш ніж зможе завершитися. Найпоширеніша це Ask to Buy, коли дитина розпочинає покупку, а організатор родини має її схвалити. У StoreKit 2 виклик покупки повертає Product.PurchaseResult.pending. У старішому StoreKit транзакція повідомляється як відкладена. Так чи інакше, Apple нікого не стягнув, а завершена транзакція, якщо вона надійде, приходить асинхронно через Transaction.updates. Покажіть клієнту стан очікування й не розблоковуйте нічого, доки не надійде завершена транзакція.
Чому надання доступу за покупкою в очікуванні коштує вам справжніх грошей
Втрата тут не того ґатунку, що повернення коштів, коли гроші, які ви провели, забирають назад. Вона гірша в один конкретний спосіб: немає грошей, щоб забрати назад, бо нічого й не було стягнуто.
Ви доставляєте, а магазин так нічого й не стягує
Коли ви розблоковуєте за замовленням в очікуванні, яке пізніше скасовується, ви вже витратилися на його обслуговування. Обчислення, що виконали функцію, оплачені вами виклики сторонніх API, виділене сховище, а для витратного товару фактична виплата проданої речі. Усе це йде за двері за замовленням, яке не принесло доходу. Повернення коштів принаймні починається зі списання, що відбулося. Помилково наданий доступ за покупкою в очікуванні ніколи не мав жодного списання, тож він навіть не з'являється як гроші, що йдуть геть. Він з'являється як ніщо, і саме тому його легко проґавити й легко повторити.
Сигнал скасування і що він означає
Магазин таки повідомляє вас, коли замовлення в очікуванні гине. У Google Play разовий продукт, що зривається, надсилає сповіщення ONE_TIME_PRODUCT_CANCELED, тип 2, а підписка, що була в очікуванні, надсилає SUBSCRIPTION_PENDING_PURCHASE_CANCELED, тип 20. Коли ті самі замовлення натомість проходять, ви отримуєте ONE_TIME_PRODUCT_PURCHASED, тип 1, або SUBSCRIPTION_PURCHASED, тип 4. В Apple немає події скасування, яку можна перехопити, бо відкладена транзакція, яку відхилено, просто ніколи не стає завершеною транзакцією. Якщо ви розблокували зарано, ця тиша і є рахунком.
| Питання | Google Play | Apple |
|---|---|---|
| Що це спричиняє | Готівка, банківський переказ, деякі платежі через рахунок оператора | Схвалення Ask to Buy або інша потрібна дія |
| Стан, який ви бачите | PurchaseState PENDING | результат в очікуванні або відкладена транзакція |
| Надавайте доступ, коли | Стан PURCHASED | Транзакція завершена |
| Пройшло | ONE_TIME_PRODUCT_PURCHASED (1), SUBSCRIPTION_PURCHASED (4) | Завершена транзакція через Transaction.updates |
| Зірвалося | ONE_TIME_PRODUCT_CANCELED (2), SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20) | Завершена транзакція так і не надходить |
| Чи стягнуто гроші | Ні, лише при PURCHASED | Ні, лише коли транзакція завершиться |
Вікно часу і хто на кого чекає
Покупка в очікуванні це не годинник, з яким ви змагаєтесь. Це годинник, який з вашого боку ще не запустився.
Google Play дає клієнту дні, а не хвилини
Готівковий платіж або банківський переказ розраховується за графіком клієнта, а не вашим. Замовлення лишається в PENDING, доки клієнт не заплатить або вікно не сплине й Google його не скасує. Ваше власне триденне вікно підтвердження, те, що автоматично повертає кошти за покупку, яку ви не підтвердили, навіть не починається, доки покупка не перейде з PENDING до PURCHASED. Тож немає поспіху обслуговувати замовлення в очікуванні. Є лише дисципліна зачекати на зміну стану.
Схвалення Apple лежить на телефоні організатора родини
Запит Ask to Buy надходить на пристрій організатора як підказка, яку він схвалює або відхиляє, коли до неї дійде. Це можуть бути хвилини, години або день по тому, і ваш застосунок не може цього прискорити. Єдина правильна поведінка це відображати стан очікування й дати StoreKit передати вам завершену транзакцію, якщо і коли надійде схвалення.

Як обробляти покупки в очікуванні без втрат
Уся робота зводиться до чотирьох звичок. Жодна з них не складна, а пропуск бодай однієї це те місце, де зникають гроші.
Надавайте за оплаченим станом, ніколи за станом очікування
У Google Play перевіряйте getPurchaseState() і надавайте лише при PURCHASED, і не підтверджуйте покупку, доки вона PENDING. В Apple розблоковуйте лише при завершеній транзакції й ніколи при результаті в очікуванні. Це одне правило закриває весь витік. Усе інше про те, щоб переконатися, що ви справді помітите, коли надійде оплачений стан.
Перевіряйте знову, коли застосунок повертається
Перехід від очікування до оплати часто відбувається, поки ваш застосунок не працює. У Google Play викликайте queryPurchasesAsync() у своєму обробнику onResume(), щоб підхопити замовлення, які стали PURCHASED у фоні, і тримайте свій слухач Real-time Developer Notifications як серверне джерело істини. В Apple слухайте Transaction.updates протягом усього життя застосунку, бо схвалена транзакція може надійти задовго після того, як початковий виклик покупки повернув результат.
Увімкніть підтримку очікування і протестуйте обидва завершення
Google вимагає викликати enablePendingPurchases(), коли ви створюєте BillingClient, а підтримка транзакцій в очікуванні для разових продуктів обов'язкова, а не опціональна. Протестуйте це, перш ніж випускати. Ліцензійні тестувальники отримують два додаткові тестові інструменти для відкладених форм оплати, де платіж автоматично завершується або автоматично скасовується за кілька хвилин, тож ви можете спостерігати і шлях оплати, і шлях зриву від початку до кінця.
Скажіть клієнту, що замовлення не завершене
Покупець в очікуванні це справжній клієнт посеред покупки, а не невдача. Покажіть йому, що замовлення чекає на його оплату або на схвалення, і дайте йому чіткий шлях назад, щоб її завершити. Тихий стан очікування втрачає продажі, які добре позначений повертає, бо більшість цих покупців досі хочуть цієї речі, і їм лишився тільки один крок.
Ті самі канали Real-time Developer Notifications і App Store Server Notifications, які RefundHalt уже читає для повернень коштів і зворотних платежів, несуть і ці сигнали. Сповіщення purchased, що каже, що замовлення в очікуванні нарешті надійшло, і сповіщення canceled, що каже, що ні, потрапляють у вашу панель поряд з рештою ваших подій доходу, тож замовлення в очікуванні, яке зірвалося, це те, що ви можете побачити, а не те, за що випадково заплатили.
Коротка версія
Покупка в очікуванні це замовлення без платежу, і обидва магазини прямо кажуть, що вам слід зачекати. Google Play повертає готівкові, банківські перекази та деякі платежі через рахунок оператора у стані PENDING і каже надавати доступ лише при PURCHASED. Apple повертає транзакцію в очікуванні або відкладену для Ask to Buy та інших потрібних дій і доставляє завершену транзакцію пізніше через Transaction.updates. Розблоковуйте за оплаченим станом, перевіряйте знову, коли ваш застосунок відновлюється, увімкніть і протестуйте підтримку очікування та позначте замовлення, що очікує, для клієнта. Зробіть це, і покупка в очікуванні не коштуватиме вам нічого. Пропустіть це, і ви надасте платний продукт за списання, яке так і не надійшло, а це єдина втрата без чека, на який можна вказати.
Поширені запитання
- Що таке покупка в очікуванні?
- Покупка в очікуванні це замовлення, яке магазин прийняв, але ще не списав кошти. У Google Play це стан покупки PENDING, що використовується для способів оплати, які розраховуються пізніше, як-от готівка, банківський переказ і деякі платежі через рахунок мобільного оператора. В Apple це транзакція в очікуванні або відкладена, найчастіше покупка Ask to Buy, що чекає на схвалення організатора родини. В обох випадках гроші ще не стягнуто, тож ви не повинні надавати доступ.
- Чи слід надавати доступ, поки покупка в очікуванні?
- Ні. Надавайте право доступу лише тоді, коли стан PURCHASED у Google Play або коли транзакція завершена в Apple. Якщо ви розблокуєте функцію, поки замовлення досі в очікуванні, а платіж так і не пройде, ви надали продукт безкоштовно, і немає списання, яке можна скасувати, бо жодного не було зроблено.
- Які способи оплати спричиняють покупку в очікуванні в Google Play?
- Способи оплати, що розраховуються поза основним потоком. Готівкові платежі у фізичному магазині, банківські перекази та деякі варіанти оплати через рахунок мобільного оператора потребують додаткових кроків між дотиком і списанням, тож Google повертає покупку в стані PENDING замість PURCHASED. Для готівкового платежу клієнт отримує код через сповіщення та електронною поштою, а потім платить у магазині-учаснику.
- Що таке Ask to Buy і як воно пов'язане з покупками в очікуванні?
- Ask to Buy це функція Family Sharing від Apple, що дозволяє дитині попросити про покупку, яку має схвалити організатор родини. Поки запит чекає, покупка перебуває в стані очікування Apple, повертається як Product.PurchaseResult.pending у StoreKit 2 або як відкладена транзакція у старішому StoreKit. Завершена транзакція надходить лише через Transaction.updates, якщо і коли організатор її схвалить.
- Що станеться, якщо покупку в очікуванні так і не оплатять?
- Замовлення скасовується, і гроші не переходять із рук у руки. У Google Play ви отримуєте сповіщення ONE_TIME_PRODUCT_CANCELED, тип 2, для разового продукту або SUBSCRIPTION_PENDING_PURCHASE_CANCELED, тип 20, для підписки. В Apple відкладена транзакція просто ніколи не стає завершеною транзакцією. Якщо ви вже надали доступ, це момент, коли втрата стає реальною.
- Чи покупка в очікуванні це те саме, що повернення коштів?
- Ні. Повернення коштів скасовує платіж, який справді був стягнутий. Покупка в очікуванні, що зривається, ніколи не була списана, тож немає чого скасовувати й ніщо не з'являється у звіті про повернення. Якщо ви розблокували її зарано, витратою є обчислення, виклики API, сховище або витратний товар, які ви витратили на обслуговування замовлення, що не принесло доходу.
Джерела та додаткове читання
- Android Developers: Integrate the Google Play Billing Library (PENDING purchase state, grant entitlement only on PURCHASED, enablePendingPurchases, queryPurchasesAsync, cash payment code flow, three-day acknowledgement window begins on transition to PURCHASED)
- Android Developers: Real-time developer notifications reference (OneTimeProductNotification ONE_TIME_PRODUCT_PURCHASED=1 and ONE_TIME_PRODUCT_CANCELED=2; SubscriptionNotification SUBSCRIPTION_PURCHASED=4 and SUBSCRIPTION_PENDING_PURCHASE_CANCELED=20)
- Android Developers: Test Google Play Billing (license testers get delayed-payment test instruments that auto-complete or auto-cancel for testing pending transactions)
- Apple Developer: Product.PurchaseResult (the pending case, returned when a purchase needs action such as Ask to Buy approval before it completes)
- Apple Developer: SKPaymentTransactionState.deferred (a transaction whose final status is pending an external action such as Ask to Buy)
- Apple Developer: Transaction.updates (StoreKit delivers transaction updates, including ones approved later, asynchronously)
RefundHalt
Автопілот повернень для App Store і Google Play
Читайте далі
Поновлення зриваються частіше, ніж клієнти скасовують підписку, і цей мимовільний відтік це дохід, який ви ще можете повернути
Відхилена картка, а не дотик до кнопки скасування, завершує велику частку підписок, і магазин продовжує намагатися стягнути оплату тижнями. Ось як працюють пільговий період оплати, повторні спроби списання та утримання облікового запису в App Store і Google Play, і скільки насправді коштує вам мимовільний відтік.
Несанкціоновані покупки в застосунках, які роблять діти, майже завжди повертають батькам, а витрати несете ви
Коли дитина купує набір монет на телефоні батьків, і Apple, і Google повертають кошти, і жоден із них не питає вас першим. Регулятори збудували це саме так. Ось як ці повернення за несанкціоновані покупки в застосунках працюють у кожному магазині, вікно у 15 хвилин, куди йдуть гроші, і скільки одна така покупка насправді вам коштує.