플러터에서 인앱 결제를 구현하는 방법은 크게 두 가지가 있습니다.
- 공식 패키지 (in_app_purchase) 사용: 무료지만, 영수증 검증 서버를 직접 구축해야 하고 구현이 매우 복잡합니다.
- RevenueCat 사용: 타사 솔루션이지만, 서버 구축이 필요 없고 구현이 압도적으로 쉬우며, 월 매출 $2,500(약 300만원)까지는 무료입니다.
구독 모델 도입이 처음이라면 RevenueCat을 사용하는 것을 강력 추천합니다. 이를 기준으로 5단계 절차를 상세히 안내해 드립니다.
1단계: 스토어 판매자 계정 설정 및 상품 등록 (가장 먼저 할 일)
코드를 짜기 전에 애플과 구글에 "나 돈 받을 거다"라고 신고하고 상품을 등록해야 합니다.
🍎 iOS (App Store Connect)
- 계약 및 세금: App Store Connect > 비즈니스(Business) 탭에서 '유료 앱 계약(Paid Apps Agreement)'을 체결하고 계좌 정보를 입력합니다. (승인까지 시간이 좀 걸립니다.)
- 상품 생성: 앱 선택 > 수익 창출(Monetization) > 구독(Subscriptions) 메뉴로 이동합니다.
- 구독 그룹 생성: 예: Premium Access 그룹 생성.
- 구독 상품 추가: 예: remove_ads_monthly라는 제품 ID(Product ID)로 '1개월' 상품을 만듭니다. 가격을 설정합니다.
🤖 Android (Google Play Console)
- 판매자 계정: Google Play Console > 설정 > 판매자 계정을 만들고 계좌를 연결합니다.
- 상품 생성: 앱 선택 > 수익 창출 > 제품 > 구독 메뉴로 이동합니다.
- 상품 추가: 예: remove_ads_monthly라는 제품 ID로 상품을 만들고, 기본 요금제(매월 자동 갱신)를 설정합니다.
"앱에 아직 구독이 없습니다. 새 APK를 업로드하세요."라는 메시지가 표시되면 AndroidManifest.xml에 다음 권한을 추가하고 Play Console에 APK를 업로드해야 합니다.
<uses-permission android:name="com.android.vending.BILLING" />
필요한 결제 권한이 있는 APK를 업로드하면 새 구독을 생성할 수 있습니다.
2단계: RevenueCat(레비뉴캣) 설정
RevenueCat은 구글/애플 결제 시스템과 플러터 앱 사이의 '중개인' 역할을 합니다.
- RevenueCat 가입 및 새 프로젝트 생성.
- 앱 추가: 프로젝트 설정에서 iOS(App Store)와 Android(Play Store) 앱을 각각 추가합니다.
- 이 과정에서 구글/애플의 공유 시크릿 키(Shared Secret) 등이 필요합니다. RevenueCat 가이드가 스크린샷과 함께 아주 친절하게 안내해 줍니다.
- Entitlements(권한) 설정:
- Products: 1단계에서 만든 구글/애플의 Product ID (remove_ads_monthly)를 RevenueCat에 등록하여 연결합니다.
- Entitlements: 유저가 얻게 될 '권한'의 이름을 정합니다. (예: premium). 이 premium 권한에 방금 등록한 Product들을 연결합니다.
- 핵심: 코드는 이제 구글/애플 ID 대신 이 premium이라는 권한만 확인하면 됩니다.
3단계: 플러터 코드 구현
이제 앱에 기능을 넣을 차례입니다.
1. 패키지 추가 (pubspec.yaml)
YAML
dependencies:
purchases_flutter: ^8.0.0 # 최신 버전 확인 필요
2. 초기화 (main.dart) 앱이 시작될 때 RevenueCat을 초기화합니다.
Dart
import 'package:purchases_flutter/purchases_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 플랫폼에 맞는 API 키 설정 (RevenueCat 대시보드에서 확인)
if (Platform.isAndroid) {
await Purchases.configure(PurchasesConfiguration("goog_api_key_..."));
} else if (Platform.isIOS) {
await Purchases.configure(PurchasesConfiguration("appl_api_key_..."));
}
runApp(const MyApp());
}
3. 구독 여부 확인 및 광고 숨기기 배너 광고가 있는 위젯에서 현재 유저가 프리미엄 유저인지 확인합니다.
Dart
bool _isPremium = false;
@override
void initState() {
super.initState();
_checkSubscription();
}
Future<void> _checkSubscription() async {
try {
CustomerInfo customerInfo = await Purchases.getCustomerInfo();
// 'premium'은 2단계에서 설정한 Entitlement 이름
if (customerInfo.entitlements.all['premium']?.isActive == true) {
setState(() {
_isPremium = true; // 광고 제거!
});
}
} catch (e) {
// 에러 처리
}
}
// 빌드 메서드에서
@override
Widget build(BuildContext context) {
return Column(
children: [
// 프리미엄이 아닐 때만 배너 표시
if (!_isPremium)
Container(height: 90, child: TopBannerRotator()),
Expanded(child: MainContent()),
],
);
}
4. 구매 화면 (Paywall) 만들기 유저가 '광고 제거하기' 버튼을 눌렀을 때 실행할 로직입니다.
Dart
Future<void> _purchasePro() async {
try {
// 판매중인 상품 목록 가져오기
Offerings offerings = await Purchases.getOfferings();
if (offerings.current != null && offerings.current!.availablePackages.isNotEmpty) {
// 월간 상품 선택 (예시)
Package package = offerings.current!.availablePackages.first;
// 결제 요청 창 띄우기
CustomerInfo customerInfo = await Purchases.purchasePackage(package);
// 결제 성공 시 확인
if (customerInfo.entitlements.all['premium']?.isActive == true) {
setState(() {
_isPremium = true;
});
// "구매 감사합니다" 팝업 등
}
}
} catch (e) {
// 유저가 취소했거나 에러 발생 시
print(e);
}
}
4단계: 필수 UI/UX 요건 (심사 거절 방지)
앱스토어 심사는 구독에 매우 까다롭습니다. 구매 화면(Paywall)에 반드시 다음이 포함되어야 합니다.
- 구매 복원(Restore) 버튼: 기기를 바꾼 유저가 이전에 산 내역을 불러오는 버튼이 필수입니다.
-
Dart
TextButton( onPressed: () async { try { CustomerInfo restoredInfo = await Purchases.restorePurchases(); if (restoredInfo.entitlements.all['premium']?.isActive == true) { setState(() => _isPremium = true); } } catch (e) { ... } }, child: Text("구매 내역 복원"), ) - 약관 링크: 이용약관(Terms of Use)과 개인정보처리방침(Privacy Policy) 링크가 구매 화면에 보여야 합니다.
- 명확한 가격 표시: "월 ₩X,XXX원, 자동 갱신됨" 문구가 명확해야 합니다.
5단계: 테스트 및 배포
- 테스터 등록:
- Android: Play Console > 내부 테스트 트랙에 이메일 등록 (실제 결제 카드를 쓰지만 '테스트용 카드' 옵션을 선택하거나 결제되지 않음).
- iOS: App Store Connect > TestFlight 사용. 'Sandbox 계정'을 만들어 설정 > App Store 메뉴에서 샌드박스 계정으로 로그인 후 테스트 (실제 결제 안 됨).
- 심사 제출: 광고 제거 기능이 제대로 작동하는지 스크린샷이나 설명을 심사 제출 시 메모에 남기면 좋습니다.
'개발이야기' 카테고리의 다른 글
| 앱스토어 구독결제 심사리젝 ( 구독만료 테스트 아이디 ) (0) | 2025.12.17 |
|---|---|
| 앱스토어 구독결제 심사 리젝 (Guideline 3.1.2 - Business - Payments - Subscriptions) (0) | 2025.12.17 |
| 구글플레이 16kb 페이지 지원 ( 플러터 업글시 주의점, 애플 로그인 에러 ) (0) | 2025.09.26 |
| flutter 안드로이드 빌드시 서명 추가하기 ( build.gradle.kts) (0) | 2025.09.16 |
| flutter 타겟 iOS 버전 13.0 으로 올리기 (0) | 2025.09.16 |