경고: AIDL은 현재 지원이 중단되었으며 향후 출시되는 버전에서 삭제될 예정입니다. 결제 관련 기능을 구현하려면 Google Play 결제 라이브러리를 사용하세요.
Android 인터페이스 정의 언어(AIDL) 인터페이스를 사용하여 Google Play 결제 시스템의 일부 기능을 구현할 수 있습니다.
제품 구매

그림 1. 구매 요청의 기본 시퀀스
Google Play Billing AIDL API를 사용한 일반적인 구매 흐름은 다음과 같습니다.
- 애플리케이션에서 Google Play로
isBillingSupported
요청을 전송하여 사용 중인 Google Play Billing AIDL API의 대상 버전이 지원되는지 확인합니다. 이 요청은 Google Play에서 사용자 국가의 결제를 지원하는지도 확인합니다. - 애플리케이션이 시작되거나 사용자가 로그인하면, Google Play를 통해 사용자가 어떤 항목을 소유하고 있는지 확인하는 것이 좋습니다. 사용자의 인앱 구매를 쿼리하려면
getPurchases
요청을 보내세요. 요청에 성공하면 Google Play는 구매한 항목의 제품 ID 목록, 개별 구매 세부정보 목록, 구매를 위한 서명 목록이 포함된Bundle
을 반환합니다. - 일반적으로, 사용자에게 구매할 수 있는 제품을 알리는 것이 좋습니다. Google Play에서 정의한 인앱 상품의 세부정보를 쿼리하기 위해 애플리케이션이
getSkuDetails
요청을 보낼 수 있습니다. 쿼리 요청에 제품 ID 목록을 지정해야 합니다. 요청에 성공하면 Google Play는 상품의 가격, 제목, 설명, 구매 유형을 비롯한 상품 세부정보가 포함된Bundle
을 반환합니다. - 사용자가 인앱 상품을 소유하지 않은 경우 이 상품의 구매를 개시할 수 있습니다. 구매 요청을 시작하기 위해, 애플리케이션은 다른 매개변수와 함께 구매할 아이템의 제품 ID를 지정하는
getBuyIntent
요청을 보냅니다. Google Play Console에서 새 인앱 상품을 만들 때 제품 ID를 기록해야 합니다.- Google Play는 애플리케이션이 구매를 위한 결제 UI를 시작하기 위해 사용하는
PendingIntent
가 포함된Bundle
을 반환합니다. - 애플리케이션은
startIntentSenderForResult
메서드를 호출하여 보류 중인 인텐트를 시작합니다. - 결제 흐름이 완료되면(즉, 사용자가 성공적으로 항목을 구매하거나 구매를 취소하면) Google Play에서 응답
Intent
를onActivityResult
메서드로 보냅니다.onActivityResult
의 결과 코드에 구매의 성공 또는 취소 여부를 나타내는 결과 코드가 포함됩니다. 응답Intent
에는 이 구매 트랜잭션을 고유하게 식별하기 위해 Google Play에서 생성한purchaseToken
문자열을 비롯하여 구매한 항목에 대한 정보가 포함됩니다.Intent
에는 비공개 개발자 키를 사용하여 서명한 구매의 서명도 포함됩니다.
- Google Play는 애플리케이션이 구매를 위한 결제 UI를 시작하기 위해 사용하는
Google Play Billing AIDL API 호출과 서버 응답에 관한 자세한 내용은 Google Play Billing AIDL API 참조를 확인하세요.
인앱 상품 소비
소비 메커니즘을 사용하면 관리 제품의 사용자 소유권을 추적할 수 있습니다.
모든 관리 제품은 Google Play Billing AIDL API에서 관리됩니다. 즉, Google Play에서 모든 관리 제품 구매에 대한 사용자의 소유권이 유지 관리되고, 필요할 경우 애플리케이션이 사용자의 구매 정보를 쿼리할 수 있다는 뜻입니다. 사용자가 관리 제품을 성공적으로 구매하면 Google Play에 구매 내역이 기록됩니다. 관리 제품이 구매되면 '소유된' 것으로 간주됩니다. '소유된' 상태의 관리 제품은 Google Play에서 구매할 수 없습니다. '소유된' 관리 제품을 Google Play에서 다시 구매할 수 있는 상태로 바꾸려면 이에 대한 소비 요청을 보내야 합니다. 관리 제품을 소비하면 '소유되지 않은' 상태로 되돌아가며 이전 구매 데이터는 삭제됩니다.

그림 2. 소비 요청의 기본 시퀀스
사용자가 소유한 상품의 목록을 검색하기 위해 애플리케이션에서 Google Play에 getPurchases
호출을 보냅니다. 애플리케이션에서는 consumePurchase
호출을 보내 소비 요청을 만들 수 있습니다. Google Play에서 구매 시 획득한 관리 제품의 고유한 purchaseToken
문자열을 요청 인수에 지정해야 합니다. Google Play에서 소비가 성공적으로 기록되었는지 나타내는 상태 코드를 반환합니다.
비소비성/소비성 관리 제품
관리 제품을 비소비성 항목으로 처리할지 소비성 항목으로 처리할지는 개발자에게 달려 있습니다.
- 비소비성 제품
- 일반적으로 개발자는 애플리케이션에서 한 번만 구매하면 영구적인 혜택을 제공하는 관리 제품의 소비를 구현하지 않습니다. 사용자가 구매한 제품은 사용자의 Google 계정과 영구적으로 연결됩니다. 비소비성 관리 제품의 예로는 프리미엄 업그레이드나 레벨 팩이 있습니다.
- 소비성 제품
-
반면 개발자는 여러 번 구매가 가능한 제품의 소비를 구현할 수 있습니다. 일반적으로 이러한 제품은 일시적으로 특정한 효과를 제공합니다. 예를 들어 사용자의 인게임 캐릭터는 인벤토리에서 생명 포인트를 획득하거나 추가 금화를 얻을 수 있습니다. 애플리케이션에서 구매한 제품의 혜택이나 효과를 제공하는 것을 관리 제품의 프로비저닝이라고 합니다. 개발자는 관리 제품이 사용자에게 프로비저닝되는 방식을 제어하고 추적해야 합니다.
중요: 애플리케이션에서 소비성 관리 제품을 프로비저닝하기 전에, 먼저 소비 요청을 Google Play에 보내고 소비가 기록되었음을 나타내는 성공 응답을 받아야 합니다.
애플리케이션에서 소비성 구매 관리
다음은 소비성 관리 제품 구매의 기본적인 흐름입니다.
getBuyIntent
메서드를 호출하여 구매 흐름을 시작합니다.- Google Play에서 반환된
Bundle
을 검사하여 구매가 성공적으로 완료되었는지 확인합니다. - 구매가 성공적이었으면
consumePurchase
메서드를 호출하여 구매를 소비합니다. - Google Play의 응답 코드를 검사하여 소비가 성공적으로 완료되었는지 확인합니다.
- 소비에 성공한 경우 애플리케이션에서 제품을 프로비저닝합니다.
이후에 사용자가 애플리케이션을 시작하거나 애플리케이션에 로그인하면 개발자는 사용자가 미결제 상태의 소비성 인앱 상품을 소유하고 있는지 확인해야 합니다. 사용자가 소비성 인앱 상품을 소유한 경우 해당 상품을 소비하고 프로비저닝해야 합니다. 다음은 애플리케이션에서 소비성 인앱 상품을 구현하는 경우 권장되는 애플리케이션 시작 흐름입니다.
getPurchases
요청을 보내 사용자가 소유한 인앱 상품이 있는지 쿼리합니다.- 소비성 인앱 상품이 있는 경우
consumePurchase
를 호출하여 항목을 소비합니다. 애플리케이션에서 소비성 제품의 구매주문서를 완료했지만 소비 요청을 보낼 기회가 생기기 전에 중지되거나 연결이 끊겼을 수 있기 때문에 이 단계가 필요합니다. - Google Play의 응답 코드를 검사하여 소비가 성공적으로 완료되었는지 확인합니다.
- 소비에 성공한 경우 애플리케이션에서 제품을 프로비저닝합니다.
리워드 구매 구성
경고: 리워드 제품은 더 이상 지원되지 않습니다. 자세한 내용은 리워드 제품 만들기를 참조하세요.
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 고객센터에 설명되어 있습니다.
리워드 요청의 대상이 아동 또는 동의 연령 미만의 사용자임을 나타내려면 다음 코드 스니펫에 나와 있는 것처럼 childDirected
및 underAgeOfConsent
추가 매개변수를 포함합니다.
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 Billing AIDL API를 사용하여 이 정보를 더 자주 쿼리할 수 있습니다. 다음 Google Play Billing AIDL API 호출은 네트워크 연결 없이 캐시 조회를 통해 처리되므로 API의 응답 시간이 크게 단축됩니다.
getBuyIntent
getPurchases
isBillingSupported