提醒:自 2021 年 11 月 1 日起,現有應用程式的所有更新都必須採用帳款服務程式庫 3.0 以上版本。瞭解詳情

將 Google Play 帳款服務程式庫整合至應用程式

本主題說明了如何將 Google Play 帳款服務程式庫整合至您的應用程式,以便開始販售產品。閱讀本主題前,請確認您已按照「事前準備」一節中的步驟完成 Google Play 設定。

本主題提供的程式碼範例是以 GitHub 上的官方範例應用程式為基礎。如需整合時可使用的範例應用程式和其他資源的完整清單,請參閱「其他資源」。

購買流程

以下是一次性消費或訂閱項目的一般購買流程。

  • 向使用者說明可以購買的內容。
  • 啟動購買流程,以便使用者接受購買。
  • 請在伺服器上驗證購買交易。
  • 將內容提供給使用者,並確認內容的交付程序。 視需要將項目標示為「已消耗」,以便使用者再次購買。

訂閱會自動續約,直到取消為止。訂閱可呈現下列狀態:

  • 有效:使用者記錄良好,有權存取訂閱。
  • 已取消:使用者已取消訂閱,但在到期前仍擁有存取權。
  • 寬限期:使用者遇到付款問題,但仍擁有存取權,同時 Google 正在重試付款方式以扣除款項。
  • 保留中:使用者遇到付款問題,但不再擁有存取權,同時 Google 正在重試付款方式以扣除款項。
  • 已暫停:使用者已暫停存取權,取消暫停後才可繼續存取。
  • 已到期:使用者已取消訂閱並失去存取權。 訂閱到期後,系統會將使用者視為已流失。

購買憑證和訂單 ID

Google Play 會使用購買憑證和訂單 ID 來追蹤產品和交易。

  • 「購買憑證」是一個字串,代表買家對 Google Play 產品的授權。它表示 Google 使用者「有權存取」由 SKU 代表的特定產品。您可以將購買憑證與 Google Play Developer API 搭配使用。
  • 「訂單 ID」是代表 Google Play 金融交易的字串。這個字串包含在透過電子郵件傳送給買家的收據中。您可以使用訂單 ID 來管理銷售和付款報表中的退款。

每次發生金融交易時,系統都會建立訂單 ID,而購買憑證只有在使用者完成購買流程時才會產生。

  • 針對一次性產品,每次購買都會建立新的購買憑證,大多數購買也會產生新的訂單 ID。如果使用者無須支付任何費用,則會產生例外,詳情請參閱「促銷代碼」。
  • 針對訂閱項目,系統會在首次購買交易發生時建立購買憑證和訂單 ID。每個連續帳單週期內的購買憑證都相同,而系統會建立新的訂單 ID。升級、降級、更換和重新申請作業時,系統都會建立新的購買憑證和訂單 ID。

訂閱項目的注意事項:

  • 訂閱項目升級、降級和其他訂閱項目購買流程產生的購買憑證必須取代先前的購買憑證。您必須作廢在 Google Play Developer API linkedPurchaseToken 欄位中的購買憑證。詳情請參閱「正確執行 linkedPurchaseToken 以避免重複訂閱」。
  • 訂閱續約的訂單號碼會包含一個額外整數,代表特定續約執行個體。舉例來說,初始訂閱訂單 ID 可能是 GPA.1234-5678-9012-34567,後續訂單 ID 則為 GPA.1234-5678-9012-34567..0 (初次續約)、GPA.1234-5678-9012-34567..1 (第二次續約),以此類推。

處理錯誤

Google Play 帳款服務程式庫會以 BillingResult 的形式傳回錯誤。 BillingResult 包含 BillingResponseCode,用來分類應用程式可能會遇到的帳單相關錯誤。 舉例來說,如果您收到 SERVICE_DISCONNECTED 錯誤代碼,您應重新初始化應用程式與 Google Play 的連線。 此外,BillingResult 還包含一則偵錯訊息,適合在開發時用來診斷錯誤。

連線至 Google Play

如要整合 Google Play 的帳單系統,首先請將 Google Play 帳款服務程式庫新增至應用程式並啟動連線。

新增 Google Play 帳款服務程式庫依附元件

請將 Google Play 帳款服務程式庫依附元件新增至應用程式的 build.gradle 檔案,如下所示:

Groovy

dependencies {
    def billing_version = "4.1.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "4.1.0"

    implementation("com.android.billingclient:billing:$billing_version")
}

如果您使用的是 Kotlin,Google Play 帳款服務程式庫 KTX 模組會包含 Kotlin 擴充功能和協同程式支援,可讓您在使用 Google Play 帳款服務程式庫時編寫慣用的 Kotlin 程式碼。如要在專案中加入這些擴充功能,請將下列依附元件新增至應用程式的 build.gradle 檔案,如下所示:

Groovy

dependencies {
    def billing_version = "4.1.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

Kotlin

dependencies {
    val billing_version = "4.1.0"

    implementation("com.android.billingclient:billing-ktx:$billing_version")
}

初始化 BillingClient

在 Google Play 帳款服務程式庫中加入依附元件後,您必須初始化 BillingClient 執行個體。BillingClient 是 Google Play 帳款服務程式庫與您其他應用程式之間通訊的主要介面。BillingClient 提供同步和非同步的便利方法,適用於許多常見的結帳作業。強烈建議您一次開啟一個有效的 BillingClient 連線,以免發生多個 PurchasesUpdatedListener 回呼單一事件。

如要建立 BillingClient,請使用 newBuilder()。 您可以將任何結構定義傳遞至 newBuilder()BillingClient 就可透過該程式碼取得應用程式結構定義。因此,您不必擔心記憶體流失的問題。如要接收購買交易的最新動態,您也必須呼叫 setListener(),並將參照傳遞至 PurchasesUpdatedListener。 這個事件監聽器會收到應用程式內所有購物交易的最新動態。

Kotlin

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   .enablePendingPurchases()
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build();

與 Google Play 建立連線

建立 BillingClient 後,您需要與 Google Play 建立連線。

如要連線至 Google Play,請呼叫 startConnection()。 連線程序為非同步,您必須導入 BillingClientStateListener 才能在用戶端完成設定並準備好提出進一步要求後收到回呼。

您還必須執行重試邏輯,處理與 Google Play 連線中斷的問題。如要執行重試邏輯,請覆寫 onBillingServiceDisconnected() 回呼方法,並確認 BillingClient 會呼叫 startConnection() 方法重新以重新連線 Google Play,然後再提出其他請求。

以下範例說明了如何發起連線,並測試是否可供使用:

Kotlin

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

顯示可供購買的產品

與 Google Play 建立連線後,您就可以開始查詢可用產品,並向使用者顯示。 如要在 Google Play 中查詢應用程式內產品的詳細資料,請呼叫 querySkuDetailsAsync()。 向使用者顯示產品之前,查詢 SKU 詳細資料是非常重要的步驟,原因在於系統會傳回本地化的產品資訊。針對訂閱項目,請確認您的產品顯示遵守所有 Play 政策

呼叫 querySkuDetailsAsync() 時,傳遞 SkuDetailsParams 的執行個體,這會指定在 Google Play 管理中心建立的產品 ID 字串清單及 SkuTypeSkuType 可以是一次性產品的 SkuType.INAPP,也可以是訂閱項目的 SkuType.SUBS

如要處理非同步作業的結果,您必須一併指定執行 SkuDetailsResponseListener 介面的事件監聽器。接著,您可以覆寫 onSkuDetailsResponse(),這會在查詢完成時通知事件監聽器,如以下範例所示:

Kotlin

suspend fun querySkuDetails() {
    val skuList = ArrayList<String>()
    skuList.add("premium_upgrade")
    skuList.add("gas")
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(SkuType.INAPP)

    // leverage querySkuDetails Kotlin extension function
    val skuDetailsResult = withContext(Dispatchers.IO) {
        billingClient.querySkuDetails(params.build())
    }

    // Process the result.
}

Java

List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    });

Google Play 帳款服務程式庫會將查詢結果儲存在 SkuDetails 物件的 List 中。 接著,您可以在清單中的每個 SkuDetails 物件上呼叫各種方法,以查看價格或說明等應用程式內產品相關資訊。如要查看可用產品的詳細資訊,請參閱 SkuDetails 類別中的方法清單。

在提供待售項目之前,請先檢查使用者是否尚未擁有該項目。如果使用者的項目庫中仍有消耗性產品,他們必須先使用該項目才能再次購買。

提供訂閱項目前,請先確認使用者尚未訂閱。

啟動購買流程

如要透過應用程式提出購買請求,請從應用程式的主要執行緒呼叫 launchBillingFlow() 方法。這個方法會參照 BillingFlowParams 物件,該物件包含從呼叫 querySkuDetailsAsync() 中取得的相關 SkuDetails 物件。 如要建立 BillingFlowParams 物件,請使用 BillingFlowParams.Builder 類別。

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
val flowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build();
int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();

// Handle the result.

launchBillingFlow() 方法會傳回 BillingClient.BillingResponseCode 中列出的其中一個回應代碼。 請務必查看這項結果,確認啟動時購買流程沒有發生錯誤。OKBillingResponseCode 表示啟動成功。

成功呼叫 launchBillingFlow() 時,系統會顯示 Google Play 購買畫面。圖 1 顯示了訂閱項目的購買畫面:

Google Play 購買畫面會顯示可供購買的訂閱項目
圖 1. Google Play 購買畫面會顯示可供購買的訂閱項目。

Google Play 會呼叫 onPurchasesUpdated(),將購物作業的結果提供給導入 PurchasesUpdatedListener 介面的事件監聽器。在初始化用戶端時,請使用 setListener() 方法指定事件監聽器。

您必須執行 onPurchasesUpdated() 來處理可能的回應代碼。 以下範例說明了如何覆寫 onPurchasesUpdated()

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

購買成功後,系統會產生與圖 2 類似的 Google Play 購買成功畫面。

Google Play 的購買成功畫面
圖 2. Google Play 的購買成功畫面。

購物成功後也會產生購物權杖,這是一個不重複 ID,代表已購買應用程式內產品的使用者和產品 ID。應用程式可在使用者的裝置上儲存購物權杖。不過,建議您將權杖傳遞至安全的後端伺服器,以便驗證購物交易並防範詐欺行為。下一節會說明這項程序。

使用者也會收到隨附交易收據的電子郵件,收據內含該筆交易的訂單 ID 或專屬 ID。使用者每次購買一次性產品、購買初始訂閱項目和後續的定期自動續訂時,都會收到內含專屬訂單 ID 的電子郵件。您可以在 Google Play 管理中心使用這組訂單 ID 管理退款。

處理購買交易

使用者完成購買後,您的應用程式就需要處理該筆購買交易。在大多數情況下,系統會透過 PurchasesUpdatedListener 傳送購買通知給應用程式。 但在某些情況下,您的應用程式會呼叫 BillingClient.queryPurchasesAsync(),如「擷取購買交易」中所述。

您的應用程式應以下列方式處理購買交易:

  1. 驗證購買交易。
  2. 將內容提供給使用者,並確認內容的交付程序。 視需要將項目標示為「已消耗」,以便使用者再次購買。

如要驗證購買交易,請先確認「購買狀態」是否為 PURCHASED, 如果狀態為 PENDING,請按照「處理未完成交易」的說明處理購買程序。針對從 onPurchasesUpdated()queryPurchasesAsync 收到的購買交易,您應進一步驗證其合法性,確保應用程式能順利授予權限。如要瞭解如何正確驗證購買交易,請參閱「在授予權限前驗證購買交易」。

驗證購買交易後,您的應用程式就可以開始將權限授予使用者。授權後,您的應用程式就必須確認購買交易。這項確認會通知 Google Play 您已授予購買交易的權限。

授權和確認購物交易的程序會因該交易的購買項目 (非消耗性產品、消耗性產品或訂閱項目) 而有所不同。

如果是消耗性產品,consumeAsync() 方法符合確認要求,並可標示您的應用程式已將權限授予使用者。這個方法也能讓應用程式再次提供一次性產品,以便使用者購買。

如要標示一次性產品已用盡,請呼叫 consumeAsync() 並加入購買憑證,Google Play 應讓這組購買憑證可用於重新購買。您也必須傳遞執行 ConsumeResponseListener 介面的物件,這個物件會處理消耗作業的結果。 您可以覆寫 onConsumeResponse() 方法,等到作業完成後,Google Play 帳款服務程式庫就會呼叫此方法。

以下範例示範如何使用相關聯的購物權杖消耗產品:

Kotlin

suspend fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);
}

如要確認非消耗性產品購物交易,請使用 Google Play 帳款服務程式庫的 BillingClient.acknowledgePurchase() 或 Google Play Developer API 的 Product.Purchases.Acknowledge。確認購物交易前,應用程式應確認該交易是否已透過 Google Play 帳款服務程式庫的 isAcknowledged() 方法或 Google Developers API 的「acknowledgementState」欄位進行確認。

以下範例說明了如何透過 Google Play 帳款服務程式庫確認購買交易:

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

suspend fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

處理訂閱項目的方式與非消耗產品相同。您可以使用 Google Play 帳款服務程式庫的 BillingClient.acknowledgePurchase() 或 Google Play Developer API 的 Purchases.Subscriptions.Acknowledge 確認訂閱項目。所有初始訂閱項目購買交易都必須獲得確認,訂閱續約則無需確認。 如要進一步瞭解確認訂閱項目的時機,請參閱「販售訂閱項目」主題。

擷取購買交易

如要確保應用程式可以順利處理所有購買交易,光使用 PurchasesUpdatedListener 監聽購買狀態更新是不夠的。您的應用程式可能並不瞭解使用者進行的所有購買交易。在以下情況中,您的應用程式可能會無法掌握或無法得知購買狀態:

  • 購物期間的網路問題:使用者成功完成購物程序且收到 Google 的確認通知,但使用者的裝置在透過 PurchasesUpdatedListener 收到購物通知前中斷網路連線。
  • 多部裝置:使用者在某部裝置上購買產品,並且希望切換裝置後也能查看該產品。
  • 處理應用程式外的購物交易:某些購物交易可在應用程式外完成,例如促銷兌換。

