정기 결제 정보

이 주제에서는 갱신 및 만료와 같은 정기 결제 수명 주기 이벤트를 처리하는 방법을 설명합니다. 또한 프로모션을 제공하고 사용자가 자신의 정기 결제를 관리하도록 허용하는 등의 추가 정기 결제 기능도 설명합니다.

앱의 정기 결제 제품을 구성하지 않았다면 제품 생성 및 구성을 참고하세요.

정기 결제 개요

정기 결제는 지정된 기간 동안 사용자가 액세스할 수 있는 일련의 혜택을 나타냅니다. 예를 들어 정기 결제로 사용자는 음악 스트리밍 서비스에 액세스할 수 있습니다.

동일한 앱 내에서 다양한 혜택 또는 한 가지 혜택의 다양한 등급(예: '실버' 등급, '골드' 등급)을 나타내는 여러 개의 정기 결제를 만들 수 있습니다.

기본 요금제혜택을 통해 동일한 정기 결제 제품의 여러 구성을 만들 수 있습니다. 예를 들어 앱을 정기 결제한 적이 없는 사용자를 위한 신규 할인 혜택을 만들 수 있습니다. 마찬가지로 이미 정기 결제 중인 사용자를 위한 업그레이드 혜택을 만들 수도 있습니다.

정기 결제 제품, 기본 요금제, 혜택에 관한 자세한 내용은 Play Console 고객센터의 문서를 참고하세요.

선불 요금제 통합

선불 요금제는 만료 시 자동으로 갱신되지 않습니다. 서비스 중단 없이 정기 결제 사용 권한을 연장하려면 사용자가 동일한 정기 결제의 선불 요금제를 충전해야 합니다.

충전의 경우 원래 구매에서와 마찬가지로 결제 흐름을 시작합니다. 구매가 충전이라고 표시할 필요는 없습니다.

선불 요금제 충전은 항상 CHARGE_FULL_PRICE 대체 모드를 사용하므로 이 모드를 명시적으로 설정하지 않아도 됩니다. 사용자에게 전체 결제 기간이 즉시 청구되며 충전에 지정된 기간까지 사용 권한이 연장됩니다.

충전이 완료되면 Purchase 결과 객체의 다음 필드가 최근에 충전된 구매를 반영하도록 업데이트됩니다.

  • 주문 ID
  • 구매 시간
  • 서명
  • 구매 토큰
  • 확인됨

다음 Purchase 필드에는 항상 원래 구매에 있는 것과 동일한 데이터가 포함됩니다.

  • 패키지 이름
  • 구매 상태
  • 제품
  • 자동 갱신

선불 구매 확인

자동 갱신 정기 결제와 마찬가지로 구매 후에는 선불 요금제를 확인해야 합니다. 최초 구매와 충전을 모두 확인해야 합니다. 자세한 내용은 구매 처리를 참고하세요.

선불 요금제의 기간이 짧을 수 있으므로 가능한 한 빨리 구매를 확인하는 것이 중요합니다.

기간이 1주 이상인 선불 요금제는 3일 이내에 확인해야 합니다.

기간이 1주일 미만인 선불 요금제는 요금제 기간의 절반 이내에 확인해야 합니다. 예를 들어 개발자는 1.5일 이내에 3일 선불 요금제를 확인해야 합니다.

딥 링크를 사용하여 사용자가 정기 결제를 관리하도록 허용

사용자가 정기 결제를 관리할 수 있는 링크를 앱의 설정 또는 환경설정 화면에 포함해야 하며 이는 앱의 자연스러운 디자인과 분위기에 통합할 수 있습니다.

만료되지 않은 정기 결제의 경우 앱에서 Google Play 정기 결제 센터로 연결되는 딥 링크를 포함할 수 있습니다. 이는 정기 결제 리소스subscriptionState 필드를 사용하여 확인할 수 있습니다. 이를 기반으로 Play 스토어 정기 결제 센터로 딥 링크를 설정할 수 있는 몇 가지 방법이 있습니다.

다음 URL을 사용하여 모든 정기 결제를 표시하는 페이지로 사용자를 안내합니다(그림 1, 2 참고).

https://play.google.com/store/account/subscriptions
Play 스토어 정기 결제 화면에는 사용자의 Google Play 결제를 사용하는 모든 정기 결제의 상태가 표시됩니다.
그림 1. Play 스토어 정기 결제 화면에는 사용자의 Google Play 결제를 사용하는 모든 정기 결제의 상태가 표시됩니다.


추가 세부정보를 보려면 정기 결제를 탭합니다.
그림 2. 추가 세부정보를 보려면 정기 결제를 탭합니다.

이 딥 링크는 사용자가 Play 스토어 정기 결제 센터에서 취소된 정기 결제를 복원하는 데 유용할 수 있습니다.

만료되지 않은 정기 결제의 관리 페이지에 직접 연결하려면 구매한 정기 결제와 연결된 패키지 이름과 productId를 표시합니다. 프로그래매틱 방식으로 기존 정기 결제의 productId를 확인하려면 앱의 백엔드를 쿼리하거나 BillingClient.queryPurchasesAsync()를 호출하여 특정 사용자와 연결된 정기 결제 목록을 확인합니다. 각 정기 결제에는 정기 결제 상태 정보의 일부로 상응하는 productId가 포함됩니다. 정기 결제 구매와 연결된 각 SubscriptionPurchaseLineItem 객체에는 사용자가 해당 항목에서 구매한 정기 결제와 연결된 productId 값이 포함됩니다.

다음 URL을 사용하여 사용자를 특정 정기 결제 관리 화면으로 안내합니다. 여기서 'your-sub-product-id'와 'your-app-package'를 각각 productId와 앱 패키지 이름으로 바꿉니다.

https://play.google.com/store/account/subscriptions?sku=your-sub-product-id&package=your-app-package

그러면 사용자는 결제 수단을 관리하고 취소, 정기 결제 재신청, 일시중지와 같은 기능에 액세스할 수 있습니다.

사용자가 정기 결제를 업그레이드, 다운그레이드 또는 변경할 수 있도록 허용

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

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

자격요건을 충족하는 사용자에게 할인을 제공하는 정기 결제 혜택을 제공하여 이러한 변경을 유도할 수 있습니다. 예를 들어 월간 요금제에서 연간 요금제로 전환할 때 첫해에 50% 할인을 제공하는 혜택을 만들고 이 혜택을 아직 구매하지 않은 월간 요금제 사용자로 혜택을 제한할 수 있습니다. 혜택 자격 기준에 관한 자세한 내용은 고객센터를 참고하세요.

그림 3은 세 가지 요금제가 있는 앱의 예를 보여줍니다.

이 앱에는 세 가지 정기 결제 등급이 있습니다.
그림 3. 이 앱에는 세 가지 정기 결제 등급이 있습니다.

앱은 그림 3과 유사한 화면을 표시하여 사용자에게 정기 결제를 변경하는 옵션을 제공할 수 있습니다. 어떠한 경우든 사용자가 현재 정기 결제 요금제와 요금제를 변경할 수 있는 옵션을 명확하게 알 수 있어야 합니다.

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

대체 모드

다음 표에는 사용 가능한 대체 모드와 사용 예가 나와 있습니다.

대체 모드

설명

사용 예

WITH_TIME_PRORATION

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

더 비싼 등급으로 업그레이드되지만 추가 결제가 즉시 이뤄지지는 않습니다.

CHARGE_PRORATED_PRICE

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

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

결제 날짜를 변경하지 않고 더 비싼 등급으로 업그레이드됩니다.

CHARGE_FULL_PRICE

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

참고: 새 정기 결제에 무료 체험판 또는 신규 할인 혜택이 포함된 경우 업그레이드 또는 다운그레이드 시 사용자에게 0달러 또는 신규 할인 혜택 가격(둘 중 적용되는 항목)이 청구됩니다.

더 짧은 결제 기간에서 더 긴 결제 기간으로 업그레이드됩니다.

WITHOUT_PRORATION

정기 결제가 즉시 업그레이드 또는 다운그레이드되며 정기 결제가 갱신되면 새로운 가격이 청구됩니다. 결제 주기는 동일하게 유지됩니다.

남은 무료 기간을 유지하면서 더 높은 정기 결제 등급으로 업그레이드됩니다.

DEFERRED

정기 결제는 정기 결제가 갱신될 때만 업그레이드 또는 다운그레이드되지만, 새로운 구매는 향후 새 사용 권한의 시작일과 함께 즉시 발행되므로 개발자는 사용자가 원하는 경우 추가로 변경하도록 허용할 수 있습니다. 예를 들어 기존 요금제로 되돌리거나 지연된 요금제 변경을 새로 시작할 수 있습니다.

비용이 더 적게 드는 등급으로 다운그레이드됩니다.

업그레이드 또는 다운그레이드 혜택의 다양한 업셀링 및 재가입 적용에 관한 자세한 내용은 혜택 및 프로모션 가이드를 참고하세요.

구매 대체 모드 설정

환경설정 및 비즈니스 로직에 따라 다양한 유형의 정기 결제 전환에 서로 다른 대체 모드를 사용할 수 있습니다. 이 섹션에서는 정기 결제 변경에 관해 대체 모드를 설정하는 방법과 적용되는 제한사항을 설명합니다.

정기 결제 재신청 또는 동일한 정기 결제 내에서 요금제 전환

Google Play Console에서 기본 대체 모드를 지정할 수 있습니다. 이 설정을 사용하면 현재 정기 결제 사용자가 동일한 정기 결제의 다른 기본 요금제나 혜택을 구매하거나 취소 후 정기 결제를 재신청하는 경우 현재 정기 결제 사용자에게 청구할 시점을 선택할 수 있습니다. 사용 가능한 옵션은 CHARGE_FULL_PRICE와 동일한 즉시 청구WITHOUT_PRORATION과 동일한 다음 결제 날짜에 청구입니다. 이는 동일한 정기 결제 내에서 기본 요금제를 전환할 때 유일하게 관련된 대체 모드입니다.

예를 들어 사용자가 취소한 후 정기 결제가 종료되기 전에 동일한 요금제의 재가입 혜택을 구현하는 경우 SubscriptionUpdateParams에 값을 표시하지 않고 일반 구매로 새 구매를 처리할 수 있습니다. 시스템은 정기 결제에서 구성한 기본 대체 모드를 사용하며, 이전 구매에서 새 구매로의 요금제 전환을 자동으로 처리합니다.

정기 결제 간에 요금제 전환 또는 기본 대체 모드 재정의

사용자가 정기 결제 제품을 변경(다른 정기 결제 구매)하는 경우 또는 어떤 이유로든 기본 대체 모드를 재정의하려는 경우 런타임 시 구매 흐름 매개변수의 일부로 비례 배분 비율을 지정합니다.

런타임 구매 흐름 구성의 일부로 SubscriptionUpdateParams를 올바르게 제공하려면 다음 제한사항에 유의하세요.

  • 업그레이드하거나 다운그레이드하는 경우 또는 선불 요금제나 자동 갱신 요금제에서 선불 요금제 동일한 정기 결제 전환을 실행하는 경우 허용되는 유일한 비례 배분 모드는 CHARGE_FULL_PRICE입니다. 다른 비례 배분 모드를 지정하면 구매가 실패하고 사용자에게 오류가 표시됩니다.
  • 선불 요금제 또는 자동 갱신 요금제에서 자동 갱신 요금제 동일한 정기 결제 내에서 요금제를 전환할 때 유효한 비례 배분 모드는 CHARGE_FULL_PRICEWITHOUT_PRORATION입니다. 다른 비례 배분 모드를 지정하면 구매가 실패하고 사용자에게 오류가 표시됩니다.

대체 예 및 동작

각 비례 배분 모드의 작동 방식을 이해하려면 다음 시나리오를 생각해 보세요.

샘와이즈는 Country Gardener 앱에서 온라인 콘텐츠를 정기적으로 결제하고 있습니다. 그리고 텍스트 전용 콘텐츠의 Tier 1 버전을 매월 정기 결제하고 있습니다. 이 정기 결제는 월 2달러의 요금이 청구되며 매월 1일에 갱신됩니다.

4월 15일에 샘와이즈는 동영상 업데이트가 포함되고 요금이 연 36달러인 연간 버전의 Tier 2 정기 결제로 업그레이드하기로 했습니다.

정기 결제를 업그레이드할 때 개발자는 비례 배분 모드를 선택합니다. 다음 목록에서는 각 비례 배분 모드가 샘와이즈의 정기 결제에 영향을 주는 방식을 설명합니다.

WITH_TIME_PRORATION

샘와이즈의 Tier 1 정기 결제가 즉시 종료됩니다. 샘와이즈는 한 달(4월 1일~30일) 요금을 결제했는데 정기 결제 기간 중간에 업그레이드했으므로 월간 정기 결제 요금의 절반(1달러)은 새 정기 결제에 적용됩니다. 그러나 새 정기 결제 요금은 연간 36달러이므로 1달러의 잔액은 10일(4월 16일~25일)에 대해서만 결제됩니다. 따라서 4월 26일에 새 정기 결제의 요금으로 36달러가 청구되며, 다음 해부터 매년 4월 26일에 36달러가 청구됩니다.

구매가 성공하는 순간 앱의 PurchasesUpdatedListener를 호출해야 하고 새 구매를 queryPurchasesAsync() 호출의 일부로 가져올 수 있습니다. 백엔드에서 즉시 SUBSCRIPTION_PURCHASED 실시간 개발자 알림을 수신합니다.

CHARGE_PRORATED_PRICE

시간 단위당 Tier 2 정기 결제 요금($36/년 = $3/월)이 시간 단위당 Tier 1 정기 결제 요금($2/월)보다 크므로 이 모드를 사용할 수 있습니다. 샘와이즈의 Tier 1 정기 결제가 즉시 종료됩니다. 샘와이즈는 한 달 전체 요금을 결제했는데 기간의 절반만 사용했으므로 월간 정기 결제 요금의 절반(1달러)은 새 정기 결제에 적용됩니다. 그러나 새 정기 결제 요금은 1년에 36달러이므로 나머지 15일의 요금은 1.50달러입니다. 따라서 새 정기 결제 요금으로 0.50달러의 차액이 청구됩니다. 5월 1일에는 새 정기 결제 등급 요금으로 36달러가 청구되며, 다음 해부터 매년 5월 1일에 36달러가 청구됩니다.

구매가 성공하는 순간 앱의 PurchasesUpdatedListener를 호출해야 하고 새 구매를 queryPurchasesAsync() 호출의 일부로 가져올 수 있습니다. 백엔드에서 즉시 SUBSCRIPTION_PURCHASED 실시간 개발자 알림을 수신합니다.

WITHOUT_PRORATION

샘와이즈의 Tier 1 정기 결제가 추가 비용 없이 Tier 2로 즉시 업그레이드됩니다. 그리고 5월 1일에 새 정기 결제 등급 요금으로 36달러가 청구되며, 다음 해부터 매년 5월 1일에 36달러가 청구됩니다.

구매가 성공하는 순간 앱의 PurchasesUpdatedListener를 호출해야 하고 새 구매를 queryPurchasesAsync() 호출의 일부로 가져올 수 있습니다. 백엔드에서 즉시 SUBSCRIPTION_PURCHASED 실시간 개발자 알림을 수신합니다.

DEFERRED

샘와이즈의 Tier 1 정기 결제는 4월 30일에 만료되기 전까지 계속됩니다. 5월 1일에 Tier 2 정기 결제가 적용되며, 새 정기 결제 등급 요금으로 36달러가 청구됩니다.

구매가 성공하는 순간 앱의 PurchasesUpdatedListener를 호출해야 하고 새 구매를 queryPurchasesAsync() 호출의 일부로 가져올 수 있습니다. 백엔드에서 즉시 SUBSCRIPTION_PURCHASED 실시간 개발자 알림을 수신합니다. 이때 다른 신규 구매를 처리하는 것과 동일한 방식으로 구매를 처리해야 합니다. 특히 새로운 구매를 확인해야 합니다. 새 정기 결제의 startTime은 대체가 적용되는 순간 채워지며 이는 이전 정기 결제가 만료될 때 발생합니다. 이때 새 정기 결제 요금제의 SUBSCRIPTION_RENEWED RTDN을 수신하게 됩니다. ReplacementMode.DEFERRED 동작에 관한 자세한 내용은 지연된 대체 처리를 참고하세요.

