Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

앱에 Google Play 결제 라이브러리 통합

이 주제에서는 제품 판매를 시작하기 위해 Google Play 결제 라이브러리를 앱에 통합하는 방법을 설명합니다. 이 주제를 읽기 전에 먼저 준비하기의 단계에 따라 Google Play 구성을 미리 설정해야 합니다.

이 주제에는 GitHub의 공식 샘플 앱을 기반으로 하는 코드 예가 포함되어 있습니다. 통합하는 동안 사용할 수 있는 샘플 앱 및 기타 리소스의 전체 목록은 추가 리소스를 참조하세요.

구매 진행 과정

다음은 일회성 구매 또는 정기 결제의 일반적인 구매 흐름입니다.

  • 사용자에게 구입할 수 있는 항목을 보여줍니다.
  • 사용자가 구매를 수락할 수 있도록 구매 흐름을 시작합니다.
  • 서버에서 구매를 인증합니다.
  • 사용자에게 콘텐츠를 제공하고 콘텐츠 전송을 확인합니다. 선택적으로, 사용자가 항목을 다시 구입할 수 있도록 항목을 소비됨으로 표시합니다.

정기 결제는 취소될 때까지 자동으로 갱신됩니다. 정기 결제는 다음 상태를 거칠 수 있습니다.

  • 활성: 사용자가 콘텐츠 사용에 문제가 없는 양호한 상태이며 정기 결제에 액세스할 수 있습니다.
  • 취소됨: 사용자가 정기 결제를 취소했지만 만료 시까지 계속 액세스할 수 있습니다.
  • 유예 기간 중: 사용자에게 결제 문제가 발생했지만 Google에서 결제 수단을 다시 시도하는 동안 사용자가 계속 액세스할 수 있습니다.
  • 보류 중: 사용자에게 결제 문제가 발생하여 Google에서 결제 수단을 다시 시도하는 동안 사용자가 더 이상 액세스할 수 없습니다.
  • 일시중지됨: 사용자가 액세스를 일시중지했으며 다시 시작할 때까지 액세스할 수 없습니다.
  • 만료됨: 사용자가 정기 결제를 취소했으며 정기 결제 액세스 권한을 잃었습니다. 만료 시 사용자가 이탈한 것으로 간주합니다.

구매 토큰 및 주문 ID

Google Play에서는 구매 토큰과 주문 ID를 사용하여 제품 및 거래를 추적합니다.

  • 구매 토큰은 Google Play에서 제품에 대한 구매자의 자격을 나타내는 문자열입니다. 이는 Google 사용자가 SKU로 표시되는 특정 제품과 관련하여 자격이 있음을 나타냅니다. 개발자는 구매 토큰을 Google Play Developer API와 함께 사용할 수 있습니다.
  • 주문 ID는 Google Play에서 금융 거래를 나타내는 문자열입니다. 이 문자열은 구매자에게 이메일로 전송되는 영수증에 포함됩니다. 주문 ID를 사용하여 환불을 관리할 수 있습니다. 주문 ID는 판매 및 판매 대금 보고서에서도 사용됩니다.

주문 ID는 금융 거래가 발생할 때마다 생성됩니다. 구매 토큰은 사용자가 구매 흐름을 완료할 때만 생성됩니다.

  • 일회성 제품의 경우 모든 구매 시 새 구매 토큰이 생성됩니다. 또한 대부분의 구매 시 새 주문 ID도 생성됩니다. 프로모션 코드에 설명된 대로 사용자에게 비용이 청구되지 않는 경우는 예외입니다.
  • 정기 결제의 경우 최초 구매 시에는 구매 토큰 및 주문 ID가 생성됩니다. 이후 계속되는 각 결제 기간에 구매 토큰은 동일하게 유지되며 새로운 주문 ID가 발급됩니다. 업그레이드, 다운그레이드, 대체 및 재가입은 모두 새로운 구매 토큰 및 주문 ID를 생성합니다.

정기 결제의 경우 다음을 유의하세요.

  • 정기 결제 업그레이드, 다운그레이드 및 기타 정기 결제 구매 흐름은 이전 구매 토큰을 대체해야 하는 구매 토큰을 생성합니다. Google Play Developer API의 linkedPurchaseToken 필드에 표시되는 구매 토큰을 무효화해야 합니다. 자세한 내용은 linkedPurchaseToken을 올바르게 구현하여 중복 정기 결제 방지를 참조하세요.
  • 정기 결제 갱신 주문 번호에는 특정 갱신 인스턴스를 나타내는 정수가 추가로 포함됩니다. 예를 들어 최초 정기 결제 주문 ID는 GPA.1234-5678-9012-34567이고, 이후 주문 ID는 GPA.1234-5678-9012-34567..0(첫 번째 갱신), GPA.1234-5678-9012-34567..1(두 번째 갱신) 등일 수 있습니다.

오류 처리

Google Play 결제 라이브러리는 BillingResult 형식으로 오류를 반환합니다. BillingResult에는 BillingResponseCode가 포함되어 있어 앱에서 발생할 수 있는 결제 관련 오류를 분류합니다. 예를 들어 SERVICE_DISCONNECTED 오류 코드가 수신되면 앱에서 Google Play와의 연결을 다시 초기화해야 합니다. 또한 BillingResult에는 개발 중에 오류를 진단하는 데 유용한 디버그 메시지가 포함되어 있습니다.

Google Play에 연결

Google Play 결제 시스템과 통합하는 첫 번째 단계는 라이브러리를 앱에 추가하고 연결을 초기화하는 것입니다.

Google Play 결제 라이브러리 종속 항목 추가

다음과 같이 앱의 build.gradle 파일에 Google Play 결제 라이브러리 종속 항목을 추가합니다.

dependencies {
    def billing_version = "3.0.0"

    implementation 'com.android.billingclient:billing:$billing_version'
}

Kotlin을 사용한다면 Play 결제 라이브러리 KTX 모듈에 Kotlin 확장과 코루틴 지원이 포함되어 있으므로 Google Play 결제 라이브러리를 사용할 때 직관적인 Kotlin을 작성할 수 있습니다. 프로젝트에서 이러한 확장을 포함하려면 다음과 같이 앱의 build.gradle 파일에 다음 종속 항목을 추가합니다.

dependencies {
    def billing_version = "3.0.0"

    implementation 'com.android.billingclient:billing-ktx:$billing_version'
}

BillingClient 초기화

Google Play 결제 라이브러리의 종속 항목을 추가한 후에는 BillingClient 인스턴스를 초기화해야 합니다. BillingClient는 Google Play 결제 라이브러리와 나머지 앱 간의 통신을 위한 기본 인터페이스입니다. BillingClient는 많은 일반적인 결제 작업에 편의 메서드(동기 메서드와 비동기 메서드 모두)를 제공합니다.

BillingClient를 생성하려면 newBuilder()를 사용합니다. 구매 관련 업데이트를 수신하려면 setListener()를 호출하여 PurchasesUpdatedListener에 대한 참조를 전달해야 합니다. 이 리스너는 앱의 모든 구매 관련 업데이트를 수신합니다.

Kotlin

private val purchaseUpdateListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(activity)
   .setListener(purchasesUpdatedListener)
   .enablePendingPurchases()
   .build()

자바

private PurchasesUpdatedListener purchaseUpdateListener = new PurchasesUpdatedListener() {
    @Override
    void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(activity)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build();

Google Play 연결 설정

BillingClient를 만든 후 Google Play에 연결해야 합니다.

Google Play에 연결하려면 startConnection()을 호출합니다. 연결 프로세스는 비동기적입니다. 그리고 클라이언트 설정이 완료되고 추가로 요청할 준비가 되면 BillingClientStateListener를 구현하여 콜백을 수신해야 합니다.

또한 Google Play와 연결이 끊어진 문제를 처리하려면 재시도 로직을 구현해야 합니다. 재시도 로직을 구현하려면 onBillingServiceDisconnected() 콜백 메서드를 재정의합니다. 그리고 추가 요청을 하기 전에 BillingClientstartConnection() 메서드를 호출하여 Google Play에 다시 연결하도록 해야 합니다.

다음 예는 연결을 시작하고 사용 준비가 되었는지 테스트하는 방법을 보여줍니다.

Kotlin

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

자바

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

구입 가능한 제품 표시

Google Play에 연결한 후에는 구매 가능한 제품을 쿼리하여 사용자에게 표시할 준비가 된 것입니다. Google Play에 인앱 상품 세부정보를 쿼리하려면 querySkuDetailsAsync()를 호출합니다. SKU 세부정보 쿼리는 현지화된 제품 정보를 반환하므로 사용자에게 제품을 표시하기 전에 중요한 단계입니다. 정기 결제의 경우 제품 디스플레이가 모든 Play 정책을 준수해야 합니다.

querySkuDetailsAsync()를 호출할 때 SkuType과 함께 Google Play Console에서 생성된 제품 ID 문자열 목록을 지정하는 SkuDetailsParams의 인스턴스를 전달합니다. SkuType은 일회성 제품의 경우 SkuType.INAPP, 정기 결제의 경우 SkuType.SUBS가 될 수 있습니다.

비동기 작업의 결과를 처리하려면 SkuDetailsResponseListener 인터페이스를 구현하는 리스너도 지정해야 합니다. 그런 후 다음 예에서와 같이 쿼리가 완료되면 리스너에 알리는 onSkuDetailsResponse()를 재정의할 수 있습니다.

Kotlin

fun querySkuDetails() {
    val skuList = ArrayList<String>()
    skuList.add("premium_upgrade")
    skuList.add("gas")
    val params = SkuDetailsParams.newBuilder()
    params.setSkusList(skuList).setType(SkuType.INAPP)
    val skuDetailsResult = withContext(Dispatchers.IO) {
        billingClient.querySkuDetails(params.build())
    }
    // Process the result.
}

자바

List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
    new SkuDetailsResponseListener() {
        @Override
        public void onSkuDetailsResponse(BillingResult billingResult,
                List<SkuDetails> skuDetailsList) {
            // Process the result.
        }
    });

Google Play 결제 라이브러리는 SkuDetails 객체의 List에 쿼리 결과를 저장합니다. 그런 후 목록의 각 SkuDetails 객체에서 다양한 메서드를 호출하여 가격 또는 설명과 같은 인앱 상품에 관한 적절한 정보를 볼 수 있습니다. 사용 가능한 제품 세부정보를 보려면 SkuDetails 클래스의 메서드 목록을 참조하세요.

판매할 항목을 제공하기 전에 사용자가 그 항목을 이미 소유하고 있지 않은지 확인합니다. 사용자의 항목 라이브러리에 소비성 항목이 여전히 있다면 사용자가 항목을 다시 구입하기 전에 먼저 항목을 소비해야 합니다.

정기 결제를 제공하기 전에 사용자가 이미 정기 결제하지 않았는지 확인합니다.

구매 흐름 시작

앱에서 구매 요청을 시작하려면 앱의 기본 스레드에서 launchBillingFlow() 메서드를 호출합니다. 이 메서드는 querySkuDetailsAsync() 호출에서 얻은 관련 SkuDetails 객체가 포함된 BillingFlowParams 객체를 참조합니다. BillingFlowParams 객체를 생성하려면 BillingFlowParams.Builder 클래스를 사용합니다.

Kotlin

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

// Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
val flowParams = BillingFlowParams.newBuilder()
        .setSkuDetails(skuDetails)
        .build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode

자바

// 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();
int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();

// Handle the result.

launchBillingFlow() 메서드는 BillingClient.BillingResponseCode에 나열된 몇 가지 응답 코드 중 하나를 반환합니다. 이 결과를 검토하여 구매 흐름을 시작하는 동안 발생한 오류가 없는지 확인합니다. OKBillingResponseCode는 성공적으로 시작되었음을 나타냅니다.

launchBillingFlow() 호출에 성공하면 시스템에서 Google Play 구매 화면을 표시합니다. 그림 1은 일회성 제품의 구매 화면을 보여줍니다.

Google Play 구매 화면에 구매 가능한 일회성 제품이 표시됩니다.
그림 1. Google Play 구매 화면에 구매 가능한 일회성 제품이 표시됩니다.

Google Play는 onPurchasesUpdated()를 호출하여 PurchasesUpdatedListener 인터페이스를 구현하는 리스너에 구매 작업의 결과를 제공합니다. 리스너는 클라이언트를 초기화할 때 setListener() 메서드를 사용하여 지정됩니다.

가능한 응답 코드를 처리하려면 onPurchasesUpdated()를 구현해야 합니다. 다음 예는 onPurchasesUpdated()를 재정의하는 방법을 보여줍니다.

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

자바

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

구매 성공 시 그림 2와 유사한 Google Play 구매 성공 화면이 생성됩니다.

Google Play의 구매 성공 화면
그림 2. Google Play의 구매 성공 화면

또한 구매 성공 시 사용자가 구매한 인앱 상품의 사용자 및 제품 ID를 나타내는 고유 식별자인 구매 토큰도 생성됩니다. 앱은 구매 토큰을 로컬에 저장할 수 있습니다. 하지만, 구매를 인증하고 사기로부터 보호할 수 있는 보안 백엔드 서버로 토큰을 전달하는 것이 좋습니다. 이 프로세스는 다음 섹션에서 자세히 설명합니다.

또한 사용자는 주문 ID 또는 거래의 고유 ID가 포함된 거래 영수증을 이메일로 받습니다. 사용자는 일회성 제품 구매 시마다 그리고 최초 정기 결제 구매 및 이후의 반복적인 자동 갱신 시에도 고유 주문 ID가 포함된 이메일을 받습니다. Google Play Console에서 주문 ID를 사용하여 환불을 관리할 수 있습니다.

구매 처리

사용자가 구매를 완료하면 앱에서 구매를 처리해야 합니다. 대부분의 경우 앱은 PurchasesUpdatedListener를 통해 구매 알림을 받습니다. 그러나 구매 가져오기에 설명된 것처럼 앱이 BillingClient.queryPurchases() 호출을 인식하는 경우가 있습니다.

앱은 다음과 같은 방식으로 구매를 처리해야 합니다.

  1. 구매를 인증합니다.
  2. 사용자에게 콘텐츠를 제공하고 콘텐츠 전송을 확인합니다. 선택적으로, 사용자가 항목을 다시 구입할 수 있도록 항목을 소비됨으로 표시합니다.

구매를 인증하려면 먼저 구매 상태PURCHASED인지 확인합니다. 구매가 PENDING이라면 대기 중인 거래 처리에 설명된 대로 구매를 처리해야 합니다. onPurchaseUpdated() 또는 queryPurchases에서 수신한 구매의 경우 앱이 자격을 부여하기 전에 구매를 추가로 인증하여 정당성을 확인해야 합니다. 구매를 적절하게 인증하는 방법을 알아보려면 자격을 부여하기 전에 구매 확인을 참조하세요.

구매를 인증했다면 앱에서 사용자에게 자격을 부여할 준비가 된 것입니다. 자격을 부여한 후 앱에서 구매를 확인해야 합니다. 이 확인은 구매와 관련된 자격을 부여했음을 Google Play에 알려줍니다.

자격을 부여하고 구매를 확인하는 프로세스는 구매가 비소비성인지, 소비성인지 아니면 정기 결제인지에 따라 다릅니다.

소비성인 경우 consumeAsync() 메서드는 확인 요구사항을 충족하며 앱이 사용자에게 자격을 부여했음을 나타냅니다. 또한 이 메서드를 사용하면 앱에서 일회성 제품을 다시 구매할 수 있습니다.

일회성 제품이 소비되었음을 나타내려면 consumeAsync()를 호출하고 Google Play에서 다시 구매할 수 있게 할 구매 토큰을 포함합니다. 또한 ConsumeResponseListener 인터페이스를 구현하는 객체를 전달해야 합니다. 이 객체는 소비 작업의 결과를 처리합니다. 작업 완료 시 Google Play 결제 라이브러리가 호출하는 onConsumeResponse() 메서드를 재정의할 수 있습니다.

다음 예는 관련 구매 토큰을 사용하여 제품을 소비하는 것을 보여줍니다.

Kotlin

fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()

    billingClient.consumeAsync(consumeParams, { billingResult, outToken ->
        if (billingResult.responseCode == BillingResponseCode.OK) {
            // Handle the success of the consume operation.
        }
    })
}

자바

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchases or your PurchasesUpdatedListener.
    Purchase purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);
}

비소비성 구매를 확인하려면 결제 라이브러리의 BillingClient.acknowledgePurchase() 또는 Google Play Developer API의 Product.Purchases.Acknowledge를 사용합니다. 구매를 확인하기 전에 Google Play 결제 라이브러리의 isAcknowledged() 메서드 또는 Google Developer API의 acknowledgementState 필드를 사용하여 앱에서 이미 구매를 확인했는지 검토해야 합니다.

다음 예는 Google Play 결제 라이브러리를 사용하여 구매를 확인하는 방법을 보여줍니다.

Kotlin

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

자바

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

정기 결제 구매는 비소비성 구매와 유사하게 처리됩니다. Google Play 결제 라이브러리의 BillingClient.acknowledgePurchase() 또는 Google Play Developer API의 Purchases.Subscriptions.Acknowledge를 사용하여 정기 결제 확인을 확인할 수 있습니다. 최초 정기 결제 구매는 모두 확인해야 합니다. 정기 결제 갱신은 확인하지 않아도 됩니다. 정기 결제를 확인해야 하는 경우에 관한 자세한 내용은 정기 결제 판매 주제를 참조하세요.

구매 가져오기

PurchasesUpdatedListener를 사용하여 구매 업데이트를 수신 대기하는 것만으로는 앱이 모든 구매를 처리하도록 보장하기에 충분하지 않습니다. 앱에서 사용자가 구매한 모든 항목을 인식하지 못할 수 있습니다. 앱에서 구매 추적을 놓치거나 구매를 인식하지 못할 수 있는 몇 가지 시나리오는 다음과 같습니다.

  • 구매 중 네트워크 문제: 사용자가 구매를 성공적으로 완료하고 Google에서 확인을 받았지만 기기가 PurchasesUpdatedListener를 통해 구매 알림을 받기 전에 네트워크 연결이 끊어졌습니다.
  • 여러 기기: 사용자는 한 기기에서 항목을 구입한 후 기기를 전환할 때 이 항목이 표시되기를 기대합니다.
  • 앱 외부에서 이루어진 구매 처리: 프로모션 사용과 같은 일부 구매는 앱 외부에서 이루어질 수 있습니다.

이러한 상황을 처리하려면 앱이 onResume()onCreate() 메서드에서 BillingClient.queryPurchases()를 호출하여 구매 처리에 설명된 대로 모든 구매가 성공적으로 처리되도록 해야 합니다.

앱 외부에서 이루어진 구매 처리

프로모션 사용과 같은 일부 구매는 앱 외부에서 발생할 수 있습니다. 사용자는 앱 외부에서 구매할 때 앱이 올바르게 수신 및 처리되었음을 사용자가 알 수 있도록 앱에서 인앱 메시지를 표시하거나 일종의 알림 메커니즘을 사용하기를 기대합니다. 허용되는 일부 메커니즘은 다음과 같습니다.

  • 인앱 팝업을 표시합니다.
  • 인앱 메시지 상자에 메시지를 전송하고 인앱 메시지 상자에 새 메시지가 있음을 명확히 알립니다.
  • OS 알림 메시지를 사용합니다.

앱에서 구매를 인식할 때 앱이 어떤 상태에 있을 수 있다는 점에 유의하시기 바랍니다. 구매가 이루어졌을 때 앱이 설치되어 있지 않았을 수도 있습니다. 사용자는 앱이 어떤 상태에 있든지 관계없이 앱을 다시 시작할 때 구매를 수신할 것으로 기대합니다.

구매가 이루어졌을 때 앱이 어떤 상태에 있든지 관계없이 구매를 감지해야 합니다. 하지만 구매 항목이 수신되었음을 사용자에게 즉시 알리지 않아도 되는 몇 가지 예외 상황이 있습니다. 예를 들면 다음과 같습니다.

  • 게임의 액션 플레이 중에 메시지를 표시하면 사용자의 주의가 산만해질 수 있습니다. 이 경우 액션 플레이가 끝난 후에 사용자에게 알려야 합니다.
  • 컷신 중에 메시지를 표시하면 사용자의 주의가 산만해질 수 있습니다. 이 경우 컷신이 끝난 후에 사용자에게 알려야 합니다.
  • 게임의 초기 튜토리얼 및 사용자 설정 중에도 사용자에게 즉시 알리지 않아도 됩니다. 신규 사용자가 게임을 연 직후 또는 처음 사용자 설정 중에 리워드를 알리는 것이 좋습니다. 그러나 사용자에게 알리는 데 메인 게임 시퀀스를 사용할 수 있을 때까지 기다려도 괜찮습니다.

앱 외부에서 이루어진 구매에 관해 사용자에게 알리는 시기 및 방법을 결정할 때는 항상 사용자를 염두에 두어야 합니다. 알림을 즉시 받지 못하면 언제든 사용자는 혼란스러워하거나 앱 사용을 중지하거나 사용자 지원에 문의하거나 소셜 미디어에 불만글을 게시할 수 있습니다.

대기 중인 거래 처리

Google Play는 대기 중인 거래 또는 사용자가 구매를 시작한 시점과 구매 결제 수단이 처리되는 시점 사이에 하나 이상의 추가 단계가 필요한 거래를 지원합니다. Google에서 사용자의 결제 수단으로 요금이 청구되었다는 알림을 받을 때까지 앱에서 이러한 유형의 구매에 자격을 부여해서는 안 됩니다.

예를 들어 사용자는 결제 방법으로 현금을 선택하여 인앱 상품의 PENDING 구매를 생성할 수 있습니다. 그런 다음, 사용자는 거래를 완료할 오프라인 상점을 선택하고 알림과 이메일을 통해 코드를 수신할 수 있습니다. 사용자는 오프라인 상점에 도착하면 계산원에게 코드를 사용하여 현금으로 결제할 수 있습니다. 그러면 Google은 개발자와 사용자 모두에게 현금이 수령되었음을 알립니다. 다음으로, 앱에서 사용자에게 자격을 부여할 수 있습니다.

대기 중인 구매를 사용 설정하려면 앱 초기화의 일부로 enablePendingPurchases()를 호출하세요.

앱이 PurchasesUpdatedListener를 통해 또는 queryPurchases()를 호출한 결과로 새 구매를 수신할 때 getPurchaseState() 메서드를 사용하여 구매 상태가 PURCHASED인지 또는 PENDING인지 확인합니다. 상태가 PURCHASED인 경우에만 자격을 부여해야 합니다. 사용자가 구매를 완료할 때 앱이 실행 중이면 PurchasesUpdatedListener가 다시 호출되며 PurchaseState는 이제 PURCHASED입니다. 이 시점에서 앱은 일회성 구매 처리를 위한 표준 메서드를 사용하여 구매를 처리할 수 있습니다. 또한 앱이 실행되지 않는 동안 PURCHASED 상태로 전환된 구매를 처리하려면 앱의 onResume()onCreate() 메서드에서 queryPurchases()를 호출해야 합니다.

또한 앱에서 OneTimeProductNotifications를 수신 대기하여 대기 중인 구매에 실시간 개발자 알림을 사용할 수 있습니다. 구매가 PENDING에서 PURCHASED로 전환되면 앱에서 ONE_TIME_PRODUCT_PURCHASED 알림을 수신합니다. 구매가 취소되면 앱에서 ONE_TIME_PRODUCT_CANCELED 알림을 수신합니다. 이 이벤트는 고객이 필수 기간 내에 결제를 완료하지 않은 경우에 발생할 수 있습니다. 이러한 알림 수신 시 Purchases.productsPENDING 상태가 포함된 Google Play Developer API를 사용할 수 있습니다.