241015~241018 TIL

나고수·2024년 10월 17일

2024 TIL

목록 보기
75/94
post-thumbnail

① 배운 것

인앱구매 요청 시 이전 구매요청이 끝나지 않았는데 또 다른 구매요청을 할 경우

  • 이런 경우에 구매요청큐에 요청들이 쌓여있어 아예 새로운 요청이 거절되거나, 중간에 로직이 꼬일 수 있으므로 이번 앱에서는 아예 하나의 요청이 끝날때까지(성공하거나, 취소되거나, 에러나거나) 다른 요청을 하지 못하도록 구현했다.
  • flutter in_app_purchase 디펜던시를 사용했고, 이 라이브러리에서는 구매상태를 error, purchase, restored, canceled, pending으로 구분한다.
  • ui단에 dim처리가된(구매 버튼을 못 누르게 하기 위함) circulatorProgressbar의 표시여부를 flag값으로 만든다. 이 값은 streamPurchased에서 직접적으로 리슨하는 상태값 말고 내가 임의로 만든 구매상태값이 바뀔때마다 콜백을 받아 원하는 로직을 처리한다.
  • pending(결제중)일때는 flag값을 true로하여 프로그래스바가 보이게하고, 나머지상태(결제중이 아님)일때는 flag값을 false로 변경하여 프로그래스바가 안보이게 한다.
  IapUtils().iapStatusChangedCallback = (status) {
      switch (status) {
        case PurchaseStatus.error:
          showSnackBar(strFailPurchase, context);
          setState(() => showCircularProgress = false);
          break;
        case PurchaseStatus.purchased:
          setState(() => showCircularProgress = false);
          break;
        case PurchaseStatus.restored:
          setState(() => showCircularProgress = false);
          break;
        case PurchaseStatus.pending:
          setState(() => showCircularProgress = true);
          break;
        case PurchaseStatus.canceled:
          setState(() => showCircularProgress = false);
          break;
      }
    };
  • 이때 주의할점이 있는데, ios같은 경우는 구매요청을 하면 상태가 바로 pending이 된다.
    즉, 구매요청을 하고 구매다이어로그가 뜨자마자 pending상태가 되고 사용자가 구매다이어로그를 닫음(canceled) or 구매완료 혹은 에러 상태가 될때까지 pending상태가 유지된다.
    그래서 한번 구매요청을하면 구매가 (취소되어서 끝나던가 완료되어서 끝나던가 에러나서 끝나던가 아무튼 끝날떄까지)끝날떄까지 그 사이에 circurlarProgressbar가 계속 보이기 때문에 다음번 구매요청이 절대 일어날 수가 없다.
//ios의 구매상태 
다이어로그가 뜸(pending)
다이어로그 닫음(canceled)
구매 완료됨(purchased)
구매 에러(error)
//구매요청 코드
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam); 
  • 그런데 안드로이드 같은 경우, 구매요청을 해서 다이어로그가 뜬다 해도 상태가 바로 pending이 되지 않는다. 안드로이드 같은 경우 구매요청을 해서 구매 다이어로그가 뜨는것은(ios같은 경우 이때 바로 pending상태임) 아무 상태도 아니다.
    안드로이드 같은 경우 구매상태는 다음과 같이 바뀐다.
//안드로이드의 구매상태 
다이어로그 뜸(아무상태 x)
다이어로그 닫음(canceld)

일반구매 시작됨(아무상태 x)
일반구매 완료됨(purchased)
일반구매 에러(error)

느린구매 시작됨(pending)
느린구매 완료됨(purchased)
느린구매 에러(error)

구매요청을 빠르게 여러번(사용자가 구매버튼-구매다이어로그에 있는 구매버튼 말고 개발자가 만든 구매버튼-을 여러번 클릭함)클릭하면 기본적으로 구매다이어로그가 보였다가 사라졌다가 다시 구매다이어로그가 보였다 사라졌다 이런식으로 작동한다. 물론! 안드로이드같은경우 구매 다이어로그가 뜨는것은 아직 아무 상태도 아니기때문에 다이어로그가 떳다 없어졌다 하더라도 로직에 버그를 발생할 수 있는건아니다. 하지만 문제는, 너무 버튼을 빠르게 누르게되면 다이어로그가 꺼졋다 다시 나와야하는데 그러지 못하고 다이어로그가 연속으로 중첩되어 보이는 경우가 생기는것이다. 그러면 한 다이어로그에서 구매요청을 완료해서 그 다이어로그가 사라져도 중첩되어있던 다이어로그가 또 보인다.

이런경우를 방지하기 위해, 나는 ios와 동일하게 다이어로그가 뜨면 바로 상태를 pending 상태인것으로 인지하여 IapUtils().iapStatusChangedCallback을 호출했다. purchaseStream에서 리슨받는 상태flag값을 바꾼게 아니라 임의로 내가 만든 상태값을 변경하고 콜백을 호출한 것이기에 원래인앱결제로직에 걸려있는 상태에는 영향을 끼치지 않아서 문제되지 않는다고 판단했다.

그리고 안드로이드, ios 둘다 구매가 완료되어서 purchaseStrem에서 purchsed 상태를 리슨받아도 바로 purchased 콜백을 호출하지않았다. 왜냐하면 이 상태에서 서버검증을 마치고 응답을 받아야 진짜 구매가 완료되기 때문이다. 그래서 purchaseStrem에서 purchased 상태를 리슨 받으면 서버검증 api를 호출하고 서버 검증이 끝나면 내가 만든 flag를 purchased로 바꾸고 purchased 콜백을 호출했다.
이때, api 호출 시 에러가 난다해도 purchaseStrem에서 상태 변화는 없다. 그래서 만약 api 호출 시 에러가 난다면 내가 임의로 만든 상태를 error로 변경하고 error 콜백을 호출해야 정상적으로 구매로직이 종료가 되고 circurlatorProgressBar가 사라지면서 다음 구매요청을 할 수 있다.

아무튼 그렇게해서 안드로이드도 구매요청을하여 다이어로그가 뜨면 바로 pending콜백을 호출하여 circurlarProgress가 뜨고 취소,에러,성공 할때까지 다음 구매요청을 할 수 없게 만들었다.


안드로이드 - 이미 보유하고 있는 아이템입니다.

내가 구현하고 있는 상품은 구독상품이 아니라 일회성 구매 상품인데, 구매요청을 하면 이미 보유하고 있는 아이템 입니다라는 팝업이 뜨면서 구매가 진행이 안되었다.
구글 플레이스토어랑 앱의 캐시와 데이터를 지워봤지만 여전히 똑같은 에러가 발생했다.
이 블로그를 참고하면 구매 후 소비를 임의로 해줘야한다고 한다. 이게 아마 플러터 코드에서는 이 부분인것 같은데 나는 이미 이 코드가 있었다.

if (purchaseDetails.pendingCompletePurchase) {
        await InAppPurchase.instance
            .completePurchase(purchaseDetails);
      }

여기서부터는 추측인데 이 문제가 일어나 이유는 저 비동기 코드가 완료되기 전에 다른 구매를 요청하면 일어나는것 같다.

일단 저 문제는 플레이스토어 콘솔에서 주문관리 - 환불 에 들어가서 해당 구매를 환불하면된다.

그리고 저 문제를 근본적으로 해결하지는 못하고 그냥 하나의 구매를 저 비동기 코드가 끝난 후 서버검증까지 완료된 후 다음 구매요청을 할 수 있게 해결함으로써 저 문제가 애초에 일어날 수 없게 대응했다.

② 회고 (restropective)

  • 해피케이스만 생각하는게 아니라 엣지 케이스를 찾아내고 (연속적인 구매요청을할때) Ios,Android 의 다른 상황을 파악해내는 실력이 생긴것 같아 뿌듯하다.

③ 개선을 위한 방법

profile
되고싶다

0개의 댓글