in_app_purchase
패키지 사용
$ flutter pub add in_app_purchase
인스턴스를 초기화해준다.
다른 라이브러리를 사용할때에도 오류 방지를 위해 main앱이 실행되기 전에 WidgetsFlutterBinding.ensureInitialized();
를 사용하여 초기화 해줘야한다
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(_MyApp());
}
인앱결제 데이터는 마켓에 올린 테스트 버전과 실기기로만 확인이 가능해 데이터 찾기에서 조금 해맸다.
아래는 구매 검증을 위해 넘긴 데이터 목록이다
구매검증이 이루어지는 함수 _verifyPurchase
의 PurchaseDetails 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
공식 예제 코드를 통째로 실행해보면 인앱결제가 어떻게 흘러가는지 알 수 있다
예시코드 중 몇가지 확인할 부분은 아래와 같다
예시코드에서는 변수로 선언하여 아래와 같이 가져오고 있지만 나같은 경우는 추후 관리를 위해 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();
}
Card _buildProductList()
부분이 버튼이 생성되는 부분으로 onPressed
부분만 주의를 기울여 사용하고 나머지부분은 ui부분으로 자유롭게 바꿔도 무방하다
_verifyPurchase(PurchaseDetails purchaseDetails)
결제 후 해당 정보가 purchaseDetails
를 통해 넘어온다 이때 영수증 값인purchaseDetails.verificationData.localVerificationData
을 서버에 넘겨 영수증 검증을 진행하고 결제 여부를 db에 저장할 수 있다
_listenToPurchaseUpdated
해당함수로 결제 결과에 따른 화면처리를 진행할 수 있다
안녕하세요~ 개발하다가 질문이 하나있어서요
ios부분인데 영수증검증이 deprecate되었더라구요 혹시
구독을 했는지 안했는지 여부를 어떤것으로 구분하시나요?