Panduan integrasi dalam aplikasi untuk pilihan penagihan

Panduan ini menjelaskan cara mengintegrasikan aplikasi Anda dengan Play Billing Library API sehingga Anda dapat menawarkan pilihan penagihan kepada pengguna.

Integrasi dengan PBL

Anda dapat mengintegrasikan pilihan penagihan dengan PBL dalam empat skenario. Skenario ini berbeda berdasarkan siapa yang merender layar pilihan dan tempat pembayaran akan dilakukan. Tabel berikut menguraikan skenario integrasi:

Tempat pembayaran dilakukan
Dalam aplikasi Link web eksternal
Layar pilihan penagihan mana yang ingin Anda render? Google Play Skenario 1A

Google merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda.

Skenario 1B

Developer aplikasi merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda.

Anda sendiri (sesuai dengan panduan UX) Skenario 2A

Google merender layar pilihan dan pengguna ditautkan ke luar aplikasi Anda ke situs Anda sendiri untuk pembelian.

Skenario 2B

Developer aplikasi merender layar pilihan dan pengguna ditautkan ke luar aplikasi Anda ke situs Anda sendiri untuk pembelian.

Ilustrasi berikut menguraikan alur pilihan penagihan untuk setiap skenario ini:

Alur pilihan penagihan yang menunjukkan urutan panggilan API dan interaksi pengguna untuk empat skenario integrasi.
Gambar 1. Skenario integrasi pilihan penagihan

Skenario integrasi PBL

Bergantung pada skenario integrasi Anda, ikuti langkah-langkah di bagian ini untuk menerapkan pilihan penagihan di aplikasi Anda.

Menangani skenario 1A

Google merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda. Lakukan langkah-langkah berikut untuk mengaktifkan pilihan penagihan dalam skenario ini:

  1. Panggil enableBillingProgram dengan EnableBillingProgramParams saat membuat instance BillingClient, lalu mulai koneksi. Contoh:

    Kotlin

    
    // Build the parameters to enable the Billing Choice program and assign the listener
    // to handle user selection of the developer-provided billing option.
    val params = EnableBillingProgramParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .setDeveloperProvidedBillingListener(developerProvidedBillingListener)
        .build()
    
    // Build the parameters to enable support for pending purchases.
    val pendingPurchasesParams = PendingPurchasesParams.newBuilder()
        .enableOneTimeProducts()
        .build()
    
    // Construct the BillingClient instance with the purchases updated listener,
    // pending purchases support, and the billing choice params.
    val billingClient = BillingClient.newBuilder(context)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases(pendingPurchasesParams)
        .enableBillingProgram(params)
        .build()
    
    // Establish a connection to Google Play
    val billingResult = suspendCancellableCoroutine { continuation ->
        billingClient.startConnection(object : BillingClientStateListener {
            // Called when the connection setup process completes.
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                // Resume the coroutine and pass back the BillingResult to the caller.
                continuation.resume(billingResult)
            }
    
            // Called if the connection to the Play Store service is dropped.
            // This prevents the await or suspension point from hanging indefinitely.
            override fun onBillingServiceDisconnected() {
                continuation.resume(
                    BillingResult.newBuilder()
                        .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
                        .setDebugMessage("Billing service disconnected during connection setup")
                        .build()
                )
            }
        })
    }
    
    

    Java

    
    EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperProvidedBillingListener(developerProvidedBillingListener)
            .build();
    
    BillingClient billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases(
                    PendingPurchasesParams.newBuilder()
                            .enableOneTimeProducts()
                            .build()
            )
            .enableBillingProgram(params)
            .build();
    
    
  2. Pastikan pilihan penagihan yang dirender Google tersedia untuk pengguna.

    Panggil isBillingProgramAvailableAsync untuk memeriksa ketersediaan program, lalu panggil queryProductDetailsAsync untuk menampilkan produk yang tersedia. Contoh:

    Kotlin

    
    val (billingResult, billingProgramAvailabilityDetails) = billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE)
    
    if (billingResult.responseCode == BillingResponseCode.OK) {
        val billingChoiceAvailabilityDetails = billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails
        if (billingChoiceAvailabilityDetails != null &&
            billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.GOOGLE_RENDERED
        ) {
            // Billing choice is available. Query products and proceed.
    
        } else {
            // Fallback to other available programs.
        }
    } else {
        // Fallback to other available programs.
    }
    
    
    

    Java

    
    // ...
    billingClient.isBillingProgramAvailableAsync(
        BillingProgram.BILLING_CHOICE,
        (billingResult, billingProgramAvailabilityDetails) -> {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails =
                    billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails();
    
                if (billingChoiceAvailabilityDetails != null &&
                    billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.GOOGLE_RENDERED) {
                    // Billing choice is available. Query products and proceed.
                } else {
                    // Fallback to other available programs.
                }
            } else {
                // Fallback to other available programs.
            }
        }
    );
    
    

    Catatan: billingProgramAvailabilityDetails memberi tahu Anda apakah layar pilihan penagihan yang dirender Google atau yang dirender Developer tersedia.

  3. Panggil launchBillingFlow untuk memicu alur pembelian saat pengguna mengklik Beli. Jika pilihan penagihan tersedia, teruskan DeveloperBillingOptionParams ke BillingFlowParams. Contoh:

    Kotlin

    
    val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .build()
    
    val billingFlowParams = BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .enableDeveloperBillingOption(developerBillingOptionParams)
        .build()
    
    val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
    
    

    Java

    
    DeveloperBillingOptionParams developerBillingOptionParams =
        DeveloperBillingOptionParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .build();
    BillingFlowParams billingFlowParams =
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .enableDeveloperBillingOption(developerBillingOptionParams)
            .build();
    
    BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    
    

    Catatan: Kontrol orang tua ditampilkan untuk pengguna yang diawasi.

  4. Tangani pilihan jenis penagihan pengguna sebagai berikut:

Menangani skenario 1B

Developer merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda. Lakukan langkah-langkah berikut untuk mengaktifkan pilihan penagihan dalam skenario ini:

  1. Panggil enableBillingProgram tanpa DeveloperProvidedBillingListener di EnableBillingProgramParams saat membuat instance BillingClient, lalu mulai koneksi. Contoh:

    Kotlin

    
    // Build the parameters to enable the Billing Choice program.
    val params = EnableBillingProgramParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .build()
    
    // Construct the BillingClient instance with the purchases updated listener,
    // pending purchases support, and the billing choice params.
    val billingClient = BillingClient.newBuilder(context)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases()
        .enableBillingProgram(params)
        .build()
    
    // Establish a connection to Google Play
    val billingResult = suspendCancellableCoroutine<BillingResult> { continuation ->
        billingClient.startConnection(object : BillingClientStateListener {
            // Called when the connection setup process completes.
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                // Resume the coroutine and pass back the BillingResult to the caller.
                continuation.resume(billingResult)
            }
    
            // Called if the connection to the Play Store service is dropped.
            // This prevents the await or suspension point from hanging indefinitely.
            override fun onBillingServiceDisconnected() {
                continuation.resume(
                    BillingResult.newBuilder()
                        .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
                        .setDebugMessage("Billing service disconnected during connection setup")
                        .build()
                )
            }
        })
    }
    
    

    Java

    
    EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .build();
    
    BillingClient billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .enableBillingProgram(params)
            .build();
    
    
  2. Pastikan pilihan penagihan yang dirender developer tersedia untuk pengguna.

    Panggil isBillingProgramAvailableAsync untuk memeriksa ketersediaan program, lalu panggil queryProductDetailsAsync untuk menampilkan produk yang tersedia. Contoh:

    Kotlin

    
    val (billingResult, billingProgramAvailabilityDetails) =
        billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE)
    
    if (billingResult.responseCode == BillingResponseCode.OK) {
        val billingChoiceAvailabilityDetails =
            billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails
    
        if (billingChoiceAvailabilityDetails != null &&
            billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.DEVELOPER_RENDERED
        ) {
            // Billing choice is available. Query products and proceed.
            // You can inspect details such as:
            // - billingChoiceAvailabilityDetails.choiceScreenType
            // - billingChoiceAvailabilityDetails.isExternalLinkAvailable
        } else {
            // Fallback to other available programs.
        }
    } else {
        // Fallback to other available programs.
    }
    
    

    Java

    
    // ...
    billingClient.isBillingProgramAvailableAsync(
        BillingProgram.BILLING_CHOICE,
        (billingResult, billingProgramAvailabilityDetails) -> {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails =
                    billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails();
    
                if (billingChoiceAvailabilityDetails != null
                        && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.DEVELOPER_RENDERED) {
                    // Billing choice is available. Query products and proceed.
                    // You can inspect details such as:
                    // - billingChoiceAvailabilityDetails.getChoiceScreenType()
                    // - billingChoiceAvailabilityDetails.isExternalLinkAvailable()
                } else {
                    // Fallback to other available programs.
                }
            } else {
                // Fallback to other available programs.
            }
        }
    );
    
    

    Catatan: billingProgramAvailabilityDetails memberi tahu Anda apakah layar pilihan penagihan yang dirender Google atau yang dirender Developer tersedia.

  3. Panggil metode getBillingChoiceInfoAsync untuk mendapatkan informasi loyalitas dan banner Layanan Penagihan Play. Contoh:

    Kotlin

    
    // 1. Create the params required for the request
    val params = GetBillingChoiceInfoParams.newBuilder()
        .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
        .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE)
        .build()
    
    // 2. Call the suspend method on your billingClient instance
    val (billingResult, playBillingChoiceInfo) = billingClient.getBillingChoiceInfo(params)
    
    if (billingResult.responseCode == BillingResponseCode.OK && playBillingChoiceInfo != null) {
        // Access the URL of the image associated with the Play Billing Choice
        val imageUrl = playBillingChoiceInfo.playBillingChoiceImageUrl
    
        // Access the Play Loyalty string information, if available
        val loyaltyInfo = playBillingChoiceInfo.playBillingLoyaltyInfo
    
        // Populate your developer-rendered UI elements
        playBillingLoyaltyTextView.text = loyaltyInfo
        loadImage(imageUrl, playBillingImageView)
    } else {
        // Handle error scenarios
    }
    
    

    Java

    
    // 1. Create the params required for the request
    GetBillingChoiceInfoParams params = GetBillingChoiceInfoParams.newBuilder()
        .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
        .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE)
        .build();
    // 2. Call the method asynchronously on your billingClient instance
    billingClient.getBillingChoiceInfoAsync(params, (billingResult, playBillingChoiceInfo) -> {
        if (billingResult.getResponseCode() == BillingResponseCode.OK && playBillingChoiceInfo != null) {
          // Access the URL of the image associated with the Play Billing Choice
            String imageUrl = playBillingChoiceInfo.getPlayBillingChoiceImageUrl();
            // Access the Play Loyalty string information, if available
            String loyaltyInfo = playBillingChoiceInfo.getPlayBillingLoyaltyInfo();
    
            // Populate your developer-rendered UI elements
            playBillingLoyaltyTextView.setText(loyaltyInfo);
              loadImage(imageUrl, playBillingImageView);
          } else {
              // Handle error scenarios
          }
    });
    
    
  4. Buat token transaksi eksternal dengan DeveloperBillingType ditetapkan ke IN_APP. Contoh:

    Kotlin

    
    // Build the parameters specifying the billing program and that the billing type is IN_APP.
    val params = BillingProgramReportingDetailsParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .setDeveloperBillingType(DeveloperBillingType.IN_APP)
        .build()
    
    // Call the suspending extension function to request the reporting details
    val (billingResult, billingProgramReportingDetails) =
        billingClient.createBillingProgramReportingDetails(params)
        
    if (billingResult.responseCode != BillingResponseCode.OK) {
        // Handle failures such as retrying due to network errors.
        return
    }
    
    // Extract the transaction token from the returned reporting details
    val transactionToken = billingProgramReportingDetails?.externalTransactionToken
    
    // Persist the external transaction token locally. Pass it to
    // DeveloperBillingOptionParams when launchBillingFlow is called.
    // It can also be used as part of your external website
    
    

    Java

    
    BillingProgramReportingDetailsParams params =
        BillingProgramReportingDetailsParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperBillingType(DeveloperBillingType.IN_APP)
            .build();
    
    billingClient.createBillingProgramReportingDetailsAsync(
        params,
        new BillingProgramReportingDetailsListener() {
            @Override
            public void onCreateBillingProgramReportingDetailsResponse(
                BillingResult billingResult,
                @Nullable BillingProgramReportingDetails billingProgramReportingDetails
            ) {
                if (billingResult.getResponseCode() != BillingResponseCode.OK) {
                    // Handle failures such as retrying due to network errors.
                    return;
                }
    
                String transactionToken =
                    billingProgramReportingDetails.getExternalTransactionToken();
    
                // Persist the external transaction token locally. Pass it to
                // DeveloperBillingOptionParams when launchBillingFlow is called.
                // It can also be used as part of your external website
            }
        }
    );
    
    
    
  5. Saat pengguna mengklik Beli, panggil showBillingProgramInformationDialog untuk menampilkan dialog informasi. Misalnya, lihat Dialog informasi untuk pengguna. BillingProgram dan transactionToken dari langkah 4 harus ditetapkan dalam permintaan.

    Catatan: Kontrol orang tua ditampilkan untuk pengguna yang diawasi.

  6. Luncurkan layar pilihan penagihan alternatif Anda jika hasil dari langkah sebelumnya adalah OK.

  7. Tangani pilihan jenis penagihan pengguna sebagai berikut:

Menangani skenario 2A

Google merender layar pilihan dan penagihan alternatif ditangani di luar aplikasi Anda. Lakukan langkah-langkah berikut untuk mengaktifkan pilihan penagihan dalam skenario ini:

  1. Panggil enableBillingProgram dengan EnableBillingProgramParams saat membuat instance BillingClient, lalu mulai koneksi. Contoh:

    Kotlin

    
    // Build the parameters to enable the Billing Choice program and assign the listener
    // to handle user selection of the developer-provided billing option.
    val params = EnableBillingProgramParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .setDeveloperProvidedBillingListener(developerProvidedBillingListener)
        .build()
    
    // Build the parameters to enable support for pending purchases.
    val pendingPurchasesParams = PendingPurchasesParams.newBuilder()
        .enableOneTimeProducts()
        .build()
    
    // Construct the BillingClient instance with the purchases updated listener,
    // pending purchases support, and the billing choice params.
    val billingClient = BillingClient.newBuilder(context)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases(pendingPurchasesParams)
        .enableBillingProgram(params)
        .build()
    
    // Establish a connection to Google Play
    val billingResult = suspendCancellableCoroutine { continuation ->
        billingClient.startConnection(object : BillingClientStateListener {
            // Called when the connection setup process completes.
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                // Resume the coroutine and pass back the BillingResult to the caller.
                continuation.resume(billingResult)
            }
    
            // Called if the connection to the Play Store service is dropped.
            // This prevents the await or suspension point from hanging indefinitely.
            override fun onBillingServiceDisconnected() {
                continuation.resume(
                    BillingResult.newBuilder()
                        .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
                        .setDebugMessage("Billing service disconnected during connection setup")
                        .build()
                )
            }
        })
    }
    
    

    Java

    
    EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperProvidedBillingListener(developerProvidedBillingListener)
            .build();
    
    BillingClient billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases(
                    PendingPurchasesParams.newBuilder()
                            .enableOneTimeProducts()
                            .build()
            )
            .enableBillingProgram(params)
            .build();
    
    
  2. Verifikasi ketersediaan hal berikut:

    • Pilihan penagihan yang dirender Google
    • Link web eksternal

    Panggil isBillingProgramAvailableAsync untuk memeriksa ketersediaan program, lalu panggil queryProductDetailsAsync untuk menampilkan produk yang tersedia. Contoh:

    Kotlin

    
    // Check the availability of the billing choice program asynchronously using coroutines
    val (billingResult, billingProgramAvailabilityDetails) =
        billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE)
    
    // Ensure the billing program query succeeded
    if (billingResult.responseCode == BillingResponseCode.OK) {
        // Retrieve the availability details specific to the billing choice program
        val billingChoiceAvailabilityDetails =
            billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails
    
        // Check if billing choice is available, renders via Google Play, and external link is supported
        if (billingChoiceAvailabilityDetails != null &&
            billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.GOOGLE_RENDERED &&
            billingChoiceAvailabilityDetails.isExternalLinkAvailable
        ) {
            // Billing choice is available and external transaction links are supported. Query products and proceed.
        } else {
            // Fallback to other available programs.
        }
    } else {
        // Fallback to other available programs.
    }
    
    
    

    Java

    
    // ...
    billingClient.isBillingProgramAvailableAsync(
        BillingProgram.BILLING_CHOICE,
        (billingResult, billingProgramAvailabilityDetails) -> {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails =
                    billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails();
    
                if (billingChoiceAvailabilityDetails != null
                        && billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.GOOGLE_RENDERED
                        && billingChoiceAvailabilityDetails.isExternalLinkAvailable()) {
                    // Billing choice is available and external transaction links are supported.
                    // Query products and proceed.
                } else {
                    // Fallback to other available programs.
                }
            } else {
                // Fallback to other available programs.
            }
        }
    );
    
    
    
  3. Panggil createBillingProgramReportingDetailsAsync untuk membuat token transaksi eksternal saat pengguna menunjukkan niat untuk membeli. Contoh:

    Kotlin

    
    // Build the parameters for creating reporting details
    val params =
        BillingProgramReportingDetailsParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
            .build()
    
    // Call the suspend function to create billing program reporting details
    val (billingResult, billingProgramReportingDetails) =
        billingClient.createBillingProgramReportingDetails(params)
    
    // Handle response failure cases
    if (billingResult.responseCode != BillingResponseCode.OK) {
        // Handle failures such as retrying due to network errors.
        return
    }
    
    // Retrieve the external transaction token
    val transactionToken =
        billingProgramReportingDetails?.externalTransactionToken
    
    // Persist the external transaction token locally. Pass it to
    // DeveloperBillingOptionParams when launchBillingFlow is called.
    // It can also be used as part of your external website
    
    

    Java

    
    BillingProgramReportingDetailsParams params =
        BillingProgramReportingDetailsParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
            .build();
    
    billingClient.createBillingProgramReportingDetailsAsync(
        params,
        new BillingProgramReportingDetailsListener() {
            @Override
            public void onCreateBillingProgramReportingDetailsResponse(
                BillingResult billingResult,
                @Nullable BillingProgramReportingDetails billingProgramReportingDetails
            ) {
                if (billingResult.getResponseCode() != BillingResponseCode.OK) {
                    // Handle failures such as retrying due to network errors.
                    return;
                }
    
                String transactionToken =
                    billingProgramReportingDetails.getExternalTransactionToken();
    
                // Persist the external transaction token locally. Pass it to
                // DeveloperBillingOptionParams when launchBillingFlow is called.
                // It can also be used as part of your external website.
            }
        }
    );
    
    
  4. Panggil launchBillingFlow untuk memicu alur pembelian saat pengguna mengklik Beli. Jika pilihan penagihan tersedia untuk pengguna, lakukan hal berikut:

    1. Teruskan DeveloperBillingOptionParams ke BillingFlowParams.
    2. Teruskan token transaksi eksternal dari langkah 3 ke DeveloperBillingOptionParams.

    Contoh:

    Kotlin

    
    // Build the developer billing option parameters with the external link URI,
    // the transaction token, and browser/app launch mode.
    val developerBillingOptionParams =
        DeveloperBillingOptionParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setLinkUri(Uri.parse("https://www.example.com/external/purchase"))
            .setExternalTransactionToken(transactionToken)
            .setLaunchMode(
                DeveloperBillingOptionParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP
            )
            .build()
    
    

    Java

    
    DeveloperBillingOptionParams developerBillingOptionParams =
        DeveloperBillingOptionParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setLinkUri(Uri.parse("https://www.example.com/external/purchase"))
            .setExternalTransactionToken(transactionToken)
            .setLaunchMode(
              DeveloperBillingOptionParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP)
            .build();
    
    

    Catatan: Kontrol orang tua ditampilkan untuk pengguna yang diawasi.

  5. Tangani pilihan jenis penagihan pengguna sebagai berikut:

Menangani skenario 2B

Developer merender layar pilihan dan penagihan alternatif ditangani di luar aplikasi. Lakukan langkah-langkah berikut untuk mengaktifkan pilihan penagihan dalam skenario ini:

  1. Panggil enableBillingProgram tanpa DeveloperProvidedBillingListener di EnableBillingProgramParams saat membuat instance BillingClient, lalu mulai koneksi. Contoh:

    Kotlin

    
    // Build the parameters to enable the Billing Choice program.
    val params = EnableBillingProgramParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .build()
    
    // Construct the BillingClient instance with the purchases updated listener,
    // pending purchases support, and the billing choice params.
    val billingClient = BillingClient.newBuilder(context)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases()
        .enableBillingProgram(params)
        .build()
    
    // Establish a connection to Google Play
    val billingResult = suspendCancellableCoroutine<BillingResult> { continuation ->
        billingClient.startConnection(object : BillingClientStateListener {
            // Called when the connection setup process completes.
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                // Resume the coroutine and pass back the BillingResult to the caller.
                continuation.resume(billingResult)
            }
    
            // Called if the connection to the Play Store service is dropped.
            // This prevents the await or suspension point from hanging indefinitely.
            override fun onBillingServiceDisconnected() {
                continuation.resume(
                    BillingResult.newBuilder()
                        .setResponseCode(BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)
                        .setDebugMessage("Billing service disconnected during connection setup")
                        .build()
                )
            }
        })
    }
    
    

    Java

    
    EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .build();
    
    BillingClient billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .enableBillingProgram(params)
            .build();
    
    
  2. Verifikasi ketersediaan hal berikut:

    • Pilihan penagihan yang dirender Google
    • Link web eksternal

    Panggil isBillingProgramAvailableAsync untuk memeriksa ketersediaan program, lalu panggil queryProductDetailsAsync untuk menampilkan produk yang tersedia. Contoh:

    Kotlin

    
    // Check the availability of the billing choice program asynchronously using a coroutine
    val (billingResult, billingProgramAvailabilityDetails) =
        billingClient.isBillingProgramAvailable(BillingProgram.BILLING_CHOICE)
    
    // Ensure the response code is OK
    if (billingResult.responseCode == BillingResponseCode.OK) {
        // Retrieve the billing choice availability details
        val billingChoiceAvailabilityDetails =
            billingProgramAvailabilityDetails.billingChoiceAvailabilityDetails
    
        // Check if billing choice details are available, choice screen is developer-rendered,
        // and external transaction links are supported.
        if (billingChoiceAvailabilityDetails != null &&
            billingChoiceAvailabilityDetails.choiceScreenType == ChoiceScreenType.DEVELOPER_RENDERED &&
            billingChoiceAvailabilityDetails.isExternalLinkAvailable
        ) {
            // Billing choice is available and external transaction links are supported.
            // Query products and proceed.
        } else {
            // Fallback to other available programs.
        }
    } else {
        // Fallback to other available programs.
    }
    
    

    Java

    
    // ...
    
    billingClient.isBillingProgramAvailableAsync(
        BillingProgram.BILLING_CHOICE,
        (billingResult, billingProgramAvailabilityDetails) -> {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                BillingChoiceAvailabilityDetails billingChoiceAvailabilityDetails =
                    billingProgramAvailabilityDetails.getBillingChoiceAvailabilityDetails();
                if (billingChoiceAvailabilityDetails != null &&
                    billingChoiceAvailabilityDetails.getChoiceScreenType() == ChoiceScreenType.DEVELOPER_RENDERED &&
                    billingChoiceAvailabilityDetails.isExternalLinkAvailable()) {
                    // Billing choice is available and external transaction links are supported. Query products and proceed.
                } else {
                    // Fallback to other available programs.
                }
            } else {
                // Fallback to other available programs.
            }
        }
    );
    
    
  3. Panggil metode getBillingChoiceInfoAsync untuk mendapatkan informasi loyalitas dan banner Layanan Penagihan Play.

    Kotlin

    
    // 1. Create the params required for the request
    val params = GetBillingChoiceInfoParams.newBuilder()
        .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
        .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE)
        .build()
    
    // 2. Call the suspend method on your billingClient instance
    val (billingResult, playBillingChoiceInfo) = billingClient.getBillingChoiceInfo(params)
    
    if (billingResult.responseCode == BillingResponseCode.OK && playBillingChoiceInfo != null) {
        // Access the URL of the image associated with the Play Billing Choice
        val imageUrl = playBillingChoiceInfo.playBillingChoiceImageUrl
    
        // Access the Play Loyalty string information, if available
        val loyaltyInfo = playBillingChoiceInfo.playBillingLoyaltyInfo
    
        // Populate your developer-rendered UI elements
        playBillingLoyaltyTextView.text = loyaltyInfo
        loadImage(imageUrl, playBillingImageView)
    } else {
        // Handle error scenarios
    }
    
    

    Java

    
    // 1. Create the params required for the request
    GetBillingChoiceInfoParams params = GetBillingChoiceInfoParams.newBuilder()
        .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
        .setPlayBillingChoiceImageLayout(GetBillingChoiceInfoParams.ImageLayout.RECTANGULAR_FOUR_BY_ONE)
        .build();
    // 2. Call the method asynchronously on your billingClient instance
    billingClient.getBillingChoiceInfoAsync(params, (billingResult, playBillingChoiceInfo) -> {
        if (billingResult.getResponseCode() == BillingResponseCode.OK && playBillingChoiceInfo != null) {
          // Access the URL of the image associated with the Play Billing Choice
            String imageUrl = playBillingChoiceInfo.getPlayBillingChoiceImageUrl();
            // Access the Play Loyalty string information, if available
            String loyaltyInfo = playBillingChoiceInfo.getPlayBillingLoyaltyInfo();
    
            // Populate your developer-rendered UI elements
            playBillingLoyaltyTextView.setText(loyaltyInfo);
              loadImage(imageUrl, playBillingImageView);
          } else {
              // Handle error scenarios
          }
    });
    
    
  4. Panggil createBillingProgramReportingDetailsAsync untuk membuat token transaksi eksternal saat pengguna menunjukkan niat untuk membeli. Contoh:

    Kotlin

    
    // Build the parameters for creating reporting details
    val params =
        BillingProgramReportingDetailsParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
            .build()
    
    // Call the suspend function to create billing program reporting details
    val (billingResult, billingProgramReportingDetails) =
        billingClient.createBillingProgramReportingDetails(params)
    
    // Handle response failure cases
    if (billingResult.responseCode != BillingResponseCode.OK) {
        // Handle failures such as retrying due to network errors.
        return
    }
    
    // Retrieve the external transaction token
    val transactionToken =
        billingProgramReportingDetails?.externalTransactionToken
    
    // Persist the external transaction token locally. Pass it to
    // DeveloperBillingOptionParams when launchBillingFlow is called.
    // It can also be used as part of your external website
    
    

    Java

    
    BillingProgramReportingDetailsParams params =
        BillingProgramReportingDetailsParams.newBuilder()
            .setBillingProgram(BillingProgram.BILLING_CHOICE)
            .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
            .build();
    
    billingClient.createBillingProgramReportingDetailsAsync(
        params,
        new BillingProgramReportingDetailsListener() {
            @Override
            public void onCreateBillingProgramReportingDetailsResponse(
                BillingResult billingResult,
                @Nullable BillingProgramReportingDetails billingProgramReportingDetails
            ) {
                if (billingResult.getResponseCode() != BillingResponseCode.OK) {
                    // Handle failures such as retrying due to network errors.
                    return;
                }
    
                String transactionToken =
                    billingProgramReportingDetails.getExternalTransactionToken();
    
                // Persist the external transaction token locally. Pass it to
                // DeveloperBillingOptionParams when launchBillingFlow is called.
                // It can also be used as part of your external website.
            }
        }
    );
    
    
  5. Luncurkan layar pilihan alternatif Anda saat pengguna mengklik Beli.

  6. Tangani pilihan jenis penagihan pengguna sebagai berikut:

    • Jika pengguna memilih Layanan Penagihan Play, panggil launchBillingFlow dengan mengikuti panduan Layanan Penagihan Play standar. Hasil penagihan akan ditampilkan ke PurchasesUpdatedListener yang terdaftar di langkah 1.

      Kontrol orang tua ditampilkan untuk pengguna yang diawasi.

    • Jika pengguna memilih penagihan alternatif Anda, panggil launchExternalLink. Contoh:

      Kotlin

      
      // An activity reference from which the purchase flow will be launched.
      val activity: Activity = ...
      
      val params = LaunchExternalLinkParams.newBuilder()
          .setBillingProgram(BillingProgram.BILLING_CHOICE)
          // You can pass along the external transaction token from
          // BillingProgramReportingDetails as a URL parameter in the URI
          .setLinkUri(yourLinkUri)
          .setLinkType(LaunchExternalLinkParams.LinkType.LINK_TO_DIGITAL_CONTENT_OFFER)
          .setLaunchMode(
              LaunchExternalLinkParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP
          )
          .build()
      
      // Call launchExternalLink with a callback
      billingClient.launchExternalLink(activity, params) { billingResult ->
          if (billingResult.responseCode == BillingResponseCode.OK) {
              // Proceed with the rest of the purchase flow. If the user
              // purchases an item, be sure to report the transaction to Google
              // Play.
          } else {
              // Handle failures such as retrying due to network errors.
          }
      }
      
      

      Java

      
      // An activity reference from which the purchase flow will be launched.
      Activity activity = ...;
      
      LaunchExternalLinkParams params = LaunchExternalLinkParams.newBuilder()
          .setBillingProgram(BillingProgram.BILLING_CHOICE)
          // You can pass along the external transaction token from
          // BillingProgramReportingDetails as a URL parameter in the URI
          .setLinkUri(yourLinkUri)
          .setLinkType(LaunchExternalLinkParams.LinkType.LINK_TO_DIGITAL_CONTENT_OFFER)
          .setLaunchMode(
              LaunchExternalLinkParams.LaunchMode.LAUNCH_IN_EXTERNAL_BROWSER_OR_APP)
          .setExternalTransactionToken(transactionToken)
          .build();
      
      LaunchExternalLinkResponseListener listener =
          new LaunchExternalLinkResponseListener() {
            @Override
            public void onLaunchExternalLinkResponse(BillingResult billingResult) {
              if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Proceed with the rest of the purchase flow. If the user
                // purchases an item, be sure to report the transaction to Google
                // Play.
              } else {
                // Handle failures such as retrying due to network errors.
              }
            }
          };
      
      billingClient.launchExternalLink(activity, params, listener);
      
      
    • Teruskan token transaksi eksternal dari langkah 4 ke LaunchExternalLinkParams. Jika menampilkan OK, lanjutkan transaksi dan laporkan transaksi ke Google Play.

      Kontrol orang tua ditampilkan untuk pengguna yang diawasi.

Pilihan penagihan selama penggantian langganan

Untuk penggantian langganan, layar pilihan pengguna tidak boleh ditampilkan karena pilihan pengguna untuk pembelian asli dipertahankan untuk upgrade dan downgrade.

Jika pembelian asli diproses melalui Layanan Penagihan Google Play, Anda harus memanggil launchBillingFlow dengan informasi penggantian langganan Layanan Penagihan Google Play standar.

Namun, jika pembelian asli diproses melalui penagihan alternatif, penanganan penggantian langganan akan sedikit berbeda berdasarkan skenario.

Penggantian langganan dalam Skenario 1A

Pengguna yang meminta upgrade atau downgrade harus melanjutkan melalui sistem penagihan alternatif developer tanpa melalui pengalaman pilihan pengguna lagi.

Untuk melakukannya, panggil launchBillingFlow saat pengguna meminta upgrade atau a downgrade. Gunakan setOriginalExternalTransactionId di dalam objek SubscriptionUpdateParams dalam parameter untuk memberikan ID transaksi eksternal bagi pembelian asli. Cara ini tidak menampilkan layar pilihan pengguna, dikarenakan pilihan pengguna untuk pembelian asli dipertahankan untuk upgrade dan downgrade. Dalam skenario ini, panggilan ke launchBillingFlow dalam skenario ini menghasilkan token transaksi eksternal baru untuk transaksi yang dapat Anda ambil dari callback.

