developer.android.com/google/play/billing/integrate
앱에 Google Play 결제 라이브러리 통합 | Google Play 결제 시스템 | Android Developers
중요: 앱이 현재 AIDL을 사용하여 Google Play 결제 시스템을 통합하는 경우 AIDL에서 Google Play 결제 라이브러리로 이전하는 데 필요한 단계에 관한 대략적인 개요는 AIDL에서 Google Play 결제 라이브러리
developer.android.com
BillingClient 인스턴스 초기화
- BillingClient
- Google Play 결제 라이브리와 나머지 앱간의 통신을 위한 기본 인터페이스
- 일반적적인 결제 작업 편의 메서드(동기 메스드, 비동기 메서드)를 제공
- 생성 : newBuilder() 사용
- 구매 관련 업데이트 수신 : setListenerI()를 호출하여 PurchaseUpdatedListener에 대한 참조를 전달
private val purchasesUpdateListener =
PurchasesUpdatedListener { billingResult, purchases ->
// To be implemented in a later section.
}
private var billingClient = BillingClient.newBuilder(activity)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build()
Google Play 연결 설정
- startConnection() : Google Play에 연결. 연결 프로세스는 비동기적
- 클라이언트 설정이 완료되고 추가로 요청할 준비가 되면 BillingClientStateListenr를 구현하여 콜백 수신
- onBillingSetUpFinished() : BillingClient 준비됨, 구매 가능한 제품을 쿼리하여 사용자에게 표시할 준비 완료
- onBillingServiceDisconnected() : Google Play와 연결이 끊어진 문제 처리(재시도 로직). 콜백 메서드를 재정의하고 추가 요청을 하기 전에 BillingClient가 startConnection()을 호출하여 Google Play에 다시 연결하도록 해야 함
Google Play 연결
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.
}
})
구입 가능한 제품 표시
- querySkuDetailAsync()
- Google Play에 인앱 상품의 현지화 된 세부 정보 쿼리
- SkuType : INAPP/SUBS
- Google Play Console에서 생성된 제품 ID
- 문자열 목록을 지정하는 SkuDetailsParams의 인스턴스
- Google Play에 인앱 상품의 현지화 된 세부 정보 쿼리
- SkuDetailResponsListenr
- 비동기 작업의 결과 처리
- 쿼리가 완료되면 리스너에게 알리는 onSkuDetailResponse() 재정의 가능
fun querySkuDetails() {
val skuList = ArrayList<String>()
skuList.add("premium_upgrade")
skuList.add("gas")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skuList).setType(SkuType.INAPP)
withContext(Dispatchers.IO) {
billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
// Process the result.
}
}
}
- 정기결제
- 모든 인앱 프로모션 또는 스플래시 화면에서 명확한 정보를 전달하는 것이 중요
- 정기결제 가격, 결제 주기 빈도, 앱을 사용하려면 정기 결제가 필요한지 등 혜택 조건을 명확하게 전달
- sku이름이 정기결제 성격을 명확하게 전달해야함
- 혜택은 사용자가 모두 이해할 수 있도록 이용약관과 독일한 언어로 현지화 되어 있어야 함
- 정기 결제 관리 또는 취소할 수 있는 방법이 앱에 분명하게 설명되어야 함
- 모든 인앱 프로모션 또는 스플래시 화면에서 명확한 정보를 전달하는 것이 중요
구매 흐름 시작
- launchBillingFlow() : 구매 요청 시작
- querySkuDetailsAsync() 호출하여 받은 SkuDetails 객체가 포함된 BillingFolowParams 객체 참조
- BillingFlowParams 객체를 생성하려면 BillingFlowParams.Builder 클래스 사용
- BillingClient.BillingClient.BillingResponseCode의 응답 코드 반환(OK : 성공적으로시작)
- 성공하면 시스템에서 Google Play 구매 화면 표시
val flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetails)
.build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
- 구매 결과
- Google Play가 onPurchaseUpdated()를 호출하여 PurchaseUpdatedListener 인터페이스를 구현하는 리스너에 구매 작업 결과를 전성
- 리스너는 클라이언트를 초기화할 때 seListener() 메소드를 사용하여 지정
- 구매 성공 : 사용자가 구매한 인앱 상품의 사용자 및 제품 ID를 나타내는 고유 식별자인 구매 토큰 생성
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.
}
}
구매 처리
- 대부분의 경우 앱은 PurchaseUpdatedListenr를 통해 구매 알림을 받으나, BillingClient.queryPurchases() 호출을 인식하는 경우가 있음
- 구매인증
- 구매상태 (PURCHASED, PENDING)
- PurchaseUpdatedListener 또는 queryPurchases에서 수신한 구매의 경우 앱이 자격을 부여하기 전에 구매를 추가로 인증하여 정당성을 확인해야함.
- 구매인증을 했다면 사용자에게 자격을 부여할 준비가 된 것, 자격을 부여한 후 구매를 확인해야 함(구매와 관련된 자격을 부여했음을 Google Play에 알려줌
- 2.0 이상의 Google 결제 라이브러리 사용 시 3일이내 구매 확인을 하지 않으면 사용자는 자동 환불(Google Play 구매 취소)
- 구매 인증 후 사용자에게 콘텐츠 제공 및 전송 확인(선택 적으로 사용자가 다시 구입할 수 있도록 항목을 소비됨으로 표시)
- 소비성인증
- consumeAsync()를 호출하고 Google Play에서 다시 구매할 수 있게 하매 토큰을 포함
- ConsumeResposneListener(서비 작업의 결과 처리) 인터페이스를 구현하는 객체를 전달해야 함
- 작업 완료시 Goolge Play 결제 라이브러리가 호출하는 onConsumeResponse() 메소드를 재정의 할 수 있음
- 소비 요청이 실패 할 수 있으므로 보안 백엔드 서버를 확인하여 각 구매 토큰이 사용하지 않았는지 확인해야 함
- 앱이 동일한 구매에 대해 여러 번 자격을 부여하지 않음
- 자격을 부여하기 전 앱이 Google Play에서 성공적인 소비응답을 받을 때까지 기다릴 수 있음
- Google Play에서 성공적인 소비 응답을 보낼 때까지 사용자의 구매를 보류하도록 선택하는 경우 소비 요청 이후 구매 추척을 놓치지 않도록 주의해야함
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.
}
})
}
- 비소비성 및 정기 결제 인증
- 최초 정기 결제 구매는 모두 확인해야 함(정기 결제 갱신은 확인하지 않아도 됨)
- isAcknowlege()메서드를 통해 앱에서 이미 구매를 확인했는지 검토
- 구매 확인이 되지 않았으면 BillingClient.acknowlegePurchase()로 비소비성 구매 확인
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())
}
}
}
}
구매 가져오기
- PurchaseUpdatedListener를 사용하여 구매 업데이트를 수신 대기하는 것만으로는 앱이 모든 구매를 처리하도록 보장할 수 없음
- 구매 중 네트워크 문제 : 사용자가 구매를 성공적으로 완료하고 Google Play에 확인을 받았지만 PurchaseUpdatedListener를 통해 구매 알림을 받기 전 네트워크 연결이 끊김
- 여러기기 : 사용자는 한 기기에서 항목을 구입한 후 기기를 전환할 때
- 앱 외부에서 이루어진 구매 처리 : 프로모션 사용과 같은 일부 구매
- 위 이유 등으로 구매 항목을 인식하지 못하는 경우
- onResume() 및 onCreate()에서 OnBillingClient.queryPurchases()를 호출하여 구매 처리
'Effective * > Effective Android' 카테고리의 다른 글
[Google Play 인앱 개념] 계정보류/복원/일시정지/재구독 (1) | 2021.01.20 |
---|---|
[웹뷰] 활용 (0) | 2021.01.16 |
[웹뷰] 기본 속성 (0) | 2021.01.13 |
[Google Play 인앱 개념] 결제 라이브러리 통합 1 (0) | 2021.01.11 |