rc-one-time-products について
買い切り商品を販売する Android 開発者向けの RevenueCat スキルです。購入フローに加え、SDK によるレシート検証、トークンの承認・消費、保留中の支払いとエラーの扱いを説明します。
主な機能
- 01商品の取得と購入開始
- 02RevenueCat による購入レシート検証
- 03トークンの自動承認または消費
- 04キャンセル・保留中の支払い・エラーの区別
- 05エンタイトルメントまたは取引によるアクセス確認
活用シーン
- 01Android アプリへの買い切り購入の追加
- 02エンタイトルメントに基づくアクセス付与
- 03保留中の支払い完了への対応
- 04Webhook によるサーバー側購入処理の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ revenuecat/ ai-toolkit --skill rc-one-time-productsインストール後、Claude Code で/rc-one-time-productsを入力して読み込まれたか確認します。
詳細
RevenueCat での買い切り商品
BillingClient の連携コードを書かずに、Android で買い切り商品を販売できます。RevenueCat SDK が商品を取得し、購入を開始し、レシートを検証して、トークンの確認または消費を行います。
フェーズ 1:範囲を決める
コードに手を加える前に、以下の質問に答えてください。
| 質問 | 決定事項 |
|---|---|
| 商品は消費型ですか、それとも非消費型ですか? | 消費型の商品は RevenueCat ダッシュボードで設定してください。SDK が代わりに consumeAsync を呼び出します。 |
| エンタイトルメントとトランザクションのどちらでアクセス権を読み取りますか? | 商品 ID からアクセスロジックを切り離せるため、エンタイトルメントを推奨します。 |
| 購入についてサーバー通知を受け取る必要がありますか? | RevenueCat の Webhook を使用してください。INITIAL_PURCHASE イベントは、新しいサブスクリプション以外の購入ごとに発生します。 |
RevenueCat を統合した後は、BillingClient.queryProductDetailsAsync、acknowledgePurchase、consumeAsync を自分で呼び出さないでください。BillingClient インスタンスは SDK が管理します。
フェーズ 2:準備する
- Google Play Console で商品を買い切り商品として設定します。
- RevenueCat ダッシュボードで商品をオファリングとパッケージに紐付け、購入時に消費する場合は消費型として設定します。
- アプリの起動時に、Android API キーと現在の App User ID を指定して
Purchases.configureが一度だけ実行されることを確認します。
フェーズ 3:実行する
オファリングを取得し、購入を開始してから、CustomerInfo で結果を読み取ります。
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.availablePackages?.first() ?: return
awaitPurchase で購入を開始します。RevenueCat バックエンドがトークンを検証するまで処理は一時停止します。
val result = Purchases.sharedInstance.awaitPurchase(
PurchaseParams.Builder(activity, pkg).build()
)
val info = result.customerInfo
エンタイトルメントでアクセス権を読み取るか、非サブスクリプションのトランザクション一覧を代わりに使用します。
val hasAccess = info.entitlements["lifetime_access"]?.isActive == true
val owned = info.nonSubscriptionTransactions
.any { it.productIdentifier == "lifetime_product_id" }
バックエンドが購入を検証すると、SDK は非消費型商品を確認し、消費型商品を自動的に消費します。acknowledgePurchase や consumeAsync を呼び出す必要はありません。
フェーズ 4:エッジケースに対応する
awaitPurchase をラップして、キャンセル、保留中の支払い、実際のエラーを区別します。
try { /* awaitPurchase */ } catch (e: PurchasesTransactionException) {
when {
e.error.code == PurchasesErrorCode.PaymentPendingError -> showPending()
e.userCancelled -> Unit
else -> showError(e.error.message)
}
}
| 結果 | SDK のシグナル | 対応 |
|---|---|---|
| 成功 | awaitPurchase が更新済みの CustomerInfo を返す | entitlements または nonSubscriptionTransactions からアクセス権を付与します。 |
| ユーザーによるキャンセル | PurchasesTransactionException、userCancelled == true | 何もせずに処理を終了します。 |
| 支払い保留中 | PurchasesTransactionException、PaymentPendingError | 保留中であることを示すメッセージを表示します。後で SDK が UpdatedCustomerInfoListener を通じてエンタイトルメントを更新します。 |
| その他のエラー | PurchasesTransactionException、その他のコード | e.error.message をユーザーに表示します。 |
フェーズ 5:検証する
awaitPurchaseから返されるresult.customerInfoを信頼してください。楽観的なローカル状態に基づいてアクセス権を付与しないでください。- 支払い保留中の購入ではエンタイトルメントは付与されません。
UpdatedCustomerInfoListenerを登録し、支払い完了時に対応できるようにしてください。 - サーバー側でプロビジョニングする場合は、RevenueCat の
INITIAL_PURCHASEWebhook を購読してください。Google Play Developer API による検証を独自に再実装しないでください。