為一次性產品新增預購優惠

1. 簡介

在本程式碼研究室中,您將著重於建立單次產品 (OTP),並為該產品新增預購優惠。

注意:開始本程式碼研究室前,請先填寫單次商品 EAP 意願調查表,申請預購功能存取權。

觀眾

本程式碼研究室適用於熟悉單次產品的 Android 應用程式開發人員,並說明如何為單次產品新增預購優惠。

必要條件

如果您是第一次使用一次性產品,建議完成「透過區域產品定價開拓新市場」程式碼研究室。

課程內容...

  • 如何使用 Google Play 管理中心,為一次性產品建立預購優惠。
  • 如何使用 Play 帳款服務程式庫 API 查詢一次性產品和對應的預購優惠詳細資料。

事前準備

2. 建構範例應用程式

本程式碼研究室會使用 Android 範例應用程式,教您如何管理一次性商品。這個範例應用程式是功能齊全的 Android 應用程式,提供完整原始碼,可展示下列各方面:

  • 將應用程式與 PBL 整合。
  • 擷取一次性產品和相關預購優惠。
  • 執行區域定價的購買流程。

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

如果您已熟悉一次性產品和 Play 帳款服務程式庫 (PBL),可以下載範例應用程式並試用。

必備條件

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

建構

這個建構步驟的目標是產生範例應用程式的已簽署 Android App Bundle 檔案。

如要產生 Android 應用程式套件,請按照下列步驟操作:

  1. GitHub 下載範例應用程式
  2. 建構範例應用程式。建構前,請先變更範例應用程式的套件名稱,然後再建構。如果 Play 管理中心有其他應用程式的套件,請確保您為範例應用程式提供的套件名稱是唯一的。

    注意:建構範例應用程式只會建立 APK 檔案,可用於在本機進行測試。不過,由於產品尚未在 Play 管理中心設定,因此執行應用程式不會擷取產品和價格。
  3. 產生已簽署的 Android App Bundle。
    1. 產生上傳金鑰和 KeyStore
    2. 使用上傳金鑰簽署應用程式
    3. 設定 Play 應用程式簽署功能

接下來,請將 Android 應用程式套件上傳至 Google Play 管理中心。

3. 在 Play 管理中心建立預購 OTP

如要在 Google Play 管理中心建立一次性產品,您必須在 Play 管理中心擁有應用程式。在 Play 管理中心建立應用程式,然後上傳先前建立的已簽署應用程式套件。

建立應用程式

如要建立應用程式,請按照下列步驟操作:

  1. 使用開發人員帳戶登入 Google Play 管理中心
  2. 按一下「建立應用程式」,開啟「建立應用程式」頁面。
  3. 輸入應用程式名稱、選取預設語言,以及其他應用程式相關詳細資料。
  4. 按一下「建立應用程式」,即可在 Google Play 管理中心建立應用程式。

現在可以上傳範例應用程式的已簽署應用程式套件。

上傳已簽署的應用程式套件

  1. 將已簽署的應用程式套件上傳至 Google Play 管理中心的內部測試群組。上傳後,您才能在 Play 管理中心設定營利相關功能。
    1. 依序點選「測試及發布」>「測試」>「內部測試版本」>「建立新版本」
    2. 輸入發布版本名稱,然後上傳已簽署的 APK 檔案。
    3. 依序點選「下一步」和「儲存並發布」

現在可以建立一次性產品。

建立一次性產品

接著,建立使用者要購買的一次性產品。

  1. Google Play 管理中心開啟範例應用程式,然後依序前往「使用 Play 營利」 >「產品」 >「一次性產品」
  2. 按一下「建立單次產品」
  3. 輸入下列產品詳細資料:
    • 產品 ID:輸入專屬 ID。例如 upcoming_movie_1
    • (選用) 標記:新增相關標記。
    • 名稱:輸入產品名稱。例如 Product Movie
    • 說明:輸入產品說明。例如 Product Description
    • (選用) 新增圖示圖片:上傳代表產品的圖示。
    注意:在本程式碼研究室中,您可以略過「稅金、法規遵循和計畫」部分的設定。
  4. 點選 [下一步]。
  5. 新增購買選項,並設定區域供應情形。單次產品至少須有一個購買選項,用於定義授權方式、價格和區域供應情形。在本程式碼研究室中,我們將為產品新增標準的「購買」選項。在「購買選項」部分,輸入下列詳細資料:
    • 購買選項 ID:輸入專屬 ID。例如 buy-movie
    • 交易類型:選取「購買」
    • (選用) 標記:新增這個購買選項專屬的標記。
    • (選用) 按一下「進階選項」,設定進階選項。在本程式碼研究室中,您可以略過進階選項設定。
  6. 接著,您必須設定購買選項的區域供應情形和價格。在區域供應情形中,你可以指定產品的供應區域,包括尚未發布應用程式的區域。根據預設,購買選項會在所有地區上線。在「供應情形和價格」部分,按一下「編輯供應情形和存取權」
    1. 選取「設為『不適用』」
    請注意,所有區域都會自動選取並設為「可用」
    1. 取消選取United States國家/地區,然後按一下「設為『不適用』」。現在一次性產品只能在 United States 中使用。
    2. 在「所有地區」下拉式選單中,選取「適用國家/地區和地域」。畫面上只會顯示 United States
    3. 按一下「價格」圖示,系統會顯示設定價格的對話方塊。
    4. 輸入 10 美元,然後按一下「儲存」
  7. 按一下「另存為草稿」

注意:請先不要啟用購買選項。設定預購優惠後,我們會啟用這項功能。因為您無法為已啟用且已設定區域供應情形的購買選項新增預購優惠。

新增預購優惠

現在,請為先前建立的「購買」購買選項新增預購優惠。預購優惠可讓使用者在商品正式發布前購買。請注意,預購優惠僅適用於「購買」購買選項,且只能為區域內的新產品設定。

新增預購優惠的步驟如下:

  1. 準備「購買」購買選項的預購優惠。
  2. 為購買選項新增預購優惠。

準備「購買」購買選項的預購優惠

  1. Google Play 管理中心開啟範例應用程式,然後依序前往「使用 Play 營利」 >「產品」 >「一次性產品」
  2. 在「單次產品」頁面中,按一下產品的向右箭頭 (upcoming_movie_1),「編輯單次產品」頁面隨即開啟。
  3. 找出先前建立的buy-movie購買選項,然後按一下向右箭頭,「編輯購買選項」頁面隨即開啟。
  4. 按一下「編輯供應狀態和存取權」,然後選取「設為可用並開放預購」
  5. 從「所有區域」下拉式選單中,選取「適用國家/地區和區域」。畫面應只會顯示您先前設定的 United States
  6. 選取國家/地區,然後按一下「設為僅開放預購」
  7. 按一下 [儲存]

請注意,你尚未為購買選項新增預購優惠。下一步是新增預購優惠。

新增預購優惠

  1. Google Play 管理中心開啟範例應用程式,然後依序前往「使用 Play 營利」 >「產品」 >「一次性產品」
  2. 在「單次產品」頁面中,依序點選產品的「新增優惠」>「預購」 (upcoming_movie_1)。「新增預購」頁面隨即開啟。
  3. 輸入預購詳細資料:
    • 預購 ID:輸入 preorder-offer-1
    • (選用) 新增折扣:你可以選取「無」、「百分比」或「絕對」折扣。在本程式碼研究室中,請選取「無」
    • (選用) 標記:新增相關標記。
    • 開始日期和時間:設定至少 3 天後的日期。
    • 結束日期和時間:設定的日期必須在開始日期至少 24 小時後。
    • 預購後何時提供:選擇產品在預購期結束後立即提供,或在稍後的特定日期/時間提供。
    • (選用) 低價保證:如要向使用者收取預購價和發售價中較低的價格,請選取這個選項。這對早期買家來說是強大的誘因。
  4. 按一下 [儲存]
  5. 開啟單次產品 (upcoming_movie_1) 的「編輯單次產品」頁面。
  6. 按一下「購買」購買選項的「啟用」圖示 (buy-movie)。
  7. 在「購買」購買選項下方,按一下預購優惠的「啟用」圖示 (preorder-offer-1)。預購優惠就會在您先前設定的日期生效。

