rc-cancellations-pauses-winback — скилл для Claude Code: отмены
Показывает в Android-приложении отмену подписки, паузу и возврат клиента. Помогает не гадать, что происходит с доступом.
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a claude-codeЧто делает rc-cancellations-pauses-winback
Скилл разбирает состояния подписки, которые приходят в CustomerInfo от RevenueCat. Отмену с остатком доступа видно по unsubscribeDetectedAt и isActive == true, проблему с оплатой — по billingIssuesDetectedAt, паузу — когда продукт ещё принадлежит пользователю, а isActive уже false. Дальше агент подсказывает, что показать в интерфейсе.
Внутри четыре фазы: определить нужное состояние, проверить готовые данные, написать код на Kotlin и протестировать. Отдельно разобрана кнопка управления подпиской: managementURL уже имеет тип Uri?, его передают в Intent напрямую, а обёртка Uri.parse(url.toString()) тут лишняя и легко ломается. Дату возобновления после паузы в SDK не найти — её берут из REST-эндпоинта /v1/subscribers/{app_user_id} по полю paused_expiration_time_ms, причём с бэкенда, потому что нужен секретный ключ RC_SECRET_API_KEY.
Есть нюансы, о которые спотыкаются. Не считайте доступ вручную по expirationDate — читайте isActive и willRenew. Возврат до истечения срока сбрасывает unsubscribeDetectedAt сам, а вот возврат после истечения приходит как RENEWAL, и в обработчике на бэкенде доступ надо выдавать и на это событие. Отдельного вебхука про паузу RevenueCat не шлёт, состояние появляется в CustomerInfo после обработки RTDN от Google Play.
Какие задачи решает
- показать баннер «подписка закончится такого-то числа» после отмены
- открыть экран управления подпиской в Google Play по тапу
- отличить паузу от полного окончания доступа
- вытащить дату возобновления после паузы через REST API
- выдать доступ при возврате клиента после истечения срока
Примеры запросов агенту
- Добавь в экран подписки баннер об отмене, если
unsubscribeDetectedAtне null, аisActivetrue - Сделай кнопку «Управлять подпиской», которая открывает
managementURL - Напиши на бэкенде запрос к
/v1/subscribers/{app_user_id}и верниpaused_expiration_time_msна клиент
Как установить rc-cancellations-pauses-winback
1Через Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a claude-code2Вручную: положить файлы
~/.claude/skills/rc-cancellations-pauses-winback/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/rc-cancellations-pauses-winback/
1Через Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a codex2Вручную: положить файлы
.agents/skills/rc-cancellations-pauses-winback/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a cursor2Вручную: положить файлы
.cursor/skills/rc-cancellations-pauses-winback/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install RevenueCat/ai-toolkit rc-cancellations-pauses-winback2Вручную: положить файлы
.github/skills/rc-cancellations-pauses-winback/SKILL.md1Через Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a gemini-cli2Командой платформы
gemini skills install https://github.com/RevenueCat/ai-toolkit --path revenuecat-play-billing/skills/rc-cancellations-pauses-winbackВручную: положить файлы
.devin/skills/rc-cancellations-pauses-winback/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Из того же набора25
Похожие
Рядом в теме «Мобильная разработка»
Всё по теме «Мобильная разработка» →Вопросы о rc-cancellations-pauses-winback
Как установить rc-cancellations-pauses-winback в Claude?
Выполните npx skills add RevenueCat/ai-toolkit --skill rc-cancellations-pauses-winback -a claude-code. Скилл окажется в ~/.claude/skills/rc-cancellations-pauses-winback/SKILL.md.
Что делает rc-cancellations-pauses-winback?
Помогает агенту разобрать состояния отмены, паузы и возврата клиента в Android-приложении на RevenueCat: какие поля CustomerInfo читать, как открыть экран управления подпиской и откуда взять дату возобновления.
Нужен ли секретный ключ RevenueCat?
Да, для даты возобновления после паузы. Её запрашивают с бэкенда через /v1/subscribers/{app_user_id} с заголовком Authorization: Bearer $RC_SECRET_API_KEY — в SDK этого значения нет.
Исходный текст
SKILL.md · MIT
---
name: rc-cancellations-pauses-winback
description: Use this skill when surfacing cancellation, pause, and winback state on Android with RevenueCat. Covers reading unsubscribeDetectedAt, billingIssuesDetectedAt, pause state via periodType, managementURL for deep link, and pause resume date lookup via the REST API.
license: Apache-2.0; see LICENSE
metadata:
author: RevenueCat
source: revenuecat-handbook chapter 13
keywords:
- android
- revenuecat
- cancellation
- pause
- winback
- management-url
---
# Cancellations, Pauses, and Winback
Detect cancellation, pause, and winback states on Android by reading `CustomerInfo`. Most of these events are passive: your app observes them rather than initiates them. Open Google Play's manage screen with `managementURL`. Look up the pause resume date from the REST API.
## Phase 1: Scope
Decide what state you need to surface to the user.
| State | Signal in CustomerInfo | Notes |
|---|---|---|
| Canceled, still has access | `entitlement.unsubscribeDetectedAt != null` and `isActive == true` | Show "ends on [expirationDate]" |
| Billing issue (grace or hold) | `entitlement.billingIssuesDetectedAt != null` | Payment failed, recovery in progress |
| Paused | `entitlement.isActive == false` while product is still owned | Resume date requires REST API |
| Active and renewing | `entitlement.willRenew == true` | Normal state |
Backend win back campaign segmentation uses the `CANCELLATION` webhook `cancel_reason` field (`UNSUBSCRIBE`, `BILLING_ERROR`, `DEVELOPER_INITIATED`, `PRICE_INCREASE`).
## Phase 2: Prepare
Confirm you already have a fetched `CustomerInfo` from `Purchases.sharedInstance.getCustomerInfo(...)` or a listener. You do not need to call `BillingClient.queryPurchasesAsync` with `setIncludeSuspendedSubscriptions(true)`. RevenueCat resolves pause state from the server side subscription state.
Rules:
- `managementURL` is already `Uri?`. Pass it straight to the Intent. Do not call `Uri.parse(url.toString())`.
- Do not compute access manually from `expirationDate`. Read `isActive` and `willRenew`.
- Pause resume date is not in the SDK. Fetch it from `/v1/subscribers/{app_user_id}` in your backend.
## Phase 3: Execute
### Detect cancellation with remaining access
```kotlin
val entitlement = customerInfo.entitlements["pro_access"]
if (entitlement?.unsubscribeDetectedAt != null && entitlement.isActive) {
entitlement.expirationDate?.let { expiry ->
showCancellationBanner(expiry)
}
}
```
`unsubscribeDetectedAt` is set when RevenueCat receives the `SUBSCRIPTION_CANCELED` RTDN. `isActive` stays `true` until the billing period ends.
### Detect pause
```kotlin
val entitlement = customerInfo.entitlements["pro_access"]
val hasAccess = entitlement?.isActive == true
// When paused, isActive == false. Pause resume date is not in the SDK.
```
### Open the subscription management screen
```kotlin
customerInfo.managementURL?.let { url ->
startActivity(Intent(Intent.ACTION_VIEW, url))
}
```
`managementURL` is typed as `Uri?`. Use it directly. Wrapping it with `Uri.parse(url.toString())` is redundant and error prone.
### Look up pause resume date via REST
From your backend, call the subscribers endpoint and read `paused_expiration_time_ms` on the subscription:
```bash
curl -H "Authorization: Bearer $RC_SECRET_API_KEY" \
https://api.revenuecat.com/v1/subscribers/$APP_USER_ID
```
The subscription object contains `paused_expiration_time_ms` when paused. Expose this to your client through your own endpoint.
### Resubscribe before expiry
Google's resubscribe before expiry flow fires `SUBSCRIPTION_RESTARTED`. RevenueCat clears `unsubscribeDetectedAt` and sets `willRenew = true`. No extra code required, just re-read `customerInfo`.
### Resubscribe after expiry
A resubscribe after expiry fires a `RENEWAL` webhook, not `INITIAL_PURCHASE`. Grant entitlement access on both events in your backend handler.
## Phase 4: Verify
| Check | How |
|---|---|
| Cancellation banner shows | Cancel in Play Store, pull fresh `CustomerInfo`, confirm `unsubscribeDetectedAt != null` and `isActive == true` |
| Management deep link opens | Tap the link, verify Play subscriptions screen opens for the correct product |
| Pause is reflected | Pause in Play Store sandbox, confirm `entitlement.isActive == false` |
| Pause resume date | Call `/v1/subscribers/{id}`, confirm `paused_expiration_time_ms` is present |
| Win back accepted | Accept a Play configured win back offer, confirm the purchase surfaces as a normal subscription in `CustomerInfo` |
## Notes
- Deferral and revocation are done through the Google Play Developer API (`purchases.subscriptionsv2.defer`, `purchases.subscriptionsv2.revoke`) from your backend. RevenueCat processes the resulting RTDN and updates `CustomerInfo` automatically.
- Win back campaigns need no SDK code. They are configured in Play Console or the RevenueCat dashboard.
- RevenueCat does not currently emit a dedicated pause webhook. Pause state appears in `CustomerInfo` once the Google Play RTDN is processed.
## References
- [Full chapter](https://www.revenuecat.com/guides/revenuecat-android-sdk/cancellations-pauses-and-winback)
© RevenueCat · MIT · RevenueCat/ai-toolkit