К основному содержанию
скилл CX Codex

rc-plan-changes — скилл для Claude Code: что делает и как установить

Скилл помогает настроить смену тарифных планов Google Play через RevenueCat на Android, выбирая верный режим перерасчёта стоимости.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add RevenueCat/ai-toolkit --skill rc-plan-changes -a claude-code
Открыть на GitHub

Что делает rc-plan-changes

Скилл инструктирует агента, как переводить пользователя с одной подписки Google Play на другую с помощью PurchaseParams.Builder и GoogleProductChangeInfo. Он раскладывает логику для перехода на годовой тариф, даунгрейда и смены планов во время бесплатного пробного периода.

Внутри заложены правила выбора GoogleReplacementMode: немедленный переход с учётом времени (WITH_TIME_PRORATION), немедленное списание пропорциональной цены (CHARGE_PRORATED_PRICE) или отложенное переключение (DEFERRED). Режим DEFERRED допустим только для понижения тарифа, иначе Google отклонит запрос на обновление.

Код извлекает текущий идентификатор через вызов substringBefore(":") из массива customerInfo.activeSubscriptions, отсекая суффикс базового плана. Для работы библиотеке требуется Google Play Billing Library версии 7 или выше в составе RevenueCat SDK.

Какие задачи решает

  • перевести активную подписку с ежемесячной на годовую с пересчётом времени
  • настроить отложенное понижение тарифа до даты следующего продления
  • извлечь чистый идентификатор продукта из activeSubscriptions без суффикса
  • исключить бесплатный доступ при переходе на платный план во время триала

Примеры запросов агенту

  • Помоги настроить апгрейд с ежемесячной подписки на годовую через GoogleProductChangeInfo в Kotlin
  • Напиши код смены подписки в RevenueCat с отложенным переходом через DEFERRED
  • Как правильно передать oldProductId при смене тарифа в RevenueCat на Android?

Как установить rc-plan-changes

1Через Skills CLI (Vercel)

npx skills add RevenueCat/ai-toolkit --skill rc-plan-changes -a claude-code

2Вручную: положить файлы

~/.claude/skills/rc-plan-changes/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/rc-plan-changes/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о rc-plan-changes

Как установить rc-plan-changes в Claude Code?

Выполните в терминале команду npx skills add RevenueCat/ai-toolkit --skill rc-plan-changes -a claude-code.

Что делает скилл rc-plan-changes?

Он даёт агенту правила перехода между подписками в Google Play на Android через RevenueCat. Скилл помогает выбрать правильный GoogleReplacementMode и собрать PurchaseParams.

Почему нельзя использовать режим DEFERRED для апгрейда подписки?

Google Play отклоняет отложенные обновления тарифа. Режим DEFERRED предназначен только для понижения плана, которое вступает в силу при следующем продлении.

Исходный текст

SKILL.md · MIT
---
name: rc-plan-changes
description: Use this skill when implementing subscription upgrades, downgrades, and plan switches via RevenueCat on Android. Covers GoogleProductChangeInfo, picking a Google replacement mode, and letting the SDK manage linkedPurchaseToken chaining.
license: Apache-2.0; see LICENSE
metadata:
  author: RevenueCat
  source: revenuecat-handbook chapter 7
  keywords:
  - android
  - revenuecat
  - upgrade
  - downgrade
  - google-product-change-info
  - replacement-mode
---

# Plan Changes on Android with RevenueCat

You use this skill when a user already has an active Google Play subscription and you need to move them to a different SKU (upgrade, downgrade, cross-grade, or trial conversion). RevenueCat exposes Google's replacement modes through a single `PurchaseParams` builder and resolves the `linkedPurchaseToken` chain server side so you do not write token chaining code.

## Phase 1: Preconditions

Confirm the following before invoking a plan change:

- The user has exactly one active Google Play subscription you intend to replace.
- You have a fresh `CustomerInfo` from `Purchases.sharedInstance.awaitCustomerInfo()` or a cached value from a recent listener callback.
- You have the target `Package` resolved from `offerings.current` (see `fetch-offerings` skill).
- Google Play Billing Library 7+ is on the classpath via the RevenueCat SDK.

Skip this skill if the user has no active subscription. For a fresh purchase, use `make-purchase` instead.

## Phase 2: Plan (pick a replacement mode)

`GoogleReplacementMode` maps one to one onto Google's billing modes. Pick based on the user intent:

| Scenario | Mode | Billing effect |
|---|---|---|
| Standard upgrade (monthly to annual) | `WITH_TIME_PRORATION` | Immediate switch, remaining time credited |
| Upgrade, keep the existing billing date | `CHARGE_PRORATED_PRICE` | Immediate switch, prorated charge now |
| Switch to or from a prepaid plan | `CHARGE_FULL_PRICE` | Immediate switch, full charge now |
| Upgrade during an active free trial | `CHARGE_PRORATED_PRICE` | Immediate switch, prorated charge now |
| Downgrade (annual to monthly) | `DEFERRED` | Switch applies at next renewal |

Do not default to `WITHOUT_PRORATION` for trial upgrades. `WITHOUT_PRORATION` applies the new plan immediately but charges nothing until the next renewal, which gives the user free premium access they did not pay for. Use `CHARGE_PRORATED_PRICE` to charge the upgrade price on the spot.

If you set no mode, `PurchaseParams` defaults to `WITHOUT_PRORATION`. Set the mode explicitly every time.

`DEFERRED` is valid only for downgrades. Google rejects deferred upgrades.

## Phase 3: Execute

Derive `currentProductId` from `CustomerInfo`. Hardcoded SKUs break when a user has migrated between plans.

```kotlin
val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()

// activeSubscriptions entries are "productId:basePlanId", strip the base plan suffix
val currentProductId = customerInfo.activeSubscriptions
    .firstOrNull()
    ?.substringBefore(":")
    ?: return  // nothing active, route to make-purchase instead

val newPackage = offerings.current
    ?.availablePackages
    ?.firstOrNull { it.identifier == "premium_annual_package" }
    ?: return

val params = PurchaseParams.Builder(activity, newPackage)
    .googleProductChangeInfo(
        GoogleProductChangeInfo(
            oldProductId = currentProductId,
            replacementMode = GoogleReplacementMode.WITH_TIME_PRORATION,
        )
    )
    .build()

try {
    val result = Purchases.sharedInstance.awaitPurchase(params)
    // result.customerInfo reflects the new subscription
} catch (e: PurchasesTransactionException) {
    if (!e.userCancelled) showError(e.error.message)
}
```

Notes on `oldProductId`:

- Pass the subscription product ID only. If you pass `"basic_monthly:monthly_plan"`, the SDK strips `:monthly_plan` for you, but the intent is clearer when you slice it yourself.
- `CustomerInfo.activeSubscriptions` uses the `productId:basePlanId` shape. `substringBefore(":")` gives you the correct value.

## Phase 4: Verify

After the suspending call returns, read the updated `CustomerInfo`:

- `customerInfo.activeSubscriptions` now contains the new `productId:basePlanId`.
- `customerInfo.entitlements["pro"]?.isActive` stays `true` across the switch; do not gate UI on the SKU string.
- For `DEFERRED` mode, `activeSubscriptions` still reports the old product until the next renewal. RevenueCat tracks the pending switch server side and flips the entitlement after Google sends the renewal RTDN.

You do not write `linkedPurchaseToken` traversal code. RevenueCat resolves the chain, marks the old token as replaced, and attributes both tokens to the same App User ID. Client code reads entitlements and trusts them.

## Common mistakes

| Mistake | Fix |
|---|---|
| Hardcoding `oldProductId` as a constant | Derive it from `customerInfo.activeSubscriptions.firstOrNull()?.substringBefore(":")` |
| Passing `"productId:basePlanId"` as `oldProductId` | Slice off the base plan with `substringBefore(":")` |
| Using `WITHOUT_PRORATION` for a trial upgrade | Use `CHARGE_PRORATED_PRICE` so the user is charged now |
| Using `DEFERRED` for an upgrade | `DEFERRED` is downgrade only; Google rejects deferred upgrades |
| Writing backend code to follow `linkedPurchaseToken` | RevenueCat does this server side, delete the code |
| Reading the SKU to decide UI state | Read `entitlements[...]?.isActive` instead |

## References

- [Full chapter](https://www.revenuecat.com/guides/revenuecat-android-sdk/subscription-upgrades-and-downgrades)

© RevenueCat · MIT · RevenueCat/ai-toolkit

Похожие скиллы

Всё по теме «Мобильная разработка» →
vercel-react-native-skills vercel Набор правил по React Native и Expo для ИИ-агента: списки, анимации, навигация. Пригодится, когда агент правит мобильный код и надо, чтобы он делал это одинаково. CX Codex +1 255 200установок animate-expo emilkowalski Создаёт мобильные анимации для React Native и Expo на базе Reanimated и Gesture Handler. Помогает избежать просадок кадров на реальных устройствах. CX Codex +1 137 900установок mobile-native emilkowalski Убирает мелочи, из-за которых веб-приложение на телефоне выдаёт себя: залипающий hover, вспышку при тапе, ошибку с 100vh. CX Codex +1 86 600установок aso coreyhaines31 Анализирует страницы приложений в App Store и Google Play по правилам поисковой оптимизации. Находит слабые места в метаданных, графике и конверсии. CX Codex +1 75 400установок swiftui-pro Paul Hudson Проверяет SwiftUI-код на устаревшие API, ошибки в структуре и проблемы доступности. Пригодится для ревью и доработки проектов на SwiftUI. CX Codex +1 35 200установок orca-emulator stablyai Управляет iOS Simulator внутри Orca с живым экраном устройства. Пригодится для тапов, жестов, поворота экрана и сбора доказательств работы iOS-приложения. CX Codex +1 5 800установок

Из того же набора33

rc-testing Скилл помогает настроить быстрое тестирование покупок RevenueCat в Android через Test Store, изолировать SDK интерфейсом для MockK и запустить проверки в GitHub Actions. CX Codex +1 627установок rc-catalog-management Ведёт каталог Android-товаров между Play Console и панелью RevenueCat: импорт продуктов, привязка к entitlement, сборка офферов. CX Codex +1 627установок rc-subscription-states Скилл для Android-проектов на RevenueCat: подсказывает агенту, как проверять доступ к платному контенту через CustomerInfo и EntitlementInfo. CX Codex +1 623установок rc-error-handling Разбирает ошибки RevenueCat Android SDK: какой код что значит и что показать пользователю. Пригодится, когда платёжный флоу ломается на проде. CX Codex +1 627установок rc-revenuecat-api-quick-reference Справочник по Android SDK 10.x и REST API RevenueCat прямо в редакторе: сигнатуры вызовов, структура Offerings, поля CustomerInfo. CX Codex +1 624установок rc-price-changes Помогает провести новую цену подписки в Google Play через RevenueCat, не трогая код приложения. CX Codex +1 618установок revenuecat-store-state Скилл проверяет и меняет цены, доступность и локализации продуктов в App Store Connect и Google Play Console через планы изменений RevenueCat без риска случайно сломать прод. CX Codex +1 617установок rc-understanding-revenuecat Объясняет агенту, как устроен RevenueCat Android SDK 10.x, до того как он напишет хоть строчку кода. Пригодится, если вы переходите с чистого Play Billing. CX Codex +1 617установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой