
Gpc Monetization
- 27 installs
- 1 repo stars
- Updated August 1, 2026
- yasserstudio/gpc-skills
Helps with ai & agent building tasks.
About
gpc-monetization is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- gpc-monetization
- AI & Agent Building
- AI-coding skill
Gpc Monetization by the numbers
- 27 all-time installs (skills.sh)
- Ranked #9,571 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-monetizationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 1 |
| Last updated | August 1, 2026 |
| Repository | yasserstudio/gpc-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
gpc-monetization
Manage subscriptions, in-app products, purchases, and pricing with GPC.
When to use
- Creating, updating, or deleting subscriptions and base plans
- Managing subscription offers (introductory, upgrade, winback)
- Creating, updating, or syncing in-app products (one-time purchases)
- Verifying, acknowledging, or consuming purchases server-side
- Cancelling, deferring, or revoking subscriptions
- Handling refunds and voided purchases
- Viewing subscription analytics (active, trial, churn, conversion)
- Migrating subscribers to new price points
- Converting prices across regions and currencies
Inputs required
- Authenticated GPC —
gpc auth statusmust show valid credentials - App package name — set via
--apporgpc config set app - Product JSON files — for create/update operations (subscription or IAP definitions)
- Purchase tokens — for verification, acknowledgement, and consumption
- Developer account ID — for some purchase operations (
--developer-id)
Procedure
0. Verify setup
gpc auth status
gpc config get appConfirm auth is valid and default app is set. If not, Read: the gpc-setup skill.
1. Subscriptions
A. List and inspect subscriptions
# List all subscriptions
gpc subscriptions list
# Get details for a specific subscription
gpc subscriptions get <product-id>
# Paginated listing
gpc subscriptions list --limit 50B. Create a subscription
Create a JSON file defining the subscription, then:
# Preview first
gpc subscriptions create --file subscription.json --dry-run
# Create
gpc subscriptions create --file subscription.json
# Specify a regions version (defaults to 2022/02)
gpc subscriptions create --file subscription.json --regions-version "2022/02"Read: references/subscription-schema.md for the JSON structure and field reference.
C. Update a subscription
# Update specific fields
gpc subscriptions update <product-id> --file updated.json --update-mask "listings"
# Preview changes
gpc subscriptions update <product-id> --file updated.json --dry-run
# Upsert: create the subscription if it does not already exist
gpc subscriptions update <product-id> --file updated.json --allow-missing
# Control propagation speed (default: LATENCY_SENSITIVE)
gpc subscriptions update <product-id> --file updated.json --latency-tolerance LATENCY_TOLERANTThe --update-mask flag controls which fields are updated. Omit it to replace the entire subscription.
The --allow-missing flag enables upsert behavior: if the subscription does not exist, it will be created instead of returning an error.
The --latency-tolerance flag controls how quickly changes propagate. Use LATENCY_SENSITIVE (default) for immediate propagation, or LATENCY_TOLERANT when you can accept a delay in exchange for higher throughput during bulk operations.
D. Batch operations
# Batch-get multiple subscriptions at once
gpc subscriptions batch-get <id1> <id2> <id3>
# Batch-update multiple subscriptions from JSON
gpc subscriptions batch-update --file batch-updates.json --dry-run
gpc subscriptions batch-update --file batch-updates.jsonE. Delete a subscription
gpc subscriptions delete <product-id> --dry-run
gpc subscriptions delete <product-id>2. Base plans
Base plans define the billing period and pricing for a subscription.
# Activate a base plan (makes it available for purchase)
gpc subscriptions base-plans activate <product-id> <base-plan-id>
# Deactivate a base plan (stops new purchases, existing subscribers unaffected)
gpc subscriptions base-plans deactivate <product-id> <base-plan-id>
# Delete a base plan
gpc subscriptions base-plans delete <product-id> <base-plan-id>
# Migrate prices for a base plan
gpc subscriptions base-plans migrate-prices <product-id> <base-plan-id> --file prices.jsonAll base plan commands support --dry-run.
3. Subscription offers
Offers define promotional pricing (introductory, upgrade, winback).
# List offers for a base plan
gpc subscriptions offers list <product-id> <base-plan-id>
# Get offer details
gpc subscriptions offers get <product-id> <base-plan-id> <offer-id>
# Create an offer
gpc subscriptions offers create <product-id> <base-plan-id> --file offer.json --dry-run
gpc subscriptions offers create <product-id> <base-plan-id> --file offer.json
# Update an offer
gpc subscriptions offers update <product-id> <base-plan-id> <offer-id> --file offer.json
# Upsert: create the offer if it does not already exist
gpc subscriptions offers update <product-id> <base-plan-id> <offer-id> --file offer.json --allow-missing
# Control propagation speed for bulk offer updates
gpc subscriptions offers update <product-id> <base-plan-id> <offer-id> --file offer.json --latency-tolerance LATENCY_TOLERANT
# Activate / deactivate an offer
gpc subscriptions offers activate <product-id> <base-plan-id> <offer-id>
gpc subscriptions offers deactivate <product-id> <base-plan-id> <offer-id>
# Delete an offer
gpc subscriptions offers delete <product-id> <base-plan-id> <offer-id>4. In-app products (IAP)
One-time purchases — consumables, non-consumables, entitlements.
A. List and inspect
gpc iap list
# Paginated listing (useful for apps with many products)
gpc iap list --page-size 50
gpc iap list --page-size 50 --next-page <page-token>
gpc iap get <sku>New in v0.9.51:gpc iap list(aliased asgpc one-time-products list) now supports--page-sizeand--next-pagefor paginated results. The response includes anextPageTokenfield when more results are available.
B. Create and update
# Create from JSON
gpc iap create --file product.json --dry-run
gpc iap create --file product.json
# Specify a regions version on create (defaults to 2022/02)
gpc iap create --file product.json --regions-version "2022/02"
# Update
gpc iap update <sku> --file updated.json --dry-run
gpc iap update <sku> --file updated.json
# Upsert: create the product if it does not already exist
gpc iap update <sku> --file updated.json --allow-missing
# Control propagation speed
gpc iap update <sku> --file updated.json --latency-tolerance LATENCY_TOLERANT
# Delete
gpc iap delete <sku>Read: references/iap-schema.md for the JSON structure and field reference.
C. Batch delete
# Delete multiple IAP products at once
gpc iap batch-delete <sku1> <sku2> <sku3> --dry-run
gpc iap batch-delete <sku1> <sku2> <sku3>D. Sync from directory
Bulk-manage IAP products from a directory of JSON files:
# Preview what would change
gpc iap sync --dir products/ --dry-run
# Apply changes
gpc iap sync --dir products/Each JSON file in the directory represents one product. GPC compares local files against the Play Store and creates, updates, or deletes as needed.
Activating/deactivating OTP offers (v0.9.57+)
One-time product offers support explicit activation/deactivation:
gpc otp offers activate --app com.example.app --product-id sku_id --offer-id offer_id
gpc otp offers deactivate --app com.example.app --product-id sku_id --offer-id offer_idMatches the subscription-offer lifecycle. Without this, OTP offers relied on state being set through batch update calls only.
OTP offer and purchase-option batch operations (v0.9.79+)
Batch operations on one-time product offers and purchase options:
# Retrieve multiple OTP offers in one call
gpc one-time-products offers batch-get --product-id <sku> --offer-ids "offer_a,offer_b"
# Update multiple OTP offers from JSON
gpc one-time-products offers batch-update --file otp-offers.json --dry-run
gpc one-time-products offers batch-update --file otp-offers.json
# Bulk activate or deactivate OTP offers
gpc one-time-products offers batch-update-states --file otp-states.json --dry-run
gpc one-time-products offers batch-update-states --file otp-states.json
# Delete multiple OTP offers at once
gpc one-time-products offers batch-delete --product-id <sku> --offer-ids "offer_a,offer_b" --dry-run
gpc one-time-products offers batch-delete --product-id <sku> --offer-ids "offer_a,offer_b"
# Bulk delete purchase options across products
gpc one-time-products purchase-options batch-delete --file po-delete.json --dry-run
gpc one-time-products purchase-options batch-delete --file po-delete.json
# Bulk update purchase-option states (activate/deactivate)
gpc one-time-products purchase-options batch-update-states --file po-states.json --dry-run
gpc one-time-products purchase-options batch-update-states --file po-states.jsonAll batch commands support --dry-run and --json. These operations cover the full v0.9.79 OTP batch surface: 4 offer methods (batch-get, batch-update, batch-update-states, batch-delete) and 2 purchase-option methods (batch-delete, batch-update-states).
5. Purchases — verification and lifecycle
A. Product purchases
# Verify a purchase
gpc purchases get <product-id> <token>
# Acknowledge (required within 3 days or purchase is refunded)
gpc purchases acknowledge <product-id> <token>
gpc purchases acknowledge <product-id> <token> --payload "order-123"
# Consume (for consumable products — allows re-purchase)
gpc purchases consume <product-id> <token>B. Subscription purchases
# Acknowledge a subscription purchase (v1 — required within 3 days)
gpc purchases subscription acknowledge <subscription-id> <token>
gpc purchases subscription acknowledge <subscription-id> <token> --payload "order-456"
# Get subscription purchase details (v2 API)
# Returns SubscriptionPurchaseV2 with onHoldStateContext, inGracePeriodStateContext (v0.9.76+)
# Both context objects are surfaced in --json output and structured display
gpc purchases subscription get <token>
# Cancel a subscription (v1 — requires subscription-id)
gpc purchases subscription cancel <subscription-id> <token>
# Cancel a subscription (v2 — supports cancellation types)
gpc purchases subscription cancel-v2 <token>
gpc purchases subscription cancel-v2 <token> --type DEVELOPER_CANCELED
# Defer expiry to a later date (v1)
gpc purchases subscription defer <subscription-id> <token> --expiry 2025-06-01T00:00:00Z
# Defer expiry (v2 — supports add-on subscriptions)
gpc purchases subscription defer-v2 <token> --until 2026-07-01T00:00:00Z
# Revoke a subscription (v2 API)
gpc purchases subscription revoke <token>C. Product purchases (v2 API)
# Get product purchase details (v2 — supports multi-offer OTPs)
gpc purchases product get-v2 <token>The v2 product purchase API returns ProductPurchaseV2 with line items, offer details, and multi-product bundle support.
D. Orders
# Get order details
gpc purchases orders get <order-id>
# Batch retrieve orders (up to 1000)
gpc purchases orders batch-get --ids "GPA.1234,GPA.5678"New in v0.9.79: Orders now expose anOfferPhaseDetailstype that replaces the flatofferPhasefield.OfferPhaseDetailsis a structured object with richer phase information (phase type, cycle counts, and pricing details). The flatofferPhasefield is deprecated; read fromofferPhaseDetailsin new code.
E. Voided purchases and refunds
# List voided purchases (default: in-app only)
gpc purchases voided --start-time 2025-01-01 --end-time 2025-03-01
# Include subscription voids (type=1)
gpc purchases voided --type 1
# Include quantity-based partial refunds
gpc purchases voided --include-partial-refunds
# Refund an order
gpc purchases orders refund <order-id> --full-refund
gpc purchases orders refund <order-id> --prorated-refundNew in v0.9.47:--type 0(default) returns only in-app purchase voids.--type 1includes subscription voids.--include-partial-refundsincludes quantity-based partial refunds.
All write operations support --dry-run.
Read: references/purchase-verification.md for server-side verification patterns and best practices.
6. Real-Time Developer Notifications (RTDN)
RTDN delivers Pub/Sub messages when subscription and purchase events occur. GPC can decode and inspect these notifications.
# Check RTDN topic configuration
gpc rtdn status
# Decode a base64-encoded Pub/Sub notification payload
gpc rtdn decode <base64-payload>
# Show setup instructions for RTDN
gpc rtdn testNotification types include: SUBSCRIPTION_PURCHASED, SUBSCRIPTION_CANCELED, SUBSCRIPTION_RENEWED, SUBSCRIPTION_REVOKED, SUBSCRIPTION_EXPIRED, ONE_TIME_PRODUCT_PURCHASED, VOIDED_PURCHASE, and more.
New in v0.9.47: RTDN commands help debug subscription lifecycle events. Set up a Pub/Sub topic in GCP, configure it in Play Console > Monetization setup, and use gpc rtdn decode to inspect payloads.7. Subscription analytics
Get insights on subscriber counts, conversion, and churn:
# Active subscribers, in-trial counts, trial→paid conversion, churn cohort
gpc subscriptions analytics
# JSON output for dashboards
gpc subscriptions analytics --jsonReports: active count, in-trial count, cancelled count, trial-to-paid conversion rate, estimated churn by cohort.
8. Base plan price migration
Migrate existing subscribers to a new price point:
# Migrate all subscribers on a base plan to a new price
gpc subscriptions base-plans migrate-prices <product-id> <base-plan-id> \
--file prices.json
# Prices file format: regional prices JSON (same as base plan prices)Subscribers are notified by Google Play and must accept or cancel. Use --dry-run to preview the migration.
9. Regional pricing
Convert a base price to all Google Play supported regions:
# Convert USD 4.99 to all regional prices
gpc pricing convert --from USD --amount 4.99
# Output as JSON for scripting
gpc pricing convert --from USD --amount 4.99 --jsonThe conversion uses Google Play's official exchange rates and rounds to locally appropriate price points.
10. Data safety (v0.9.75+)
Manage your app's Data Safety section declarations via the API:
# Get current data safety labels
gpc data-safety get
# Update data safety declarations from CSV
gpc data-safety update --file safety.csvVerification
gpc subscriptions listreturns your subscriptionsgpc iap listreturns your in-app productsgpc purchases get <product-id> <token>returns v1 purchase details for a valid tokengpc purchases product get-v2 <token>returns v2 purchase details with line itemsgpc purchases orders get <order-id>returns order detailsgpc pricing convert --from USD --amount 9.99 --jsonreturns regional prices- All
--dry-runcommands show what would change without modifying data - JSON output works on all commands (
--jsonflag)
Failure modes / debugging
| Symptom | Likely Cause | Fix |
|---|---|---|
PRODUCT_NOT_FOUND | Invalid product ID or SKU | Verify with gpc subscriptions list or gpc iap list |
INVALID_PURCHASE_TOKEN | Token expired, already consumed, or wrong app | Verify token matches the app and product |
PURCHASE_NOT_ACKNOWLEDGED | Purchase not acknowledged within 3 days | Acknowledge immediately; if >3 days, purchase was auto-refunded |
SUBSCRIPTION_NOT_FOUND | Wrong subscription ID in cancel/defer | Use gpc purchases subscription get <token> to find the correct ID |
INVALID_JSON in create/update | Malformed product JSON file | Validate JSON structure against the schema reference |
PERMISSION_DENIED on purchases | Service account lacks financial permissions | Grant "View financial data" and "Manage orders" in Play Console |
--update-mask error | Invalid field path in update mask | Check API docs for valid field names; omit flag to replace all fields |
iap sync deletes unexpected products | Directory missing some product files | Use --dry-run first; sync deletes products not in the directory |
Related skills
- gpc-setup — authentication and configuration required before monetization commands
- gpc-release-flow — releasing app updates that include new products or pricing changes
- gpc-vitals-monitoring — monitoring reviews that mention billing issues
- gpc-ci-integration — automating IAP sync and purchase verification in CI/CD
{
"skill_name": "gpc-monetization",
"evals": [
{
"id": 1,
"prompt": "I need to create a monthly subscription for our premium tier at $4.99/month with a 7-day free trial offer. The subscription ID should be premium_monthly. Can you show me the JSON files I need and the GPC commands to set it all up?",
"expected_output": "Provides subscription JSON with base plan, creates it with gpc subscriptions create, then creates a free trial offer",
"files": [],
"expectations": [
"Shows a subscription JSON with productId premium_monthly and a base plan with P1M billing period",
"Shows the price object with units 4 and nanos 990000000 for $4.99",
"Uses gpc subscriptions create --file with --dry-run first",
"Shows an offer JSON with PRICING_PHASE_TYPE_FREE and P1W duration",
"Uses gpc subscriptions offers create to add the free trial"
]
},
{
"id": 2,
"prompt": "We have about 30 in-app products (coins packs, power-ups, etc.) defined as JSON files in our iap/ directory. We want to sync them all to the Play Store at once. Some are new, some are updated. How do we do this safely?",
"expected_output": "Shows how to use gpc iap sync with dry-run preview before applying",
"files": [],
"expectations": [
"Shows gpc iap sync --dir iap/ --dry-run to preview changes",
"Explains that sync creates, updates, and deletes to match the directory",
"Warns that missing files in the directory will cause deletions",
"Shows gpc iap sync --dir iap/ to apply changes",
"Mentions the JSON structure for IAP files (sku, defaultPrice, listings)"
]
},
{
"id": 3,
"prompt": "A user reported they purchased our premium unlock but didn't get the feature. They sent us the purchase token. How do I verify the purchase, check if it was acknowledged, and fix it if needed? Also we need to convert our $9.99 price to all regional prices.",
"expected_output": "Shows purchase verification, acknowledgement check, and regional price conversion",
"files": [],
"expectations": [
"Shows gpc purchases get <product-id> <token> --json to verify",
"Explains purchaseState (0=purchased) and acknowledgementState (0=not acknowledged)",
"Shows gpc purchases acknowledge <product-id> <token> if not acknowledged",
"Mentions the 3-day acknowledgement deadline before auto-refund",
"Shows gpc pricing convert --from USD --amount 9.99 for regional pricing"
]
}
]
}
In-App Product (IAP) JSON Schema
Reference for the JSON structure used with gpc iap create, gpc iap update, and gpc iap sync.
IAP object
{
"sku": "coins_100",
"status": "statusActive",
"purchaseType": "managedUser",
"defaultPrice": {
"priceMicros": "990000",
"currency": "USD"
},
"listings": {
"en-US": {
"title": "100 Coins",
"description": "A pack of 100 coins to use in-game."
},
"ja-JP": {
"title": "100\u30b3\u30a4\u30f3",
"description": "\u30b2\u30fc\u30e0\u5185\u3067\u4f7f\u3048\u308b100\u30b3\u30a4\u30f3\u30d1\u30c3\u30af\u3002"
}
},
"defaultLanguage": "en-US"
}Key fields
| Field | Type | Required | Description |
|---|---|---|---|
sku | string | Yes | Unique product ID (letters, numbers, underscores, periods) |
status | string | No | statusActive or statusInactive |
purchaseType | string | Yes | managedUser (one-time) or subscription (legacy) |
defaultPrice | object | Yes | Base price for the product |
listings | object | Yes | Localized title and description by language code |
defaultLanguage | string | Yes | Primary language code |
Price object
{
"priceMicros": "4990000",
"currency": "USD"
}priceMicros— price in micros (1,000,000 micros = 1 currency unit)4990000= $4.99990000= $0.99
Purchase types
| Type | Description |
|---|---|
managedUser | One-time purchase (non-consumable by default; call consume to allow re-purchase) |
subscription | Legacy subscription type (use gpc subscriptions for new subscriptions) |
Sync directory structure
When using gpc iap sync --dir products/, each file represents one product:
products/
├── coins_100.json
├── coins_500.json
├── premium_unlock.json
└── remove_ads.jsonFile names don't matter — the sku field inside each file is the identifier.
Sync behavior
| Local file | Play Store | Action |
|---|---|---|
| Exists | Missing | Create |
| Exists | Exists | Update (if different) |
| Missing | Exists | Delete |
Use --dry-run to preview changes before applying.
Listing limits
| Field | Max length |
|---|---|
title | 55 characters |
description | 80 characters |
Purchase Verification
Server-side purchase verification patterns using GPC.
Why verify server-side
- Client-side purchase data can be tampered with
- Server verification confirms the purchase is genuine
- Required for acknowledging purchases (must acknowledge within 3 days)
- Needed for granting entitlements, managing consumables, and handling subscriptions
Product purchase verification
# Verify a one-time purchase (v1 — requires product-id)
gpc purchases get <product-id> <purchase-token>
# Verify a one-time purchase (v2 — supports multi-offer OTPs, no product-id needed)
gpc purchases product get-v2 <purchase-token>
# JSON output for parsing
gpc purchases get <product-id> <purchase-token> --json
gpc purchases product get-v2 <purchase-token> --jsonPurchase states
| State | Value | Meaning |
|---|---|---|
PURCHASED | 0 | Purchase completed |
CANCELED | 1 | Purchase cancelled |
PENDING | 2 | Purchase pending (e.g., slow payment method) |
Acknowledgement states
| State | Value | Meaning |
|---|---|---|
NOT_ACKNOWLEDGED | 0 | Not yet acknowledged — must acknowledge within 3 days |
ACKNOWLEDGED | 1 | Already acknowledged |
Acknowledgement flow
# 1. Verify the purchase
gpc purchases get coins_100 <token> --json
# 2. Check purchaseState=0 and acknowledgementState=0
# 3. Acknowledge with optional developer payload
gpc purchases acknowledge coins_100 <token> --payload "order-abc-123"If a purchase is not acknowledged within 3 days, Google automatically refunds it.
Consumption flow (consumable products)
# 1. Verify and acknowledge
gpc purchases get coins_100 <token> --json
gpc purchases acknowledge coins_100 <token>
# 2. Grant the item to the user in your backend
# 3. Consume to allow re-purchase
gpc purchases consume coins_100 <token>Subscription purchase verification
# Get subscription details (v2 API)
gpc purchases subscription get <purchase-token> --jsonSubscription states (v2)
| State | Meaning |
|---|---|
SUBSCRIPTION_STATE_ACTIVE | Active and billing |
SUBSCRIPTION_STATE_CANCELED | User cancelled, active until period end |
SUBSCRIPTION_STATE_IN_GRACE_PERIOD | Payment failed, grace period active |
SUBSCRIPTION_STATE_ON_HOLD | Payment failed, account on hold |
SUBSCRIPTION_STATE_PAUSED | User paused the subscription |
SUBSCRIPTION_STATE_EXPIRED | Subscription expired |
SUBSCRIPTION_STATE_PENDING_PURCHASE_CANCELED | Pending purchase was cancelled |
Subscription lifecycle management
# Cancel (v1 — takes effect at end of billing period)
gpc purchases subscription cancel <subscription-id> <token>
# Cancel (v2 — supports cancellation types)
gpc purchases subscription cancel-v2 <token>
gpc purchases subscription cancel-v2 <token> --type DEVELOPER_CANCELED
# Defer expiry (v1 — extend the subscription)
gpc purchases subscription defer <subscription-id> <token> \
--expiry 2025-12-31T00:00:00Z
# Defer expiry (v2 — supports add-on subscriptions)
gpc purchases subscription defer-v2 <token> --until 2026-07-01T00:00:00Z
# Revoke (immediate termination, v2 API)
gpc purchases subscription revoke <token>Voided purchases
Monitor refunds, chargebacks, and revoked purchases:
# List voided purchases in a date range
gpc purchases voided --start-time 2025-01-01 --end-time 2025-03-01
# With pagination
gpc purchases voided --start-time 2025-01-01 --max-results 100
# JSON for processing
gpc purchases voided --start-time 2025-01-01 --jsonVoid reasons
| Reason | Meaning |
|---|---|
OTHER | Unspecified |
REMORSE | User requested refund |
NOT_RECEIVED | User claims item not received |
DEFECTIVE | User claims item is defective |
ACCIDENTAL_PURCHASE | Accidental purchase |
FRAUD | Fraudulent transaction |
FRIENDLY_FRAUD | Chargeback |
Orders
# Get order details
gpc purchases orders get <order-id>
# Batch retrieve orders (up to 1000)
gpc purchases orders batch-get --ids "GPA.1234,GPA.5678"Refunds
Google recommends using the Orders API for refunds (the v1 subscriptions.refund endpoint is deprecated):
# Full refund
gpc purchases orders refund <order-id> --full-refund
# Prorated refund (for subscriptions)
gpc purchases orders refund <order-id> --prorated-refund
# Preview
gpc purchases orders refund <order-id> --full-refund --dry-runFor subscription refunds, use gpc purchases subscription get <token> --json to find the latestSuccessfulOrderId, then refund via gpc purchases orders refund.
CI/CD integration
Automate purchase verification in your backend deployment:
# Verify a test purchase in CI (v1)
gpc purchases get $PRODUCT_ID $TEST_TOKEN --json | jq '.purchaseState'
# Verify a test purchase in CI (v2)
gpc purchases product get-v2 $TEST_TOKEN --json | jq '.purchaseStateContext.state'Exit code 0 means the purchase exists. Check the JSON state field for the actual state.
Purchase token security (v0.9.74+)
Purchase tokens are sensitive — treat them like credentials.
GPC protections (automatic, no configuration needed):
- Redacted in JSON output: Any
purchaseTokenfield ingpc rtdnoutput shows only the first 8 characters followed by...REDACTED. This applies togpc rtdn decodeandgpc rtdn status. - Redacted in error messages: When a purchase-related API call fails, the HTTP layer runs
redactPath()on the request URL and error message before surfacing them. Tokens embedded in paths or error bodies are truncated the same way. - URL-encoded in API paths: Token parameters are passed through
encodeURIComponentwhen constructing request URLs, preventing injection or path-traversal issues.
Implication for CI logs: Purchase tokens will not appear in plain text in GPC output, even in --verbose or --json modes. If you need the full token for debugging, retrieve it from your Pub/Sub subscription or the Play Console directly.
Subscription JSON Schema
Reference for the JSON structure used with gpc subscriptions create and gpc subscriptions update.
Subscription object
{
"productId": "premium_monthly",
"listings": [
{
"languageCode": "en-US",
"title": "Premium Monthly",
"description": "Unlock all premium features with monthly billing.",
"benefits": [
"Unlimited access",
"No ads",
"Priority support"
]
}
],
"basePlans": [
{
"basePlanId": "monthly",
"state": "ACTIVE",
"autoRenewingBasePlanType": {
"billingPeriodDuration": "P1M",
"gracePeriodDuration": "P3D",
"accountHoldDuration": "P30D",
"resubscribeState": "RESUBSCRIBE_STATE_ACTIVE",
"prorationMode": "CHARGE_ON_NEXT_BILLING_DATE"
},
"regionalConfigs": [
{
"regionCode": "US",
"price": {
"currencyCode": "USD",
"units": "4",
"nanos": 990000000
}
}
],
"offerTags": [
{ "tag": "premium" }
]
}
],
"taxAndComplianceSettings": {
"eeaWithdrawalRightType": "EEA_WITHDRAWAL_RIGHT_TYPE_DIGITAL_CONTENT",
"isTokenizedDigitalAsset": false
}
}Key fields
Subscription
| Field | Type | Required | Description |
|---|---|---|---|
productId | string | Yes | Unique product identifier (letters, numbers, underscores) |
listings | array | Yes | Localized title, description, and benefits |
basePlans | array | Yes | One or more billing configurations |
taxAndComplianceSettings | object | No | EEA withdrawal rights, tokenized asset flags |
Base plan
| Field | Type | Required | Description |
|---|---|---|---|
basePlanId | string | Yes | Unique within the subscription |
state | string | No | ACTIVE or INACTIVE (set via activate/deactivate commands) |
autoRenewingBasePlanType | object | Yes* | For auto-renewing plans |
prepaidBasePlanType | object | Yes* | For prepaid plans (mutually exclusive with autoRenewing) |
regionalConfigs | array | Yes | Per-region pricing |
offerTags | array | No | Tags for offer eligibility filtering |
*One of autoRenewingBasePlanType or prepaidBasePlanType is required.
Billing period durations
| Duration | Meaning |
|---|---|
P1W | Weekly |
P1M | Monthly |
P3M | 3 months |
P6M | 6 months |
P1Y | Yearly |
Price object
{
"currencyCode": "USD",
"units": "4",
"nanos": 990000000
}units— whole currency units (string)nanos— fractional units in billionths (990000000 = $0.99)- Together:
units=4+nanos=990000000= $4.99
Listing object
| Field | Type | Required | Description |
|---|---|---|---|
languageCode | string | Yes | BCP-47 language code (e.g., en-US, ja-JP) |
title | string | Yes | Display name (max 55 characters) |
description | string | No | Description (max 80 characters) |
benefits | array | No | Up to 4 benefit strings (max 40 chars each) |
Offer JSON structure
{
"offerId": "intro_free_week",
"phases": [
{
"recurrenceCount": 1,
"duration": "P1W",
"pricingInfo": {
"pricingPhaseType": "PRICING_PHASE_TYPE_FREE"
}
}
],
"eligibilityCriteria": {
"acquisitionRule": {
"scope": {
"anySubscriptionInApp": true
}
}
},
"offerTags": [
{ "tag": "intro" }
],
"state": "ACTIVE"
}Pricing phase types
| Type | Description |
|---|---|
PRICING_PHASE_TYPE_FREE | Free trial period |
PRICING_PHASE_TYPE_DISCOUNTED | Reduced price (requires price field) |
PRICING_PHASE_TYPE_REGULAR | Regular subscription price |
Price migration JSON
Used with gpc subscriptions base-plans migrate-prices:
{
"regionalPriceMigrations": [
{
"regionCode": "US",
"oldestAllowedPriceVersionTime": "2025-01-01T00:00:00Z",
"newPrice": {
"currencyCode": "USD",
"units": "5",
"nanos": 990000000
}
}
],
"regionsVersion": {
"version": "2024.1"
}
}Create and update parameters
regionsVersion (create endpoints)
Controls the regional pricing version used when creating subscriptions or in-app products. Defaults to 2022/02. Pass via --regions-version on create commands.
{
"regionsVersion": {
"version": "2022/02"
}
}allowMissing (update endpoints)
When set to true, the update call behaves as an upsert: if the subscription, offer, or in-app product does not exist, it will be created instead of returning a NOT_FOUND error. Pass via --allow-missing on update commands.
This is useful for CI/CD pipelines where you want to ensure a product exists without checking first.
latencyTolerance (update endpoints)
Controls how quickly changes propagate after an update. Pass via --latency-tolerance on update commands.
| Value | Behavior |
|---|---|
PRODUCT_UPDATE_LATENCY_TOLERANCE_LATENCY_SENSITIVE | Changes propagate immediately (default) |
PRODUCT_UPDATE_LATENCY_TOLERANCE_LATENCY_TOLERANT | Changes may take longer to propagate but allows higher throughput for bulk operations |
CLI shorthand values: LATENCY_SENSITIVE (default), LATENCY_TOLERANT.
Update masks
Common --update-mask values for gpc subscriptions update:
| Mask | What it updates |
|---|---|
listings | All localized listings |
basePlans | All base plan configurations |
taxAndComplianceSettings | Tax and compliance settings |
Multiple masks can be comma-separated: --update-mask "listings,basePlans".
#!/usr/bin/env node
/**
* Detection script for GPC CLI.
* Returns JSON with installation status, version, auth state, and config.
* Used by Claude Code skill system for deterministic environment detection.
*
* Exit codes:
* 0 — GPC detected (may or may not be authenticated)
* 1 — GPC not found
*/
import { execSync } from "node:child_process";
import { existsSync } from "node:fs";
import { join } from "node:path";
function run(cmd) {
try {
return execSync(cmd, { encoding: "utf-8", timeout: 10000 }).trim();
} catch {
return null;
}
}
const result = {
installed: false,
version: null,
installMethod: null,
authStatus: null,
authMethod: null,
profile: null,
envAuth: false,
defaultApp: null,
configFile: null,
nodeVersion: process.version,
};
// Check if gpc is installed globally
const versionOutput = run("gpc --version");
if (!versionOutput) {
// Try npx
const npxVersion = run("npx gpc --version 2>/dev/null");
if (!npxVersion) {
console.log(JSON.stringify(result, null, 2));
process.exit(1);
}
result.version = npxVersion;
result.installed = true;
result.installMethod = "npx";
} else {
result.version = versionOutput;
result.installed = true;
result.installMethod = "global";
}
// Check auth status
const authOutput = run("gpc auth status --json 2>/dev/null");
if (authOutput) {
try {
const auth = JSON.parse(authOutput);
result.authStatus = auth.status || "unknown";
result.authMethod = auth.method || null;
result.profile = auth.profile || null;
} catch {
result.authStatus = "parse_error";
}
}
// Check for env-based auth
if (process.env.GPC_SERVICE_ACCOUNT) {
result.envAuth = true;
}
// Check default app
const configOutput = run("gpc config get app --json 2>/dev/null");
if (configOutput) {
try {
const config = JSON.parse(configOutput);
result.defaultApp = config.value || config.app || null;
} catch {
result.defaultApp = configOutput || null;
}
}
// Check for .gpcrc.json in current directory
const rcPath = join(process.cwd(), ".gpcrc.json");
if (existsSync(rcPath)) {
result.configFile = rcPath;
}
console.log(JSON.stringify(result, null, 2));
process.exit(0);