Google Play 결제를 통한 정기 결제 대체 구현

1. 소개

이 Codelab에서는 Google Play Billing Library (PBL)를 사용하여 정기 결제 요금제 변경을 관리하는 방법을 알아봅니다. 백엔드 실시간 개발자 알림 (RTDN)을 처리하는 방법을 알아보면서 다양한 교체 모드가 가격 및 사용자 사용 권한에 미치는 영향을 파악합니다.

시청자층

Android 앱 개발자를 위해 설계된 이 Codelab에서는 정교한 정기 결제 관리 기능을 구현하는 방법을 안내합니다. 이 가이드를 통해 사용자가 다양한 정기 결제 요금제 간에 업그레이드, 다운그레이드 또는 전환할 수 있는 원활한 환경을 제공할 수 있습니다.

학습할 내용…

  • Play Console에서 정기 결제를 만드는 방법
  • 앱의 업그레이드 및 다운그레이드 정책에 맞는 올바른 ReplacementMode (예: WITH_TIME_PRORATIONDEFERRED)를 선택하는 방법
  • 요금제 교체를 위해 Google Play 구매 흐름을 트리거하도록 launchBillingFlow 내에서 BillingFlowParams를 구성하는 방법
  • 실시간 개발자 알림 (RTDN) 및 purchases.subscriptionsv2 API를 사용하여 백엔드에서 이전 액세스 권한을 안전하게 취소하고 새 액세스 권한을 부여하는 방법

필요한 항목

2. 샘플 앱 빌드

이 Codelab에서는 샘플 Android 앱을 사용하여 PBL에서 정기 결제 교체를 구현하는 방법을 보여줍니다. 샘플 앱은 다음 측면을 보여주는 완전한 소스 코드가 있는 완전한 기능을 갖춘 Android 앱으로 설계되었습니다.

  • PBL과 앱 통합
  • 정기 결제 교체 구현

정기 결제 교체 및 PBL에 이미 익숙하다면 샘플 앱을 다운로드하여 사용해 보세요.

다음 데모 동영상은 샘플 앱이 배포되고 실행된 후의 모습과 동작을 보여줍니다.

기본 요건

샘플 앱을 빌드하고 배포하기 전에 다음을 실행하세요.

빌드

Codelab을 따르는 데 필요한 대로 샘플 앱을 빌드하려면 다음 단계를 따르세요.

  1. GitHub에서 샘플 앱을 다운로드합니다.
  2. 샘플 앱의 build.gradle 내에서 applicationId을 업데이트하여 Play Console에 있는 앱의 애플리케이션 ID를 반영합니다.
  3. 샘플 앱을 빌드합니다.
    참고: 이렇게 하면 로컬 테스트를 위해 앱이 성공적으로 빌드됩니다. 하지만 Play Console에서 필수 정기 결제가 아직 생성되지 않았으므로 앱을 실행해도 제품과 가격이 가져와지지 않습니다. 다음 섹션에서는 개발자 콘솔에서 정기 결제를 만드는 방법을 설명합니다.

3. Play Console에서 정기 결제 만들기

Google Play 정기 결제 시스템은 개발자가 유연하게 정기 결제를 생성, 관리, 판매할 수 있도록 지원합니다. Play Console에서 여러 기본 요금제(각각 여러 혜택 포함)가 있는 정기 결제를 구성할 수 있습니다. 정기 결제 혜택에는 다양한 가격 모델과 지원 옵션이 있을 수 있습니다. 이 Codelab에서는 다양한 가격대의 일반적인 구독 제품을 시뮬레이션하는 프리미엄 플랜, Basic Plan, Lite Plan의 세 가지 구독을 만듭니다. 각각 하나의 월간 반복 기본 요금제가 있습니다.

새 구독 생성

새 정기 결제를 만들려면 다음 단계를 따르세요.

  1. Play Console을 열고 정기 결제 페이지 (Play를 통한 수익 창출 > 제품 > 정기 결제)로 이동합니다.
  2. 구독 만들기를 클릭합니다.
  3. 구독 세부정보를 입력합니다.
    • ProductID : 고유한 제품 ID를 입력합니다. premium_plan를 입력합니다.
    • 이름 : 구독의 닉네임을 입력합니다. 예: Premium Plan
  4. 만들기를 클릭합니다.

기본 요금제 만들기

  1. Play Console을 열고 정기 결제 페이지 (Play를 통한 수익 창출 > 제품 > 정기 결제)로 이동합니다.
  2. 기본 요금제를 만들려는 정기 결제 옆에 있는 오른쪽 화살표를 클릭하여 정기 결제 세부정보를 확인합니다.
  3. 기본 요금제 추가를 클릭합니다.
  4. 기본 요금제 ID를 입력합니다. 예: monthly-auto-renewing
  5. 유형을 자동 갱신으로 선택합니다.
  6. 자동 갱신 기본 요금제의 경우 다음을 설정합니다.
    • 결제 기간: 월간
    • 유예 기간: 7일
    • 요금제 및 혜택 변경: 청구일에 청구
    • 재구독: 허용
  7. 가격 및 구매 가능 여부 섹션에서 가격 설정을 클릭하여 기본 요금제의 가격을 설정합니다.
  8. 모든 국가 및 지역을 선택한 다음 가격 설정을 클릭합니다.
  9. 이 기본 요금제의 가격을 $10로 설정하고 업데이트를 클릭합니다.
  10. 기본 요금제 가격을 설정한 후 오른쪽 하단에서 저장을 클릭한 다음 활성화를 클릭합니다.

샘플 앱의 정기 결제 만들기

이 Codelab에서는 다음 구성으로 구독 두 개를 추가로 만듭니다.

  • 기본 요금제
    • 제품 ID: basic_plan
    • 이름: Basic Plan
    • 기본 요금제 ID: monthly-auto-renewing
    • 가격: 5달러
  • Lite 요금제
    • 제품 ID: lite_plan
    • 이름: Lite Plan
    • 기본 요금제 ID: monthly-auto-renewing
    • 가격: 3달러

샘플 앱은 이러한 제품 ID와 기본 요금제 ID를 사용하도록 구성되어 있습니다. 구성을 다르게 하여 여러 구독을 만들 수 있습니다. 이 경우 만든 제품 ID를 사용하도록 샘플 앱을 수정해야 합니다.

정기 결제 생성 동영상

다음 동영상에서는 앞에서 설명한 Play 개발자 콘솔에서 정기 결제를 만드는 단계를 보여줍니다.

4. 정기 결제 교체

PBL과 통합하는 개발자는 기존 정기 결제 사용자에게 요구에 더 잘 부합하도록 정기 결제 요금제를 변경할 수 있는 다양한 옵션을 제공할 수 있습니다.

  • 기본프리미엄 정기 결제와 같은 여러 정기 결제 등급을 판매하는 경우 사용자가 다른 정기 결제의 기본 요금제나 혜택을 구매하여 등급을 전환하도록 허용할 수 있습니다.
  • 사용자가 월간 요금제에서 연간 요금제로 전환하는 등 현재 결제 기간을 변경하도록 허용할 수 있습니다.
  • 사용자가 자동 갱신 요금제와 선불 요금제 간에 전환하도록 허용할 수도 있습니다.

사용자가 정기 결제를 업그레이드하거나 다운그레이드, 변경하기로 하는 경우 개발자는 현재 결제 기간의 일할 계산된 금액이 적용되는 방식과 사용자의 사용 권한이 변경되는 시기를 결정하는 대체 모드를 지정합니다.

Play 결제 라이브러리는 이 동작을 제어하기 위한 여러 ReplacementMode 옵션을 제공합니다.

사용 가능한 대체 모드

  • WITH_TIME_PRORATION: 정기 결제 항목이 즉시 업그레이드 또는 다운그레이드됩니다. 남은 기간은 가격 차이를 기반으로 조정되며 다음 결제일을 업데이트하여 새 정기 결제에 적립됩니다. 이는 기본 동작입니다.
  • CHARGE_PRORATED_PRICE: 정기 결제 항목이 즉시 업그레이드되며 결제 주기는 동일하게 유지됩니다. 남은 기간의 가격 차이는 사용자에게 청구됩니다.
  • CHARGE_FULL_PRICE: 정기 결제 항목이 즉시 업그레이드되거나 다운그레이드되고 새 사용 권한과 관련된 전체 가격이 사용자에게 즉시 청구됩니다. 이전 정기 결제의 남은 값은 동일한 사용 권한에서는 이월되고, 다른 사용 권한으로 전환한 경우 시간에 따라 비례 배분됩니다.
  • WITHOUT_PRORATION: 정기 결제 항목이 즉시 업그레이드 또는 다운그레이드되며 정기 결제가 갱신되면 새로운 가격이 청구됩니다. 결제 주기는 동일하게 유지됩니다.
  • DEFERRED: 정기 결제가 갱신될 때만 정기 결제 항목이 업그레이드 또는 다운그레이드됩니다.

5. WITH_TIME_PRORATION

이 교체 모드에서는 정기 결제 항목이 즉시 업그레이드 또는 다운그레이드됩니다. 남은 기간은 가격 차이를 기반으로 조정되며 다음 결제일을 앞당겨 새 정기 결제에 적립됩니다. 이것이 기본 동작입니다.

예시 시나리오

사용자가 월별 결제 주기의 중간인 4월 15일에 Basic 요금제 (월 $4.99)에서 Premium 요금제 (월 $9.99)로 전환합니다.

이 시나리오에서는 다음과 같습니다.

  • 사용자가 Premium 요금제를 즉시 이용할 수 있습니다.
  • Google Play에서 일할 계산 기간을 자동으로 계산합니다. 예를 들어 Play에서 남은 Basic 요금제 15일이 Premium 요금제 7일과 같다고 계산하는 경우 다음 청구일은 4월 21일로 앞당겨집니다.
  • 사용자가 즉시 결제할 필요는 없습니다.

코드 스니펫

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

WITH_TIME_PRORATION으로 업그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Premium 요금제로 전환합니다.

사용자의 사용 권한이 Premium 요금제로 즉시 업그레이드됩니다. 사용자가 즉시 결제해야 하는 금액은 0달러입니다. Basic 요금제의 잔액은 Premium 요금제의 기간으로 일할 계산되어 다음 갱신일이 앞당겨집니다. 사용자에게는 새로 조정된 결제일에 갱신 금액인 9.99달러가 청구됩니다.

WITH_TIME_PRORATION으로 다운그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Lite 요금제로 전환합니다.

사용자의 사용 권한이 즉시 Lite 요금제로 다운그레이드됩니다. 즉시 결제해야 하는 금액은 0달러입니다. Basic 요금제의 잔액이 Lite 요금제의 기간으로 일할 계산되어 다음 갱신일이 크게 연장됩니다. 사용자에게는 새로 조정된 결제일에 갱신 금액인 2.99달러가 청구됩니다.

결론

이 섹션에서는 WITH_TIME_PRORATION가 가격 차이에 따라 다음 갱신까지의 시간을 조정하여 즉시 청구하지 않고 사용자 권한을 수정하는 방법을 알아봤습니다. 사용자를 업그레이드하거나 다운그레이드하는 효과적인 기본 전략입니다.

6. CHARGE_PRORATED_PRICE

이 교체 모드에서는 정기 결제 항목이 즉시 업그레이드되며 결제 주기는 동일하게 유지됩니다. 남은 기간의 가격 차이는 사용자에게 청구됩니다.

참고: 이 옵션은 시간 단위당 가격이 상승하는 정기 결제 항목 업그레이드에만 사용할 수 있습니다.

예시 시나리오

기본 요금제 (월 $4.99)를 이용하는 사용자가 4월 20일에 Premium 요금제 (월 $9.99)로 업그레이드하기로 결정했으며, 월별 결제 주기가 약 10일 남았습니다.

이 시나리오에서는 다음과 같습니다.

  • 사용자가 Premium 요금제를 즉시 이용할 수 있습니다.
  • 사용자에게 현재 결제 주기의 남은 10일에 대한 일할 계산된 차액이 즉시 청구됩니다. 이는 약 2.99달러로, Premium 요금제 10일치에 해당합니다.
  • 사용자의 청구일은 변경되지 않습니다.

코드 스니펫

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

CHARGE_PRORATED_PRICE로 업그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Premium 요금제로 전환합니다.

사용자가 즉시 Premium 요금제로 업그레이드되며 원래 갱신일은 유지됩니다. 즉시 지불해야 하는 금액은 현재 주기의 남은 일수에 대한 Premium 요금과 Basic 요금의 일할 계산된 차액입니다. 갱신일에 사용자에게 Premium 갱신 금액인 $9.99가 청구됩니다.

CHARGE_PRORATED_PRICE로 다운그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Lite 요금제로 전환합니다.

이 교체 모드는 시간 단위당 가격이 인상되는 정기 결제 항목 업그레이드에만 사용할 수 있으므로 다운그레이드 중에 오류가 발생합니다. 결제 절차가 실패하고 다운그레이드에는 비례 배분 모드가 지원되지 않는다는 오류가 사용자에게 표시됩니다.

결론

이 섹션에서는 CHARGE_PRORATED_PRICE를 통해 결제 주기를 그대로 유지하면서 남은 결제 기간에 대한 정확한 가격 차이를 사용자에게 청구하여 즉시 업그레이드할 수 있는 방법을 설명했습니다. 이는 사용자가 결제일을 변경하지 않고 더 비싼 등급으로 업그레이드하려는 경우에 유용합니다.

7. CHARGE_FULL_PRICE

이 교체 모드에서는 정기 결제 항목이 즉시 업그레이드되거나 다운그레이드되고 새 사용 권한과 관련된 전체 가격이 사용자에게 즉시 청구됩니다. 이전 정기 결제의 남은 값은 동일한 사용 권한에서는 이월되고, 다른 사용 권한으로 전환한 경우 시간에 따라 비례 배분됩니다.

예시 시나리오

사용자가 Basic 요금제 (4월 1일부터 월 $4.99)를 사용하고 있습니다. 4월 20일에 사용자가 Premium 요금제 (월 $9.99)로 전환하고 싶어 합니다.

이 시나리오에서는 다음과 같습니다.

  • 사용자에게 Premium 요금제 ($9.99)의 전체 가격이 즉시 청구됩니다.
  • Basic 요금제의 남은 값 (예: 10일)이 Premium 요금제의 상응하는 시간으로 변환됩니다. 이 예시에서 10일의 Basic은 5일의 Premium과 같습니다.
  • 이 일할 계산된 시간이 포함되도록 사용자의 다음 갱신일이 조정됩니다. 따라서 갱신일은 5월 25일 (4월 20일 + 1개월 + 5일)이 됩니다.
  • 이후 갱신은 5월 25일부터 매월 진행됩니다.

코드 스니펫

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

CHARGE_FULL_PRICE로 업그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Premium 요금제로 전환합니다.

사용자가 Premium 요금제로 즉시 업그레이드됩니다. 즉시 결제해야 하는 금액은 Premium 요금제의 정가인 $9.99입니다. Basic 요금제에서 남은 금액은 새로운 Premium 요금제의 기간으로 변환되어 첫 번째 갱신일이 약간 연장됩니다. 이후 갱신 금액은 주기당 9.99달러입니다.

CHARGE_FULL_PRICE로 다운그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Lite 요금제로 전환합니다.

사용자가 즉시 Lite 요금제로 다운그레이드되고 새 결제 주기가 시작됩니다. 즉시 결제해야 하는 금액은 목표 가격인 $2.99입니다. Basic 요금제의 미사용 부분이 새 Lite 요금제의 기간으로 비례 배분되어 첫 번째 갱신일이 연장됩니다. 이후 갱신 금액은 주기당 2.99달러입니다.

결론

이 섹션에서는 CHARGE_FULL_PRICE가 전환 당일에 사용자에게 전액을 청구하여 새 결제 주기를 즉시 시작하는 방법을 설명했습니다. 이전 요금제에서 남은 잔액은 다음 갱신일까지 균등하게 적용됩니다.

8. WITHOUT_PRORATION

이 교체 모드에서는 정기 결제 항목이 즉시 업그레이드 또는 다운그레이드되며 정기 결제가 갱신되면 새로운 가격이 청구됩니다.

예시 시나리오

사용자가 Basic 요금제 (4월 1일부터 월 $4.99)를 사용하고 있습니다. 4월 20일에 사용자가 Premium 요금제 (월 $9.99)로 전환하고 싶어 합니다.

이 시나리오에서는 다음과 같습니다.

  • 사용자가 Premium 요금제를 즉시 이용할 수 있습니다.
  • 사용자는 다음 정기 결제 갱신일 (5월 1일)까지 인상된 $9.99 가격을 지불하지 않아도 됩니다.

코드 스니펫

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

WITHOUT_PRORATION으로 업그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Premium 요금제로 전환합니다.

사용자는 기존 갱신일을 유지하면서 즉시 Premium 요금제로 업그레이드됩니다. 즉시 결제해야 하는 금액은 0달러입니다. 사용자는 다음 결제일에 새 갱신 금액인 $9.99로 전환되기 전까지 지정된 주기의 남은 기간 동안 Premium 요금제를 이용할 수 있습니다.

WITHOUT_PRORATION으로 다운그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Lite 요금제로 전환합니다.

사용자가 즉시 Lite 요금제로 다운그레이드되어 요금을 지불한 Basic 기능을 이용할 수 없게 됩니다. 즉시 결제해야 하는 금액은 0달러입니다. 결제 주기는 수정되지 않으며, 사용자는 다음 예정된 갱신일에 새로운 인하된 요금인 $2.99를 지불합니다.

결론

이 섹션에서는 결제 주기를 그대로 유지하면서 결제 비용 없이 WITHOUT_PRORATION가 사용자의 권한을 즉시 교체하는 방법을 보여주었습니다.

9. DEFERRED

이 교체 모드에서는 정기 결제가 갱신될 때만 정기 결제 항목이 업그레이드 또는 다운그레이드되지만 새로운 구매는 즉시 발행됩니다. 기존 항목은 갱신 불가능으로 설정되어 현재 결제 주기가 끝나면 만료되지만 새로 요청된 사용 권한은 바로 시작됩니다.

예시 시나리오

사용자가 Basic 요금제 (4월 1일부터 월 $4.99)를 사용하고 있습니다. 4월 20일에 사용자가 Premium 요금제 (월 $9.99)로 전환하고 싶어 합니다.

이 시나리오에서는 다음과 같습니다.

  • 사용자에게 즉시 청구되는 금액은 없습니다.
  • 사용자는 현재 결제 주기 (4월 30일)가 끝날 때까지 Basic 기능을 계속 이용할 수 있습니다.
  • 다음 갱신일 (5월 1일)에 구독 요금제가 Premium으로 자동 업그레이드됩니다.

코드 스니펫

// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.DEFERRED;

SubscriptionProductReplacementParams subscriptionProductReplacementParams =
    SubscriptionProductReplacementParams.newBuilder()
        .setOldProductId(oldProductId)
        .setReplacementMode(replacementMode)
        .build();

ProductDetailsParams productDetailsParams =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
        .setOfferToken(offerToken)
        .build();

List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setProductDetailsParamsList(productDetailsParamsList)
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
        .build();

billingClient.launchBillingFlow(activity, billingFlowParams);

지연으로 업그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.DEFERRED로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Premium 요금제로 전환합니다.

사용자는 현재 결제 주기가 끝날 때까지 Basic 요금제를 유지합니다. 즉시 결제해야 하는 금액은 0달러입니다. 갱신일에 권한이 Premium 요금제로 업그레이드되고 새로운 갱신 금액인 $9.99가 청구됩니다.

DEFERRED로 다운그레이드

이 시나리오를 시뮬레이션하려면 다음 단계를 따르세요.

  • 샘플 앱의 MainActivity에서 코드 스니펫의 replacementModeSubscriptionProductReplacementParams.ReplacementMode.DEFERRED로 업데이트합니다.
  • 애플리케이션을 다시 빌드하고 실행합니다.
  • Google Play 스토어에서 기존 정기 결제를 취소하고 만료되도록 합니다 (있는 경우).
  • Basic 요금제를 구매합니다.
  • Lite 요금제로 전환합니다.

사용자는 현재 결제 주기가 끝날 때까지 Basic 요금제를 유지합니다. 즉시 결제해야 하는 금액은 0달러입니다. 갱신일에 권한이 Lite 요금제로 업그레이드되고 새로운 갱신 금액인 $2.99가 청구됩니다.

결론

이 섹션에서는 DEFERRED 교체 모드가 활성 사용자의 유료 기간이 끝날 때까지 업그레이드 또는 다운그레이드를 연기하는 방법을 설명했습니다. 따라서 이미 구매한 기능을 잃지 않도록 다운그레이드하는 데 특히 적합합니다.

10. 백엔드 및 클라이언트 측 처리

사용자가 정기 결제 교체를 성공적으로 트리거한 후 서비스 중단이나 이중 청구와 같은 문제를 방지하기 위해 앱과 백엔드 모두 변경사항을 올바르게 처리해야 합니다.

예시 시나리오

  • 사용자가 Basic 월간 요금제 (product_id basic_plan, purchase_token basic_purchase_token_123)를 사용하고 있습니다.
  • 사용자가 즉시 교체 모드 (WITHOUT_PRORATION, WITH_TIME_PRORATION, CHARGE_PRORATED_PRICE, CHARGE_FULL_PRICE 중 하나)를 사용하여 Premium 요금제로 전환합니다.
  • 구독 전환이 완료되면 Google에서는 이를 신규 구독으로 취급하고 프리미엄 플랜 (product_id premium_plan 및 purchase_token premium_purchase_token_123)에 대한 새 구매 토큰을 생성합니다.

클라이언트 측 처리

onPurchasesUpdated

교체 구매가 완료되면 PurchasesUpdatedListener이 트리거됩니다. 전환되었지만 Google Play에서는 Premium 요금제를 새 구매로 취급합니다.

앱은 premium_purchase_token_123 구매 토큰과 product_id premium_plan가 포함된 Purchase 객체를 수신합니다. 이 경우를 신규 구독자와 똑같이 취급해야 합니다. 토큰을 확인하고 액세스 권한을 부여할 준비를 해야 합니다.

@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
        for (Purchase purchase : purchases) {
            // purchase.getPurchaseToken() = premium_purchase_token_123
            // purchase.getProducts() will contain premium_plan
            // Verify the purchase and grant entitlement
            handleNewPurchase(purchase);
        }
    }
}

queryPurchasesAsync

queryPurchasesAsync는 앱에서 구매한 활성 정기 결제만 반환합니다. 이 메서드를 사용하여 사용자에게 표시할 권한을 결정해야 합니다. 즉시 교체의 경우 queryPurchasesAsync()는 이전 BASIC 구매 토큰의 반환을 중지하고 이제 새 PREMIUM 구매 토큰만 반환합니다.

앱이 다시 시작되거나 구매가 완료될 때마다 이 메서드를 호출합니다. Premium 토큰이 있으면 즉시 Premium 기능을 부여하고 Basic 기능을 삭제합니다.

백엔드 처리 (RTDN)

교체가 발생하면 Google Play에서 구성된 Pub/Sub 주제로 실시간 개발자 알림 (RTDN)을 전송합니다.

  • 즉시 교체의 경우 Google에서 새 구매 토큰이 포함된 SUBSCRIPTION_PURCHASED RTDN을 전송합니다.샘플 RTDN 페이로드
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • 서버가 RTDN에서 새 구매 토큰을 수신하면 새 구매 토큰으로 purchases.subscriptionsV2 API를 호출하여 구매 세부정보를 가져옵니다. API 응답에는 구매 토큰이 신규 정기 결제 구매를 참조하는지 아니면 정기 결제 교체를 참조하는지 확인하는 데 사용되는 linkedPurchaseToken 필드가 포함됩니다.
  • 정기 결제 교체의 경우 linkedPurchaseToken는 이전 정기 결제의 구매 토큰을 나타냅니다. 이 시나리오에서는 basic_purchase_token_123입니다.샘플 GET purchases.subscriptionsV2 응답
    curl \
    'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/<application_id>/purchases/subscriptionsv2/tokens/premium_purchase_token_123' \
    --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
    --header 'Accept: application/json'
    
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "...",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123", // The purchase token of the subscription that was replaced (Basic Plan in this case)
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // productID of the new subscription (Premium Plan in this case)
          "expiryTime": "....",
          "autoRenewingPlan": {...},
          "offerDetails": {
            "basePlanId": "monthly-auto-renewing" // base plan ID of the new subscription
          },
          "itemReplacement": { // Details about the subscription replacement
            "productId": "subscription_basic", // productID of the old subscription (Basic Plan in this case)
            "replacementMode": "CHARGE_PRORATED_PRICE", // Replacement strategy used for this subscription change
            "basePlanId": "monthly-auto-renewing" // base plan ID of the old subscription 
          },
          "offerPhase": {...}
        }
      ],
      "etag": "<etag_value>"
    }
    
  • 새 Premium 구매를 확인해야 합니다. 이 작업은 앱 내에서 또는 백엔드에서 실행할 수 있습니다. 3일 이내에 구매를 확인하지 않으면 환불되고 권한이 취소됩니다. 구매 처리 및 확인에 관한 자세한 내용은 개발자 문서를 참고하세요.

결론

이 섹션에서는 클라이언트와 백엔드 모두에서 즉각적인 정기 결제 교체를 처리하는 단계를 다뤘습니다. Google Play에서는 새 요금제를 완전히 새로운 구매로 취급하여 새 구매 토큰을 발급합니다. 클라이언트에서 PurchasesUpdatedListener를 사용하여 이 신규 구매를 처리하고 queryPurchasesAsync의 응답에 따라 권한을 업데이트해야 합니다. 백엔드에서 새 토큰의 SUBSCRIPTION_PURCHASED RTDN을 수신 대기하고 purchases.subscriptionsv2 API를 사용하여 이전 정기 결제의 linkedPurchaseToken를 식별하고 새 사용 권한을 부여하는 동안 이전 토큰과 연결된 액세스 권한을 즉시 취소해야 합니다. 항상 새로운 구매를 확인해야 합니다.

11. 지연된 교체 처리

즉시 교체 모드와 달리 ReplacementMode.DEFERRED는 현재 결제 주기가 끝날 때까지 정기 결제 변경 및 사용 권한 업데이트를 연기합니다. 지연된 교체를 처리하려면 사용자가 적절한 시점에 올바른 권한을 받을 수 있도록 특정 로직이 필요합니다.

예시 시나리오

  • 사용자에게 4월 15일에 갱신되는 Basic 월간 요금제 (product_id basic_plan, purchase_token basic_purchase_token_123)가 있습니다.
  • 4월 1일에 사용자는 ReplacementMode.DEFERRED를 사용하여 Premium 요금제로 전환하기로 결정합니다.
  • Google은 Premium 요금제 (product_id premium_plan 및 purchase_token premium_purchase_123)에 대한 구매 토큰을 즉시 생성하지만 사용자에게 청구되는 금액과 권한은 4월 15일로 예정되어 있습니다.

지연된 교체 처리

1. 구매 흐름이 성공한 직후(앱)

  • PurchasesUpdatedListener는 구매 흐름이 완료된 후 호출됩니다. 앱은 새 구매 토큰 premium_purchase_token_123이 포함된 Purchase 객체를 수신하지만, 사용자가 Basic 요금제에 대한 사용 권한만 있으므로 product_id는 여전히 이전 basic_plan를 참조합니다. 이를 신규 구매와 똑같이 취급하고 토큰을 확인해야 합니다.
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
            for (Purchase purchase : purchases) {
                // purchase.getPurchaseToken() = premium_purchase_token_123
                // purchase.getProducts() will contain basic_plan
                // Verify and acknowledge the purchase
                handleNewPurchase(purchase);
            }
        }
    }
    
  • queryPurchasesAsync는 새 구매 토큰 (premium_purchase_token_123)과 이와 연결된 원래 사용 권한 (basic_plan)이 있는 구매를 즉시 반환합니다. 이를 통해 사용자에게 Basic 요금제의 권한을 계속 부여할 수 있습니다.