預購優惠建立影片

以下影片會逐步說明如何建立預購優惠,與先前所述的步驟相同。

4. 與 PBL 整合

如要將應用程式與 Play 帳款服務程式庫 (PBL) 整合,請按照下列步驟操作:

  1. 將 Play 帳款服務程式庫依附元件新增至範例應用程式。
    dependencies {
    val billing_version = "8.1.0"
    
    implementation("com.android.billingclient:billing-ktx:$billing_version")
    }
    
  2. 初始化 BillingClient。BillingClient 是位於應用程式的用戶端 SDK,可與 Play 帳款服務程式庫通訊。下列程式碼片段說明如何初始化帳單用戶端。
    private BillingClient createBillingClient() {
    return BillingClient.newBuilder(activity)
        .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
        // For one-time products, add a listener to process and acknowledge the purchases. This will notify
        // Google the purchase was processed.
        // For client-only apps, use billingClient.acknowledgePurchase().
        // If you have a secure backend, you must acknowledge purchases on your server using the
        // server-side API.
        // See https://developer.android.com/google/play/billing/security#acknowledge
        // In this sample snippet purchases aren't processed. You must
        // implement your business logic to process and acknowledge the purchases.
        .setListener((billingResult, purchases) -> {})
        .enableAutoServiceReconnection()
        .build();
     }
    
  3. 連線至 Google Play。下列程式碼片段說明如何連線至 Google Play。
    /**
    * Starts the billing connection with Google Play. This method should be called exactly once
    * before any other methods in this class.
    *
    * @param productList The list of products to query for after the connection is established.
    */
    public void startBillingConnection(List<Product> productList) {
        billingClient.startConnection(
            new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                Log.d(TAG, "Billing Client Connection Successful");
                queryProductDetails(productList);
                } else {
                Log.e(TAG, "Billing Client Connection Failed: " + billingResult.getDebugMessage());
                listener.onBillingSetupFailed(billingResult); // Propagate the error to the listener to show a message to the user.
                }
            }
    
            @Override
            public void onBillingServiceDisconnected() {
                Log.e(TAG, "Billing Client Connection Lost");
                listener.onBillingError("Billing Connection Lost");
            }
            });
    }
    
  4. 擷取一次性產品詳細資料。將應用程式與 PBL 整合後,您必須將一次性產品詳細資料擷取到應用程式中。下列程式碼片段說明如何在應用程式中擷取一次性產品詳細資料。
    private void queryProductDetails(List<Product> productList) {
        QueryProductDetailsParams queryProductDetailsParams =
            QueryProductDetailsParams.newBuilder().setProductList(productList).build();
    
        billingClient.queryProductDetailsAsync(
            queryProductDetailsParams,
            new ProductDetailsResponseListener() {
            @Override
            public void onProductDetailsResponse(
                BillingResult billingResult, QueryProductDetailsResult productDetailsResponse) {
                if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                List<ProductDetails> productDetailsList =
                    productDetailsResponse.getProductDetailsList();
                    listener.onProductDetailsResponse(productDetailsList);
                } else {
                Log.e(TAG, "QueryProductDetailsAsync Failed: " + billingResult.getDebugMessage());
                listener.onBillingError("Query Products Failed: " + billingResult.getResponseCode());
                }
            }
            });
    }
    
    ProductDetails 中擷取一次性產品 (本例為 upcoming_movie_1),會收到類似以下的回應:
    {
        "productId": "upcoming_movie_1",
        "type": "inapp",
        "title": "Purrfect Mayhem: The Final Playback (Movies All Day | Play Samples)",
        "name": "Purrfect Mayhem: The Final Playback",
        "description": "Yolo and Thorne must reach the original broadcasting site to initiate the \"Final Playback\" and save the timeline. Follow them through their race against the Clockinators.",
        "skuDetailsToken": "<---skuDetailsToken--->",
        "oneTimePurchaseOfferDetails": {},
        "oneTimePurchaseOfferDetailsList": [
            {
                "priceAmountMicros": 8500000,
                "priceCurrencyCode": "USD",
                "formattedPrice": "$8.50",
                "offerIdToken": "<---offerIdToken--->",
                "offerId": "preorder",
                "purchaseOptionId": "buy-option",
                "offerTags": [],
                "validTimeWindow": {
                    "startTimeMillis": 1756771200000,
                    "endTimeMillis": 1785542400000
                },
                "preorderDetails": {
                    "preorderReleaseTimeMillis": 1785542400000,
                    "preorderPresaleEndTimeMillis": 1785542400000
                }
            }
        ]
    }
    
    請注意,預購優惠詳細資料會顯示在 oneTimePurchaseOfferDetailsList 中。這個清單包含 1 個購買選項 (buy-option),預購優惠是在 Play 管理中心設定。您可以透過 offerIdToken 識別每個購買選項。
  5. 擷取優惠權杖和預購優惠詳細資料。您需要優惠權杖,才能在步驟 6 中啟動結帳流程。
    @Override
    public void onProductDetailsResponse(List<ProductDetails> productDetailsList) {
    
    if (productDetailsList != null && !productDetailsList.isEmpty()) {
    
    // Process productDetailsList returned by QueryProductDetailsResult
    for (ProductDetails productDetails : productDetailsResult.getProductDetailsList()) {
      for (OneTimePurchaseOfferDetails oneTimePurchaseOfferDetails :
          productDetails.getOneTimePurchaseOfferDetailsList()) {
        // Checks if the offer is a preorder offer.
        if (oneTimePurchaseOfferDetails.getPreorderDetails() != null) {
          // Process the returned PreorderDetails
          OneTimePurchaseOfferDetails.PreorderDetails preorderDetails =
              oneTimePurchaseOfferDetails.getPreorderDetails();
          // Get preorder release time in millis.
          long preorderReleaseTimeMillis = preorderDetails.getPreorderReleaseTimeMillis();
          // Get preorder presale end time in millis.
          long preorderPresaleEndTimeMillis = preorderDetails.getPreorderPresaleEndTimeMillis();
          // Get offer ID
            String offerId = oneTimePurchaseOfferDetails.getOfferId();
          // Get the associated purchase option ID
          if (oneTimePurchaseOfferDetails.getPurchaseOptionId() != null) {
            String purchaseOptionId = oneTimePurchaseOfferDetails.getPurchaseOptionId();
          }
        }
      }
      }
      } else {
            Log.e(TAG, "No product details found for " + productId);
        }
    }
    
  6. 啟動結帳流程。
    /**
     * Launches the billing flow for the product with the given offer token.
    *
    * @param activity The activity instance from which the billing flow will be launched.
    * @param productDetails The product details of the product to purchase.
    * @param offerToken The offer token of the product to purchase.
    * @return The result of the billing flow.
    */
    public void launchPurchase(Activity activity, ProductDetails productDetails, String offerToken) {
        ImmutableList<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
            ImmutableList.of(
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(productDetails)
                    .setOfferToken(offerToken)
                    .build());
        BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build();
        billingClient.launchBillingFlow(activity, billingFlowParams);
    }
    

5. 測試購買選項

在正式版應用程式中提供一次性產品前,您可以先使用授權測試人員和 Play Billing Lab 測試 PBL 整合服務。

如要瞭解如何使用 Play Billing Lab 測試購買選項,請參閱「透過區域產品定價開拓新市場」程式碼研究室。

6. 後續步驟

參考文件

7. 恭喜!

恭喜!您已順利在 Google Play 管理中心建立一次性產品的預購優惠。您現在已深入瞭解 Google Play 彈性產品目錄,可供一次性購買。

問卷調查

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