버전 4 또는 5에서 Google Play 결제 라이브러리 6으로 이전

이 주제에서는 Google Play 결제 라이브러리 4 또는 5에서 Google Play 결제 라이브러리 6으로 이전하는 방법과 새로운 정기 결제 기능을 사용하는 방법을 설명합니다.

버전 6.0.0의 전체 변경사항 목록은 출시 노트를 참고하세요.

개요

Google Play 결제 라이브러리 6은 버전 5에서 도입된 새로운 정기 결제 기능을 기반으로 하며 몇 가지 개선사항이 추가됩니다. 이러한 기능을 사용하면 정기 결제를 더 다양한 방식으로 판매할 수 있고, 갈수록 늘어나는 SKU를 만들고 관리할 필요가 없어 운영 비용이 줄어듭니다.

Play 결제 라이브러리 5에서 도입된 새로운 기능에 관한 자세한 내용은 Play Console 정기 결제의 최근 변경사항을 참고하세요.

이전 버전과 호환되는 Play 결제 라이브러리 업그레이드

기존의 모든 정기 결제 제품은 Play 결제 라이브러리 5의 2022년 5월 출시와 새로운 정기 결제 플랫폼에 따라 이 새로운 패러다임으로 자동 전환되었습니다. 즉, Play 결제 라이브러리의 새 버전과 호환되는 카탈로그를 만들기 위해 정기 결제 제품 구성을 변경할 필요가 없습니다. 정기 결제 SKU가 이전 버전과 호환되는 정기 결제로 전환된 방식에 관한 자세한 내용은 Play Console 도움말이전 정기 결제와 함께 사용하기 섹션을 참고하세요.

이전 버전의 앱도 계속 작동함

이전 버전과 호환되는 정기 결제 카탈로그를 사용하는 경우, 앱의 모든 기존 버전이 의도한 대로 작동합니다. 일회성 제품 구매도 이전 버전에서 문제없이 작동합니다.

지원 중단된 메서드(예: querySkuDetailsAsync())를 사용하는 앱 버전은 이전 버전과 호환되지 않는 기본 요금제나 혜택을 판매할 수 없습니다. 이전 버전과 호환되는 혜택에 관한 내용은 관련 Play Console 고객센터 도움말에서 확인할 수 있습니다.

Play 결제 라이브러리 5 또는 6으로 업그레이드

Play 결제 라이브러리 5 및 6에는 SkuDetails를 결제 흐름 매개변수로 받는 지원 중단된 querySkuDetailsAsyncBillingFlowParams.Builder.setSkuDetails 메서드가 포함되어 있습니다. 즉, 이전의 여러 단계를 계획하여 Play 결제 라이브러리 6으로 점진적으로 이전할 수 있습니다.

이전을 위한 첫 번째 단계로, 라이브러리 버전을 업데이트하고, 카탈로그와 백엔드는 그대로 둔 상태에서 지원 중단된 메서드를 사용하는 앱을 테스트하면 됩니다. queryPurchases, launchPriceChangeFlow 또는 setVrPurchaseFlow를 사용하지 않는 경우에도 의도한 대로 작동합니다. 그런 다음 이 과정을 반복하여 2022년 5월에 출시된 새로운 정기 결제 기능을 완전히 도입하면 됩니다.

이전에 Google Play 결제 라이브러리 5 이전을 통해 이러한 기능을 도입했다면 Google Play 결제 라이브러리 업데이트사용자의 정기 결제 구매 변경 섹션으로 곧바로 넘어가도 좋습니다. 그보다 이전 버전에서 시작했거나 아직 새로운 기능을 완전히 도입하지 않았다면 아래의 전체 이전 단계에서 새로운 기능을 도입하는 방법을 알아보세요.

전체 이전 단계

백엔드 제품 카탈로그에서 새 구독 만들기

이제 Play Console 또는 Play Developer API를 사용하면 여러 기본 요금제(각각 여러 혜택 포함)가 있는 단일 정기 결제를 구성할 수 있습니다. 정기 결제 혜택에는 유연한 가격 모델과 지원 옵션이 있습니다. 다양한 자동 갱신 및 선불 요금제를 사용하여 정기 결제 수명 주기 동안 혜택을 만들 수 있습니다.

앱을 이전하기 전에 Play 결제 라이브러리 6 통합을 위한 새 정기 결제 플랫폼의 항목 구조에 따라 새 제품을 만드는 것이 좋습니다. 단일 정기 결제에서 동일한 사용 권한 혜택을 나타내는 이전 카탈로그의 중복 제품을 통합하고, 기본 요금제 및 혜택 구성을 사용하여 제공하려는 모든 옵션을 나타낼 수 있습니다. 이 권장사항에 관한 자세한 내용은 Play Console 도움말에서 이전 정기 결제 사용 섹션을 참고하세요.

2022년 5월 출시 이후에는 전환된 정기 결제 제품을 수정하지 않는 것이 좋습니다. 이러한 이전 빌드에 영향을 미칠 수 있는 변경사항을 도입하지 않고 지원 중단된 메서드(예: querySkuDetailsAsync())를 사용하는 앱 버전으로 판매되도록 제품을 그대로 두어야 합니다.

전환 프로세스를 통해 2022년 5월 이전 카탈로그에 있던 정기 결제 제품은 읽기 전용으로 되어 기존 통합에 문제를 일으킬 수 있는 실수로 인한 변경을 방지합니다. 이러한 정기 결제도 변경할 수는 있지만 프런트엔드 및 백엔드 통합에 영향을 미칠 수 있습니다.

  • 프런트엔드에서 querySkuDetailsAsync()를 사용하여 정기 결제 제품 세부정보를 가져오는 앱 버전은 이전 버전과 호환되는 기본 요금제 및 혜택만 판매할 수 있으며, 이전 버전과 호환되는 기본 요금제 및 혜택의 조합은 하나만 있을 수 있습니다. 따라서 전환된 정기 결제에 새 요금제나 혜택을 추가하면 추가된 새 기본 요금제나 혜택은 이러한 이전 버전의 앱에서 판매할 수 없습니다.

  • 백엔드에서는 Play Console UI에서 전환된 정기 결제를 수정하면 inappproducts 엔드포인트를 사용하여 이를 관리할 수 없습니다(이 목적으로 엔드포인트를 호출한 경우). 또한 새로운 정기 결제 구매 상태 엔드포인트(purchases.subscriptionsv2.get)로 이전하여 이러한 정기 결제 구매를 관리해야 합니다. 이전 구매 상태 엔드포인트(purchases.subscriptions.get)는 이전 버전과 호환되는 기본 요금제 및 혜택 구매를 처리하는 데 필요한 데이터만 반환하기 때문입니다. 자세한 내용은 정기 결제 구매 상태 관리 섹션을 참고하세요.

새로운 API로 백엔드 정기 결제 카탈로그 관리

Google Play Developer API로 정기 결제 제품 카탈로그를 자동으로 관리하는 경우 새로운 정기 결제 제품 정의 엔드포인트를 사용하여 정기 결제와 기본 요금제, 혜택을 만들고 관리해야 합니다. 이 버전의 제품 카탈로그 API 변경사항에 관한 자세한 내용은 2022년 5월 정기 결제 기능 가이드를 참고하세요.

Google Play 결제 정기 결제의 자동 제품 카탈로그 관리 모듈을 이전하려면 inappproducts API를 새로운 Subscription Publishing API로 대체하여 정기 결제 카탈로그를 관리하고 게시하세요. 새로운 엔드포인트는 세 가지입니다.

이러한 새로운 엔드포인트에는 기본 요금제 및 혜택 태그, 지역 타겟팅, 선불 요금제 등 카탈로그의 새로운 기능을 모두 활용하는 데 필요한 기능이 모두 있습니다.

일회성 구매 제품의 인앱 상품 카탈로그를 관리하려면 inappproducts API를 계속 사용해야 합니다.

.

지원 중단된 메서드(예: querySkuDetailsAsync())를 사용하는 앱 버전은 이전 버전과 호환되지 않는 기본 요금제나 혜택을 판매할 수 없습니다. 이전 버전과 호환되는 혜택 관련 내용은 여기에서 확인할 수 있습니다.

Google Play 결제 라이브러리 업데이트

새 정기 결제 제품 카탈로그를 만들고 나면 Google 결제 라이브러리 5로 앱을 이전할 수 있습니다. 기존 Play 결제 라이브러리 종속 항목을 앱의 build.gradle 파일의 업데이트된 버전으로 바꿉니다.

dependencies {
    def billingVersion = "6.0.0"

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

메서드 호출을 수정하지 않았더라도 프로젝트가 즉시 빌드됩니다. Play 결제 라이브러리 6은 이전 버전과 호환되기 때문입니다. SKU의 개념은 지원 중단된 것으로 간주되지만, 앱 포팅을 보다 단순한 증분 프로세스로 만들기 위해 여전히 존재합니다.

결제 클라이언트 초기화 및 Google Play 연결 설정

Android 앱에서 구매를 시작하는 첫 번째 단계는 동일하게 유지됩니다.

구입 가능한 제품 표시

사용자가 구매할 수 있는 모든 혜택을 획득하려면 다음 단계를 따르세요.

  • SkuDetailsParamsQueryProductDetailsParams로 바꿉니다.
  • BillingClient.queryProductDetailsAsync()를 사용하도록 BillingClient.querySkuDetailsAsync() 호출을 전환합니다.

이제 쿼리 결과는 SkuDetails가 아닌 ProductDetails입니다. 각 ProductDetails 항목에는 제품(ID, 제품명, 유형 등)에 관한 정보가 포함되어 있습니다. 정기 결제 제품의 경우 ProductDetails에는 정기 결제 혜택 세부정보 목록인 List<ProductDetails.SubscriptionOfferDetails>가 포함되어 있습니다. 일회성 구매 제품의 경우 ProductDetails에는 ProductDetails.OneTimePurchaseOfferDetails가 포함되어 있습니다. 이를 통해 사용자에게 표시할 혜택을 결정할 수 있습니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

Kotlin

val skuList = ArrayList<String>()

skuList.add("up_basic_sub")

val params = SkuDetailsParams.newBuilder()

params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS).build()

billingClient.querySkuDetailsAsync(params) {
    billingResult,
    skuDetailsList ->
    // Process the result
}

Java

List<String> skuList = new ArrayList<>();

skuList.add("up_basic_sub");

SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();

params.setSkusList(skuList).setType(SkuType.SUBS).build();

billingClient.querySkuDetailsAsync(params,
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    }
);

Kotlin

val productList =
    listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("up_basic_sub")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )

val params = QueryProductDetailsParams.newBuilder().setProductList(productList).build()

billingClient.queryProductDetailsAsync(params) {
    billingResult,
    productDetailsList ->
    // Process the result
}

Java

ImmutableList<Product> productList = ImmutableList.of(Product.newBuilder()
                                            .setProductId("up_basic_sub")
                                            .setProductType(ProductType.SUBS)
                                            .build());

QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
    .setProductList(productList)
    .build();

billingClient.queryProductDetailsAsync(
        params,
        new ProductDetailsResponseListener() {
                public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) {
                    // Process the result
                }
        }
);

queryProductDetailsAsync의 콜백은 List<ProductDetails>를 반환합니다. 각 ProductDetails 항목에는 제품(ID, 제품명, 유형 등)에 관한 정보가 포함되어 있습니다. 주요 차이점은 이제 정기 결제 제품에도 사용자에게 제공되는 모든 혜택이 포함된 List<ProductDetails.SubscriptionOfferDetails>가 포함된다는 것입니다.

이전 버전의 Play 결제 라이브러리는 새 객체(정기 결제, 기본 요금제, 혜택 등)를 지원하지 않으므로 새 시스템은 각 정기 결제 SKU를 이전 버전과 호환되는 단일 기본 요금제 및 혜택으로 변환합니다. 사용 가능한 일회성 구매 제품도 ProductDetails 객체로 포팅됩니다. 일회성 구매 제품의 혜택 세부정보는 getOneTimePurchaseOfferDetails() 메서드로 액세스할 수 있습니다.

드물지만 일부 기기는 Google Play 서비스 버전이 오래되어 ProductDetailsqueryProductDetailsAsync()를 지원하지 않습니다. 이 경우를 적절하게 지원하려면 queryProductDetailsAsync를 호출하기 전에 isFeatureSupported()를 호출하여 PRODUCT_DETAILS를 확인하세요. 응답이 OK이면 기기가 이 기능을 지원하는 것으로, queryProductDetailsAsync()를 호출해도 좋습니다. 응답이 FEATURE_NOT_SUPPORTED이면 대신 querySkuDetailsAsync()를 사용하여 이전 버전과 호환되는 제품 목록을 요청할 수 있습니다. 이전 버전과의 호환성 기능을 사용하는 방법에 관한 자세한 내용은 2022년 5월 정기 결제 기능 가이드를 참고하세요.

혜택 구매 흐름 시작

혜택의 구매 흐름을 시작하는 것은 SKU의 흐름을 시작하는 것과 매우 유사합니다. 버전 6을 사용하여 구매 요청을 시작하려면 다음을 실행하세요.

  • BillingFlowParamsSkuDetails를 사용하는 대신 ProductDetailsParams를 사용합니다.
  • 혜택 세부정보(예: 혜택 ID, 기본 요금제 ID 등)는 SubscriptionOfferDetails 객체를 사용하여 가져올 수 있습니다.

사용자가 선택한 혜택으로 제품을 구매하려면 선택한 혜택의 offerToken을 가져와서 ProductDetailsParams 객체에 전달합니다.

BillingFlowParams 객체를 만들고 나면 BillingClient를 사용하여 결제 흐름을 시작하는 것은 동일하게 유지됩니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
val billingFlowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(skuDetails)
                            .build()

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;
// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Kotlin

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...
// Retrieve a value for "productDetails" by calling queryProductDetailsAsync()
// Get the offerToken of the selected offer
val offerToken = productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken

val productDetailsParamsList =
    listOf(
        BillingFlowParams.ProductDetailsParams.newBuilder()
            .setProductDetails(productDetails)
            .setOfferToken(offerToken)
            .build()
    )
val billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .build()

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

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

// Retrieve a value for "productDetails" by calling queryProductDetailsAsync()
// Get the offerToken of the selected offer
String offerToken = productDetails
                     .getSubscriptionOfferDetails()
                     .get(selectedOfferIndex)
                     .getOfferToken();
// Set the parameters for the offer that will be presented
// in the billing flow creating separate productDetailsParamsList variable
ImmutableList<ProductDetailsParams> productDetailsParamsList =
        ImmutableList.of(
                 ProductDetailsParams.newBuilder()
                     .setProductDetails(productDetails)
                     .setOfferToken(offerToken)
                     .build()
        );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

구매 처리

Google Play 결제 라이브러리 6으로 구매를 처리하는 것은 이전 버전과 유사합니다.

사용자가 소유한 모든 활성 구매를 가져오고 새 구매를 쿼리하려면 다음을 실행하세요.

  • BillingClient.SkuType 값을 queryPurchasesAsync()에 전달하는 대신 BillingClient.ProductType 값이 포함된 QueryPurchasesParams 객체를 전달합니다.

다음 예는 이러한 변경사항을 적용하기 전과 후의 앱 모습을 보여줍니다.

Kotlin

billingClient.queryPurchasesAsync(BillingClient.SkuType.SUBS) {
    billingResult,
    purchaseList -> {
        // Process the result
    }
}

Java


billingClient.queryPurchasesAsync(
    BillingClient.SkuType.SUBS,
    new PurchasesResponseListener() {
        public void onQueryPurchasesResponse(
                BillingResult billingResult,
                List<Purchase> purchases) {
            // process the result
        }
    }
);

Kotlin

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
        .setProductType(BillingClient.ProductType.SUBS)
        .build()
) { billingResult, purchaseList ->
    // Process the result
}

Java

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder().setProductType(ProductType.SUBS).build(),
    new PurchasesResponseListener() {
        public void onQueryPurchasesResponse(
                BillingResult billingResult,
                List<Purchase> purchases) {
            // Process the result
        }
    }
);

앱 외부 구매대기 중인 거래를 관리하는 단계는 변경되지 않았습니다.

백엔드의 새 API로 정기 결제 구매 상태 관리

이전 단계에서 만든 새 제품의 구매를 처리할 수 있도록 백엔드의 정기 결제 구매 상태 관리 구성요소를 이전해야 합니다. 현재 정기 결제 구매 상태 관리 구성요소는 2022년 5월 출시 전에 정의한 전환된 정기 결제 제품에서 평소대로 작동하며 이전 버전과 호환되는 혜택의 구매를 관리하는 데 충분하지만 새 기능은 지원하지 않습니다.

백엔드에서 구매 상태를 확인하고 Play 결제 정기 결제 사용 권한을 관리하는 정기 결제 구매 상태 관리 모듈을 위한 새 Subscription Purchases API를 구현해야 합니다. 이 API의 이전 버전은 새 플랫폼에서 구매를 관리하는 데 필요한 모든 세부정보를 반환하지 않습니다. 이전 버전의 변경사항에 관한 자세한 내용은 2022년 5월 새로운 정기 결제 기능 가이드를 참고하세요.

일반적으로 SubscriptionNotification 실시간 개발자 알림을 받을 때마다 Subscription Purchases API를 호출하여 정기 결제 상태에 관한 최신 정보를 가져옵니다. purchases.subscriptions.get 호출을 Subscription Purchases API의 새 버전인 purchases.subscriptionsv2.get으로 바꿔야 합니다. 새 모델에서 정기 결제의 구매 사용 권한을 관리하는 데 충분한 정보를 제공하는 SubscriptionPurchaseV2라는 새 리소스가 있습니다.

이 새로운 엔드포인트는 제품을 판매하는 앱의 버전과 제품이 정의된 시점(2022년 5월 출시 전 또는 후)과 관계없이 모든 정기 결제 제품 및 모든 구매의 상태를 반환합니다. 따라서 이전 후에는 이 버전의 정기 결제 구매 상태 관리 모듈만 있으면 됩니다.

사용자의 정기 결제 구매 변경

Play 결제 라이브러리 5 및 이전 버전에서는 업그레이드나 다운그레이드와 같은 사용자의 정기 결제 구매 변경사항을 적용하는 용도로 ProrationMode가 사용되었습니다. 이 메서드는 지원 중단되었으며 버전 6에서 ReplacementMode로 대체되었습니다.

정기 결제 가격 변경 처리

이전에 지원 중단된 launchPriceConfirmationFlow API가 Play 결제 라이브러리 6에서 삭제되었습니다. 대안을 확인하려면 가격 변경 가이드를 참고하세요.

Play 결제 라이브러리 오류 처리

Play 결제 라이브러리 6에서는 사용자 기기와 Google Play 시스템 간의 네트워크 연결 문제를 나타내는 새로운 NETWORK_ERROR 코드가 추가되었습니다. SERVICE_TIMEOUTSERVICE_UNAVAILABLE 코드도 변경되었습니다. 자세한 내용은 BillingResult 응답 코드 처리를 참고하세요.

대기 중인 거래 처리

버전 6.0.0부터 Play 결제 라이브러리는 대기 중인 구매에 대해 주문 ID를 생성하지 않습니다. 대기 중인 구매의 주문 ID는 구매가 PURCHASED 상태로 변경된 후에 생성됩니다. 통합에서 거래가 완전히 완료된 후에 주문 ID를 사용해야 합니다. 레코드에서는 계속해서 구매 토큰을 사용할 수 있습니다. 대기 중인 구매 처리에 관한 자세한 내용은 Play 결제 라이브러리 통합 가이드구매 수명 주기 관리 가이드를 참고하세요.