2. 구매 흐름이 성공한 직후(백엔드)

  • SUBSCRIPTION_PURCHASED RTDN은 새 구매 토큰 (premium_purchase_token_123)의 구매 흐름 직후 전송됩니다.샘플 RTDN 페이로드
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":4, // SUBSCRIPTION_PURCHASED
        "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription
      }
    }
    
  • 새 구매 토큰으로 GET purchases.subscriptionsv2를 호출하여 구매 세부정보를 가져옵니다. 응답에 2개의 항목이 포함됩니다.
    • 하나는 이전 정기 결제 (기본 요금제)를 나타내며 expiryTime이 미래입니다. 이전 구독은 갱신되지 않으며 새 구독 (프리미엄 요금제)이 포함된 deferredItemReplacement이 있습니다. 이는 만료 시 이전 권한의 교체가 대기 중임을 나타냅니다.
    • 새로 구매한 정기 결제를 나타내는 항목 'expiryTime'이 설정되어 있지 않습니다.
    샘플 API 응답
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "startTime": "2026-05-07T15:50:11.383Z",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>",
      "linkedPurchaseToken": "basic_purchase_token_123",
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // Premium Plan has no expiry time
          "autoRenewingPlan": {...},
          "offerDetails": {...},
          "itemReplacement": {. // Subscription replacement details
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {}
        },
        {
          "productId": "basic_plan", // Subscription to be replaced
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time in the future
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "deferredItemReplacement": { // identifier indicating this subscription will be replaced upon renewal
            "productId": "subscription_premium"
          },
          "latestSuccessfulOrderId": "GPA.<order_id>",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    
  • 새 구매 토큰을 확인해야 합니다. 이 작업은 앱 내에서 또는 백엔드에서 실행할 수 있습니다. 구매 처리 및 확인에 관한 자세한 내용은 개발자 문서를 참고하세요.
  • 이전 구매 토큰 (basic_purchase_token_123)에 대해 SUBSCRIPTION_EXPIRED RTDN이 전송됩니다. 샘플 RTDN 페이로드
    {
      "version":"1.0",
      "packageName":"com.google.play.billing.samples.subscriptions",
      "eventTimeMillis":"...",
      "subscriptionNotification":
      {
        "version":"1.0",
        "notificationType":13, // SUBSCRIPTION_EXPIRED
        "purchaseToken":"basic_purchase_token_123" //purchase token for the old subscription
      }
    }
    
  • 이전 구매 토큰으로 GET purchases.subscriptionsv2 API를 호출하면 만료된 것으로 표시됩니다 (SUBSCRIPTION_STATE_EXPIRED). 이전 요금제의 사용 권한이 남은 기간 동안 새 구매로 이전됩니다.

3. 교체일 - 구매 흐름 후 첫 번째 갱신 (앱)

  • queryPurchasesAsync는 새 구매 토큰 (premium_purchase_token_123)과 이와 연결된 새 정기 결제 (premium_plan)가 있는 구매를 반환합니다.
  • 구매 흐름에 성공했을 때 이미 새로운 구매가 처리되었을 것이므로 사용자에게 올바른 정기 결제에 대한 액세스 권한이 부여되었는지 확인하는 것 외에 특별한 조치를 취하지 않아도 됩니다.

4. 대체 날짜 - 구매 흐름 후 첫 번째 갱신 (백엔드)

  • ReplacementMode.DEFERRED를 사용하면 첫 번째 갱신 시 SUBSCRIPTION_RENEWED RTDN을 처리하는 다른 갱신의 표준 동작을 따릅니다. 이 경우 대체를 위한 특별한 로직이 필요하지 않습니다.
  • 새 구매 토큰으로 GET purchases.subscriptionsv2를 호출하여 구매 세부정보를 가져옵니다. 응답에 2개의 항목이 포함됩니다.
    • 하나는 이전 정기 결제 (기본 요금제)를 나타내며 expiryTime가 과거입니다. 이전 정기 결제에는 더 이상 deferredItemReplacement 필드의 값이 설정되지 않습니다.
    • 하나는 향후 expiryTime이 있고 autoRenewEnabled 필드가 true로 설정된 새 정기 결제를 나타냅니다.
    샘플 API 응답
    {
      "kind": "androidpublisher#subscriptionPurchaseV2",
      "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
      "latestOrderId": "GPA.<order_id>..0",
      "linkedPurchaseToken": "basic_purchase_token_123", // purchase token of the old subscription
      "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
      "lineItems": [
        {
          "productId": "premium_plan", // New subscription
          "expiryTime": "2026-05-07T16:00:09.437Z", // Expiry time set in the future
          "autoRenewingPlan": {
            "autoRenewEnabled": true, // Auto Renewing Flag set to True
            "recurringPrice": {...}
          },
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {. // Details of the subscription replacement
            "productId": "basic_plan",
            "replacementMode": "DEFERRED",
            "basePlanId": "monthly-auto-renewing"
          },
          "offerPhase": {...}
        },
        {
          "productId": "basic_plan", // Old subscription, Does not contains the deferredItemReplacement field
          "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time set in the past
          "autoRenewingPlan": {},
          "offerDetails": {...},
          "latestSuccessfulOrderId": "GPA.<order_id>..0",
          "itemReplacement": {...},
        }
      ],
      "etag": "<etag>"
    }
    

결론

이 섹션에서는 ReplacementMode.DEFERRED에 필요한 고유한 처리를 자세히 설명했습니다. 즉시 모드와 달리 사용 권한 변경은 현재 결제 주기가 끝날 때만 발생한다는 것을 배웠습니다. 이 섹션에서는 앱과 백엔드 모두가 초기 구매를 올바르게 처리하고, 새 토큰을 확인하고, 이전 정기 결제가 만료되고 새 정기 결제가 활성화될 때 사용 권한 전환을 관리하는 데 필요한 단계를 다루었습니다.

12. 정기 결제 교체 Playground

샘플 앱의 교체 Playground 기능을 사용하면 Google Play Console 계정에 구성된 정기 결제 제품의 정기 결제 업그레이드 및 다운그레이드를 테스트할 수 있습니다. 이 섹션에서는 대체 플레이그라운드 기능을 사용하는 방법을 설명합니다.

설정

대체 Playground 기능을 사용하려면 다음을 확인하세요.

  • build.gradle 파일의 packageId이 Google Play Console에 구성된 애플리케이션과 일치합니다.
  • 테스트 사용자 계정이 Google Play Console에 라이선스 테스터로 등록되어 있습니다. 라이선스 테스트에 대해 자세히 알아보려면 앱의 결제 구현 테스트를 참고하세요.

정기 결제 교체 Playground

샘플 앱에는 정기 결제 변경사항을 시뮬레이션할 수 있는 교체 Playground 탭이 포함되어 있습니다. Play Console에 정의된 정기 결제를 쿼리하고 다양한 대체 모드를 사용하여 정기 결제 간 전환을 테스트할 수 있습니다. 이 플레이그라운드를 사용하면 다양한 모드가 정기 결제의 결제 주기와 권한에 미치는 영향을 파악하여 비즈니스 요구사항에 가장 적합한 옵션을 결정할 수 있습니다.

플레이그라운드를 사용하여 대체 항목을 시뮬레이션하려면 다음 단계를 따르세요.

  1. Playground 탭으로 이동합니다.
  2. 구독 중인 경우: 강조 표시됩니다. 대체될 정기 결제입니다.

Playground 홈

  1. 활성 상태인 구독이 없는 경우: 먼저 구독을 구매해야 합니다.
    • 플레이그라운드에는 이 Codelab을 위해 생성된 Basic, Premium, Lite 요금제가 기본적으로 표시됩니다.
    • Play Console에 구성된 다른 요금제로 테스트하려면 맞춤 요금제 추가를 클릭하고 productIdbasePlanId로 검색합니다.
    • 선택한 구독을 구매합니다.
    • 이제 사용자가 새로 구매한 활성 정기 결제가 표시됩니다. 맞춤 구독 추가
  2. 사용자가 전환하려는 대상 구독을 선택합니다.
  3. 전환의 대체 모드를 선택합니다.

교체 모드 선택

  1. 교체 테스트 버튼을 클릭하여 정기 결제 교체를 시뮬레이션합니다.
  2. 정기 결제 대체에 대한 계산된 세부정보 (예: 즉시 청구 및 결제 주기 조정)가 포함된 Google Play 결제 하단 시트가 표시됩니다.

정기 결제 교체 청구 장바구니

  1. 거래를 완료합니다.
  2. Play 스토어 앱 내의 구독 관리 페이지로 이동하여 활성 상태인 구독 변경사항과 업데이트된 갱신일 및 가격에 관한 세부정보를 확인하세요.

Play 스토어 정기 결제 관리

13. 다음 단계

참조 문서

14. 축하합니다

축하합니다. 다양한 일할 계산 모드를 사용하여 정기 결제 대체 구현을 완료하고 요금제 전환을 위한 백엔드 처리를 구성했습니다.

학습한 내용

  • 특정 대체 모드로 SubscriptionProductReplacementParams를 구성하는 방법
  • 즉시 업그레이드와 지연된 다운그레이드의 차이점
  • RTDN을 사용하여 linkedPurchaseToken로 이전 정기 결제 토큰을 폐기하는 방법

설문조사

이 Codelab에 대한 의견을 보내주시면 감사하겠습니다. 잠시 시간을 내어 설문조사에 참여해 주세요.