본문 바로가기
오늘은 뭘 배울까?/삽질 기록

결제 구현 중 productDetailsList가 empty list를 받아오는 이유

by Kim Juhwan 2023. 11. 2.

1. 증상
2. 원인 및 해결 방법
   2-1. 타이밍 이슈
   2-2. 플레이 콘솔에 상품을 제대로 등록했는가?
   2-3. 패키지명이 일치하는가?

 

 

 


 

 

1. 증상

요즘 구독 결제 기능을 개발하고 있는데

플레이 콘솔에 등록된 상품 정보를 불러오지 못하는 이슈가 있었다.

 

코드를 정확히 짚어서 말해보자면

    private fun querySubscriptionProductDetails() {
        val params = QueryProductDetailsParams.newBuilder()

        val productList: MutableList<QueryProductDetailsParams.Product> = arrayListOf()
        for (product in LIST_OF_SUBSCRIPTION_PRODUCTS) {
            productList.add(
                QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(product)
                    .setProductType(BillingClient.ProductType.SUBS)
                    .build(),
            )
        }

        params.setProductList(productList).let { productDetailsParams ->
            billingClient.queryProductDetailsAsync(productDetailsParams.build(), this) // 1. 여기까지 도달하면
        }
    }

    override fun onProductDetailsResponse( // 2. 이 함수가 콜백되면서
        billingResult: BillingResult,
        productDetailsList: MutableList<ProductDetails>, // 3. 여기에 상품 정보가 들어있어야 한다.
    ) {
        val response = BillingResponse(billingResult.responseCode)
        val debugMessage = billingResult.debugMessage
        
        .
        .
        .
    }

 

queryProductDetailsAsync가 실행되면 비동기로 상품 정보를 불러오고

상품 정보를 불러오면 onProductDetailsResponse가 콜백 된다.

그리고 productDetailsList안에는 불러온 상품 정보가 있어야 한다.

 

근데..! 근데!!!!!! 왜 empty list인 거냐고!!!!

정말 이걸로 몇 시간을 삽질했는지 모르겠다.

 

나처럼 바보같이 삽질하는 사람이 한 명쯤은 있지 않을까? 

오늘도 그 한 명을 위해 기록을 남겨본다. 🫠

 

2. 원인 및 해결 방법

나의 경우는 [목차 2-3]에서 소개할 원인 때문에 안 됐던 거고...

삽질하면서 원인이 될만한 다른 이유들도 몇 개 알게 되어서 먼저 적어보려 한다.

 

2-1. 타이밍 이슈

  1. billingClient를 통해 연결 시도
  2. 연결 완료
  3. 상품 정보 요청
  4. 상품 정보 받음. 야호!

올바른 순서는 위와 같다.

 

  1. billingClient.startConnection(this) - 연결 시도
  2. onBillingSetupFinished - 연결 완료
  3. queryProductDetailsAsync - 상품 정보 요청
  4. productDetailsList - 상품 정보 받음

코드로 보면 위와 같다.

만약 본인이 짠 코드가 위의 순서대로 흘러가지 않는다면 타이밍 이슈를 의심해봐야 한다.

연결이 완료되기도 전에 상품 정보를 요청하면 당연히 상품 정보는 empty list일 수밖에 없다.

 

2-2. 플레이 콘솔에 상품을 제대로 등록했는가?

플레이콘솔 - 출시한 앱 - 수익 창출 - 제품 - 정기 결제(혹은 인앱 상품)

 

productDetailsList에 들어있는 값은 플레이 콘솔에서 만든 상품의 상세 정보이다.

이 글을 검색해서 들어온 경우라면

순서상 상품은 등록했었을 테니 웬만해서 문제는 없겠으나 한 번 더 검토하자.

 

                QueryProductDetailsParams.Product.newBuilder()
                    .setProductId(product) // 찾으려는 상품 ID
                    .setProductType(BillingClient.ProductType.SUBS) // 구독 상품인지, 인앱 상품인지
                    .build(),

콘솔에 등록한 상품의 ID와 내가 찾으려는 상품 ID가 일치하는가?

콘솔에 등록한 상품이 구독 상품인가 인앱 상품인가?

나의 경우는 구독 상품이기 때문에 ProductType.SUBS를 넘겨주었다.

 

2-3. 패키지명이 일치하는가?

드디어 나왔다. 내가 삽질한 이유

상품 정보가 등록된 앱의 패키지 명과

지금 빌드 중인 앱의 패키지 명이 일치하는지 확인해 보자.

 

...
applicationIdSuffix = ".debug"
...

우리 팀이 개발 중인 앱은 Build Variants에 따라 패키지명을 다르게 사용하고 있다.

위 코드처럼 debug 환경일 때는 suffix로 .debug를 붙이고 있다.

근데 이게 문제가 될 줄이야 (이마 탁 🤦🏻‍♂️)

 

패키지 명이 다르면 등록된 앱의 상품 정보를 가져오지 못한다.

너무나 당연한 사실을... 난 한참 동안 깨닫지 못하고... 몇 시간을... 따흐흑... ㅠㅠㅠ

 

 

 

 

참고로 빌드된 앱에서 상품 정보까지는 가져올 수 있는데

실제로 결제가 진행되는 플로우를 테스트하려면

플레이 콘솔에 내부 테스트나 비공개 테스트로 올려야만 가능하다.

IDE에서 그냥 바로 빌드한 앱은 결제를 진행하려고 해도 인앱 결제 바텀시트까지만 뜨지 진행은 안된다.

 

아무튼 현타가 와서 주저리주저리 적어봤는데

누군가에게 이 글이 꼭 도움이 되길

 

 


💡 느낀 점

  • 삽질은 했지만 덕분에 코드 하나하나 다 뜯어보는 기회가 되었다. 현타와 뿌듯함이 공존하는 미묘 복잡한 기분
  • billing 버전이 최근에 6으로 올라가면서 함수들도 바뀌는 바람에 정보가 많이 없어 더 헤맸던 것 같다 😢
    빨리 개발 끝내고 6 버전 사용법을 포스팅 해야겠으...

📘 참고한 자료


 

 

반응형

댓글