RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/ビジネス
ビジネス/2026-05-06中級

Google Play サブスクリプションオファーで解約を防ぎ収益を最大化する:Rork アプリ実践ガイド

Google Play のサブスクリプションオファー機能(無料トライアル・入門価格・アップグレードオファー・ウィンバックオファー)を活用して、Rorkで開発したアプリの収益を最大化する実践的な設定・実装ガイド。

google-play5subscription10offersmonetization11rork58android3

App Store のサブスクリプション最適化に注目が集まりがちですが、Google Play にもApp Storeに劣らない強力なオファー機能があります。そして多くの個人開発者が、この機能を十分に活用できていません。

Rorkで開発したAndroidアプリに、Google Playのサブスクリプションオファーを実装する方法を整理します。設定からコードまで、一通り解説します。

Google Play サブスクリプションオファーの種類

Google Play Consoleでは、1つのサブスクリプション商品に複数の「オファー」を設定できます。主なオファー種別:

1. 無料トライアル(Free Trial)

最もポピュラーな形式。7日・14日・30日の無料試用期間を設定できます。注意点は、同じユーザーが同じアプリで何度も無料トライアルを利用できないことです(Google側で制御されます)。

2. 入門価格(Introductory Price)

最初の数ヶ月を通常より安い価格で提供します。たとえば「初月$0.99、2ヶ月目から$4.99」のような設定です。無料トライアルより転換率は下がりますが、有料転換ユーザーの長期維持率が高い傾向があります(最初から課金する意欲のあるユーザーだから)。

3. アップグレードオファー

無料プランや下位プランのユーザーに、上位プランへの特別価格でのアップグレードを提案します。既存ユーザー向けのため、プロモーションが有効な対象に絞り込みやすい。

4. ウィンバックオファー(Win-back Offer)

過去にサブスクリプションを解約したユーザーに再サブスクを促すオファーです。Google Playが自動的に「過去の購読者」を対象として設定でき、割引価格を提示できます。個人的に最も活用が不足しているオファー種別だと感じています。

Play Console でのオファー設定手順

  1. Play Console → 対象アプリ → 「収益化」→「製品」→「サブスクリプション」
  2. 対象サブスクリプションを選択 → 「オファーを追加」
  3. オファーの基本情報を設定

オファーID(重要): コード内でオファーを参照する際のIDです。後から変更できないので、命名規則を決めておきましょう。

例:

  • trial-7days — 7日間無料トライアル
  • intro-1month-half — 初月50%オフ入門価格
  • winback-30pct — 解約者向け30%オフウィンバック

対象設定: オファーを誰に見せるかを設定します。

  • 新規ユーザーのみ(未購入者)
  • 既存ユーザー(購入済み)
  • 解約済みユーザー(ウィンバック)

Android(Kotlin)での実装

Google Play Billing Library v7 でオファーを実装するコードです。

// BillingManager.kt
import com.android.billingclient.api.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
 
class BillingManager(private val activity: Activity) {
 
    private lateinit var billingClient: BillingClient
    
    private val _subscriptionOffers = MutableStateFlow<List<ProductDetails.SubscriptionOfferDetails>>(emptyList())
    val subscriptionOffers: StateFlow<List<ProductDetails.SubscriptionOfferDetails>> = _subscriptionOffers
 
    fun initialize() {
        billingClient = BillingClient.newBuilder(activity)
            .setListener { billingResult, purchases ->
                if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
                    purchases?.forEach { handlePurchase(it) }
                }
            }
            .enablePendingPurchases()
            .build()
        
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(result: BillingResult) {
                if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                    loadSubscriptionOffers()
                }
            }
            override fun onBillingServiceDisconnected() {
                // 再接続ロジック
            }
        })
    }
 
    private fun loadSubscriptionOffers() {
        val queryParams = QueryProductDetailsParams.newBuilder()
            .setProductList(
                listOf(
                    QueryProductDetailsParams.Product.newBuilder()
                        .setProductId("premium_monthly")  // Play Consoleで設定したID
                        .setProductType(BillingClient.ProductType.SUBS)
                        .build()
                )
            )
            .build()
        
        billingClient.queryProductDetailsAsync(queryParams) { result, productDetailsList ->
            if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                val product = productDetailsList.firstOrNull() ?: return@queryProductDetailsAsync
                val offers = product.subscriptionOfferDetails ?: emptyList()
                
                // オファーをログ出力(デバッグ用)
                offers.forEach { offer ->
                    println("Offer ID: ${offer.offerId}")
                    println("Tags: ${offer.offerTags}")
                    offer.pricingPhases.pricingPhaseList.forEach { phase ->
                        println("  Phase: ${phase.formattedPrice} for ${phase.billingCycleCount} cycles")
                    }
                }
                
                _subscriptionOffers.value = offers
            }
        }
    }
 
    fun getOfferForUser(
        offers: List<ProductDetails.SubscriptionOfferDetails>,
        isReturningUser: Boolean,
        hasUsedTrial: Boolean,
    ): ProductDetails.SubscriptionOfferDetails? {
        return when {
            // 過去の購読者 → ウィンバックオファーを優先
            isReturningUser -> offers.firstOrNull { offer ->
                offer.offerTags.contains("winback")
            }
            // 新規ユーザーでトライアル未使用 → 無料トライアル
            !hasUsedTrial -> offers.firstOrNull { offer ->
                offer.offerTags.contains("trial")
            }
            // トライアル使用済み → 入門価格
            else -> offers.firstOrNull { offer ->
                offer.offerTags.contains("introductory")
            }
        } ?: offers.firstOrNull()  // フォールバック: 通常価格
    }
 
    fun launchPurchaseFlow(
        productDetails: ProductDetails,
        selectedOffer: ProductDetails.SubscriptionOfferDetails,
    ) {
        val offerToken = selectedOffer.offerToken
        
        val productDetailsParamsList = listOf(
            BillingFlowParams.ProductDetailsParams.newBuilder()
                .setProductDetails(productDetails)
                .setOfferToken(offerToken)  // 選択したオファーのトークンを指定
                .build()
        )
        
        val billingFlowParams = BillingFlowParams.newBuilder()
            .setProductDetailsParamsList(productDetailsParamsList)
            .build()
        
        billingClient.launchBillingFlow(activity, billingFlowParams)
    }
 
    private fun handlePurchase(purchase: Purchase) {
        if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
            // 購入確認(Acknowledgement)— 3日以内に必須
            if (!purchase.isAcknowledged) {
                val acknowledgeParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
                    .build()
                billingClient.acknowledgePurchase(acknowledgeParams) { result ->
                    if (result.responseCode == BillingClient.BillingResponseCode.OK) {
                        println("Purchase acknowledged: ${purchase.orderId}")
                    }
                }
            }
            // バックエンドに購入情報を送信して会員ステータスを更新
            updateSubscriptionStatus(purchase.purchaseToken)
        }
    }
 
    private fun updateSubscriptionStatus(purchaseToken: String) {
        // RevenueCatまたは自前バックエンドに通知
    }
}

オファーの効果を最大化するUIデザイン

ペイウォールでのオファー表示のポイントを整理します。

無料トライアルは「残り期間」で訴求する

「7日間無料」より「7日間、完全無料でお試しください」の方が、ユーザーの心理的ハードルが下がります。さらに「今日開始 → 1週間後にキャンセルしなければ$4.99/月」のタイムラインを視覚的に示すと、透明性が高まり転換率が上がることが多いです。

ウィンバックオファーは「理由」を添える

「おかえりなさい。お戻りいただける方に、最初の2ヶ月を30%オフでご用意しました」という文脈がある方が、単なる割引表示より効果的です。

オファーのA/Bテスト(RevenueCat Experimentsとの組み合わせ)

RevenueCatのExperiments機能とGoogle Playのオファーを組み合わせることで、どのオファーが最も長期的な収益に貢献するか(LTVで評価)を計測できます。転換率だけでなくLTVで評価することが重要で、無料トライアルより入門価格の方がLTVが高いケースもあります。

全体を振り返って

Google Playのサブスクリプションオファーは、設定さえすれば自動的に機能する強力なツールです。特にウィンバックオファーは、既に「一度興味を持ってくれた」ユーザーへのアプローチとして高い費用対効果があります。

最小限の実装として、まず「7日間無料トライアル」と「ウィンバックオファー(30%オフ)」の2種類を設定することをお勧めします。この2つだけで、多くのアプリでサブスクリプション収益の改善が見込めます。

RevenueCat を使った高度なサブスクリプション分析・実装については、プレミアム記事「Rork + Stripe で Web SaaS の決済基盤を完全構築する」もあわせてご参照ください。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

ビジネス2026-04-28
Rork アプリの収益モデル比較 — 広告・サブスク・買い切りを実運用の数字で選ぶ
壁紙系アプリの広告運用がピークを迎えた時期の実体験をもとに、Rork アプリの広告・サブスクリプション・買い切りを同一条件で試算し直し、ジャンル別の使い分けと実装の留意点を整理しました。
ビジネス2026-05-06
Rork + Stripe で Web SaaS の決済基盤を構築する:Next.js × Cloudflare Workers × Supabase
Rork Maxで生成したNext.jsアプリにStripeの月額サブスクリプション・単品購入・Webhookを完全実装するガイド。Cloudflare Workers + Supabase構成でSaaS課金基盤を一から構築する実践的な手順を解説します。
ビジネス2026-05-02
Rork でリリースしたアプリの最初の 90 日 — 運用初期に判断すべき 8 つの分岐
Rork でアプリをリリースした直後、90 日間で開発者が直面する 8 つの判断分岐をまとめました。アプリストアのレビュー対応から広告収益の最適化、機能追加の優先順位付けまで、私自身が 12 年の個人開発経験で学んだ実践的な判断基準を共有します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →