구독이 포함된 Android SaaS 앱: 2025년 완전 가이드
Source: Dev.to
Market Context 2025
전 세계 SaaS 시장 규모는 2025년 기준 4,082.1 억 USD 로 추정되며, 2034년에는 12,513.5 억 USD에 이를 것으로 전망됩니다.
구독 결제 관리 시장은 2030년까지 179.5 억 USD에 도달할 것으로 예상되며, 연평균 성장률(CAGR)은 16.9 %입니다.
1. Google Play Billing Library
Current Version
- Play Billing Library 7.x (2025년 8월까지 필수)
- 중요 마감일: 2025년 8월 31일까지 모든 신규 앱 및 업데이트는 Billing Library 7 이상을 사용해야 합니다. 확장 프로그램은 2025년 11월 1일까지 제공됩니다.
Key Features in Version 7.0+
- 할부 구독 – 사용자가 더 작고 관리하기 쉬운 할부로 결제 (브라질, 프랑스, 이탈리아, 스페인에서 이용 가능)
- 구독에 대한 보류 중 거래 – 활성화 전
SUBSCRIPTION_STATE_PENDING상태 처리 - ReplacementMode API – 업그레이드/다운그레이드 시 기존
ProrationMode를 대체 - User Choice Billing – 대체 결제 옵션을 위한
enableUserChoiceBilling()
Implementation
// build.gradle.kts
implementation("com.android.billingclient:billing-ktx:7.1.1")
// Initialize BillingClient
val billingClient = BillingClient.newBuilder(context)
.setListener { billingResult, purchases ->
// Handle purchase updates
purchases?.forEach { purchase ->
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
// Verify on backend, then acknowledge
verifyAndAcknowledge(purchase)
}
}
}
.enablePendingPurchases(
PendingPurchasesParams.newBuilder()
.enablePrepaidPlans()
.build()
)
.build()
// Connect to Google Play
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
querySubscriptions()
}
}
override fun onBillingServiceDisconnected() {
// Implement retry logic
}
})
// Query available subscriptions
fun querySubscriptions() {
val params = QueryProductDetailsParams.newBuilder()
.setProductList(
listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_monthly")
.setProductType(BillingClient.ProductType.SUBS)
.build(),
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_yearly")
.setProductType(BillingClient.ProductType.SUBS)
.build()
)
)
.build()
billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList ->
// Check for installment plans (PBL 7+)
productDetailsList.forEach { product ->
product.subscriptionOfferDetails?.forEach { offer ->
offer.installmentPlanDetails?.let { installment ->
// Handle installment subscription display
val commitments = installment.installmentPlanCommitment
}
}
}
}
}
// Launch purchase flow
fun launchPurchase(activity: Activity, productDetails: ProductDetails, offerToken: String) {
val flowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setOfferToken(offerToken)
.build()
)
)
.build()
billingClient.launchBillingFlow(activity, flowParams)
}
Google Play Commission Rates 2025
| Scenario | Rate |
|---|---|
| 첫 $1 M 연간 매출 | 15 % |
| 첫 해 이후 구독 | 15 % |
| 첫 해 구독 (표준) | 30 % |
2. 2025 Policy Update: Alternative Billing in the US
Major Change (October 2025)
Epic Games 판결 이후, Google은 이제 미국 개발자들이 대체 결제 수단을 제공하도록 허용합니다:
- Google Play Billing만을 독점적으로 사용할 필요 없음
- 개발자는 대체 결제 옵션에 대해 사용자와 커뮤니케이션 가능
- 결제 수단에 따라 가격 제한이 없음
- Stripe, PayPal, 혹은 자체 솔루션을 통해 결제 처리 가능
External Offers Program (EEA)
유럽 경제 지역(EEA)에서는 개발자가 앱 외부에서 사용자를 유도해 오퍼를 홍보할 수 있으며, 이는 Google Play 결제 정책을 따릅니다.
// User Choice Billing implementation
val billingClient = BillingClient.newBuilder(context)
.enableUserChoiceBilling(UserChoiceBillingListener { userChoiceDetails ->
// User selected alternative billing
val externalTransactionToken = userChoiceDetails.externalTransactionToken
// Process with your payment provider
processAlternativePayment(externalTransactionToken)
})
.build()
3. Third‑Party Subscription Platforms
RevenueCat
가장 인기 있는 크로스‑플랫폼 구독 관리 SDK.
// Setup
Purchases.configure(
PurchasesConfiguration.Builder(context, "your_api_key")
.appUserID(userId)
.build()
)
// Fetch offerings
Purchases.sharedInstance.getOfferingsWith(
onError = { error -> /* Handle error */ },
onSuccess = { offerings ->
offerings.current?.availablePackages?.let { packages ->
// Display subscription options
}
}
)
// Make purchase
Purchases.sharedInstance.purchase(
PurchaseParams.Builder(activity, packageToPurchase).build(),
onError = { error, userCancelled -> },
onSuccess = { storeTransaction, customerInfo ->
if (customerInfo.entitlements["premium"]?.isActive == true) {
// Unlock premium features
}
}
)
// Check subscription status
Purchases.sharedInstance.getCustomerInfo(
onError = { /* Handle */ },
onSuccess = { customerInfo ->
val isPremium = customerInfo.entitlements["premium"]?.isActive == true
}
)
Pricing: 월 $2,500 MTR까지 무료, 이후 MTR의 1 % 차감.
Adapty
더 정교한 A/B 테스트 기능을 제공하는 강력한 대안.
Advantages over RevenueCat
- 보다 정교한 다변량 페이월 테스트
- 무료 플랜에서도 더 많은 분석 지표 제공
- 실시간 대시보드 데이터
- 빠른 고객 지원 응답 시간
- 세부 제어가 가능한 노코드 페이월 빌더
Pricing: 월 $99 부터 시작.
Comparison Table
| Feature | RevenueCat | Adapty |
|---|---|---|
| SDK Platforms | iOS, Android, Flutter, RN, Unity, Cordova, Ionic | iOS, Android, Flutter, RN, Unity |
| Free Tier Analytics | 6 metrics | 10+ metrics |
| Real‑time Data | No | Yes |
| A/B Testing | Basic | Advanced multivariate |
| Paywall Builder | Native (Paywalls v2) | No‑code drag‑drop |
| Starting Price | Free / $8 mo | $99 /mo |
4. Backend Architecture with Supabase
Supabase는 PostgreSQL, 인증, 실시간 기능을 갖춘 오픈소스 백엔드로 SaaS 앱에 최적입니다.
Database Schema for Subscriptions
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Users table
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
-- Subscriptions table
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
product_id TEXT NOT NULL,
purchase_token TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('active','canceled','past_due','expired')),
period_start TIMESTAMP WITH TIME ZONE NOT NULL,
period_end TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
-- Indexes for fast lookup
CREATE INDEX idx_subscriptions_user_id ON subscriptions(user_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
Supabase의 Row‑Level Security(RLS) 정책을 사용해 사용자가 자신의 구독 레코드만 읽고 쓸 수 있도록 제한하고, Google Play 구매 알림을 처리하기 위한 웹훅 리스너를 연동해 실시간 상태 업데이트를 구현합니다.