Install
$ agentstack add skill-revenuecat-play-billing-skills-rtdn-reference ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
RTDN Reference
Phase 0: Intent
Tell the user: "I will look up the RTDN notification type you asked about and recommend handling."
Phase 1: Locate
Map the user's question to one of the tables in Phase 2:
- Integer code given (for example
notificationType: 5): determine which notification family the user is in.
- If the payload wraps the code in
subscriptionNotification, use the Subscription Notification Types table. - If the payload wraps the code in
oneTimeProductNotification, use the One Time Product Notification Types table. - If the payload has
voidedPurchaseNotification, use the Voided Purchase Notification section. - If the payload has
testNotification, use the Test Notification section.
- Event name given (for example
SUBSCRIPTION_ON_HOLD): search the Type column in the Subscription or One Time Product table. - Symptom given (for example "user lost access after failed payment"): match the trigger phrase in the Trigger column. Account hold and grace period map to codes 5 and 6 respectively in the subscription table.
- Unassigned code (14, 15, 16, 21): report that the code is reserved and not emitted.
After locating the row, jump to Phase 3 for the handling guidance for that family.
Phase 2: Reference tables
RTDN Message Structure
Every RTDN message arrives as a Cloud Pub/Sub message with a base64 encoded JSON payload in the data field. The decoded JSON has this structure:
{
"version": "1.0",
"packageName": "com.example.app",
"eventTimeMillis": "1234567890123",
"subscriptionNotification": { },
"oneTimeProductNotification": { },
"voidedPurchaseNotification": { },
"testNotification": { }
}
Only one notification field is present per message.
Subscription Notification Types
| Code | Type | Trigger | Subscription State After | Required Action | |------|------|---------|--------------------------|-----------------| | 1 | SUBSCRIPTION_RECOVERED | Payment recovered from grace period, account hold, or pause resumes | ACTIVE | Grant access. Call subscriptionsv2.get to confirm state. | | 2 | SUBSCRIPTION_RENEWED | Active subscription successfully renewed | ACTIVE | Extend entitlement. Update expiryTime in your database. | | 3 | SUBSCRIPTION_CANCELED | Subscription canceled (user, developer, or system) | CANCELED | Check canceledStateContext for reason. User retains access until expiryTime. | | 4 | SUBSCRIPTION_PURCHASED | New subscription purchased | ACTIVE | Verify purchase. Grant access. Acknowledge within 3 days. | | 5 | SUBSCRIPTION_ON_HOLD | Grace period expired, entered account hold | ON_HOLD | Revoke access. Notify user to fix payment method. | | 6 | SUBSCRIPTION_IN_GRACE_PERIOD | Renewal payment failed, entered grace period | IN_GRACE_PERIOD | Retain access. Notify user about payment issue. Show in app message. | | 7 | SUBSCRIPTION_RESTARTED | User restored canceled subscription from Play Store before expiration | ACTIVE | Grant access. Same purchase token. | | 8 | SUBSCRIPTION_PRICE_CHANGE_CONFIRMED | (Deprecated) User confirmed a price change | No state change | Deprecated. Use type 19 instead. | | 9 | SUBSCRIPTION_DEFERRED | Subscription billing date extended via subscriptionsv2.defer | ACTIVE | Update expiryTime in your database. Extend access. | | 10 | SUBSCRIPTION_PAUSED | Subscription paused (effective at end of current period) | PAUSED | Revoke access when pause begins. | | 11 | SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED | Pause schedule modified (pause set, changed, or removed) | Varies | Call API to check current pause schedule. | | 12 | SUBSCRIPTION_REVOKED | Subscription revoked (refund, developer revocation) | EXPIRED | Immediately revoke access. | | 13 | SUBSCRIPTION_EXPIRED | Subscription expired after cancellation or account hold failure | EXPIRED | Revoke access. Clean up entitlement records. | | 17 | SUBSCRIPTION_ITEMS_CHANGED | Items in subscription bundle changed (add ons) | Varies | Call API to get updated line items. | | 18 | SUBSCRIPTION_CANCELLATION_SCHEDULED | Installment subscription cancellation scheduled at end of commitment | ACTIVE (until commitment ends) | Note the scheduled cancellation. User retains access through commitment period. | | 19 | SUBSCRIPTION_PRICE_CHANGE_UPDATED | Price change details updated for existing subscribers | Varies | Call API to get updated price change information. | | 20 | SUBSCRIPTION_PENDING_PURCHASE_CANCELED | Pending subscription purchase was canceled | EXPIRED | Do not grant access. Clean up any pending records. | | 22 | SUBSCRIPTION_PRICE_STEP_UP_CONSENT_UPDATED | Price step up consent period begun or user provided consent (South Korea) | Varies | Call API to check consent status. |
Notification type codes 14, 15, 16, and 21 are not assigned. Do not expect to receive these values.
Subscription notification payload:
{
"version": "1.0",
"notificationType": 4,
"purchaseToken": "purchase-token-string",
"subscriptionId": "premium_monthly"
}
One Time Product Notification Types
| Code | Type | Trigger | Required Action | |------|------|---------|-----------------| | 1 | ONE_TIME_PRODUCT_PURCHASED | One time product purchase completed | Verify purchase. Grant access. Acknowledge or consume. | | 2 | ONE_TIME_PRODUCT_CANCELED | Pending one time product purchase was canceled | Do not grant access. Clean up pending records. |
One time product notification payload:
{
"version": "1.0",
"notificationType": 1,
"purchaseToken": "purchase-token-string",
"sku": "remove_ads"
}
One time product notifications include the sku field (the product ID); subscription notifications include subscriptionId.
Voided Purchase Notification
Sent when a purchase is voided due to a refund, chargeback, or revocation.
{
"purchaseToken": "purchase-token-string",
"orderId": "GPA.1234-5678-9012-34567",
"productType": 1,
"refundType": 1
}
| Field | Values | |-------|--------| | productType | 1 = Subscription, 2 = One time product | | refundType | 1 = Full refund, 2 = Quantity based refund |
Test Notification
{
"version": "1.0"
}
Test notifications have only a version field inside the testNotification object.
Phase 3: Recommended handling
Subscription notifications
For every subscription notification:
- Decode the base64
datafield from the Pub/Sub message. - Parse the
DeveloperNotificationand readsubscriptionNotification.notificationType. - Extract
purchaseTokenandsubscriptionId. - Call
purchases.subscriptionsv2.geton the Google Play Developer API with the purchase token. - Apply the action from the Required Action column in the table, using the API response as the source of truth.
- Acknowledge the Pub/Sub message.
Code-specific notes:
- Codes
4(purchase) and1(recovered) require acknowledging the purchase within 3 days or Google refunds it. - Code
3(canceled) is not an immediate revocation. Grant access untillineItems[].expiryTime. - Code
5(on hold) is the revoke-now signal after a failed renewal. Code6(grace period) is the keep-access signal. - Code
12(revoked) requires immediate access revocation regardless ofexpiryTime. - Codes
11,17,19,22have aVariesstate; the notification flags that something changed and the API holds the new value. - Code
8is deprecated. New integrations should handle code19instead.
One time product notifications
- Read
oneTimeProductNotification.notificationTypeandpurchaseToken. - Call
purchases.products.geton the Google Play Developer API. - For code
1, verify the purchase state, grant the entitlement, then acknowledge (non-consumable) or consume (consumable) within 3 days. - For code
2, discard any pending record you created when the user began checkout.
Voided purchase notifications
- Revoke access immediately for the
purchaseToken. - Call the Voided Purchases API (
purchases.voidedpurchases.list) for full details, includingvoidedReasonandvoidedSource. - Use
productTypeto route to your subscription or one time product tables. - Use
refundTypeto decide whether to revoke the full entitlement or only an adjusted quantity.
Test notifications
Acknowledge the Pub/Sub message without processing it as a real event. Log receipt so you can confirm your integration is wired up when you trigger a test from Play Console.
Handler pattern (applies to every RTDN)
- Receive the Pub/Sub message.
- Decode the base64
datafield. - Parse the JSON
DeveloperNotification. - Identify which notification field is present.
- Extract the
purchaseToken. - Call the Google Play Developer API for current state.
- Update your database from the API response, not from the notification alone.
- Acknowledge the Pub/Sub message.
Never rely solely on the RTDN payload for business logic. RTDNs signal that something changed. The API tells you the current state.
References
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: RevenueCat
- Source: RevenueCat/play-billing-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.