Register now for Android Dev Summit 2019!

AIDL로 Google Play 결제 사용

경고: AIDL은 현재 지원이 중단되었으며 향후 출시되는 버전에서 삭제될 예정입니다. Google Play 결제 기능을 구현하려면 Google Play 결제 라이브러리를 사용하세요.

AIDL(Android 인터페이스 정의 언어) 인터페이스를 사용해 Google Play 결제 서비스의 일부 기능을 구현할 수 있습니다.

제품 구매

그림 1. 구매 요청의 기본 시퀀스

Google Play 결제 API를 사용한 일반적인 구매 흐름은 다음과 같습니다.

  1. 애플리케이션에서 isBillingSupported 요청을 Google Play에 보내 사용 중인 Google Play 결제 API의 타겟 버전이 지원되는지 확인합니다. 이 요청은 Google Play에서 사용자 국가의 결제를 지원하는지도 확인합니다.
  2. 애플리케이션이 시작되거나 사용자가 로그인하면, Google Play를 통해 사용자가 어떤 항목을 소유하고 있는지 확인하는 것이 좋습니다. 사용자의 구매 항목을 쿼리하려면 getPurchases 요청을 보냅니다. 요청이 성공하면 Google Play에서 구매한 항목의 제품 ID 목록, 개별 구매 항목의 세부정보 목록, 구매를 위한 서명 목록이 포함된 Bundle을 반환합니다.
  3. 일반적으로, 사용자에게 구매할 수 있는 제품을 알리는 것이 좋습니다. 애플리케이션에서 getSkuDetails 요청을 보내 Google Play에 정의한 인앱 상품의 세부정보를 쿼리할 수 있습니다. 이때 쿼리 요청에 제품 ID 목록을 지정해야 합니다. 요청이 성공하면 Google Play에서 제품 가격, 제목, 설명, 구매 유형을 비롯한 제품 세부정보가 포함된 Bundle을 반환합니다.
  4. 사용자가 인앱 상품을 소유하지 않은 경우 이 상품의 구매를 개시할 수 있습니다. 구매 요청을 시작하려면 애플리케이션에서 getBuyIntent 요청을 보내 구매할 항목의 제품 ID를 다른 매개변수와 함께 지정합니다. Google Play Console에서 새 인앱 상품을 만들 때 제품 ID를 기록해야 합니다.
    1. Google Play는 애플리케이션에서 구매용 체크아웃 UI를 시작하는 데 사용하는 PendingIntent가 포함된 Bundle을 반환합니다.
    2. 애플리케이션은 startIntentSenderForResult 메서드를 호출하여 대기 중인 인텐트를 실행합니다.
    3. 체크아웃 흐름이 완료되면(즉, 사용자가 성공적으로 항목을 구매하거나 구매를 취소하면) Google Play에서 응답 IntentonActivityResult 메서드로 보냅니다. onActivityResult의 결과 코드에 구매의 성공 또는 취소 여부를 나타내는 결과 코드가 포함됩니다. 응답 Intent에는 이 구매 트랜잭션을 고유하게 식별하기 위해 Google Play에서 생성한 purchaseToken 문자열을 비롯하여 구매한 항목에 대한 정보가 포함됩니다. Intent에는 비공개 개발자 키로 서명된 구매 서명도 포함됩니다.

Google Play 결제 API 호출과 서버 응답에 대한 자세한 내용은 Google Play 결제 참조 문서를 참고하세요.

인앱 상품 소비

소비 메커니즘을 사용하면 관리되는 제품의 사용자 소유권을 추적할 수 있습니다.

모든 관리되는 제품은 Google Play Billing API에서 관리됩니다. 즉, 모든 관리되는 제품 구매의 사용자 소유권을 Google Play에서 유지 관리하며, 필요한 경우 애플리케이션에서 사용자의 구매 정보를 쿼리할 수 있습니다. 사용자가 관리되는 제품을 성공적으로 구매하면 Google Play에 구매 내역이 기록됩니다. 관리되는 제품이 구매되면 '소유된' 것으로 간주됩니다. '소유된' 상태의 관리되는 제품은 Google Play에서 구매할 수 없습니다. '소유된' 관리되는 제품을 Google Play에서 다시 구매할 수 있는 상태로 바꾸려면 이에 대한 소비 요청을 보내야 합니다. 관리되는 제품을 소비하면 '소유되지 않은' 상태로 되돌아가며 이전 구매 데이터는 삭제됩니다.

그림 2. 소비 요청의 기본 시퀀스

사용자가 소유한 제품의 목록을 검색하기 위해 애플리케이션에서 Google Play에 getPurchases 호출을 보냅니다. 애플리케이션에서는 consumePurchase 호출을 보내 소비 요청을 만들 수 있습니다. Google Play에서 구매 시 획득한 관리되는 제품의 고유한 purchaseToken 문자열을 요청 인수에 지정해야 합니다. Google Play에서 소비가 성공적으로 기록되었는지 나타내는 상태 코드를 반환합니다.

비소비성/소비성 관리되는 제품

관리되는 제품을 비소비성 항목으로 처리할지 소비성 항목으로 처리할지는 개발자에게 달려 있습니다.

비소비성 제품
일반적으로 개발자는 애플리케이션에서 한 번만 구매하면 영구적인 혜택을 제공하는 관리되는 제품의 소비를 구현하지 않습니다. 사용자가 구매한 제품은 사용자의 Google 계정과 영구적으로 연결됩니다. 비소비성 관리되는 제품의 예로는 프리미엄 업그레이드나 레벨 팩이 있습니다.
소비성 제품
반면 개발자는 여러 번 구매가 가능한 제품의 소비를 구현할 수 있습니다. 일반적으로 이러한 제품은 일시적으로 특정한 효과를 제공합니다. 예를 들어 사용자의 인게임 캐릭터는 인벤토리에서 생명 포인트를 획득하거나 추가 금화를 얻을 수 있습니다. 애플리케이션에서 구매한 제품의 혜택이나 효과를 제공하는 것을 관리되는 제품의 프로비저닝이라고 합니다. 개발자는 관리되는 제품이 사용자에게 프로비저닝되는 방식을 제어하고 추적해야 합니다.

중요: 애플리케이션에서 소비성 관리되는 제품을 프로비저닝하기 전에, 먼저 소비 요청을 Google Play에 보내고 소비가 기록되었음을 나타내는 성공 응답을 받아야 합니다.

애플리케이션에서 소비성 구매 관리

다음은 소비성 관리되는 제품 구매의 기본적인 흐름입니다.

  1. getBuyIntent 메서드를 호출하여 구매 흐름을 시작합니다.
  2. Google Play에서 반환된 Bundle을 검사하여 구매가 성공적으로 완료되었는지 확인합니다.
  3. 구매가 성공적이었으면 consumePurchase 메서드를 호출하여 구매를 소비합니다.
  4. Google Play의 응답 코드를 검사하여 소비가 성공적으로 완료되었는지 확인합니다.
  5. 소비에 성공한 경우 애플리케이션에서 제품을 프로비저닝합니다.

이후에 사용자가 애플리케이션을 시작하거나 애플리케이션에 로그인하면 개발자는 사용자가 미결제 상태의 소비성 인앱 상품을 소유하고 있는지 확인해야 합니다. 사용자가 소비성 인앱 상품을 소유한 경우 해당 상품을 소비하고 프로비저닝해야 합니다. 다음은 애플리케이션에서 소비성 인앱 상품을 구현하는 경우 권장되는 애플리케이션 시작 흐름입니다.

  1. getPurchases 요청을 보내 사용자가 소유한 인앱 상품이 있는지 쿼리합니다.
  2. 소비성 인앱 상품이 있는 경우 consumePurchase를 호출하여 항목을 소비합니다. 애플리케이션에서 소비자 제품의 구매주문서를 완료했지만 소비 요청을 보낼 기회가 생기기 전에 중지되거나 연결이 끊겼을 수 있기 때문에 이 단계가 필요합니다.
  3. Google Play의 응답 코드를 검사하여 소비가 성공적으로 완료되었는지 확인합니다.
  4. 소비에 성공한 경우 애플리케이션에서 제품을 프로비저닝합니다.

리워드 구매 구성

AIDL을 사용하여 리워드 제품 작업을 하는 경우 사용자가 리워드를 수집하기 전에 구매 `인텐트`를 캐시해야 합니다. 사용자가 리워드 수집을 위한 조치를 취할 때까지 백그라운드 스레드에서 구매 인텐트를 호출하고 성공 응답 `인텐트`를 저장할 수 있습니다.

SKU 나열 및 로드

사용자에게 리워드 제품을 제공하기 전에 getSkuDetails()를 호출하여 제품 세부정보를 확인합니다. 새 JSON 필드인 'rewardToken'은 SKU 목록의 각 리워드 제품에 맞게 채워집니다.

최고의 사용자 환경을 제공하려면 사용자에게 리워드 제품을 제공하기 전에 광고가 로드되어 제공되어야 합니다. 그렇게 하려면 백그라운드 스레드에서 getBuyIntentExtraParams()를 호출합니다. BILLING_RESPONSE_RESULT_OK 응답을 받은 후 사용자가 리워드 제품을 사용할 수 있도록 설정하고, 반환된 PendingIntent 개체를 나중에 사용할 수 있도록 저장합니다. 다음 코드 스니펫은 리워드 제품과 관련된 광고를 로드하는 절차를 보여줍니다.

Kotlin

val rewardToken = skuDetailsJson.optString("rewardToken")
val extraParams = Bundle().putString("rewardToken", rewardToken)

// This call blocks the current thread, so do this in the background.
val buyIntentBundle : Bundle = mService.getBuyIntentExtraParams(9, packageName,
        sku, "inapp", "", extraParams)

val response = buyIntentBundle.getInt("RESPONSE_CODE")
if (response == BILLING_RESPONSE_RESULT_OK) {
    // Enable rewarded product.

    // Save this object for use later.
    val pendingIntentToSave = bundle.getParcelable(RESPONSE_BUY_INTENT)
} else {
    // Don't offer rewarded product.
}

자바

String rewardToken = skuDetailsJson.optString("rewardToken");
Bundle extraParams = new Bundle();
extraParams.putString("rewardToken", rewardToken);

// This call blocks the current thread, so do this in the background.
Bundle buyIntentBundle = mService.getBuyIntentExtraParams(9, getPackageName(),
        sku, "inapp", "", extraParams);

int response = buyIntentBundle.getInt("RESPONSE_CODE");
if (response == BILLING_RESPONSE_RESULT_OK) {
    // Enable rewarded product.

    // Save this object for use later.
    PendingIntent pendingIntentToSave = bundle.getParcelable(RESPONSE_BUY_INTENT);
} else {
    // Don't offer rewarded product.
}

연령에 적합한 광고 명시

아동 온라인 개인정보 보호법(COPPA)개인정보 보호법(GDPR)을 포함한 어린이 및 미성년 사용자 관련 법적 의무를 준수하도록 지원하기 위해, 미국에서 어린이를 대상으로 또는 해당 국가에서 콘텐츠 적합 연령 미만의 사용자를 대상으로 취급할 수 있는 광고를 앱에 명시해야 합니다. . 광고 요청을 아동 대상 서비스로 취급하도록 태그해야 하는 경우와 법정 연령 미만 사용자를 대상으로 하는 서비스로 취급하도록 태그해야 하는 경우 및 파급 효과에 관한 내용은 AdMob 고객센터에 설명되어 있습니다.

리워드 요청의 대상이 어린이 또는 동의 연령 미만 사용자임을 나타내려면 다음 코드 스니펫에 나와 있는 것처럼 childDirectedunderAgeOfConsent 추가 매개변수를 포함합니다.

Kotlin

val rewardToken = skuDetailsJson.optString("rewardToken")
val extraParams = Bundle().putString("rewardToken", rewardToken)
        .putInt("childDirected", ChildDirected.CHILD_DIRECTED)
        .putInt("underAgeOfConsent", UnderAgeOfConsent.UNDER_AGE_OF_CONSENT)

// This call blocks the current thread, so do this in the background.
val buyIntentBundle : Bundle = mService.getBuyIntentExtraParams(9, packageName,
        sku, "inapp", "", extraParams)

자바

Bundle extraParams = new Bundle();
extraParams.putString("rewardToken", rewardToken);
extraParams.putInt("childDirected", ChildDirected.CHILD_DIRECTED);
extraParams.putInt("underAgeOfConsent", UnderAgeOfConsent.UNDER_AGE_OF_CONSENT);

// This call blocks the current thread, so do this in the background.
Bundle buyIntentBundle =
  mService.getBuyIntentExtraParams(
    9, getPackageName(), sku, "inapp", "", extraParams);

사용자에게 보상하기 전에 광고 재생

사용자가 버튼을 클릭하여 광고 시청을 시작하면 앱에서는 저장된 PendingIntent 개체를 사용하여 광고 재생을 시작할 수 있습니다. 그렇게 하려면 startIntentSenderForResult()를 호출합니다.

Kotlin

startIntentSenderForResult(
    pendingIntentToSave,
    RC_BUY, Intent(),
    0,
    0,
    0
)

자바

startIntentSenderForResult(pendingIntentToSave, RC_BUY, new Intent(),
        0, 0, 0);

그 후, 다음 코드 스니펫에 나와 있는 것처럼 onActivityResult()에서 결제 워크플로 결과를 처리합니다. 광고 재생 절차를 처리할 때 Google Play는 다른 결제 흐름에서와 동일한 서버 응답 코드 집합을 사용합니다.

Kotlin

fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent) {
    if (requestCode == RC_BUY) {
        int responseCode = data.getIntExtra(RESPONSE_CODE)
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA)
        String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE)

        // Handle reward purchase.
    }
}

자바

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RC_BUY) {
        int responseCode = data.getIntExtra(RESPONSE_CODE);
        String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
        String signature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);

        // Handle reward purchase.
    }
}

로컬 캐싱

이제 Google Play 클라이언트가 Google Play 결제 정보를 기기에서 로컬로 캐시하므로 Google Play Billing API를 사용해 이 정보를 더 자주 쿼리할 수 있습니다. 다음 Google Play Billing API 호출은 네트워크 연결 없이 캐시 조회를 통해 처리되므로 API의 응답 시간이 크게 단축됩니다.

  • getBuyIntent
  • getPurchases
  • isBillingSupported