Kotlin


// The external transaction ID from the current
// alternative billing subscription.
val externalTransactionId = //... ;

val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder()
    .setBillingProgram(BillingProgram.BILLING_CHOICE)
    .build()

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        listOf(
            BillingFlowParams.ProductDetailsParams.newBuilder()
                // Fetched using queryProductDetailsAsync.
                .setProductDetails(productDetailsNewPlan)
                // offerIdToken can be found in
                // ProductDetails=>SubscriptionOfferDetails.
                .setOfferToken(offerTokenNewPlan)
                .build()
        )
    )
    .setSubscriptionUpdateParams(
        BillingFlowParams.SubscriptionUpdateParams.newBuilder()
            .setOriginalExternalTransactionId(externalTransactionId)
            .build()
    )
    .enableDeveloperBillingOption(developerBillingOptionParams)
    .build()

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

// When the user selects the alternative billing flow,
// the DeveloperProvidedBillingListener is triggered.

Java


// The external transaction ID from the current
// alternative billing subscription.
String externalTransactionId = //... ;

DeveloperBillingOptionParams developerBillingOptionParams =
    DeveloperBillingOptionParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .build();

List<ProductDetailsParams> productDetailsParamsList = new ArrayList<>();
productDetailsParamsList.add(
    ProductDetailsParams.newBuilder()
        // Fetched using queryProductDetailsAsync.
        .setProductDetails(productDetailsNewPlan)
        // offerIdToken can be found in
        // ProductDetails=>SubscriptionOfferDetails
        .setOfferToken(offerTokenNewPlan)
        .build());

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder()
                .setOriginalExternalTransactionId(externalTransactionId)
                .build())
        .enableDeveloperBillingOption(developerBillingOptionParams)
        .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

// When the user selects the alternative billing flow,
// the DeveloperProvidedBillingListener is triggered.


Setelah upgrade atau downgrade selesai, Anda harus melaporkan transaksi baru menggunakan token transaksi eksternal yang diperoleh melalui panggilan sebelumnya untuk pembelian langganan baru.

Penggantian langganan dalam Skenario 1B

Dalam skenario ini, token transaksi eksternal baru harus dibuat. Satu-satunya perbedaan dari pembelian normal adalah, dalam skenario ini, pilihan pengguna dipertahankan, dan Anda tidak perlu menampilkan layar pilihan untuk upgrade atau downgrade. Namun, Anda harus menampilkan dialog informasi satu kali dan konfirmasi orang tua.

Untuk contoh kode integrasi, lihat langkah 4 di Skenario 1B: Developer merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda.

Setelah upgrade atau downgrade selesai, Anda harus melaporkan transaksi baru menggunakan token transaksi eksternal yang diperoleh melalui panggilan sebelumnya untuk pembelian langganan baru.

Penggantian langganan dalam Skenario 2A

Untuk langganan yang awalnya dibeli melalui situs developer atau aplikasi pembayaran setelah pilihan pengguna, pengguna yang meminta upgrade atau downgrade harus melanjutkan melalui situs developer atau aplikasi pembayaran tanpa melalui pengalaman pilihan pengguna lagi.

Untuk melakukannya, panggil launchBillingFlow saat pengguna meminta upgrade atau a downgrade. Gunakan setOriginalExternalTransactionId, bukan menetapkan parameter lain di bagian objek SubscriptionUpdateParams, untuk memberikan ID transaksi eksternal bagi pembelian asli. DeveloperBillingOptionParams juga harus diberikan dalam panggilan ini. Cara ini tidak menampilkan layar pilihan pengguna, dikarenakan pilihan pengguna untuk pembelian asli dipertahankan untuk upgrade dan downgrade. Contoh:

Kotlin


val externalTransactionId = //... ;

// 1. Construct DeveloperBillingOptionParams indicating the billing program
val developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder()
    .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
    .build()

// 2. Build BillingFlowParams combining DeveloperBillingOptionParams and SubscriptionUpdateParams
val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        listOf(
            BillingFlowParams.ProductDetailsParams.newBuilder()
                // Fetched using queryProductDetailsAsync.
                .setProductDetails(productDetailsNewPlan)
                // offerIdToken can be found in ProductDetails=>SubscriptionOfferDetails.
                .setOfferToken(offerTokenNewPlan)
                .build()
        )
    )
    .setSubscriptionUpdateParams(
        SubscriptionUpdateParams.newBuilder()
            .setOriginalExternalTransactionId(externalTransactionId)
            .build()
    )
    .enableDeveloperBillingOption(developerBillingOptionParams)
    .build()

Java


String externalTransactionId = //... ;

// 1. Construct DeveloperBillingOptionParams indicating the billing program
DeveloperBillingOptionParams developerBillingOptionParams =
    DeveloperBillingOptionParams.newBuilder()
        .setBillingProgram(BillingClient.BillingProgram.BILLING_CHOICE)
        .build();

// 2. Add ProductDetailsParams
List productDetailsParamsList = new ArrayList<>();
productDetailsParamsList.add(
    ProductDetailsParams.newBuilder()
        // Fetched using queryProductDetailsAsync.
        .setProductDetails(productDetailsNewPlan)
        // offerIdToken can be found in ProductDetails=>SubscriptionOfferDetails
        .setOfferToken(offerTokenNewPlan)
        .build());

// 3. Build BillingFlowParams combining DeveloperBillingOptionParams and SubscriptionUpdateParams
BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder()
                .setOriginalExternalTransactionId(externalTransactionId)
                .build())
        .enableDeveloperBillingOption(developerBillingOptionParams)
        .build();


Anda juga harus membuat token transaksi eksternal baru. Contoh:

Kotlin


val params =
    BillingProgramReportingDetailsParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
        .build()

billingClient.createBillingProgramReportingDetailsAsync(
    params,
    object : BillingProgramReportingDetailsListener {
        override fun onCreateBillingProgramReportingDetailsResponse(
            billingResult: BillingResult,
            billingProgramReportingDetails: BillingProgramReportingDetails?
        ) {
            if (billingResult.responseCode != BillingResponseCode.OK) {
                // Handle failures such as retrying due to network errors.
                return
            }
            val externalTransactionToken =
                billingProgramReportingDetails?.externalTransactionToken
            // Persist the external transaction token locally. Pass it to
            // the external website using DeveloperBillingOptionParams when
            // launchBillingFlow is called.
        }
    }
)

Java


BillingProgramReportingDetailsParams params =
    BillingProgramReportingDetailsParams.newBuilder()
        .setBillingProgram(BillingProgram.BILLING_CHOICE)
        .setDeveloperBillingType(DeveloperBillingType.EXTERNAL_LINK)
        .build();

billingClient.createBillingProgramReportingDetailsAsync(
    params,
    new BillingProgramReportingDetailsListener() {
      @Override
      public void onCreateBillingProgramReportingDetailsResponse(
          BillingResult billingResult,
          @Nullable BillingProgramReportingDetails billingProgramReportingDetails) {
        if (billingResult.getResponseCode() != BillingResponseCode.OK) {
          // Handle failures such as retrying due to network errors.
          return;
        }
        String transactionToken =
            billingProgramReportingDetails.getExternalTransactionToken();
        // Persist the external transaction token locally. Pass it to
        // the external website using DeveloperBillingOptionParams when
        // launchBillingFlow is called.
      }
    });

Setelah membuat token baru, Anda harus memanggil launchBillingFlow metode untuk meluncurkan alur pembelian.

Setelah upgrade atau downgrade selesai, Anda harus melaporkan transaksi baru menggunakan token transaksi eksternal yang diperoleh melalui panggilan sebelumnya untuk pembelian langganan baru.

Penggantian langganan dalam Skenario 2B

Langkah-langkah untuk menangani penggantian langganan dalam skenario ini mirip dengan langkah-langkah yang dijelaskan dalam Penggantian langganan dalam Skenario 2A. Satu-satunya perbedaan adalah setelah membuat token transaksi, Anda harus memanggil launchExternalLink untuk menampilkan dialog penafian link keluar, bukan memanggil metode launchBillingFlow. Dalam skenario ini, pilihan pengguna dipertahankan, dan Anda tidak perlu menampilkan layar pilihan untuk upgrade atau downgrade.

Untuk contoh kode integrasi, lihat langkah 6 di Skenario 2B: Developer merender layar pilihan dan penagihan alternatif ditangani dalam aplikasi Anda.

Setelah upgrade atau downgrade selesai, Anda harus melaporkan transaksi baru menggunakan token transaksi eksternal yang diperoleh melalui panggilan sebelumnya untuk pembelian langganan baru.