Skip to main content
skill CX Codex

rc-one-time-products — Claude Code skill: what it does, how to install

Provides best practices for Android one-time purchases via RevenueCat SDK without manual BillingClient plumbing. Helpful for consumable items and lifetime access.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add RevenueCat/ai-toolkit --skill rc-one-time-products -a claude-code
Open on GitHub

What rc-one-time-products does

The skill covers a structured workflow for selling non-subscription products on Android. Setup begins in Google Play Console and the RevenueCat dashboard, where consumable goods must be flagged for automatic consumption. Initialisation requires calling Purchases.configure once on application start with an Android API key and user identifier.

Transactions execute through awaitPurchase, which suspends until RevenueCat verifies the purchase receipt. Developers must not call acknowledgePurchase or consumeAsync directly. The SDK manages the underlying BillingClient session and consumes or acknowledges tokens automatically. Entitlement status is then checked via CustomerInfo.

Handling exceptions requires wrapping calls with PurchasesTransactionException. The skill instructs the agent to ignore user cancellations silently, while routing PaymentPendingError to an informative interface state. Pending orders do not grant entitlements immediately, so apps must listen to UpdatedCustomerInfoListener or server webhooks instead.

What it helps with

  • configure one-time Android purchases using awaitPurchase
  • handle payment cancellations without raising false runtime errors
  • inspect granted features via CustomerInfo entitlements
  • receive backend purchase confirmations through the INITIAL_PURCHASE webhook

Example prompts for the agent

  • Show how to retrieve packages and initiate a purchase using awaitPurchase in Kotlin.
  • Catch PurchasesTransactionException to handle PaymentPendingError and cancellation separately.
  • How do I check lifetime access in CustomerInfo without relying on product IDs?

How to install rc-one-time-products

1Skills CLI (Vercel)

npx skills add RevenueCat/ai-toolkit --skill rc-one-time-products -a claude-code

2Manually: put the files in place

~/.claude/skills/rc-one-time-products/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/rc-one-time-products/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about rc-one-time-products

How do I install rc-one-time-products in Claude Code?

Run npx skills add RevenueCat/ai-toolkit --skill rc-one-time-products -a claude-code.

What does rc-one-time-products do?

It instructs AI coding tools on implementing Android one-time and consumable purchases using the RevenueCat SDK.

Should I call acknowledgePurchase or consumeAsync directly?

No, you must avoid calling them manually. The RevenueCat SDK owns the BillingClient lifecycle and acknowledges or consumes tokens automatically once verified.

Source text

SKILL.md · MIT
---
name: rc-one-time-products
description: Use this skill when selling one time products on Android with RevenueCat. Covers fetching offerings, running awaitPurchase on a non subscription package, and relying on the SDK to acknowledge or consume automatically.
license: Apache-2.0; see LICENSE
metadata:
  author: RevenueCat
  source: revenuecat-handbook chapter 3
  keywords:
  - android
  - revenuecat
  - one-time-products
  - awaitpurchase
  - consumable
  - non-consumable
---

# One Time Products with RevenueCat

Sell one time products on Android without writing BillingClient glue. The RevenueCat SDK fetches products, launches the purchase, verifies the receipt, and acknowledges or consumes the token for you.

## Phase 1: Scope

Answer these before touching code.

| Question | Decision |
|---|---|
| Is the product consumable or non consumable? | Mark consumables in the RevenueCat dashboard so the SDK calls `consumeAsync` for you. |
| Do you read access through entitlements or transactions? | Prefer entitlements so access logic stays decoupled from product IDs. |
| Do you need server notification of the purchase? | Use RevenueCat webhooks. The `INITIAL_PURCHASE` event fires for every new non subscription purchase. |

Do not call `BillingClient.queryProductDetailsAsync`, `acknowledgePurchase`, or `consumeAsync` yourself once RevenueCat is integrated. The SDK owns the `BillingClient` instance.

## Phase 2: Prepare

- Configure the product in Google Play Console as a one time product.
- In the RevenueCat dashboard, attach the product to an offering and package, and flag it as consumable if it should be consumed on purchase.
- Confirm `Purchases.configure` runs once on app start with your Android API key and the current App User ID.

## Phase 3: Execute

Fetch offerings, launch the purchase, then read the result off `CustomerInfo`.

```kotlin
val offerings = Purchases.sharedInstance.awaitOfferings()
val pkg = offerings.current?.availablePackages?.first() ?: return
```

Launch the purchase with `awaitPurchase`. It suspends until the RevenueCat backend verifies the token.

```kotlin
val result = Purchases.sharedInstance.awaitPurchase(
    PurchaseParams.Builder(activity, pkg).build()
)
val info = result.customerInfo
```

Read access through entitlements, or fall back to the non subscription transactions list.

```kotlin
val hasAccess = info.entitlements["lifetime_access"]?.isActive == true
val owned = info.nonSubscriptionTransactions
    .any { it.productIdentifier == "lifetime_product_id" }
```

The SDK acknowledges non consumables and consumes consumables automatically once the backend verifies the purchase. You do not call `acknowledgePurchase` or `consumeAsync`.

## Phase 4: Handle Edge Cases

Wrap `awaitPurchase` to separate cancellation, pending payments, and real errors.

```kotlin
try { /* awaitPurchase */ } catch (e: PurchasesTransactionException) {
    when {
        e.error.code == PurchasesErrorCode.PaymentPendingError -> showPending()
        e.userCancelled -> Unit
        else -> showError(e.error.message)
    }
}
```

| Outcome | SDK signal | Action |
|---|---|---|
| Success | `awaitPurchase` returns with updated `CustomerInfo` | Grant access from `entitlements` or `nonSubscriptionTransactions`. |
| User cancel | `PurchasesTransactionException`, `userCancelled == true` | Swallow silently. |
| Pending payment | `PurchasesTransactionException`, `PaymentPendingError` | Show a pending message. The SDK updates the entitlement later through `UpdatedCustomerInfoListener`. |
| Other error | `PurchasesTransactionException`, other codes | Surface `e.error.message` to you. |

## Phase 5: Verify

- Trust `result.customerInfo` from `awaitPurchase`. Do not grant access from optimistic local state.
- Pending purchases do not grant entitlements. Register an `UpdatedCustomerInfoListener` so you react when the payment completes.
- For server side provisioning, subscribe to the RevenueCat `INITIAL_PURCHASE` webhook. Do not reimplement Google Play Developer API verification.

## References

- [Full chapter](https://www.revenuecat.com/guides/revenuecat-android-sdk/one-time-products-with-revenuecat)

© RevenueCat · MIT · RevenueCat/ai-toolkit

Similar skills

Everything for Mobile →

From the same set36

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor