透過 Google Play 帳款服務導入會員方案變更功能

1. 簡介

本程式碼研究室會教您如何使用 Google Play 帳款服務程式庫 (PBL) 管理會員方案變更。您將瞭解各種換貨模式對價格和使用者授權的影響,同時學習如何處理後端即時開發人員通知 (RTDN)。

觀眾

本程式碼研究室專為 Android 應用程式開發人員設計,提供實作進階訂閱管理功能的指引。這份指南可協助您提供流暢的使用者體驗,讓使用者升級、降級或轉換訂閱方案。

課程內容...

  • 如何在 Play 管理中心建立訂閱項目
  • 如何選擇正確的 ReplacementMode (例如 WITH_TIME_PRORATIONDEFERRED),以符合應用程式的升級和降級政策。
  • 如何在 launchBillingFlow 中設定 BillingFlowParams,以便在更換方案時觸發 Google Play 購買流程。
  • 如何使用即時開發人員通知 (RTDN) 和 purchases.subscriptionsv2 API,在後端安全地撤銷舊存取權並授予新存取權

軟硬體需求

2. 建構範例應用程式

本程式碼實驗室會使用 Android 範例應用程式,說明如何在 PBL 中實作訂閱項目更換功能。這個範例應用程式是功能齊全的 Android 應用程式,提供完整原始碼,可展示下列各方面:

  • 將應用程式與 PBL 整合
  • 實作訂閱項目更換功能

如果您已熟悉訂閱項目更換和 PBL,可以下載範例應用程式並試用。

以下示範影片顯示範例應用程式部署及執行後的樣貌和行為。

必要條件

建構及部署範例應用程式前,請先完成下列步驟:

建構

如要建構範例應用程式,以便按照程式碼研究室的指示操作,請執行下列步驟:

  1. GitHub 下載範例應用程式
  2. 更新範例應用程式 build.gradle 中的 applicationId,以反映 Play 管理中心中應用程式的應用程式 ID。
  3. 建構範例應用程式
    注意:這樣就能成功建構應用程式,以進行本機測試。不過,由於您尚未在 Play 管理中心建立必要訂閱項目,因此執行應用程式時不會擷取產品和價格。下個單元將說明如何在開發人員控制台中建立訂閱項目。

3. 在 Play 管理中心建立訂閱項目

Google Play 訂閱系統可讓您靈活地建立、管理及銷售訂閱項目。您可以在 Play 管理中心設定含有多項基本方案的訂閱項目,而且每個方案都能提供多種優惠。訂閱項目優惠可以有多種計費模式和資格條件。在本程式碼研究室中,您將建立三項訂閱項目:付費方案基本方案Lite 方案,模擬各種價位的典型訂閱方案。每個方案都只有一個按月計費的週期性基本方案。

建立新訂閱

如何建立新訂閱項目

  1. 開啟 Play 管理中心,然後前往「訂閱項目」頁面 (依序點選「透過 Google Play 營利」 >「產品」 >「訂閱項目」)。
  2. 按一下「Create Subscription」 (建立訂閱項目)
  3. 輸入訂閱方案詳細資料:
    • ProductID:輸入專屬產品 ID。輸入 premium_plan
    • 名稱:輸入訂閱項目的簡稱。範例:Premium Plan
  4. 按一下「建立」

建立 Base 方案

  1. 開啟 Play 管理中心,然後前往「訂閱項目」頁面 (依序點選「透過 Google Play 營利」 >「產品」 >「訂閱項目」)。
  2. 找出要建立基本方案的訂閱項目,點選旁邊的向右箭頭即可查看詳細資料。
  3. 按一下「新增基本方案」
  4. 輸入基本方案 ID。例如 monthly-auto-renewing
  5. 選擇「自動續訂」類型。
  6. 如果是自動續訂型基本方案,請設定下列項目:
    • 帳單週期:每月
    • 寬限期:7 天
    • 計費方案和優惠變更:在帳單結算日收費
    • 重新訂閱:按一下「允許」
  7. 在「價格與供應情形」部分,按一下「設定價格」即可設定基本方案價格。
  8. 選取所有國家/地區,然後按一下「設定價格」
  9. 將這個基本方案的價格設為 $10 美元,然後按一下「更新」
  10. 設定基本方案價格後,請依序點按右下方的「儲存」和「啟用」

為範例應用程式建立訂閱項目

以本程式碼研究室為例,請建立兩個額外訂閱項目,並採用下列設定:

  • 基本方案
    • 產品 ID:basic_plan
    • 名稱:基本方案
    • 基本方案 ID:monthly-auto-renewing
    • 價格:$5 美元
  • Lite 方案
    • 產品 ID:lite_plan
    • 名稱:Lite 方案
    • 基本方案 ID:monthly-auto-renewing
    • 價格:$3

範例應用程式已設定為使用這些產品 ID 和基本方案 ID。您可以建立不同設定的訂閱項目,但這樣就必須修改範例應用程式,才能使用您建立的產品 ID。

訂閱項目建立影片

以下影片說明如何按照上述步驟,在 Play 管理中心建立訂閱項目。

4. 更換訂閱方案

整合 PBL 的開發人員可以為現有訂閱者提供多種選項,讓他們根據需求變更訂閱方案:

  • 如果您販售多個訂閱層級 (例如「基本級」和「進階級」訂閱項目),則可讓使用者購買不同訂閱項目的基本方案或優惠,藉此轉換層級。
  • 您可以讓使用者變更目前的帳單週期,例如從月費方案改成年繳方案。
  • 您也可以允許使用者切換自動續訂或預付方案。

使用者決定升級、降級或變更訂閱項目時,您可以指定「取代模式」,決定如何套用目前帳單週期的按比例計費價值,以及使用者的授權變更生效時間。

Play 結帳程式庫提供多種 ReplacementMode 選項,可控管這項行為。

可用的取代模式

  • WITH_TIME_PRORATION:訂閱項目會立即升級或降級。根據費用差額調整任何剩餘時間,並更新下一個帳單日期,計入新的訂閱項目。這是預設行為
  • CHARGE_PRORATED_PRICE:系統會立即升級訂閱項目,並保持相同的帳單週期,接著,系統會向使用者收取剩餘訂閱期的費用差額。
  • CHARGE_FULL_PRICE:系統會立即升級或降級訂閱項目,並向使用者收取新授權的全額費用。系統會將之前訂閱項目的剩餘價值,轉用於相同的授權,或者在切換至不同的授權時依比例計算時間。
  • WITHOUT_PRORATION:系統會立即升級或降級訂閱項目,並在訂閱項目續訂時依新的價格收費。帳單週期維持不變。
  • DEFERRED:只有在續訂時,系統才會升級或降級訂閱項目。

5. WITH_TIME_PRORATION

在這個更換模式中,訂閱項目會立即升級或降級。系統會根據費用差額調整任何剩餘時間,並將下一個帳單日期往後推延,計入新的訂閱項目。此為預設行為。

範例情境

使用者在 4 月 15 日 (月結週期的一半) 從 Basic 方案 ($4.99 美元/月) 改為 Premium 方案 ($9.99 美元/月)。

此時:

  • 使用者會立即取得 Premium 方案的存取權。
  • Google Play 會自動計算按比例分配的期間。舉例來說,如果 Google Play 計算出「基本」方案剩餘的 15 天價值等同於「進階」方案的 7 天,則下一個帳單結算日會提前至 4 月 21 日。
  • 使用者無須立即付款。

程式碼片段

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

升級 (WITH_TIME_PRORATION)

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Premium 方案。

使用者的授權會立即升級為 Premium 方案。使用者需立即支付的金額為 $0.00 美元。系統會按比例將基本方案的剩餘價值換算成進階方案的時間,因此下次續訂日會提前。系統會在新的帳單日期向使用者收取續訂費用 $9.99 美元。

降級 (WITH_TIME_PRORATION)

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Lite 方案。

系統會立即將使用者的授權降級為 Lite 方案。即期付款金額為 $0.00 美元。基本方案的剩餘價值會按比例換算成 Lite 方案的時間,因此下次續訂日會大幅延後。系統會在新的帳單結算日向使用者收取 $2.99 美元的續訂費用。

結論

在本節中,您瞭解了 WITH_TIME_PRORATION 如何根據價格差異調整下次續訂時間,藉此修改使用者授權,而不立即收費。這是升級或降級使用者的有效預設策略。

6. CHARGE_PRORATED_PRICE

在這個替換模式中,訂閱項目會立即升級,帳單週期則維持不變。接著,系統會向使用者收取剩餘訂閱期的費用差額。

注意:這個選項僅適用於升級訂閱項目,亦即每個時間單位的費用增加。

範例情境

使用者訂閱 Basic 方案 (每月 $4.99 美元),並在 4 月 20 日決定升級為 Premium 方案 (每月 $9.99 美元),此時距離月結帳單週期結束還有約 10 天。

此時:

  • 使用者會立即取得 Premium 方案的存取權。
  • 系統會立即向使用者收取當前帳單週期剩餘 10 天的按比例計算差額。這筆款項約為 $2.99 美元,相當於 10 天的 進階方案費用。
  • 使用者的帳單結算日不會變更。

程式碼片段

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

以 CHARGE_PRORATED_PRICE 升級

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Premium 方案。

使用者會立即升級至 Premium 方案,並保留原先的續訂日期。你必須立即支付的金額,是當前週期剩餘天數的 Premium基本版方案 價格差額 (按比例計算)。續訂時,系統會向使用者收取 Premium 續訂全額費用 $9.99 美元。

降級並支付 CHARGE_PRORATED_PRICE

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Lite 方案。

這個更換模式僅適用於升級訂閱項目,也就是每個時間單位的費用增加,因此降級時會發生錯誤。結帳流程會失敗,並向使用者顯示錯誤訊息,指出降級不支援依比例計費模式。

結論

本節說明 CHARGE_PRORATED_PRICE 如何讓使用者立即升級,並在帳單週期不變的情況下,向使用者收取剩餘帳單週期的確切差價。如果使用者想升級至較昂貴的層級,但不想變更帳單日期,這個方法就很有用。

7. CHARGE_FULL_PRICE

在這個替換模式中,訂閱項目會立即升級或降級,並向使用者收取新授權的全額費用。系統會將之前訂閱項目的剩餘價值,轉用於相同的授權,或者在切換至不同的授權時依比例計算時間。

範例情境

使用者訂閱 Basic 方案 (4 月 1 日起每月 $4.99 美元)。使用者在 4 月 20 日想改用 Premium 方案 (每月 $9.99 美元)。

此時:

  • 系統會立即向使用者收取 Premium 方案的全額費用 ($9.99 美元)。
  • 基本方案的剩餘價值 (例如 10 天的價值) 會轉換為等值的 進階方案時間。在本例中,10 天的「基本」相當於 5 天的「進階」
  • 系統會調整使用者的下次續訂日期,將這段按比例計算的時間納入考量。因此,續訂日會變成 5 月 25 日 (4 月 20 日 + 1 個月 + 5 天)。
  • 5 月 25 日起,系統會每月續訂。

程式碼片段

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

以 CHARGE_FULL_PRICE 升級

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Premium 方案。

使用者會立即升級至 Premium 方案。你必須立即支付的金額為 Premium 方案的全額費用 $9.99 美元。基本方案的剩餘價值會轉換為新 Premium 方案的時間,因此第一個續訂日會稍微延後。之後,每個週期的續訂金額為 $9.99 美元。

降級並支付全額費用

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Lite 方案。

使用者會立即降級至 Lite 方案,並開始新的帳單週期。你必須立即支付 $2.99 美元的目標價格。系統會將基本版方案未使用的部分按比例換算成時間,加入新的 Lite 方案,延長首次續訂日期。之後,每個週期續訂費用為 $2.99 美元。

結論

在本節中,我們說明瞭 CHARGE_FULL_PRICE 如何在切換當天向使用者收取全額費用,並立即開始新的帳單週期。先前方案的剩餘餘額會平均分配到下個續訂日期。

8. WITHOUT_PRORATION

在這個更換模式中,訂閱項目會立即升級或降級,並在續訂時依新的價格收費。

範例情境

使用者訂閱 Basic 方案 (4 月 1 日起每月 $4.99 美元)。使用者在 4 月 20 日想改用 Premium 方案 (每月 $9.99 美元)。

此時:

  • 使用者會立即取得 Premium 方案的存取權。
  • 使用者在下次訂閱續訂日期 (5 月 1 日) 前,不必支付較高的 $9.99 美元費用。

程式碼片段

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

升級並採用 WITHOUT_PRORATION

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Premium 方案。

使用者會立即升級至 Premium 方案,並保留現有的續訂日期。即期付款金額為 $0.00 美元。使用者在下個帳單日期前,仍可使用 Premium 方案,之後續訂時則須支付 $9.99 美元。

降級 (WITHOUT_PRORATION)

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Lite 方案。

系統會立即將使用者降級至 Lite 方案,使用者將無法再使用已付費的 Basic 功能。即期付款金額為 $0.00 美元。帳單週期維持不變,使用者將在下次續訂時支付新的較低費率 $2.99 美元。

結論

本節示範如何WITHOUT_PRORATION立即更換使用者的權利,且不收取結帳費用,同時維持帳單週期不變。

9. DEFERRED

在這個更換模式中,系統只會在續訂時升級或降級訂閱項目,但會立即發出新購買交易。現有項目會設為不可續訂,並在目前帳單週期結束時到期,而新申請的權益會在到期後立即生效。

範例情境

使用者訂閱 Basic 方案 (4 月 1 日起每月 $4.99 美元)。使用者在 4 月 20 日想改用 Premium 方案 (每月 $9.99 美元)。

此時:

  • 使用者不會立即產生費用。
  • 在目前的帳單週期結束前 (4 月 30 日),使用者仍可繼續使用 基本版 功能。
  • 訂閱方案會在下一個續訂日 (5 月 1 日) 自動升級為 Premium

程式碼片段

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.DEFERRED;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

使用 DEFERRED 升級

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.DEFERRED
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Premium 方案。

在目前的帳單週期結束前,使用者仍會維持 Basic 方案。即期付款金額為 $0.00 美元。續訂時,系統會將其權益升級至 Premium 方案,並收取 $9.99 美元的新續訂金額。

使用 DEFERRED 降級

如要模擬這種情況,請按照下列步驟操作:

  • 在範例應用程式的 MainActivity 中,將程式碼片段中的 replacementMode 更新為 SubscriptionProductReplacementParams.ReplacementMode.DEFERRED
  • 重新建構並啟動應用程式。
  • 從 Google Play 商店取消現有訂閱方案 (如有),並等待方案到期。
  • 購買「基本」方案。
  • 改用 Lite 方案。

在目前的帳單週期結束前,使用者仍會維持 Basic 方案。即期付款金額為 $0.00 美元。續訂時,他們的權益會升級為 Lite 方案,並以 $2.99 美元的新續訂金額收費。

結論

本節說明 DEFERRED 取代模式如何延後升級或降級,直到有效使用者的付費時間結束為止。因此,如果想降級但不想失去已購買的功能,這項功能就特別適合你。

10. 後端和用戶端處理程序

使用者成功觸發訂閱項目更換程序後,請確保應用程式和後端都能正確處理這項變更,以免發生服務中斷或重複計費等問題。

範例情境

  • 使用者採用 Basic 月費方案 (product_id basic_plan 和 purchase_token basic_purchase_token_123)。
  • 使用者透過立即更換模式 (WITHOUT_PRORATIONWITH_TIME_PRORATIONCHARGE_PRORATED_PRICECHARGE_FULL_PRICE 其中之一) 切換至進階方案
  • 訂閱方案切換成功後,Google 會將其視為「新」訂閱方案,並為付費方案建立新的購買憑證 (product_id premium_plan 和 purchase_token premium_purchase_token_123)。

用戶端處理

onPurchasesUpdated

當替代購買交易完成時,系統會觸發 PurchasesUpdatedListener。雖然這是切換,但 Google Play 會將進階版方案視為全新購買

應用程式會收到 Purchase 物件,內含 premium_purchase_token_123 購買交易權杖和 product_id premium_plan。您必須將這視為新訂閱者:驗證權杖並準備授予存取權

@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
        for (Purchase purchase : purchases) {
            // purchase.getPurchaseToken() = premium_purchase_token_123
            // purchase.getProducts() will contain premium_plan
            // Verify the purchase and grant entitlement
            handleNewPurchase(purchase);
        }
    }
}

queryPurchasesAsync

queryPurchasesAsync 只會傳回透過應用程式購買的有效訂閱項目。您應使用這個方法判斷要向使用者顯示哪些權限。如果是立即更換,queryPurchasesAsync() 會停止傳回舊的 BASIC 購買憑證,只傳回新的 PREMIUM 購買憑證。

每當應用程式繼續執行或完成購買交易時,請呼叫這個方法。如果系統偵測到 Premium 權杖,請立即授予 Premium 功能,並移除 Basic 功能。

後端處理 (RTDN)

發生更換時,Google Play 會將即時開發人員通知 (RTDN) 傳送至您設定的 Pub/Sub 主題。

  • 如果是立即換貨,Google 會傳送 SUBSCRIPTION_PURCHASED RTDN,並附上新的購買憑證。RTDN 酬載範例
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • 伺服器收到 RTDN 傳送的新購買憑證後,請使用該憑證呼叫 purchases.subscriptionsV2 API,擷取購買交易詳細資料。API 回應包含 linkedPurchaseToken 欄位,用於判斷購買交易權杖是指新的訂閱交易,還是訂閱項目更換交易。
  • 如果是訂閱項目更換,linkedPurchaseToken 是指舊訂閱項目的購買憑證。在本情境中,這個值是 basic_purchase_token_123GET purchases.subscriptionsV2 回應範例
    curl \
    'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/<application_id>/purchases/subscriptionsv2/tokens/premium_purchase_token_123' \
    --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
    --header 'Accept: application/json'
    
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "...",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123", // The purchase token of the subscription that was replaced (Basic Plan in this case)
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // productID of the new subscription (Premium Plan in this case)
          "expiryTime": "....",
          "autoRenewingPlan": {...},
          "offerDetails": {
            "basePlanId": "monthly-auto-renewing" // base plan ID of the new subscription
          },
          "itemReplacement": { // Details about the subscription replacement
            "productId": "subscription_basic", // productID of the old subscription (Basic Plan in this case)
            "replacementMode": "CHARGE_PRORATED_PRICE", // Replacement strategy used for this subscription change
            "basePlanId": "monthly-auto-renewing" // base plan ID of the old subscription 
          },
          "offerPhase": {...}
        }
      ],
      "etag": "<etag_value>"
    }
    
  • 必須確認這筆新的 Premium 購買交易。您可以在應用程式或後端執行這項操作。如果未在 3 天內確認購買交易,系統將退款並撤銷授權。如要進一步瞭解如何處理及確認購買交易,請參閱開發人員說明文件

結論

本節說明如何在用戶端和後端處理立即更換訂閱方案的程序。您瞭解 Google Play 會將新方案視為全新購買交易,並核發新的購買憑證。在用戶端,您必須使用 PurchasesUpdatedListener 處理這筆新交易,並根據 queryPurchasesAsync 的回應更新授權。在後端,您應監聽新權杖的 SUBSCRIPTION_PURCHASED RTDN,使用 purchases.subscriptionsv2 API 識別舊訂閱項目的 linkedPurchaseToken,並立即撤銷與舊權杖相關聯的存取權,同時授予新授權。請務必確認這筆新購買交易。

11. 處理 DEFERRED 取代項目

與立即更換模式不同,ReplacementMode.DEFERRED 會將訂閱方案變更和授權更新延後至目前的帳單週期結束時。處理延後換貨需要特定邏輯,確保使用者在適當時間收到正確的權利。

範例情境

  • 使用者採用 Basic 月費方案 (product_id basic_plan 和 purchase_token basic_purchase_token_123),將於 4 月 15 日續訂
  • 4 月 1 日,使用者決定透過 ReplacementMode.DEFERRED 改用 Premium 方案。
  • Google 會立即為「Premium」方案建立「新」購買憑證 (product_id premium_plan 和 purchase_token premium_purchase_123),但系統會在 4 月 15 日向使用者收費,並提供相應權益。

處理延遲取代模式

1. 購買流程成功後立即執行 (應用程式)

  • 購買流程完成後,系統會叫用 PurchasesUpdatedListener。應用程式會收到包含新購買憑證 premium_purchase_token_123Purchase 物件,但 product_id 仍會參照舊 basic_plan,因為使用者只有 Basic 方案的授權。您必須將此視為新交易,並確認憑證。
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
            for (Purchase purchase : purchases) {
                // purchase.getPurchaseToken() = premium_purchase_token_123
                // purchase.getProducts() will contain basic_plan
                // Verify and acknowledge the purchase
                handleNewPurchase(purchase);
            }
        }
    }
    
  • queryPurchasesAsync 會立即傳回購買交易,其中包含新的購買憑證 (premium_purchase_token_123),以及相關聯的原始授權 (basic_plan)。您可以繼續依此授權使用者使用 Basic 方案。

2. 購買流程成功後立即執行 (後端)

  • 系統會在購買流程後立即傳送 SUBSCRIPTION_PURCHASED RTDN,提供新的購買憑證 (premium_purchase_token_123)。RTDN 酬載範例
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • 使用新的購買憑證呼叫 GET purchases.subscriptionsv2,即可擷取購買詳細資料。回應包含 2 個委刊項。
    • 一個代表舊訂閱項目 (基本方案),且 expiryTime 為未來時間。舊訂閱方案不會續訂,且deferredItemReplacement會包含新訂閱方案 (進階版方案)。這表示舊授權到期後,系統會替換為新授權。
    • 一張代表新購買的訂閱項目,未設定「expiryTime」的值
    API 回應範例
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "2026-05-07T15:50:11.383Z",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123",
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // Premium Plan has no expiry time
          "autoRenewingPlan": {...},
          "offerDetails": {...},
          "itemReplacement": {. // Subscription replacement details
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {}
        },
        {
          "productId": "basic_plan", // Subscription to be replaced
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time in the future
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "deferredItemReplacement": { // identifier indicating this subscription will be replaced upon renewal
            "productId": "subscription_premium"
          },
          "latestSuccessfulOrderId": "GPA.<order_id>",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    
  • 必須確認新的購買憑證。您可以在應用程式或後端執行這項操作。如要進一步瞭解如何處理及確認購買交易,請參閱開發人員說明文件
  • 系統會針對舊的購買憑證 (basic_purchase_token_123) 傳送 SUBSCRIPTION_EXPIRED RTDN。RTDN 酬載範例
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":13, // SUBSCRIPTION_EXPIRED
        "purchaseToken":"basic_purchase_token_123" //purchase token for the old subscription
      }
    }
    
  • 使用舊購買憑證呼叫 GET purchases.subscriptionsv2 API 時,會顯示為已過期 (SUBSCRIPTION_STATE_EXPIRED)。舊方案剩餘時間的授權會轉移給新購買交易。

3. 執行取代模式時 - 購買流程後首次續訂 (應用程式)

  • queryPurchasesAsync 會傳回購買交易,其中包含新的購買憑證 (premium_purchase_token_123),以及相關聯的新訂閱項目 (premium_plan)。
  • 購買流程成功後,新購買交易應該已處理完畢,因此您除了確定已授予使用者正確的訂閱方案存取權外,不需採取任何特殊動作。

4. 執行取代模式時 - 購買流程後首次續訂 (後端)

  • 使用 ReplacementMode.DEFERRED 時,首次續訂會依循任何其他續訂的標準行為,處理 SUBSCRIPTION_RENEWED RTDN。發生這種情況時,您不需要處理任何取代模式的特殊邏輯。
  • 使用新的購買憑證呼叫 GET purchases.subscriptionsv2,即可擷取購買詳細資料。回應包含 2 個委刊項。
    • 一個代表舊訂閱項目 (基本方案),且 expiryTime 為過去的日期。舊訂閱方案的 deferredItemReplacement 欄位將不再有設定值。
    • 一個代表新訂閱項目,其中包含未來的 expiryTime,且 autoRenewEnabled 欄位設為 true
    API 回應範例
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>..0",
      "linkedPurchaseToken": "basic_purchase_token_123", // purchase token of the old subscription
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // New subscription
          "expiryTime": "2026-05-07T16:00:09.437Z", // Expiry time set in the future
          "autoRenewingPlan": {
            "autoRenewEnabled": true, // Auto Renewing Flag set to True
            "recurringPrice": {...}
          },
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {. // Details of the subscription replacement
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {...}
        },
        {
          "productId": "basic_plan", // Old subscription, Does not contains the deferredItemReplacement field
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time set in the past
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    

結論

本節詳細說明 ReplacementMode.DEFERRED 的專屬處理方式。您瞭解到,與立即模式不同,權益變更只會在目前的帳單週期結束時發生。本節說明應用程式和後端必須執行的步驟,才能正確處理初次購買交易、確認新權杖,以及在舊訂閱方案到期且新方案生效時管理授權切換。

12. 訂閱項目更換遊樂場

您可以使用範例應用程式中的「Replacement Playground」功能,測試 Google Play 管理中心帳戶中設定的訂閱商品升級和降級作業。本節說明如何使用「替代項目遊樂場」功能。

設定

如要使用「更換遊樂場」功能,請確認下列事項:

  • build.gradle 檔案中的 packageId 與 Google Play 管理中心設定的應用程式相符。
  • 您的測試使用者帳戶已在 Google Play 管理中心註冊為授權測試人員。如要進一步瞭解授權測試,請參閱「測試應用程式的結帳服務實作方式」。

訂閱項目更換 Playground

範例應用程式包含「Replacement Playground」分頁,可模擬訂閱項目變更。您可以查詢 Play 管理中心中定義的訂閱項目,並使用各種替代模式測試訂閱方案切換作業。這個遊樂場可協助您瞭解不同模式對訂閱項目的帳單週期和權益有何影響,進而判斷哪些選項最符合業務需求。

如要使用 Playground 模擬替換作業,請按照下列步驟操作:

  1. 前往「Playground」分頁。
  2. 如果訂閱方案有效:系統會醒目顯示。這是要更換的訂閱方案。

Playground 首頁

  1. 如果沒有有效訂閱方案:請先購買訂閱方案。
    • Playground 預設會列出為本程式碼研究室建立的「Basic」、「Premium」和「Lite」方案。
    • 如要測試 Play 管理中心設定的其他方案,請按一下「新增自訂方案」,然後依 productIdbasePlanId 搜尋。
    • 購買所選訂閱方案。
    • 系統現在應該會顯示使用者新購買的有效訂閱方案。新增自訂訂閱項目
  2. 選取使用者要切換的目標訂閱方案。
  3. 選取轉場效果的「取代模式」

選取更換模式

  1. 按一下「測試更換」按鈕,模擬更換訂閱項目。
  2. 您會看到 Google Play 帳款底部功能表,其中包含計算出的會員方案變更詳細資料 (例如立即收費和帳單週期調整)。

訂閱方案更換帳單購物車

  1. 完成交易。
  2. 前往 Play 商店應用程式的「管理訂閱項目」頁面,即可查看現有訂閱項目的異動,以及更新後的續訂日期和價格詳細資料。

管理 Play 商店訂閱項目

13. 後續步驟

參考文件

14. 恭喜

恭喜!您已成功使用各種比例分配模式實作訂閱項目更換功能,並設定方案轉換的後端處理程序。

目前所學內容

  • 如何使用特定取代模式設定 SubscriptionProductReplacementParams
  • 立即升級和延後降級的差異。
  • 如何使用 RTDN,透過 linkedPurchaseToken 停用舊的訂閱權杖。

問卷調查

我們非常重視您對本程式碼研究室的意見。請考慮花幾分鐘時間填寫問卷調查。