개요
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를 추가할 필요가 없음
- App Store와 Google Play를 위한 하나의 API
- 일회성 상품과 자동 갱신 구독
- 스토어에서 바로 가져오는 현지화된 제목, 설명, 가격
- 구독 기간, 혜택, 가격 단계
- 모든 거래 상태를 이벤트로 제공: 구매 중, 구매 완료, 실패, 취소, 복원, 보류
- 구매 복원(iOS) 및 보유 구매 목록 조회(Android)
- 서버 측 검증용 데이터: iOS 앱 영수증, Google의 서명된 구매 데이터와 서명
- Android: 구매를 Play 공개 키로 확인
지원 플랫폼
다른 플랫폼에서는 InAppBilling.isSupported가 false를 반환하므로, 같은 코드를 크로스 플랫폼 앱에 그대로 둘 수 있습니다.
시작하기
애플리케이션 디스크립터에 확장을 추가합니다:
<extensions>
<extensionID>com.klug.billing</extensionID>
</extensions>
매니페스트 항목이나 액티비티는 필요 없습니다. App Store Connect와 Google Play Console에서 같은 상품 ID로 상품을 만들고, Play Console(수익 창출 설정)에서 앱의 base64 공개 키를 복사하세요.
코드 예제
저희 앱에서 실행되는 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 | iOS와 Android에서는 true, 그 외에는 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 애플리케이션 1개에 대한 연간 라이선스이며 12개월간 모든 업데이트를 제공합니다. 결제 후 확장, ActionScript 라이브러리, 문서를 이메일로 보내 드리며, 보통 영업일 기준 1일 이내입니다. 라이선스 및 약관
요구 사항
| 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의 서명된 구매 데이터를 검증 |