← 所有扩展

原生扩展

KlugBilling

com.klug.billing

无需 APM。下载即用,再简单不过。 只需更新 Adobe AIR SDK。如需帮助,我们随时为您服务。

适用于 App Store 和 Google Play 上 AIR 应用的应用内购买与订阅,两个商店使用同一套 ActionScript API。

iOS Android 已投入生产 €19 / 年起 · 购买

概览

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 公钥校验购买

支持的平台

iOS iPhone · iPad · App Store
Android 手机 · 平板 · 折叠屏设备 · Google Play Billing Library 8

在其他平台上,InAppBilling.isSupported 返回 false,因此同一份代码可以保留在跨平台应用中。

入门

将扩展添加到您的应用描述文件中:

app.xml
<extensions>
    <extensionID>com.klug.billing</extensionID>
</extensions>

无需任何清单条目和 Activity。在 App Store Connect 和 Google Play Console 中使用相同的商品 ID 创建商品,并从 Play Console(创收设置)复制应用的 base64 公钥。

代码示例

四个完整流程,取自在我们应用中运行的 API。每次调用都会立即返回;结果以 InAppBilling.service 上的事件形式到达。完整文档 →

1 · 连接商店
Store.as
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
    });
}
2 · 显示带本地化价格的商品
Products.as
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
    }
}
3 · 购买、交付、完成
Purchase.as
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;
        }
    }
}
4 · 恢复以前的购买
Restore.as
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 账户的购买。
applicationReceiptiOS:供服务器使用的 base64 格式应用收据,可通过 refresh() 刷新。
dispose()断开与商店的连接。

事件

InAppBillingEventSETUP_SUCCESS, SETUP_FAILURE, RESTORE_PURCHASES_SUCCESS, RESTORE_PURCHASES_FAILED
ProductEventPRODUCTS_LOADED (products), INVALID_PRODUCT, PRODUCTS_FAILED
PurchaseEventPURCHASES_UPDATED(data = Purchase 对象), PURCHASE_FAILED, FINISH_SUCCESS, FINISH_FAILED, RESTORE_PURCHASES_SUCCESS, RESTORE_PURCHASES_FAILED, GET_PURCHASES_COMPLETE, GET_PURCHASES_FAILED
AvailabilityEventCOMPLETE,附带可用性结果
ApplicationReceiptEventREFRESH_SUCCESS, REFRESH_FAILED

购买 KlugBilling

一个 AIR 应用的年度许可,含 12 个月内的所有更新。付款后,您将通过电子邮件收到扩展、ActionScript 库和文档,通常在一个工作日内。许可与条款

基础版
€19 / 年
  • 一个 AIR 应用的许可
  • 12 个月内的所有更新
  • 不含支持
购买基础版
标准版
€149 / 年
  • 一个 AIR 应用的许可
  • 12 个月内的所有更新
  • 7 天内通过电子邮件答复
购买标准版
优先版
€299 / 年
  • 一个 AIR 应用的许可
  • 12 个月内的所有更新
  • 48 小时内通过电子邮件答复
购买优先版
税费在结账时由 Link (Stripe) 计算。您需要在结账时输入 AIR 应用的应用 ID。

要求

AIR SDK51.x (HARMAN)
iOSiOS 13 或更高版本,在 App Store Connect 中创建商品
AndroidAndroid 6.0 (API 23) 或更高版本,在 Google Play Console 中创建商品
Google PlayPlay Billing Library 8.3,在打包时添加
服务器(推荐)校验 iOS 应用收据或 Google 的已签名购买数据