이 가이드에서는 사용자에게 결제 선택권을 제공할 수 있도록 앱을 Play Billing Library API와 통합하는 방법을 설명합니다.
PBL과의 통합
네 가지 시나리오에서 결제 선택을 PBL과 통합할 수 있습니다. 선택 화면을 렌더링하는 주체와 결제가 이루어지는 위치에 따라 시나리오가 달라집니다. 다음 표에는 통합 시나리오가 나와 있습니다.
| 결제는 어디에서 이루어지나요? | |||
| 인앱 | 외부 웹 링크 | ||
| 어떤 결제 선택 화면을 렌더링하시겠어요? | Google Play의 | 시나리오 1A
Google에서 선택 화면을 렌더링하고 개발자 제공 결제는 앱 내에서 처리됩니다. |
시나리오 1B
앱 개발자가 선택 화면을 렌더링하고 개발자 제공 결제는 앱 내에서 처리됩니다. |
| 자체 (UX 가이드라인에 따름) | 시나리오 2A
Google에서 선택 화면을 렌더링하고 사용자는 구매를 위해 앱 외부에서 개발자의 자체 웹사이트로 연결됩니다. |
시나리오 2B
앱 개발자가 선택 화면을 렌더링하고 사용자가 구매를 위해 앱 외부에서 개발자 자체 웹사이트로 연결됩니다. |
|
다음 그림에서는 이러한 각 시나리오의 결제 선택 흐름을 자세히 설명합니다.
PBL 통합 시나리오
통합 시나리오에 따라 이 섹션의 단계에 따라 앱에서 결제 선택을 구현합니다.
시나리오 1A 처리
Google에서 선택 화면을 렌더링하고 개발자 제공 결제 시스템은 앱 내에서 처리됩니다. 이 시나리오에서 결제 선택을 사용 설정하려면 다음 단계를 따르세요.
BillingClient 인스턴스를 구성할 때
EnableBillingProgramParams를 사용하여enableBillingProgram를 호출한 다음 연결을 시작합니다. 예를 들면 다음과 같습니다.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() ) } }) } 자바
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();Google에서 렌더링한 결제 선택사항이 사용자에게 제공되는지 확인합니다.
isBillingProgramAvailableAsync를 호출하여 프로그램 사용 가능 여부를 확인한 다음queryProductDetailsAsync를 호출하여 사용 가능한 제품을 표시합니다. 예를 들면 다음과 같습니다.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. }자바
// ... 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. } } );참고:
billingProgramAvailabilityDetails는 Google에서 렌더링한 결제 선택 화면과 개발자가 렌더링한 결제 선택 화면 중 어떤 화면을 사용할 수 있는지 알려줍니다.사용자가 구매를 클릭할 때 구매 흐름을 트리거하려면
launchBillingFlow을 호출합니다. 결제 선택사항을 사용할 수 있는 경우DeveloperBillingOptionParams를BillingFlowParams에 전달합니다. 예를 들면 다음과 같습니다.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)자바
DeveloperBillingOptionParams developerBillingOptionParams = DeveloperBillingOptionParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .enableDeveloperBillingOption(developerBillingOptionParams) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);참고: 자녀 보호 기능은 감독 대상 사용자에게 표시됩니다.
다음과 같이 결제 유형의 사용자 선택을 처리합니다.
- 사용자가 Play 결제를 선택하면 결제 결과가 1단계에 등록된
PurchasesUpdatedListener에 반환됩니다. - 사용자가 개발자 제공 결제를 선택하면 결제 결과가 1단계에서 등록된
DeveloperProvidedBillingListener에 반환됩니다. 이 경우 반환된DeveloperProvidedBillingDetails에는externalTransactionToken이 포함됩니다. 이 토큰은 거래 보고에 사용됩니다.
- 사용자가 Play 결제를 선택하면 결제 결과가 1단계에 등록된
시나리오 1B 처리
개발자가 선택 화면을 렌더링하고 개발자 제공 결제가 앱 내에서 처리됩니다. 이 시나리오에서 결제 선택을 사용 설정하려면 다음 단계를 따르세요.
BillingClient인스턴스를 생성할 때EnableBillingProgramParams에서DeveloperProvidedBillingListener없이enableBillingProgram를 호출한 다음 연결을 시작합니다. 예를 들면 다음과 같습니다.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() ) } }) }자바
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build();개발자가 렌더링한 결제 선택사항이 사용자에게 제공되는지 확인합니다.
isBillingProgramAvailableAsync를 호출하여 프로그램 사용 가능 여부를 확인한 다음queryProductDetailsAsync를 호출하여 사용 가능한 제품을 표시합니다. 예를 들면 다음과 같습니다.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. }자바
// ... 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. } } );참고:
billingProgramAvailabilityDetails는 Google에서 렌더링한 결제 선택 화면과 개발자에서 렌더링한 결제 선택 화면의 사용 가능 여부를 알려줍니다.getBillingChoiceInfoAsync메서드를 호출하여 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 }자바
// 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 } });DeveloperBillingType이 IN_APP으로 설정된 외부 거래 토큰을 만듭니다. 예를 들면 다음과 같습니다.
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자바
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 } } );사용자가 구매를 클릭하면
showBillingProgramInformationDialog를 호출하여 정보 대화상자를 표시합니다. 예를 들어 사용자를 위한 정보 대화상자를 참고하세요. 4단계의 BillingProgram 및 transactionToken은 요청에 설정해야 합니다.참고: 자녀 보호 기능은 감독 대상 사용자에게 표시됩니다.
이전 단계의 결과가
OK인 경우 개발자 제공 결제 선택 화면을 실행합니다.다음과 같이 결제 유형의 사용자 선택을 처리합니다.
- 사용자가 Play 결제를 선택하면 표준 Play 결제 안내에 따라
launchBillingFlow를 호출합니다. 결제 결과는 1단계에 등록된PurchasesUpdatedListener에 반환됩니다. - 사용자가 개발자 제공 결제를 선택하면 개발자가 직접 거래를 처리하고 4단계에서 생성된 토큰을 사용하여 Play에 보고해야 합니다.
- 사용자가 Play 결제를 선택하면 표준 Play 결제 안내에 따라
시나리오 2A 처리
Google에서 선택 화면을 렌더링하고 대체 결제는 앱 외부에서 처리됩니다. 이 시나리오에서 결제 선택을 사용 설정하려면 다음 단계를 따르세요.
BillingClient 인스턴스를 구성할 때
EnableBillingProgramParams를 사용하여enableBillingProgram를 호출한 다음 연결을 시작합니다. 예를 들면 다음과 같습니다.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() ) } }) } 자바
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();다음 항목의 사용 가능 여부를 확인합니다.
- Google에서 렌더링한 결제 선택
- 외부 웹 링크
isBillingProgramAvailableAsync를 호출하여 프로그램 사용 가능 여부를 확인한 다음queryProductDetailsAsync를 호출하여 사용 가능한 제품을 표시합니다. 예를 들면 다음과 같습니다.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. }자바
// ... 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. } } );사용자가 구매 의도를 표시할 때
createBillingProgramReportingDetailsAsync를 호출하여 외부 거래 토큰을 생성합니다. 예를 들면 다음과 같습니다.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자바
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. } } );사용자가 구매를 클릭할 때 구매 흐름을 트리거하려면
launchBillingFlow을 호출합니다. 사용자가 결제 선택을 사용할 수 있는 경우 다음을 실행합니다.- DeveloperBillingOptionParams를
BillingFlowParams에 전달합니다. - 3단계의 외부 거래 토큰을
DeveloperBillingOptionParams에 전달합니다.
예를 들면 다음과 같습니다.
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()자바
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();참고: 자녀 보호 기능은 감독 대상 사용자에게 표시됩니다.
- DeveloperBillingOptionParams를
다음과 같이 결제 유형의 사용자 선택을 처리합니다.
- 사용자가 Play 결제를 선택하면 표준 Play 결제 안내에 따라
launchBillingFlow를 호출합니다. 결제 결과는 1단계에 등록된PurchasesUpdatedListener에 반환됩니다. - 사용자가 개발자 제공 결제를 선택하면 결제 결과가
- 단계에서 등록된
- 반환된
DeveloperProvidedBillingDetails에는 유효한 토큰인 경우 4단계에서DeveloperBillingOptionParams에 전달된externalTransactionToken이 포함됩니다.
DeveloperProvidedBillingListener에 반환됩니다. - 반환된
- 사용자가 Play 결제를 선택하면 표준 Play 결제 안내에 따라
시나리오 2B 처리
개발자가 선택 화면을 렌더링하고 개발자 제공 결제는 앱 외부에서 처리됩니다. 이 시나리오에서 결제 선택을 사용 설정하려면 다음 단계를 따르세요.
BillingClient 인스턴스를 생성할 때
EnableBillingProgramParams에서DeveloperProvidedBillingListener없이enableBillingProgram을 호출한 다음 연결을 시작합니다. 예를 들면 다음과 같습니다.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() ) } }) }자바
EnableBillingProgramParams params = EnableBillingProgramParams.newBuilder() .setBillingProgram(BillingProgram.BILLING_CHOICE) .build(); BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableBillingProgram(params) .build();다음 항목의 사용 가능 여부를 확인합니다.
- Google에서 렌더링한 결제 선택
- 외부 웹 링크
isBillingProgramAvailableAsync를 호출하여 프로그램 사용 가능 여부를 확인한 다음queryProductDetailsAsync를 호출하여 사용 가능한 제품을 표시합니다. 예를 들면 다음과 같습니다.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. }자바
// ... 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. } } );getBillingChoiceInfoAsync메서드를 호출하여 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 }자바
// 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 } });사용자가 구매 의도를 표시할 때
createBillingProgramReportingDetailsAsync를 호출하여 외부 거래 토큰을 생성합니다. 예를 들면 다음과 같습니다.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자바
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. } } );사용자가 구매를 클릭하면 대체 선택 화면을 실행합니다.
다음과 같이 결제 유형의 사용자 선택을 처리합니다.
사용자가 Play 결제를 선택하면 표준 Play 결제 안내에 따라
launchBillingFlow를 호출합니다. 결제 결과는 1단계에 등록된PurchasesUpdatedListener에 반환됩니다.감독 대상 사용자에게 자녀 보호 기능이 표시됩니다.
사용자가 개발자 제공 결제를 선택하면 launchExternalLink를 호출합니다. 예를 들면 다음과 같습니다.
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. } }자바
// 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);4단계의 외부 거래 토큰을
LaunchExternalLinkParams에 전달합니다. OK가 반환되면 거래를 진행하고 Google Play에 거래를 보고합니다.감독 대상 사용자에게 자녀 보호 기능이 표시됩니다.
구독 교체 중 결제 선택
정기 결제 교체의 경우 업그레이드와 다운그레이드에 대해 원래 구매와 관련된 사용자 선택이 그대로 유지되므로 사용자 선택 화면이 표시되지 않아야 합니다.
원래 구매가 Google Play 결제를 통해 처리된 경우 표준 Google Play 결제 정기 결제 교체 정보와 함께 launchBillingFlow를 호출해야 합니다.
하지만 원래 구매가 개발자 제공 결제를 통해 처리된 경우 시나리오에 따라 정기 결제 교체 처리가 약간 다릅니다.
시나리오 1A의 정기 결제 교체
업그레이드 또는 다운그레이드를 요청하는 사용자는 사용자 선택 환경을 다시 거치지 않고 개발자의 개발자 제공 결제 시스템으로 진행되도록 해야 합니다.
이렇게 하려면 사용자가 업그레이드 또는 다운그레이드를 요청하는 경우 launchBillingFlow를 호출합니다. 매개변수의 SubscriptionUpdateParams 객체 내에서 setOriginalExternalTransactionId를 사용하여 원래 구매의 외부 거래 ID를 제공합니다. 이렇게 해도 업그레이드와 다운그레이드에 대해 원래 구매와 관련된 사용자 선택이 그대로 유지되므로 사용자 선택 화면이 표시되지 않습니다. 여기서 launchBillingFlow를 호출하면 거래의 새 외부 거래 토큰이 생성됩니다. 이 토큰은 콜백에서 가져올 수 있습니다.
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.
자바
// 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.
업그레이드 또는 다운그레이드가 완료되면 새로운 정기 결제 구매에 대한 이전 호출을 통해 획득한 외부 거래 토큰을 사용하여 새 거래를 보고해야 합니다.
시나리오 1B의 정기 결제 교체
이 시나리오에서는 새 외부 거래 토큰을 생성해야 합니다. 일반 구매와의 유일한 차이점은 이 시나리오에서는 사용자 선택이 유지되므로 업그레이드 또는 다운그레이드에 대한 선택 화면을 표시하지 않아도 된다는 것입니다. 하지만 일회성 정보 대화상자와 부모 확인은 표시해야 합니다.
샘플 통합 코드는 시나리오 1B의 4단계: 개발자가 선택 화면을 렌더링하고 앱 내에서 대체 결제가 처리됨을 참고하세요.
업그레이드 또는 다운그레이드가 완료되면 새로운 정기 결제 구매에 대한 이전 호출을 통해 획득한 외부 거래 토큰을 사용하여 새 거래를 보고해야 합니다.
시나리오 2A의 정기 결제 교체
사용자 선택 후 개발자의 웹사이트 또는 결제 앱을 통해 구매된 정기 결제인 경우, 업그레이드 또는 다운그레이드를 요청하는 사용자는 사용자 선택 환경을 다시 거치지 않고 개발자의 웹사이트 또는 결제 앱으로 진행되도록 해야 합니다.
이렇게 하려면 사용자가 업그레이드 또는 다운그레이드를 요청하는 경우 launchBillingFlow를 호출합니다. SubscriptionUpdateParams 객체 아래에 다른 매개변수를 지정하는 대신 setOriginalExternalTransactionId를 사용하여 원래 구매의 외부 거래 ID를 제공합니다.
이 호출에서 DeveloperBillingOptionParams도 제공해야 합니다. 이렇게 해도 업그레이드와 다운그레이드에 대해 원래 구매와 관련된 사용자 선택이 그대로 유지되므로 사용자 선택 화면이 표시되지 않습니다. 예를 들면 다음과 같습니다.
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()
자바
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();
새 외부 거래 토큰도 생성해야 합니다. 예를 들면 다음과 같습니다.
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.
}
}
)
자바
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.
}
});
새 토큰을 생성한 후 launchBillingFlow 메서드를 호출하여 구매 흐름을 시작해야 합니다.
업그레이드 또는 다운그레이드가 완료되면 새로운 정기 결제 구매에 대한 이전 호출을 통해 획득한 외부 거래 토큰을 사용하여 새 거래를 보고해야 합니다.
시나리오 2B의 정기 결제 교체
이 시나리오에서 정기 결제 교체를 처리하는 단계는 시나리오 2A의 정기 결제 교체에 설명된 단계와 유사합니다. 유일한 차이점은 트랜잭션 토큰을 생성한 후 launchBillingFlow 메서드를 호출하는 대신 launchExternalLink를 호출하여 링크아웃 면책 조항 대화상자를 표시해야 한다는 것입니다. 이 시나리오에서는 사용자 선택이 유지되므로 업그레이드 또는 다운그레이드 선택 화면을 표시하지 않아도 됩니다.
샘플 통합 코드는 시나리오 2B의 6단계: 개발자가 선택 화면을 렌더링하고 앱 내에서 대체 결제가 처리됨을 참고하세요.
업그레이드 또는 다운그레이드가 완료되면 새로운 정기 결제 구매에 대한 이전 호출을 통해 획득한 외부 거래 토큰을 사용하여 새 거래를 보고해야 합니다.