본문 바로가기

Android 공부/Android Library Study

안드로이드 인앱결제 - 구매 로직


안녕하세요!

- '담다' 시간표를 개발하는 감자튀김입니다.


개인 개발을 하면서 광고를 보여주기에는 UI/UX가 불편할 것 같고, 어떻게 하면 좋을까? 하다가 '인앱결제'로 광고제거를 하면 좋겠다라는 생각으로 인앱결제 기능을 만들기 시작했습니다.


안드로이드 개발자 사이트(https://developer.android.com/google/play/billing/billing_library_overview?hl=en)

에서는 이와 같이 친절하게 알려주고 있습니다만, 봐도 모르는게 초보 개발자의 특징이겠죠. 그래서, 이렇게만 하면 된다는 한글 가이드가 필요할 것 같아서 짧게 블로그 씁니다.


0. 종속성부터 추가하자.


깔끔하게 종속성을 부여해줍시다.


1
2
3
4
dependencies {
    ...
    implementation 'com.android.billingclient:billing:2.0.1'
}
cs


1. connectBilling()


해당 developer의 내용과는 다르게 Builder를 만들기 위해서는 enablePendingPurchases()를 추가해서 Builder를 만들어야 하더라구여. 저는 billingClient와 연결할 수 있도록 connectBilling() 함수를 만들었고, startConnect와 동시에 showPurchaseDialog() 라는 메소드를 호출해, 구매 대화상자를 보여주도록 했습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun connectBillingClient(){
        val billingClient = BillingClient.newBuilder(activity).enablePendingPurchases().setListener(this).build()
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready. You can query purchases here.
                    showPurchaseDialog(billingClient)
                }
            }
            override fun onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
 
            }
        })
    }
cs


2. 자신이 등록한 물품의 id를 skuList에 등록해주자.



인앱결제로 저는 저의 앱에서 사용할 제품을 미리 등록시켜놨고, 그에 대한 id로 remove_id를 사용하고 있습니다.

그래서 Remove Ads를 어플리케이션에서 보여줘야 하고, skuList에 미리 만들어놓은 id를 추가해주고, SkuDetailsParams에 자신이 사용하고 있는 id들을 등록해주고, Builder를 만들어주고 리턴합니다.


1
2
3
4
5
6
7
private fun makeBillingParams(): SkuDetailsParams.Builder {
        val skuList = ArrayList<String>()
        skuList.add("remove_ad")
        return SkuDetailsParams.newBuilder().apply{
            setSkusList(skuList).setType(BillingClient.SkuType.INAPP)
        }
}
cs

3. showPurchaseDialog()


 그래서 여태까지 만든 billingClinent 객체와 SkuDetailsParams를 활용해서 구매 대화상자를 보여지도록 합니다. makeBillingParams() 함수는 skuList를 기반으로 쿼리가 만들어지고, 해당 쿼리를 querySkuDetailAsync에 넣게 되면, billinngResult에 skuDetailsList라는 자신이 개발자 사이트에서 등록한 물품을 볼 수 있습니다.

 그래서 if("remove_ad" == sku)를 통해서 복수 개의 아이템을 처리할 수 있으며, billingClient.launchBilloingFlow()를 호출하게 되면, 대화상자가 나오게 됩니다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun showPurchaseDialog(billingClient: BillingClient){
        // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
        billingClient.querySkuDetailsAsync(makeBillingParams().build()) { billingResult, skuDetailsList ->
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && skuDetailsList != null) {
                for (skuDetails in skuDetailsList) {
                    val sku = skuDetails.sku
                    val price = skuDetails.price
                    if ("remove_ad" == sku) {
                        val flowParams = BillingFlowParams.newBuilder()
                                .setSkuDetails(skuDetails)
                                .build()
                        val responseCode = billingClient.launchBillingFlow(activity, flowParams)
                    }
                }
            }
        }
    }
cs


4. 구매후 로직을 처리하자.


 그리고 코드를 구현하고 있는 클래스에 PurchaseUpdatedListener를 상속받게 되면, interface에 의해서 onPurchaseUpdated를 구현해야하는데, 이 때, billingResult라는 값이 나오게 되고, 구매하게 되면 해당 로직을 처리할 수 있도록 코드가 유도된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BillingManager(val activity: Activity) : PurchasesUpdatedListener {
    //...
    override fun onPurchasesUpdated(billingResult: BillingResult?, purchases: MutableList<Purchase>?) {
        if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
            for (purchase in purchases) {
                //handlePurchase(purchase)
            }
        } else if (billingResult?.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
            // Handle an error caused by a user cancelling the purchase flow.
        } else {
            // Handle any other error codes.
        }
    }
}
cs


5. 끝인 줄 알았는데, 이렇게 하면 안 되더라... 모든게 환불 처리가 되고 있더라..

 구매 후에는 구매했다고 소비를 해야하는 로직이 필요하다. 다시 onPurchasedUpdated 로직으로 돌아간다.

이어서... 새로운 에디터를 통해서 글을 이어서 써보려고 합니다.


인앱결제 - 구매하고 난 후 : https://gamjatwigim.tistory.com/109