플러터(Flutter)를 사용하여 앱 내에서 상품을 구매하고 소비하는 인앱 결제 기능을 구현하는 방법을 소개합니다. 인앱 결제는 사용자가 앱에서 콘텐츠, 기능, 서비스 등을 구매할 수 있도록 하는 강력한 기능입니다. 이 글에서는 플러터에서 인앱 결제를 구현하는 방법을 간단하게 설명합니다.
먼저, 진행하기 전에 가장 궁금하신 게 결제자를 어떻게 판별하고 관리하는지 궁금하실 거예요.
구글에서 전체적으로 관리를 제공해주지 않기 때문에 별도로 백엔드를 구현하셔서 주문 고객 등 관리를 별도로 해주셔야 합니다.
필요한 패키지 설치
플러터 인앱 결제를 구현하기 위해 필요한 패키지를 설치하려면, pubspec.yaml 파일에 아래와 같이 in_app_purchase 패키지를 추가하세요.
dependencies:
flutter:
sdk: flutter
in_app_purchase: ^3.1.5
권한 설정
android\app\src\main\AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wudy.app">
<uses-permission android:name="com.android.vending.BILLING" />
android\build.gradle
classpath 'com.google.gms:google-services:4.3.14' 플러그인 추가
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.14'
}
android\app\build.gradle
implementation 'com.android.billingclient:billing:5.1.0', implementation "com.android.billingclient:billing-ktx:5.1.0" 2개 의존성 추가
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
...
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.billingclient:billing:5.1.0'
implementation "com.android.billingclient:billing-ktx:5.1.0"
}
인앱 상품 추가
Google Play Console에서 인앱 상품을 추가하기 위해 판매자 계정 설정을 진행합니다.
판매자 계정 설정
인앱 상품 클릭 시 판매자 계정설정이 나옵니다. 클릭하여 이동해 주세요.
결제 프로필을 생성해 줍니다.
항목을 체크하면 아래와 같이 진행됩니다.
필요한 정보를 입력하여 제출을 통해 결제 프로필을 생성해 주세요.
결제 프로필 화면에서는 추가로 필요한 정보를 입력해 주세요.
다시 인앱 상품 페이지로 돌아오면, 앱에 아직 인앱 상품이 없다고 나옵니다. 개발 중인 앱을 업로드해 주세요.
트랙 내용이 나오면 트랙 관리로 들어가서 비공개 형태로 앱을 업로드해줘야 합니다.
새 버전 만들기를 통하여 기존 앱 등록과 동일하게 진행해 주시면 됩니다. 이동하기 전에 테스터 선택을 하여 테스트를 진행할 유저들을 추가해 주세요. 본인을 추가해도 됩니다. 비공개이기 때문에 테스터에 추가된 인원만 가능합니다.
앱을 전체 만들어서 올릴 필요는 없습니다. 진행 중이던 앱을 업로드를 일단 강제로 진행하시고 업로드 시도가 발생되면, 인앱 상품에 상품을 추가할 수 있습니다.
상품 만들기를 클릭하여 상품을 추가해 주세요.
각 정보를 다 입력해 주시고, 가격 쪽에는 가격 테이블(템플릿)을 미리 만들어 놓을 수도 있고, 상품마다 개별로 가격 설정도 할 수 있습니다. 설정 후 저장을 눌러 상품을 추가해 주세요.
이제 인앱 상품이 생성되었습니다. 제품 ID를 통해서 실제 앱에서 데이터를 가져와 인앱 결제 할 수 있게 해 보겠습니다.
결제 구현
in_app_purchase_service.dart
productID 변수에 제품 ID를 넣어주면, 데이터를 가져와서 결제를 가능하게 도와줍니다.
// ignore_for_file: unnecessary_cast
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
/// 인앱 결제 서비스
class InAppPurchaseService extends GetxService {
static InAppPurchaseService get to => Get.find();
// Instance ▼ ============================================
/// 구매를 위한 인스턴스를 가져옵니다.
final Rx<InAppPurchase> iap = InAppPurchase.instance.obs;
// 구매 세부 정보에 대한 업데이트 스트림을 수신하는 구독
late Rx<StreamSubscription?> subscription = (null as StreamSubscription?).obs;
// Data ▼ ================================================
/// 구매를 위한 인스턴스를 가져옵니다.
RxString productID = '여기에 제품 ID'.obs;
// Playstore 또는 앱 스토어에서 쿼리한 제품 목록 유지
RxList<ProductDetails> products = <ProductDetails>[].obs;
// 과거 구매 사용자 목록
RxList<PurchaseDetails> purchases = <PurchaseDetails>[].obs;
// Function ▼ ============================================
/// 상품 목록 조회 방법
///
/// Future<void>
Future<void> fetchUserProducts() async {
// 구매를 위한 인스턴스를 가져옵니다.
ProductDetailsResponse response = await iap.value.queryProductDetails({
productID.value,
});
// 제품 목록 데이터 추가
products.assignAll(response.productDetails);
}
/// 과거 구매 사용자를 검색하는 방법
///
/// @return Future<void>
Future<void> fetchPastPurchases() async {
/// 모든 이전 구매를 복원합니다.
/// `applicationUserName`은 초기 `PurchaseParam`에서 전송된 내용과 일치해야 합니다.
/// 초기 `PurchaseParam`에 `applicationUserName`이 지정되지 않은 경우 null을 사용합니다.
/// 복원된 구매는 [PurchaseStatus.restored] 상태로 [purchaseStream]을 통해 전달됩니다.
/// 이러한 구매를 수신하고, 영수증을 확인하고, 콘텐츠를 전달하고, 각 구매에 대해 [finishPurchase] 메서드를 호출하여 구매 완료를 표시해야 합니다.
/// 이것은 소비된 제품을 반환하지 않습니다.
/// `사용하지 않는 소모품을 복원하려면 자체 서버에서 사용자에 대한 소모품 정보를 유지해야 합니다.`
// await iap.value.restorePurchases();
}
/// 상품을 이미 구매했는지 여부를 확인하는 방법입니다.
///
/// @return void
void verifyPurchases() {
try {
PurchaseDetails purchase = purchases.firstWhere(
(purchase) => purchase.productID == productID.value,
);
if (purchase.status == PurchaseStatus.purchased) {
// 구매 완료
}
} catch (e) {
// 구매 미완료
}
}
/// 제품 구매 방법
///
/// [prod] 구매할 제품
///
/// @return 구매 성공 여부
@required
void purchaseProduct(ProductDetails prod) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: prod);
iap.value.buyConsumable(purchaseParam: purchaseParam, autoConsume: false);
}
/// 구매 세부 정보에 대한 업데이트 스트림을 수신하는 구독
///
/// @return Future<void>
Future<void> initialize() async {
// 인앱 구매가 가능한지 체크
if (await iap.value.isAvailable()) {
// 구매 가능한 상품 목록 조회
await fetchUserProducts();
// 과거 구매 사용자 목록 조회
// await fetchPastPurchases();
// 상품을 이미 구매했는지 체크
verifyPurchases();
// 구매 세부 정보에 대한 업데이트 스트림을 수신하는 구독
subscription.value = iap.value.purchaseStream.listen((data) {
// 구매 세부 정보를 업데이트
purchases.addAll(data);
// 상품을 이미 구매했는지 체크
verifyPurchases();
});
}
}
@override
Future<void> onInit() async {
await initialize();
super.onInit();
}
@override
void onReady() {
super.onReady();
}
@override
void onClose() {
// 구독 해제
subscription.value?.cancel();
super.onClose();
}
}
premium.dart
class PremiumView extends GetView<MyPageController> {
const PremiumView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
Center(
child: ElevatedButton(
onPressed: () {
// 버튼 클릭 시 수행할 작업
InAppPurchaseService.to.purchaseProduct(
InAppPurchaseService.to.products[0],
);
},
child: const Text('프리미엄 회원으로 업그레이드하기'),
),
),
],
),
),
);
}
}
위와 같이 적용해 주면 아래의 영상처럼 작동하게 됩니다.
'Dart > Flutter' 카테고리의 다른 글
[Flutter] 소셜 애플 로그인(Sign In with Apple) 구현하기 (0) | 2023.04.03 |
---|---|
[Flutter] 플러터 인앱(in_app_purchase) 정기결제 구현하기 (0) | 2023.03.19 |
[Flutter] 어노테이션(annotation) `@` 사용해보기 (0) | 2023.03.19 |
[Flutter] Gradlew signingReport를 이용한 SHA1 및 SHA256 키 확인 간편 가이드 (0) | 2023.03.18 |
[Flutter] Error (Xcode): 34 duplicate symbols for architecture arm64 해결하기 (0) | 2023.02.14 |