[Flutter] flutter 인앱결제 구현 (2) - 코드구현

soonmuu·2023년 3월 10일
1

flutter 인앱결제

목록 보기
2/2
post-thumbnail

인앱 패키지 사용

in_app_purchase 패키지 사용

패키지 설치

$ flutter pub add in_app_purchase

패키지 사용

인스턴스를 초기화해준다.
다른 라이브러리를 사용할때에도 오류 방지를 위해 main앱이 실행되기 전에 WidgetsFlutterBinding.ensureInitialized(); 를 사용하여 초기화 해줘야한다

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(_MyApp());
}

패키지 데이터 설명

인앱결제 데이터는 마켓에 올린 테스트 버전과 실기기로만 확인이 가능해 데이터 찾기에서 조금 해맸다.
아래는 구매 검증을 위해 넘긴 데이터 목록이다

구매검증이 이루어지는 함수 _verifyPurchasePurchaseDetails purchaseDetails 를 통해 아래 데이터를 얻을 수 있다.

 Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
 	// 마켓에 등록한 상품 아이디(문자열)
  	print(purchaseDetails.productID);
    
    // 구매 상태 PurchaseStatus.purchased 형태로 리턴한다 - android
    print(purchaseDetails.status);
    
    // 구매 날짜 1679542316652 형태로 리턴한다 - android
    print(purchaseDetails.transactionDate);
    
    // 구매 영수증 검증데이터가 base64로 암호화된 문자열로 리턴된다 - ios
    // 구매 영수증 아래와 같은 형태로 리턴된다 - android
    /*
    {
    "orderId":"주문아이디","
    packageName":"패키지명",
    "productId":"상품 아이디",
    "purchaseTime": 주문시간,
    "purchaseState": 0, // 주문상태
    "purchaseToken": "구매토큰",
    "quantity":1, // 수량
    "acknowledged":false // 정기결제 여부
    }
    */
  
    print(purchaseDetails.verificationData.localVerificationData);
    
    // 구매 토큰
    print(purchaseDetails.verificationData.serverVerificationData);
    
    // 구매 마켓 안드로이드면 google_play를 리턴한다
    print(purchaseDetails.verificationData.source);
 }

예시코드

https://pub.dev/packages/in_app_purchase/example
공식 예제 코드를 통째로 실행해보면 인앱결제가 어떻게 흘러가는지 알 수 있다

예시코드 중 몇가지 확인할 부분은 아래와 같다

1. 인앱상품 아이디 리스트

예시코드에서는 변수로 선언하여 아래와 같이 가져오고 있지만 나같은 경우는 추후 관리를 위해 restful api로 데이터를 받아 리스트를 만들었다

! 주의할점

아이디값을 api로 받아와서 initStoreInfo(value); 함수를 실행시키면 아이디 리스트는 중복제거를 위해 list.toSet() 으로 Set 데이터로 변형해야한다.

Set데이터는 순서가 없음으로 이 데이터를 다시 리스트화 시킬때 순서가 뒤바뀔수 있으니 상품 순서를 명확히 하고 싶다면 api로 받아온 리스트로 버튼을 구성하고 인앱정보를 아이디로 불러오는게 더 정확하다.

공식문서 코드
Widget _buildProductList() {
	...
     productList.addAll(_products.map(
      (ProductDetails productDetails) {
        final PurchaseDetails? previousPurchase = 
        purchases[productDetails.id];
        return ListTile(
        	...
        );
     }
}
내코드
//  List<PointListModel> inappList기 api로 받아온 데이터이다
Widget _buildProductList() {
    productList.addAll(inappList.asMap().entries.map(
      (entry) {
      	// 상품 아이디로 인앱상품 정보를 받아온다
        ProductDetails productDetails =
            _products.where((product) => product.id == entry.value.id).first;

        return _buildPaymentButton(
       		...
       );
        
}
예시
const String _kConsumableId = 'consumable';
const String _kUpgradeId = 'upgrade';
const String _kSilverSubscriptionId = 'subscription_silver';
const String _kGoldSubscriptionId = 'subscription_gold';
const List<String> _kProductIds = <String>[
  _kConsumableId,
  _kUpgradeId,
  _kSilverSubscriptionId,
  _kGoldSubscriptionId,
];


  void initState() {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subscription =
        purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscription.cancel();
    }, onError: (Object error) {
      // handle error here.
    });
    initStoreInfo();
    super.initState();
  }
내 코드
  
  void initState() {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subscription =
        purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscription.cancel();
    }, onError: (Object error) {
      // handle error here.
    });
    
    // 데이터를 받아와 초기화 실행을 하였다.
    getInappProduct(type: platform).result.then((value) {
      initStoreInfo(value);
    });


    super.initState();
  }

2. 버튼 커스텀

Card _buildProductList() 부분이 버튼이 생성되는 부분으로 onPressed 부분만 주의를 기울여 사용하고 나머지부분은 ui부분으로 자유롭게 바꿔도 무방하다

3. 구매 검증

_verifyPurchase(PurchaseDetails purchaseDetails) 결제 후 해당 정보가 purchaseDetails를 통해 넘어온다 이때 영수증 값인purchaseDetails.verificationData.localVerificationData 을 서버에 넘겨 영수증 검증을 진행하고 결제 여부를 db에 저장할 수 있다

4. 결과 처리

_listenToPurchaseUpdated 해당함수로 결제 결과에 따른 화면처리를 진행할 수 있다

profile
프론트엔드

1개의 댓글

comment-user-thumbnail
2023년 10월 13일

안녕하세요~ 개발하다가 질문이 하나있어서요
ios부분인데 영수증검증이 deprecate되었더라구요 혹시
구독을 했는지 안했는지 여부를 어떤것으로 구분하시나요?

답글 달기