알림
1. 2023년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 5 이상을 사용해야 합니다. 2023년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 5 이상이 요구됩니다. 자세히 알아보기
2. 앱이 Android 14 이상을 타겟팅하는 경우 PBL 5.2.1 또는 PBL 6.0.1 이상으로 업데이트해야 합니다.

개발자 제공 결제를 위한 인앱 통합 가이드(대한민국만 해당)

이 가이드에서는 개발자 제공 결제 API를 앱에 통합하는 방법을 설명합니다.

Play 결제 라이브러리 설정

Android 앱에 Play 결제 라이브러리 종속 항목을 추가합니다. 개발자 제공 결제 API를 사용하려면 버전 5.2 이상을 사용해야 합니다. 구 버전에서 이전해야 하는 경우, 개발자 제공 결제를 구현하기 전에 이전 가이드의 안내를 따르세요.

Google Play에 연결

통합 프로세스의 첫 번째 단계는 Google Play 결제 통합 가이드와 동일하되 BillingClient 초기화에 약간의 수정이 적용됩니다.

  • 이용자에게 결제 옵션 선택권을 제공하고자 함을 알리는 새 메서드 enableAlternativeBilling을 호출해야 합니다.
  • 이용자가 개발자 제공 결제를 선택하는 케이스를 처리하기 위해 AlternativeBillingListener를 등록해야 합니다.

다음 예는 이러한 수정을 적용하여 BillingClient를 초기화하는 방법을 보여줍니다.

Kotlin

val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // Handle new Google Play purchase.
   }

val alternativeBillingListener =
   AlternativeBillingListener { alternateChoiceDetails ->
       // Handle alternative billing choice.
   }

var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   .enablePendingPurchases()
   .enableAlternativeBilling(alternativeBillingListener)
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // Handle new Google Play purchase.
    }
};

private AlternativeBillingListener alternativeBillingListener = new AlternativeBillingListener() {
    @Override
    public void userSelectedAlternativeBilling(
        AlternativeChoiceDetails alternateChoiceDetails) {
        // Handle new Google Play purchase.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .enableAlternativeBilling(alternativeBillingListener)
    .build();

BillingClient를 초기화한 후에는 통합 가이드에 설명된 대로 Google Play에 연결해야 합니다.

사용 가능한 제품 표시

Google Play 결제 시스템 통합과 동일한 방식으로 이용자에게 사용 가능한 제품을 표시할 수 있습니다. 이용자가 구매할 수 있는 제품을 확인하고 구매할 제품을 선택하면 이용자 선택 결제 흐름을 시작합니다. 아래 섹션에서 이를 설명합니다.

이용자 선택 결제 흐름 시작

launchBillingFlow()를 호출하여 이용자 선택 결제 흐름을 시작합니다. 이는 Google Play 결제 시스템 통합 시의 구매 흐름 시작과 동일하게 동작합니다. 즉, ProductDetails 인스턴스와 이용자가 구매하려는 제품 및 혜택에 해당하는 offerToken을 제공합니다. 이용자가 Google Play 결제 시스템을 선택하면 이 정보를 사용하여 구매 흐름이 계속 진행됩니다.

개발자가 launchBillingFlow를 호출하면 Google Play 결제 시스템이 다음 로직을 수행합니다.

  • 시스템이 이용자의 Google Play 국가가 대한민국인지 확인합니다. 이용자의 Google Play 국가가 대한민국인 경우 Google Play가 BillingClient의 구성에 따라 개발자 제공 결제가 사용 설정되었는지 확인합니다.

    • 개발자 제공 결제가 사용 설정된 경우, 새로운 UX를 표시합니다.
    • 개발자 제공 결제가 사용 설정되지 않은 경우, 선택 화면을 표시하지 않고 표준 Google Play 결제 시스템 UX를 표시합니다.
  • 이용자의 Google Play 국가가 대한민국이 아닌 경우 구매 흐름이 이용자 선택권이 없는 표준 Google Play 결제 시스템 UX를 표시합니다.

이용자의 Play 국가가 대한민국임 이용자의 Play 국가가 대한민국이 아님
BillingClient 설정 중에 enableAlternativeBilling이 호출됨 이용자에게 새로운 UX가 표시됨 이용자에게 표준 Google Play 결제 시스템 UX가 표시됨
BillingClient 설정 중에 enableAlternativeBilling이 호출되지 않음 이용자에게 표준 Google Play 결제 시스템 UX가 표시됨 이용자에게 표준 Google Play 결제 시스템 UX가 표시됨

이용자 선택사항 처리

구매 흐름의 나머지를 어떤 식으로 처리할지는 이용자가 Google Play 결제 시스템과 개발자 제공 결제 시스템 중 어느 것을 선택했는지에 따라 달라집니다.

이용자가 개발자 제공 결제 시스템을 선택한 경우

이용자가 개발자 제공 결제 시스템을 선택한 경우 Google Play는 AlternativeBillingListener를 호출하여 개발자 제공 결제 시스템의 구매 흐름을 시작해야 한다고 앱에 알립니다. 구체적으로, userSelectedAlternativeBilling() 메서드가 호출됩니다.

AlternativeChoiceDetails 객체에 제공되는 외부 거래 토큰은 개발자 제공 결제 흐름에 입력할 이용자가 선택한 옵션의 서명을 나타냅니다. 이 토큰을 사용하여 백엔드 통합 가이드의 설명처럼 이 선택으로 발생하는 거래를 보고합니다.

AlternativeBillingListener는 다음 작업을 실행해야 합니다.

  • 개발자 제공 결제 시스템의 구매 흐름에 표시될 수 있도록 이용자가 구매한 제품을 가져옵니다.
  • 외부 거래 토큰으로 수신된 문자열을 수집하여 백엔드로 전송해 유지합니다. 이는 나중에 이용자가 특정 구매를 완료하면 Google Play에 외부 거래를 보고하는 데 사용됩니다.
  • 개발자의 대체 구매 흐름을 시작합니다.

이용자가 개발자 제공 결제 시스템을 사용하여 구매를 완료하면 백엔드에서 Google Play Developer API를 호출하여 Google Play에 거래를 보고하고 externalTransactionToken 및 추가 거래 세부정보를 제공해야 합니다. 자세한 내용은 백엔드 통합 가이드를 참고하세요.

다음 예는 AlternativeBillingListener를 구현하는 방법을 보여줍니다.

Kotlin

private val alternativeBillingListener =
    AlternativeBillingListener { alternativeChoiceDetails ->
        // Get the products being purchased by the user.
        val products = alternativeChoiceDetails.products

        // Send external transaction token to developer backend server
        // this devBackend object is for demonstration purposes,
        // developers can implement this step however best fits their
        // app to backend communication.
        devBackend.sendExternalTransactionStarted(
            alternativeChoiceDetails.externalTransactionToken,
            user
        )

        // Launch alternative billing
        // ...
        // The developer backend handles reporting the transaction
        // to Google Play's backend once the alternative billing
        // purchase is completed.
    }

Java

private AlternativeBillingListener alternativeBillingListener = new AlternativeBillingListener() {
    @Override
    public void userSelectedAlternativeBilling(
           AlternativeChoiceDetails alternateChoiceDetails) {
       // Get the products being purchased by the user.
       List<Product> products =
              alternativeChoiceDetails.getProducts();

       // Send external transaction token to developer backend server
       // this devBackend object is for demonstration purposes,
       // developers can implement this step however best fits their
       // app to backend communication.
       devBackend.sendExternalTransactionStarted(
              alternateChoiceDetails.getExternalTransactionToken(),
              user
       );

       // Launch alternative billing
       // ...
       // The developer backend handles reporting the transaction
       // to Google Play's backend once the alternative billing
       // purchase is completed.
    }
};

이용자가 Google Play 결제 시스템을 선택한 경우

이용자가 Google Play 결제 시스템을 선택하면 Google Play를 통해 구매가 계속 진행됩니다.

  • Google Play 결제 시스템을 통해 새 인앱 구매를 처리하는 방법에 관한 자세한 내용은 라이브러리 통합 가이드의 구매 처리를 참고하세요.
  • 정기 결제 구매에 관한 추가 가이드는 정기 결제 관리 가이드의 신규 정기 결제를 참고하세요.

정기 결제 변경사항 처리

개발자 제공 결제 시스템을 사용하는 개발자는 이용자의 선택에 따라 구매를 Google Play 결제 시스템을 통해 처리하거나 externalTransactionId를 사용하여 보고해야 합니다. 이용자 선택 흐름을 통해 처리된 기존 정기 결제는 만료될 때까지 동일한 결제 시스템을 통해 변경할 수 있습니다.

이 섹션에서는 몇 가지 일반적인 정기 결제 변경 시나리오를 처리하는 방법을 설명합니다.

업그레이드 및 다운그레이드 흐름

업그레이드 및 다운그레이드 흐름은 정기 결제가 원래 Google Play 결제 시스템과 개발자 제공 결제 시스템 중 어느 쪽을 통해 구매되었는지에 따라 다르게 처리해야 합니다.

개발자 제공 결제 시스템을 통해 구매한 정기 결제

이용자 선택 후 개발자 제공 결제 시스템을 통해 구매된 정기 결제인 경우, 업그레이드 또는 다운그레이드를 요청하면 이용자 선택 환경을 다시 거치지 않고 개발자 제공 결제 시스템으로 넘어가도록 해야 합니다.

이를 위해서 이용자가 업그레이드 또는 다운그레이드를 요청하는 경우 launchBillingFlow를 호출합니다. 매개변수에 SubscriptionUpdateParams 객체를 지정하는 대신 setOriginalExternalTransactionId를 사용하여 원래 구매의 외부 거래 ID를 제공합니다. 이렇게 해도 업그레이드와 다운그레이드에 대해 원래 구매와 관련된 이용자 선택이 그대로 유지되므로 이용자 선택 화면이 표시되지 않습니다. 이 경우 launchBillingFlow를 호출하면 거래의 새 외부 거래 토큰이 생성됩니다. 이 토큰은 콜백 함수를 통해 가져올 수 있습니다.

Kotlin

// 기존의 개발자 제공 정기 결제의 외부 거래 ID
val externalTransactionId = //... ;

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

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

// 이용자가 개발자 제공 결제 흐름을 선택할 경우,
// AlternativeBillingListener가 호출됨

Java

// 기존의 개발자 제공 정기 결제의 외부 거래 ID
String externalTransactionId = //... ;

BillingFlowParams billingFlowParams =
        BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(
                ImmutableList.of(
                    ProductDetailsParams.newBuilder()
                        // Fetched via queryProductDetailsAsync.
                        .setProductDetails(productDetailsNewPlan)
                        // offerIdToken can be found in
                        // ProductDetails=>SubscriptionOfferDetails
                        .setOfferToken(offerTokenNewPlan)
                    .build()
                )
            )
            .setSubscriptionUpdateParams(
                SubscriptionUpdateParams.newBuilder()
                    .setOriginalExternalTransactionId(externalTransactionId)
                    .build()
            )
            .build();

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

// 이용자가 개발자 제공 결제 흐름을 선택할 경우,
// AlternativeBillingListener가 호출됨

개발자 제공 결제 시스템에서 업그레이드 또는 다운그레이드가 완료되면 새로운 정기 결제 구매를 위해 이전 호출을 통해 획득한 외부 거래 토큰을 사용하여 새 거래를 보고해야 합니다.

Google Play 결제 시스템을 통해 구매한 정기 결제

마찬가지로, 현재 정기 결제를 이용자 선택 후 Google Play 결제 시스템을 통해 구매한 이용자에게는 Google Play 결제 시스템의 업그레이드 또는 다운그레이드 흐름을 표시해야 합니다. 다음 안내에서는 Google Play 결제 시스템을 통해 업그레이드 또는 다운그레이드 구매 흐름을 시작하는 방법을 설명합니다.

  1. 새 요금제에 대해 선택한 혜택의 offerToken을 확인합니다.

    Kotlin

    val offerTokenNewPlan = productDetailsNewPlan
                 .getSubscriptionOfferDetails(selectedOfferIndex)
                 .getOfferToken()
    

    Java

    String offerTokenNewPlan = productDetailsNewPlan
                         .getSubscriptionOfferDetails(selectedOfferIndex)
                         .getOfferToken();
    
  2. 새 구매를 처리하기 위해 Google Play 결제 시스템에 적절한 정보(기존 정기 결제의 구매 토큰 포함)를 전송합니다.

    Kotlin

    val billingFlowParams =
        BillingFlowParams.newBuilder().setProductDetailsParamsList(
            listOf(
                BillingFlowParams.ProductDetailsParams.newBuilder()
                    .setProductDetails(productDetailsNewPlan)
                    .setOfferToken(offerTokenNewPlan)
                    .build()
            )
        )
        .setSubscriptionUpdateParams(
            BillingFlowParams.SubscriptionUpdateParams.newBuilder()
                .setOldPurchaseToken(oldToken)
                .setReplaceProrationMode(BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE)
                .build()
            )
            .build()
    
    BillingClient.launchBillingFlow(activity, billingFlowParams)
    

    Java

    BillingFlowParams billingFlowParams =
            BillingFlowParams.newBuilder()
                .setProductDetailsParamsList(
                    ImmutableList.of(
                        ProductDetailsParams.newBuilder()
                            // Fetched via queryProductDetailsAsync
                            .setProductDetails(productDetailsNewPlan)
                            // offerIdToken can be found in
                            // ProductDetails=>SubscriptionOfferDetails.
                            .setOfferToken(offerTokenNewPlan)
                            .build()
                    )
                )
                .setSubscriptionUpdateParams(
                    SubscriptionUpdateParams.newBuilder()
                        // purchaseToken can be found in
                        // Purchase#getPurchaseToken
                        .setOldPurchaseToken("old_purchase_token")
                        .setReplaceProrationMode(ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE)
                        .build()
                )
                .build();
    
    BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
    

이 구매는 Google Play 결제 시스템에서 진행됩니다. 앱은 구매 결과와 함께 PurchasesUpdatedListener.onPurchaseUpdated 호출을 수신합니다. 구매가 성공하면 onPurchaseUpdated 메서드로 새 구매 정보를 수신하고, 또한 백엔드로는 SUBSCRIPTION_PURCHASED 실시간 개발자 알림이 수신됩니다. 새 구매의 상태를 가져올 때 linkedPurchaseToken 속성이 이전 정기 결제의 구매에 연결되므로 권장사항에 따라 이전 정기 결제 구매를 사용 중지할 수 있습니다.

정기 결제 취소 및 복원

이용자는 언제든지 정기 결제를 취소할 수 있어야 합니다. 이용자가 정기 결제를 취소하는 경우, 지불한 기간이 종료될 때까지 사용 권한 해지가 지연될 수 있습니다. 예를 들어, 이용자가 월간 정기 결제를 기간의 중간을 넘겨서 취소했다면 액세스 권한이 삭제될 때까지 최대 2주 동안 서비스에 계속 액세스할 수 있습니다. 이 기간 동안 정기 결제는 기술적으로 여전히 활성 상태이므로 이용자는 서비스를 이용할 수 있습니다.

이용자가 이 활성 기간 중에 취소를 번복하는 경우도 종종 있습니다. 이 가이드에서는 이를 복원이라고 합니다. 다음 섹션에서는 개발자 제공 결제 API 통합에서 복원 시나리오를 처리하는 방법을 설명합니다.

개발자 제공 결제 시스템을 통해 구매한 정기 결제

취소된 정기 결제의 외부 거래 ID가 있다면 launchBillingFlow를 호출하여 정기 결제를 복원하지 않아도 되므로 이러한 유형의 활성화에서 launchBillingFlow를 사용해서는 안 됩니다. 이용자가 취소된 정기 결제의 활성 기간 중에 정기 결제를 복원하면 이 시점에는 거래가 발생하지 않으며, 현재 주기가 만료되고 다음번 갱신이 발생하는 시점에 갱신을 보고하면 됩니다. 여기에는 이용자가 복원의 일환으로 크레딧이나 특별 갱신 가격 혜택을 받는 경우가 포함됩니다(예: 이용자가 정기 결제를 지속하도록 유도하는 프로모션).

Google Play 결제 시스템을 통해 구매한 정기 결제

일반적으로 이용자는 Google Play 결제 시스템에서 정기 결제를 복원할 수 있습니다. 원래 Google Play 결제 시스템에서 구매된 후 취소된 정기 결제인 경우 이용자는 Google Play의 정기 결제 재신청 기능을 사용하여 정기 결제가 활성 상태인 동안 취소를 번복하도록 선택할 수 있습니다. 이 경우 백엔드로 SUBSCRIPTION_RESTARTED 실시간 개발자 알림이 수신되고, 새 토큰은 발급되지 않습니다. 즉, 정기 결제를 계속 이어가는 데 원래의 토큰이 사용됩니다. Google Play 결제 시스템에서 복원을 관리하는 방법을 알아보려면 정기 결제 관리 가이드의 복원을 참고하세요.

launchBillingFlow를 호출하여 앱에서 Google Play 결제 시스템의 복원을 트리거할 수도 있습니다. 이렇게 하는 방법에 관한 설명은 정기 결제 만료 전 - 앱 내에서를 참고하세요. 원래 구매에서 이용자 선택 흐름을 거친 이용자의 경우(정기 결제를 취소했으나 아직 활성 상태인 경우) 시스템이 자동으로 이용자의 선택사항을 감지하여 구매 항목 복원을 위한 이용자 인터페이스를 표시합니다. Google Play를 통해 정기 결제 재구매를 확인하라는 메시지가 표시되나, 이용자 선택 흐름을 다시 거칠 필요는 없습니다. 이 경우 이용자에게 새 구매 토큰이 발급됩니다. 백엔드로 SUBSCRIPTION_PURCHASED 실시간 개발자 알림이 수신되고, 업그레이드 또는 다운그레이드의 경우와 같이 새 구매 상태의 linkedPurchaseToken 값이 취소된 정기 결제의 기존 구매 토큰으로 설정됩니다.

정기 결제 재신청

취소로 인해, 또는 복원 없이 결제가 거부되어 정기 결제가 완전히 만료된 경우(만료된 계정 보류) 이용자가 사용 권한을 다시 시작하려면 정기 결제를 재신청해야 합니다.

앱을 통해 첫 구매 때와 비슷한 절차로 정기 결제를 재신청할 수도 있습니다. 이 경우 이용자는 어느 결제 시스템을 사용할지 선택할 수 있습니다. 이 경우 이용자 선택 결제 흐름 시작에 설명된 대로 launchBillingFlow를 호출할 수 있습니다.

다음 단계

인앱 통합을 완료했으면 백엔드 통합을 진행할 수 있습니다.