CHARGE_FULL_PRICE

샘와이즈의 Tier 1 정기 결제가 즉시 종료됩니다. Tier 2 정기 결제는 오늘 시작되어 36달러가 청구됩니다. 샘와이즈는 한 달 전체 요금을 결제했는데 기간의 절반만 사용했으므로 월간 정기 결제 요금의 절반(1달러)은 새 정기 결제에 적용됩니다. 새 정기 결제 요금이 연간 36달러이므로 일 년의 1/36일이 정기 결제 기간(최대 10일)에 추가됩니다. 따라서 다음번 청구는 오늘부터 1년 10일 후에 이루어지고 요금은 36달러입니다. 그 후에는 샘와이즈에게 다음 해부터 매년 36달러가 청구됩니다.

비례 배분 모드 선택 시 대체 권장사항을 검토하세요.

앱 내에서 정기 결제 변경 트리거

앱은 구매 흐름 시작과 동일한 단계를 사용하여 사용자에게 업그레이드 또는 다운그레이드를 제공할 수 있습니다. 그러나 업그레이드 또는 다운그레이드할 때 다음 예와 같이 현재 정기 결제, 향후(업그레이드 또는 다운그레이드된) 정기 결제 및 사용할 대체 모드에 관한 세부정보를 제공해야 합니다.

Kotlin

val offerToken = productDetails
        .getSubscriptionOfferDetails(selectedOfferIndex)
        .getOfferToken()

val billingParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(
       listOf(
           BillingFlowParams.ProductDetailsParams.newBuilder()
               .setProductDetails(productDetails)
               .setOfferToken(offerToken)
               .build()
       )
       ).setSubscriptionUpdateParams(
           BillingFlowParams.SubscriptionUpdateParams.newBuilder()
               .setOldPurchaseToken("old_purchase_token")
               .setSubscriptionReplacementMode(
                 BillingFlowParams.ReplacementMode.CHARGE_FULL_PRICE
               )
               .build()
       ).build()

billingClient.launchBillingFlow(
    activity,
    billingParams
   )
// ...

Java

String offerToken = productDetails
    .getSubscriptionOfferDetails(selectedOfferIndex)
    .getOfferToken();

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        ImmuableList.of(
            ProductDetailsParams.newBuilder()
                // fetched via queryProductDetailsAsync
                .setProductDetails(productDetails)
                // offerToken can be found in
                // ProductDetails=>SubscriptionOfferDetails
                .setOfferToken(offerToken)
                .build()))
    .setSubscriptionUpdateParams(
        SubscriptionUpdateParams.newBuilder()
            // purchaseToken can be found in Purchase#getPurchaseToken
            .setOldPurchaseToken("old_purchase_token")
            .setSubscriptionReplacementMode(ReplacementMode.CHARGE_FULL_PRICE)
            .build())
    .build();

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

대체 권장사항

다음 표는 다양한 비례 배분 시나리오와 함께 각 시나리오에 권장되는 사항을 보여줍니다.

시나리오 권장되는 대체 모드 결과
비용이 더 많이 드는 등급으로 업그레이드 CHARGE_PRORATED_PRICE 사용자는 동일한 결제 기간을 유지하면서 즉시 액세스 권한을 받습니다.
비용이 더 적게 드는 등급으로 다운그레이드 DEFERRED 사용자는 비용이 더 많이 드는 등급에 대해 이미 결제했으므로 다음 결제일까지 액세스 권한을 유지합니다.
무료 체험판 사용 중 업그레이드하고, 무료 체험판 유지 WITHOUT_PRORATION 사용자는 무료 체험판 액세스 권한을 유지하지만 남은 무료 체험판 기간에 대해 더 높은 등급으로 업그레이드합니다.
무료 체험판 사용 중 업그레이드 - 무료 체험판 액세스 종료 CHARGE_PRORATED_PRICE 사용자는 새 등급에 즉시 액세스할 수 있지만 무료 체험판을 더 이상 사용할 수 없습니다.

정기 결제 변경 구매 처리

요금제 변경은 모든 약관 및 목적상 새로운 구매이므로 결제 흐름이 성공적으로 완료된 후 이와 같이 처리 및 확인되어야 합니다. 새 구매를 적절하게 처리하는 것 외에도 대체되는 구매를 폐기해야 합니다.

인앱 동작은 모든 새 구매에서와 동일합니다. 앱은 PurchasesUpdatedListener에서 새로운 구매의 결과를 수신하며, 새로운 구매는 queryPurchasesAsync에서 사용할 수 있습니다.

구매가 기존 구매를 대체하는 경우 Google Play Developer API는 정기 결제 리소스에서 linkedPurchaseToken을 반환합니다. linkedPurchaseToken에서 제공된 토큰을 무효화하여 서비스 액세스 권한을 얻는 데 이전 토큰이 사용되지 않도록 해야 합니다. 구매 업그레이드 및 다운그레이드 처리에 관한 자세한 내용은 업그레이드, 다운그레이드, 재등록을 참고하세요.

새 구매 토큰을 수신할 때는 새 구매 토큰 인증 시와 동일한 인증 프로세스를 따릅니다. Google Play 결제 라이브러리의 BillingClient.acknowledgePurchase() 또는 Google Play Developer API의 Purchases.subscriptions:acknowledge를 사용하여 이러한 구매를 확인해야 합니다.

지연된 대체 처리

지연된 대체 모드를 사용하면 사용자가 새 요금제를 시작하기 전에 기존 요금제의 남은 사용 권한을 소진하도록 할 수 있습니다.

새 구매에 ReplacementMode.DEFERRED를 사용하면 queryPurchasesAsync()는 지연된 대체가 다음 갱신일에 발생할 때까지 이전 제품과 연결된 상태로 유지되는 새 구매 토큰을 구매 흐름 후에 반환하며 이후 새 제품이 반환됩니다.

이전에는 지원 중단된 ProrationMode.DEFERRED를 사용하여 이러한 사용자 환경을 제공할 수 있었지만 ProrationMode.DEFERRED는 Play 결제 라이브러리 6에서 지원 중단되었습니다. 동작이 어떻게 다른지 알아보려면 다음 표를 참고하세요.

시간

ProrationMode.DEFERRED(지원 중단됨)

ReplacementMode.DEFERRED

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

PurchasesUpdatedListener는 구매 후 업그레이드 또는 다운그레이드 성공 여부 상태와 함께 호출됩니다.

기존 요금제의 사용 권한은 다음 갱신일까지 유지됩니다. 앱이 올바른 사용 권한을 부여하도록 queryPurchasesAsync()는 대체가 발생할 때까지 원래 구매 토큰과 원래 사용 권한을 사용하여 Purchase 객체를 반환합니다.

새 구매 토큰이 표시되지 않으므로 지금은 처리할 수 없습니다.

PurchasesUpdatedListener는 구매 후 업그레이드 또는 다운그레이드 성공 여부 상태와 함께 호출됩니다.

queryPurchasesAsync() 구매 토큰과 이와 연결된 원래 사용 권한이 있는 구매를 즉시 반환합니다.

새 구매 토큰이 표시되므로 대체가 발생할 때를 고려하여 이 시점에서 새 구매 토큰을 처리해야 합니다.

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

SUBSCRIPTION_PURCHASED RTDN은 구매 흐름 후에 전송되지 않습니다. 백엔드는 아직 새 구매를 인식하지 못합니다.

이전 product_id가 있는 SUBSCRIPTION_PURCHASED RTDN은 새 구매 토큰의 구매 흐름 직후 전송됩니다.

새 구매 토큰으로 purchases.subscriptionsv2.get 메서드를 호출하면 항목 2개가 포함된 구매 시간을 나타내는 'startTime'이 있는 구매가 반환됩니다.

  • 하나는 이전 사용 권한을 나타내며 미래에 'expiryTime'이 있습니다. 이전 사용 권한은 갱신되지 않으며 사용 권한의 제품이 포함된 DeferredItemReplacement를 포함합니다. 이는 만료 시 이전 사용 권한이 대체 대기 중임을 나타냅니다.
  • 다른 하나는 새로 구매한 사용 권한을 나타냅니다. 'expiryTime'에 설정된 값이 없습니다.

이전 구매 토큰에 대해 SUBSCRIPTION_EXPIRED가 전송되었습니다. 이전 구매 토큰으로 purchases.subscriptionsv2.get 메서드를 호출하면 만료된 것으로 표시됩니다(이전 요금제의 사용 권한이 남은 기간 동안 새 구매로 이전됨).

대체 시 - 구매 흐름 후 첫 번째 갱신(앱)

queryPurchasesAsync() 구매 토큰과 사용 권한이 있는 새 Purchase 객체를 반환합니다.

이제 새 구매 토큰이 표시되므로 처리됩니다.

queryPurchasesAsync() 구매 토큰과 이와 연결된 새 사용 권한이 있는 구매를 즉시 반환합니다.

구매 흐름에 성공했을 때 이미 새로운 구매가 처리되었을 것이므로 앱은 올바른 사용 권한이 부여되었는지 확인하는 것 외에 특별한 조치를 취하지 않아야 합니다.

대체 시 - 구매 흐름 후 첫 번째 갱신(백엔드)

이제 새 구매가 첫 번째 SUBSCRIPTION_RENEWED RTDN이 전송되면 처리되고 확인될 수 있습니다.

정기 결제 리소스의 linkedPurchaseToken을 사용하면 정기 결제 백엔드에서(해당하는 경우) 새 사용 권한으로 업데이트해야 할 사용자를 결정할 수 있습니다.

새 구매 토큰에 대해 SUBSCRIPTION_PURCHASED RTDN이 전송되고 'startTime'으로 기록되었을 때 새 구매가 처리되고 확인되었습니다.

ReplacementMode.DEFERRED를 사용하면 첫 번째 갱신 시 다른 갱신의 표준 동작을 따르므로 이 이벤트가 발생해도 대체를 위한 특별한 로직을 처리할 필요가 없습니다.

새 구매 토큰으로 purchases.subscriptionsv2.get 메서드를 호출하면 항목 2개가 포함된 구매가 반환됩니다.

  • 하나는 이전 사용 권한을 나타내며 과거의 `expiryTime` 이 있고 DeferredItemReplacement에 설정된 값이 없습니다.
  • 다른 하나는 사용 권한을 나타내며 향후 'expiryTime'을 포함하고 auto_renewing_enabled 플래그가 사용 설정되어 있습니다.

이제 ReplacementMode.DEFERRED를 지원 중단된 ProrationMode.DEFERRED 대신 사용해야 합니다. 사용 권한 변경과 관련하여 동일한 동작을 나타내지만 다른 새 구매 동작과 더 일관된 구매를 관리하는 방법을 제공합니다.

고객 관리

실시간 개발자 알림을 사용하면 사용자가 취소하기로 결정한 시점을 실시간으로 감지할 수 있습니다. 사용자가 정기 결제를 취소했지만 정기 결제가 만료되기 전에 사용자에게 푸시 알림 또는 인앱 메시지를 보내 정기 결제를 재신청하도록 요청할 수 있습니다.

사용자가 정기 결제를 취소한 후 개발자는 앱에서 또는 Play 스토어를 통해 사용자를 재확보할 수 있습니다. 다음 표에서는 다양한 정기 결제 시나리오와 함께 관련 사용자 재확보 작업 및 앱 요구사항을 설명합니다.

정기 결제 만료 전 정기 결제 만료 후
인앱 Play 스토어 내에서 인앱 Play 스토어 내에서
재확보 특징 인앱 정기 결제 복원 인앱 정기 결제 정기 결제 재신청
사용자가 결제 흐름을 거치는지 여부 아니요
사용자의 정기 결제가 동일한 SKU와 연결된 상태로 유지되는지 여부 사용자가 동일 SKU 또는 다른 SKU에 가입할 수 있음 사용자가 동일 SKU 또는 다른 SKU에 가입할 수 있음
새 구매 토큰 생성 여부 아니요
기본적으로 사용 설정되는지 여부 아니요 예, 모든 개발자에게 필요한 지원 아니요

결제 라이브러리 2.0 이상이 없는 앱: 아니요

결제 라이브러리 2.0 이상이 있는 앱: 예, 개발자는 콘솔에서 선택 해제할 수 있음

사용자에게 청구되는 시점

동일한 SKU를 사용하는 경우: 현재 결제 기간 종료 시

다른 SKU를 사용하는 경우: 비례 배분 모드에 따라 다름

현재 결제 기간 종료 시 즉시 즉시
필요한 구현 앱 내 재가입 UI 제공

정기 결제 상태 변경 감지

Play 스토어 딥 링크

앱 내 재가입 UI 제공 앱 외부 구매 처리

정기 결제 만료 전 - 앱 내에서

취소되었지만 아직 만료되지 않은 정기 결제의 경우 새 정기 결제 사용자와 동일한 인앱 상품 구매 흐름을 적용함으로써 정기 결제 사용자가 앱 내에서 정기 결제를 복원하도록 할 수 있습니다. UI가 사용자에게 기존 정기 결제가 있다는 점을 반영하는지 확인합니다. 예를 들어 재활성화 버튼을 사용하여 사용자의 현재 만료일 및 반복 가격을 표시할 수 있습니다.

일반적으로 개발자는 다음과 같이 사용자가 이미 정기 결제한 SKU 및 동일한 가격을 사용자에게 제공하려고 합니다.

  • 동일한 SKU를 사용하여 새 정기 결제 구매를 시작합니다.
  • 새 정기 결제는 이전 정기 결제를 대체하고 동일한 만료일에 갱신됩니다. 이전 정기 결제는 즉시 만료된 것으로 표시됩니다.
  • 예를 들어 아킬레스가 Example Music App을 정기 결제하고 있으며, 이 정기 결제는 8월 1일에 만료될 예정이라고 가정해 보겠습니다. 아킬레스는 7월 10일에 1개월 정기 결제를 동일한 월별 가격으로 다시 결제합니다. 그러면 새 정기 결제는 나머지 잔액으로 비례 배분되고, 즉시 활성화되며, 여전히 8월 1일에 갱신됩니다.

새로운 무료 체험판 또는 재가입 할인과 같은 다른 가격을 제공하려는 경우 다음과 같이 사용자에게 다른 SKU를 제공하면 됩니다.

  • 대체 모드 WITHOUT_PRORATION을 사용하여 다른 SKU로 업그레이드 또는 다운그레이드를 시작합니다.
  • 새 정기 결제는 이전 정기 결제를 대체하고 동일한 만료일에 갱신됩니다. 원래 만료일에 신규 할인 가격을 포함한 새 SKU 가격이 사용자에게 청구됩니다. 난독화된 계정 ID를 사용하여 이전 정기 결제가 생성된 경우 같은 ID를 업그레이드와 다운그레이드를 위해 BillingFlowParams에 전달해야 합니다.
  • 예를 들어 아킬레스가 Example Music App을 정기 결제하고 있으며, 이 정기 결제는 8월 1일에 만료될 예정이라고 가정해 보겠습니다. 7월 10일에 그는 연간 정기 결제를 신규 할인 가격으로 다시 결제합니다. 새 정기 결제가 즉시 활성화되며, 8월 1일에 신규 할인 가격이 사용자에게 청구됩니다.
  • 재가입 SKU에 무료 체험판 또는 신규 할인 가격을 포함하기로 했다면 Google Play Console에서 사용자가 앱당 하나의 무료 체험판을 받도록 제한하는 옵션인 앱당 하나의 무료 체험판 허용 체크박스를 선택 해제하여 사용자가 자격요건을 충족하도록 합니다.

구매 토큰을 받으면 새 정기 결제에서와 마찬가지로 구매를 처리합니다. 또한 Google Play Developer API는 정기 결제 리소스에서 linkedPurchaseToken을 반환합니다. linkedPurchaseToken에서 제공된 토큰을 무효화하여 서비스 액세스 권한을 얻는 데 이전 토큰이 사용되지 않도록 해야 합니다.

정기 결제 만료 전 - Play 스토어에서