如要處理這類情況,請務必讓應用程式使用 onResume() 方法呼叫 BillingClient.queryPurchasesAsync(),以確保所有購物交易的處理程序都按照這篇文章的說明成功完成。

處理應用程式外的購物交易

某些購物交易 (例如促銷兌換) 可能會在應用程式外進行。使用者在應用程式外購物時,會希望應用程式顯示應用程式內通訊訊息,或透過某種通知機制讓使用者瞭解應用程式已正確接收並處理購物交易。可接受的機制如下:

  • 顯示應用程式內彈出式視窗。
  • 將訊息傳送至應用程式內通訊訊息方塊,並清楚說明應用程式內通訊訊息方塊中有新訊息。
  • 使用作業系統通知訊息。

請注意,即使您的應用程式辨識出購買交易,應用程式仍可能處於任何狀態,使用者甚至可能在購買後仍未安裝您的應用程式。無論應用程式處於何種狀態,使用者都希望重新啟用應用程式時收到購買項目。

在購買發生時,無論應用程式處於何種狀態,您都必須偵測出購買交易。然而,在某些例外情況下,我們可能不會立即通知使用者已收到項目。例如:

  • 在遊戲需要大量操作的部分中,顯示訊息可能會分散使用者的注意力。在此情況下,您必須在操作結束後通知使用者。
  • 在剪輯過程中,顯示訊息可能會分散使用者的注意力。在此情況下,您必須在剪輯結束後通知使用者。
  • 遊戲的初始教學課程和使用者設定期間。建議您在使用者開啟遊戲後或初次進行使用者設定期間,立即向新使用者發出獎勵通知。不過,也可以等到主要遊戲序列可通知使用者為止。

決定向使用者發出應用程式外購物通知的時機和方式時,請一律將使用者納入考量。假如使用者沒有立即收到通知,可能會感到困惑,而且可能會因此停止使用您的應用程式、聯絡使用者支援團隊,或在社群媒體上投訴。

處理未完成交易

Google Play 支援「未完成交易」,這類交易程序從使用者發起購物開始到系統處理購物交易付款方式的這段期間,需要完成其他一或多個步驟。除非 Google 通知使用者的付款方式支付成功,否則您的應用程式不應向這類購買交易授權。

舉例來說,使用者可以選擇使用現金付款,以建立 PENDING 狀態的應用程式內產品購物交易。使用者可以選擇在實體商店完成交易,並透過通知和電子郵件接收代碼。當使用者來到實體商店時,就可以向收銀員兌換代碼,再用現金付款。接著,Google 會通知您和使用者已收到現金,您的應用程式就可以向使用者授權。

您的應用程式必須呼叫 enablePendingPurchases() 來完成初始化應用程式,以便支援未完成交易。

應用程式收到新的購買交易時,無論是透過 PurchasesUpdatedListener 還是呼叫 queryPurchasesAsync(),都要使用 getPurchaseState() 方法來判斷購買狀態是 PURCHASEDPENDING

如果使用者完成購買時您的應用程式正在運作,系統會再次呼叫 PurchasesUpdatedListener,而 PurchaseState 現已變為 PURCHASED。這時,應用程式可按照一次性消費的標準處理方法處理購物交易。應用程式也應使用應用程式的 onResume() 方法呼叫 queryPurchasesAsync(),以處理在應用程式未運作時轉為 PURCHASED 狀態的購物交易。

應用程式也可以監聽 OneTimeProductNotifications,將即時開發人員通知用於未完成的購物交易。當購物交易從 PENDING 轉為 PURCHASED 時,應用程式會收到 ONE_TIME_PRODUCT_PURCHASED 通知。如果購買交易被取消,應用程式會收到 ONE_TIME_PRODUCT_CANCELED 通知,如果客戶在指定的時間範圍內未完成付款,就可能發生這種情況。 收到這類通知時,您可以使用 Google Play Developer API,其中包含 Purchases.productsPENDING 狀態。

如需測試此情境的詳細步驟,請參閱「測試未完成購買交易」。

處理多件購買交易

在 Google Play 帳款服務程式庫 4.0 以上版本中,Google Play 允許客戶在購物車中指定購買數量,藉此在同一筆交易中購買多件相同的應用程式內產品。 應用程式應處理多件購買交易,並按照指定的購買數量授予權限。

為了滿足多件購買交易的需求,應用程式的佈建邏輯需要檢查項目數量。您可以透過下列任一 API 存取「quantity」欄位:

新增了處理多項購物交易的邏輯後,您必須在 Google Play 管理中心的應用程式內產品管理頁面中,為對應的產品啟用多項交易的功能。