Обзор
KlugBilling продаёт разовые товары и подписки из вашего приложения AIR. На iPhone и iPad расширение работает с App Store, на Android — с Google Play, а ваш код ActionScript остаётся одинаковым для обоих.
Расширение загружает ваши товары с локализованными названиями и ценами, открывает окно покупки магазина, сообщает о каждом шаге транзакции и закрывает её, как только вы предоставили товар. Восстановление прежних покупок, чек приложения iOS и подписанные данные покупок Google Play включены, так что ваш сервер может проверить каждую продажу.
На Android KlugBilling использует актуальную Google Play Billing Library 8. Она добавляется автоматически при упаковке приложения и соответствует требованиям Google Play к новым приложениям и обновлениям.
KlugBilling продаёт подписки наших приложений на iOS и Android, нашего доказательства работоспособности.
Возможности
- Автономность: один ANE, без базовой библиотеки и зависимых ANE
- Единый API для App Store и Google Play
- Разовые товары и автоматически продлеваемые подписки
- Локализованные названия, описания и цены прямо из магазина
- Периоды подписки, предложения и ценовые фазы
- Каждое состояние транзакции как событие: покупка, куплено, ошибка, отменено, восстановлено, отложено
- Восстановление покупок (iOS) и список имеющихся покупок (Android)
- Данные для проверки на сервере: чек приложения iOS, подписанные данные покупки Google и подпись
- Android: покупки проверяются по вашему открытому ключу Play
Поддерживаемые платформы
На других платформах InAppBilling.isSupported возвращает false, поэтому один и тот же код можно оставить в кроссплатформенном приложении.
Начало работы
Добавьте расширение в дескриптор вашего приложения:
<extensions>
<extensionID>com.klug.billing</extensionID>
</extensions>
Записи в манифесте и activity не нужны. Создайте товары в App Store Connect и в Google Play Console с одинаковыми идентификаторами продуктов и скопируйте открытый ключ base64 вашего приложения из Play Console (Monetization setup).
Примеры кода
Четыре полных сценария из API, который работает в наших приложениях. Каждый вызов возвращается сразу; результаты приходят как события на InAppBilling.service. Полная документация →
import com.klug.billing.*; import com.klug.billing.events.*; var store:InAppBillingService = InAppBilling.service; if ( InAppBilling.isSupported ) // iOS, Android { store.addEventListener( InAppBillingEvent.SETUP_SUCCESS, onSetup ); store.addEventListener( InAppBillingEvent.SETUP_FAILURE, onSetupFailed ); store.addEventListener( PurchaseEvent.PURCHASES_UPDATED, onPurchasesUpdated ); // see 3 var service:BillingService = new BillingService(); if ( store.platform == "android" ) service.setServiceType( InAppBillingServiceTypes.GOOGLE_PLAY_INAPP_BILLING ) .setGooglePlayPublicKey( "MIIBIjANBgkqh..." ); // from the Play Console, Android only else service.setServiceType( InAppBillingServiceTypes.APPLE_INAPP_PURCHASE ); store.setup( service ); } function onSetup( e:InAppBillingEvent ):void { store.checkAvailability( function( result:String ):void { if ( result == InAppBillingAvailability.AVAILABLE ) loadProducts(); // see 2 else hideShop(); // e.g. purchases disabled by Screen Time }); }
function loadProducts():void { store.addEventListener( ProductEvent.PRODUCTS_LOADED, onProducts ); store.addEventListener( ProductEvent.PRODUCTS_FAILED, function( e:ProductEvent ):void { trace( "store not reachable: " + e.message ); }); // one-time products first, subscriptions second store.getProducts( [ "com.example.removeads" ], [ "com.example.pro.monthly", "com.example.pro.yearly" ] ); } function onProducts( e:ProductEvent ):void { for each ( var p:Product in e.products ) { // e.g. "Pro Yearly · 19,99 €" in the user's currency and language addShopButton( p.id, p.title + " · " + p.priceString ); // your app function } }
function buy( productId:String ):void { var request:PurchaseRequest = new PurchaseRequest().setProductId( productId ); if ( store.checkPurchaseRequestValid( request ) ) store.makePurchase( request ); } // every step of every transaction, also open ones from earlier sessions function onPurchasesUpdated( e:PurchaseEvent ):void { for each ( var p:Purchase in e.data ) { switch ( p.transactionState ) { case Purchase.STATE_PURCHASED: case Purchase.STATE_RESTORED: // iOS: p.transactionReceipt (app receipt) // Android: p.originalMessage + p.signature (Google's signed purchase data) verifyOnServer( p, function():void { // your server call unlock( p.productId ); // your app function store.finishPurchase( p ); // close it, or the store sends it again }); break; case Purchase.STATE_FAILED: case Purchase.STATE_CANCELLED: store.finishPurchase( p ); break; case Purchase.STATE_DEFERRED: showHint( "Waiting for approval" ); // e.g. Ask to Buy break; } } }
function restore():void { if ( store.isGetPurchasesSupported ) // Android: list what the Google account owns { store.addEventListener( PurchaseEvent.GET_PURCHASES_COMPLETE, function( e:PurchaseEvent ):void { for each ( var p:Purchase in e.data ) unlock( p.productId ); }); store.getPurchases(); } else // iOS: restored transactions arrive in onPurchasesUpdated with STATE_RESTORED { store.addEventListener( PurchaseEvent.RESTORE_PURCHASES_SUCCESS, function( e:PurchaseEvent ):void { showHint( "Purchases restored" ); }); store.restorePurchases(); } }
Обзор API
| InAppBilling.isSupported | True на iOS и Android, false на остальных. |
| InAppBilling.service | Единственный сервис магазина; через него идут все вызовы и события ниже. |
| setup(service) | Подключается к магазину, описанному BillingService. Результат: SETUP_SUCCESS или SETUP_FAILURE. |
| checkAvailability(callback) | AVAILABLE, NOT_AVAILABLE или STORE_UPGRADE_REQUIRED. |
| canMakePayments | False, если покупки на устройстве отключены. |
| getProducts(productIds, subscriptionIds) | Загружает данные магазина. Результат: PRODUCTS_LOADED, INVALID_PRODUCT или PRODUCTS_FAILED. |
| getProduct(id) | Загруженный Product: title, description, price, priceString, currencyCode, subscriptionPeriod, subscriptionOffers. |
| makePurchase(request) | Открывает окно покупки магазина для PurchaseRequest; предложение подписки необязательно. |
| finishPurchase(purchase) | Закрывает транзакцию после предоставления товара. Результат: FINISH_SUCCESS или FINISH_FAILED. |
| getPendingPurchases() | Транзакции, которые магазин ещё держит открытыми. |
| restorePurchases() | iOS: восстанавливает прежние покупки Apple ID. |
| getPurchases() | Android: перечисляет покупки аккаунта Google. |
| applicationReceipt | iOS: чек приложения в base64 для вашего сервера, с refresh(). |
| dispose() | Отключается от магазина. |
События
| InAppBillingEvent | SETUP_SUCCESS, SETUP_FAILURE, RESTORE_PURCHASES_SUCCESS, RESTORE_PURCHASES_FAILED |
| ProductEvent | PRODUCTS_LOADED (products), INVALID_PRODUCT, PRODUCTS_FAILED |
| PurchaseEvent | PURCHASES_UPDATED (data = объекты Purchase), PURCHASE_FAILED, FINISH_SUCCESS, FINISH_FAILED, RESTORE_PURCHASES_SUCCESS, RESTORE_PURCHASES_FAILED, GET_PURCHASES_COMPLETE, GET_PURCHASES_FAILED |
| AvailabilityEvent | COMPLETE с результатом проверки доступности |
| ApplicationReceiptEvent | REFRESH_SUCCESS, REFRESH_FAILED |
Купить KlugBilling
Годовая лицензия на одно приложение AIR со всеми обновлениями в течение 12 месяцев. После оплаты вы получаете расширение, библиотеку ActionScript и документацию по e-mail, обычно в течение одного рабочего дня. Лицензия и условия
- Лицензия на одно приложение AIR
- Все обновления в течение 12 месяцев
- Без поддержки
- Лицензия на одно приложение AIR
- Все обновления в течение 12 месяцев
- Ответ по e-mail в течение 7 дней
- Лицензия на одно приложение AIR
- Все обновления в течение 12 месяцев
- Ответ по e-mail в течение 48 часов
Требования
| AIR SDK | 51.x (HARMAN) |
| iOS | iOS 13 или новее, товары в App Store Connect |
| Android | Android 6.0 (API 23) или новее, товары в Google Play Console |
| Google Play | Play Billing Library 8.3, добавляется при упаковке |
| Сервер (рекомендуется) | Проверяет чек приложения iOS или подписанные данные покупки Google |