정기 결제가 취소되었지만 아직 활성 상태인 동안 사용자는 정기 결제 재신청(이전의 복원)을 클릭하여 Google Play 정기 결제 센터에서 정기 결제를 복원할 수 있습니다. 이렇게 하면 동일한 정기 결제 및 구매 토큰이 유지됩니다.

취소된 정기 결제와 정기 결제 재신청 버튼이 표시된 Google Play 스토어 앱의 정기 결제 섹션
그림 8. Google Play 스토어 앱의 계정 > 정기 결제 섹션에 취소된 정기 결제와 정기 결제 재신청 버튼이 표시되어 있습니다.

정기 결제 복원에 관한 자세한 내용은 복원을 참고하세요.

정기 결제 만료 후 - 앱 내에서

새 정기 결제 사용자와 동일한 인앱 상품 구매 흐름을 적용함으로써 만료된 정기 결제 사용자가 앱 내에서 정기 결제를 재신청하도록 할 수 있습니다. 다음 내용을 참고하세요.

  • 사용자에게 할인을 제공하려면 정기 결제에 특별 가격이 적용된 제품 ID(재가입 SKU라고도 함)를 제공하는 것이 좋습니다. 앱 내에서 쿠폰을 제공하거나 이메일과 같이 앱 외부에서 사용자에게 쿠폰을 알릴 수 있습니다.
  • 정기 결제 재가입을 시작하려면 Google Play 결제 라이브러리를 사용하여 Android 앱에서 구매 흐름을 시작합니다. 이 흐름은 새 정기 결제와 동일한 프로세스이지만, 사용자에게 제공할 SKU를 결정할 수 있습니다.
  • 재가입 SKU에 무료 체험판 또는 신규 할인 가격을 포함하기로 했다면 Google Play Console에서 사용자가 앱당 하나의 무료 체험판을 받도록 제한하는 옵션인 앱당 하나의 무료 체험판 허용 체크박스를 선택 해제하여 사용자가 자격요건을 충족하도록 합니다.
  • 사용자가 동일한 SKU로 정기 결제를 재개하면 더 이상 무료 체험판 또는 신규 할인 가격을 사용할 수 없습니다. UI에 이 사항이 반영되었는지 확인합니다.

구매 토큰을 받으면 새 정기 결제에서와 마찬가지로 구매를 처리합니다. 정기 결제 리소스에서 linkedPurchaseToken을 수신하지 않습니다.

정기 결제 만료 후 - Play 스토어에서

옵션이 사용 설정된 경우 사용자는 Google Play 정기 결제 센터에서 정기 결제 재신청을 클릭하여 만료 후 최대 1년 동안 동일한 SKU의 정기 결제를 다시 신청할 수 있습니다. 그러면 새 정기 결제 및 구매 토큰이 생성됩니다.

취소된 정기 결제 및 만료된 정기 결제와 정기 결제 재신청 버튼 및 삭제 버튼이 표시된 Google Play 스토어 앱의 정기 결제 섹션
그림 9. Google Play 스토어 앱의 계정 > 정기 결제 섹션에 취소 및 만료된 정기 결제와 정기 결제 재신청삭제 버튼이 표시되어 있습니다.

정기 결제 재신청은 앱 외부 구매로 간주되므로 앱 외부에서 이루어진 구매 처리 권장사항을 따라야 합니다.

정기 결제 프로모션

프로모션 코드를 만들어 기존 정기 결제에 대한 연장된 무료 체험판을 선택된 사용자에게 제공할 수 있습니다. 자세한 내용은 프로모션 코드를 참고하세요.

무료 체험판의 경우 Google Play에서는 무료 체험판을 시작하기 전에 사용자에게 유효한 결제 수단이 있는지 인증합니다. 일부 사용자에게 이 인증은 결제 수단에 대한 보류 또는 청구로 표시될 수 있습니다. 이 보류 또는 청구는 일시적이며 나중에 취소 또는 환불됩니다.

무료 체험판 기간이 종료되면 사용자의 결제 수단으로 정식 정기 결제 금액이 청구됩니다.

사용자가 무료 체험판 기간에 언제든지 정기 결제를 취소하면 정기 결제는 무료 체험판이 끝날 때까지 활성 상태로 유지되며 무료 체험판 사용 기간이 종료되면 요금이 청구되지 않습니다.

취소 또는 환불, 해지

Google Play Developer API를 사용하여 정기 결제를 취소, 환불 또는 해지할 수 있습니다. 이 기능은 Google Play Console에서도 사용할 수 있습니다.

  • 취소: 사용자가 Google Play에서 정기 결제를 취소할 수 있습니다. 또한 사용자가 앱 또는 웹사이트에서 취소할 수 있는 옵션을 제공할 수도 있습니다. 앱은 취소에 설명된 대로 이러한 취소를 처리해야 합니다.
  • 환불: 개발자가 환불할 때 사용자는 정기 결제를 계속 사용할 수 있습니다. 예를 들어 사용자가 제품에 액세스하지 못하게 하는 기술적 오류가 발생했지만 오류가 해결된 경우 환불을 사용할 수 있습니다. 가장 최근 결제 금액을 초과하여 환불하거나 부분 환불을 처리하려면 Google Play Console을 사용해야 합니다.
  • 해지: 개발자가 해지할 때 사용자는 정기 결제 액세스 권한을 즉시 잃게 됩니다. 예를 들어 사용자가 제품에 액세스하지 못하게 하는 기술적 오류가 발생했으며 사용자가 제품을 계속 사용하지 않으려는 경우에 이 기능을 사용할 수 있습니다. 앱은 해지에 설명된 대로 이러한 취소를 처리해야 합니다.

다음 표에서는 취소, 환불 및 해지의 차이점을 보여줍니다.

갱신 중지 환불 액세스 취소
취소 아니요 아니요
환불 아니요 아니요
해지

정기 결제 사용자의 결제 연기

Google Play Developer API의 Purchases.subscriptions:defer를 사용하여 자동 갱신 정기 결제 사용자의 다음 결제일을 연기할 수 있습니다. 연기 기간 동안 사용자는 전체 액세스 권한으로 정기 결제 콘텐츠에 액세스할 수 있지만 요금이 청구되지 않습니다. 정기 결제 갱신일은 새로운 날짜를 반영하여 업데이트됩니다.

선불 요금제의 경우 결제 연기 API를 사용하여 만료 시간을 연기할 수 있습니다.

결제 연기를 통해 다음을 할 수 있습니다.

  • 영화 구매 1주일 무료 혜택 제공과 같은 특별 이벤트로 사용자에게 무료 액세스 권한을 부여합니다.
  • 서비스 차원으로 고객에게 무료 액세스 권한을 부여합니다.

결제는 API 호출당 최소 하루, 최대 1년까지 연기할 수 있습니다. 결제를 훨씬 더 연기하려면 새 결제일이 도래하기 전에 API를 다시 호출하면 됩니다.

예를 들어 다르시가 Fishing Quarterly 앱의 온라인 콘텐츠를 매월 정기 결제하고 있으며 보통 매월 1일에 1.25파운드가 청구된다고 가정해 보겠습니다. 3월에 다르시는 앱 게시자에 관한 온라인 설문조사에 참여했습니다. 이에 대한 리워드로 게시자는 5월 15일(기존에 예정된 결제일인 4월 1일로부터 6주 후)까지 다음 결제를 연기함으로써 6주간 무료로 콘텐츠를 제공합니다. 4월 또는 5월 초 요금이 청구되지 않지만 다르시는 여전히 콘텐츠에 액세스할 수 있습니다. 5월 15일에는 월 정기 결제 비용으로 정상 요금인 1.25파운드가 청구됩니다. 다르시의 다음 갱신일은 이제 6월 15일입니다.

결제 연기 시 이메일을 통해 또는 앱 내에서 사용자에게 알림을 보내 결제일이 변경되었음을 알릴 수 있습니다.

결제 거부 처리

정기 결제 갱신에 결제 문제가 있는 경우 Google은 정기 결제를 취소하기 전에 한동안 정기 결제를 주기적으로 갱신하려고 합니다. 이 복구 기간은 유예 기간과 그 이후의 계정 보류 기간으로 구성될 수 있습니다. 이 기간 동안 Google은 사용자에게 결제 수단 업데이트를 요청하는 이메일과 알림을 전송합니다.

결제가 거부되면 정기 결제의 유예 기간이 시작됩니다(구성된 경우). 유예 기간 중에도 사용자가 계속해서 정기 결제 사용 권한을 이용할 수 있어야 합니다.

유예 기간이 종료되면 정기 결제의 계정 보류 기간이 시작됩니다. 계정 보류 기간 중에는 사용자가 정기 결제 사용 권한을 이용할 수 없어야 합니다.

Google Play Console에서 각 자동 갱신 기본 요금제의 유예 기간 및 계정 보류 기간의 길이를 지정할 수 있습니다. 길이를 기본값보다 짧게 지정하면 결제 거부에서 복구되는 정기 결제 수가 줄어들 수 있습니다.

결제 거부 과정에서 정기 결제 복구 가능성을 극대화하려면 사용자에게 결제 문제를 알리고 문제 해결을 요청하세요.

유예 기간계정 보류 섹션에 설명된 대로 직접 이 작업을 실행하거나 Google에서 앱 사용자에게 메시지를 표시하도록 인앱 메시지 API를 구현할 수 있습니다.

인앱 메시지

InAppMessageCategoryId.TRANSACTIONAL로 인앱 메시지를 사용 설정한 경우 Google Play에서는 유예 기간과 계정 보류 기간 동안 하루에 한 번씩 사용자에게 메시지를 표시하여 앱을 나가지 않고도 결제 문제를 해결할 기회를 제공합니다.

사용자에게 결제 문제를 해결하라고 알리는 스낵바 메시지
그림 20. 사용자에게 결제 문제를 해결하라고 알리는 스낵바 메시지

사용자가 앱을 열 때마다 이 API를 호출하여 메시지를 표시할지 결정하는 것이 좋습니다.

사용자가 정기 결제를 성공적으로 복구하면 구매 토큰과 함께 응답 코드 SUBSCRIPTION_STATUS_UPDATED가 수신됩니다. 그러면 이 구매 토큰을 사용하여 Google Play Developer API를 호출하고 앱에서 정기 결제 상태를 새로고침해야 합니다.

인앱 메시지 통합

사용자에게 인앱 메시지를 표시하려면 BillingClient.showInAppMessages()를 사용합니다.

다음은 인앱 메시지 흐름을 트리거하는 예시입니다.

Kotlin

val inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build()

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        object : InAppMessageResponseListener() {
            override fun onInAppMessageResponse(inAppMessageResult: InAppMessageResult) {
                if (inAppMessageResult.responseCode == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        })

Java

InAppMessageParams inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build();

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        new InAppMessageResponseListener() {
            @Override
            public void onInAppMessageResponse(InAppMessageResult inAppMessageResult) {
                if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        });