mcpField
G
gpc-monetization

gpc-monetization

github.com/yasserstudioOther140 installsUpdated 1w ago
Repository

About gpc-monetization

A skill for managing Google Play subscriptions, one-time products, offers, and purchase lifecycles through GPC. It covers listing, editing, batch operations, purchase handling, refunds, and pricing tasks.

Key features

  1. 01Subscription and base plan management
  2. 02Offer and in-app product operations
  3. 03Purchase verification and lifecycle actions
  4. 04Batch operations and directory sync
  5. 05External transaction refunds

Use cases

  1. 01Creating or updating subscription pricing and offers
  2. 02Syncing in-app products from JSON files
  3. 03Verifying purchases and handling refunds

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-monetization

After installing, type/gpc-monetizationin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
evals.json
references/
iap-schema.md
purchase-verification.md
subscription-schema.md
scripts/
detect_gpc.mjs

Details

From the project README

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 status must show valid credentials
  • App package name — set via --app or gpc 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 app

Confirm 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 50
B. 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"

Fixed in v0.9.84: --regions-version is now actually sent to the Google Play API on subscriptions and one-time-products create, update, and offer commands. In v0.9.77-v0.9.83 it was accepted by GPC but silently ignored. Default is 2022/02 if not specified. IAP and subscription list --json also now use the unified { <key>, nextPageToken, meta.count } envelope (GPC v0.9.83).

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_TOLERANT

The --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.json
E. 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.json

All 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 as gpc one-time-products list) now supports --page-size and --next-page for paginated results. The response includes a nextPageToken field 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.

One-time product offers -- single-offer commands (v0.9.96)

Google Play publishes only batch endpoints for one-time product offers, so otp offers get, create, update, and delete are sent as single-item batch requests and require --purchase-option. Omitting it fails immediately as a missing-option error, before any API call. The - wildcard works only with offers list -- run that first if you do not know which purchase option an offer belongs to.

# Find the purchase option an offer belongs to
gpc otp offers list premium_upgrade

gpc otp offers get premium_upgrade launch_discount --purchase-option buy_once
gpc otp offers create premium_upgrade --file offer.json --purchase-option buy_once
gpc otp offers update premium_upgrade launch_discount --file offer-update.json --purchase-option buy_once
gpc otp offers delete premium_upgrade launch_discount --purchase-option buy_once

offers create probes for the offer ID first and refuses with API_ALREADY_EXISTS (409) rather than overwriting -- the underlying batch endpoint is an upsert. Use offers update to change an existing offer.

Fixed in v0.9.96: gpc one-time-products create / update and the four single-offer commands above previously failed with a route-not-found error (Play serves the write route under a different spelling than the read routes). Requires v0.9.96+.

One-time product offer payload (Google's schema)

An offer file is a Google Play OneTimeProductOffer: one of discountedOffer, preOrderOffer or gameRewardOffer, plus regionalPricingAndAvailabilityConfigs as a list of { regionCode, availability, relativeDiscount | absoluteDiscount | noOverride }. relativeDiscount is the fraction the user pays (0.7 = 30% off); prices themselves live on the purchase option. Do not use subscription fields (pricingPhases, a regionalConfigs map, newSubscriberAvailability) -- Google rejects them, and GPC docs before v0.9.99 showed that wrong shape.

{
  "offerId": "launch-discount",
  "discountedOffer": { "startTime": "2026-10-01T00:00:00Z", "endTime": "2026-10-31T23:59:59Z", "redemptionLimit": "1000" },
  "regionalPricingAndAvailabilityConfigs": [
    { "regionCode": "US", "availability": "AVAILABLE", "relativeDiscount": 0.7 }
  ]
}

Play Games Rewards (v0.9.99+ typed/documented): "gameRewardOffer": { "redemptionLimit": "1" } ("1"-"50", "0"/unset = unlimited). Google's guide creates rewards offers in Play Console; GPC lists, reads and updates them.

External transactions (alternative billing, US programs)
gpc ext-txn create --file txn.json --transaction-id order-1001    # body sent as-is
gpc ext-txn get order-1001
gpc ext-txn refund order-1001 --full
gpc ext-txn refund order-1001 --partial-amount 4990000 --currency EUR --refund-id order-1001-r1
  • Refunds need GPC v0.9.99+: earlier versions never sent Google's required refundTime (and partial refundId), so Google rejected them. --refund-time <RFC 3339 with zone> overrides "now"; future times are refused. Bad options exit 2 with EXT_TXN_REFUND_INVALID before anything is sent, and the prompt states the amount.
  • --partial-amount is pre-tax micros (4990000 = 4.99), not major units.
  • From v1.0.0-rc.1 a refund without a terminal needs --yes, or it exits 2 with CONFIRMATION_REQUIRED.
  • US external content links program: add externalContentLinkDetails { linkType: LINK_TO_DIGITAL_CONTENT_OFFER | LINK_TO_APP_DOWNLOAD, installedAppPackage, externalAppCategory: APP | GAME } to the create file (separate from the older externalOfferDetails). Google requires reporting purchases from Oct 1 2026 and app downloads by Dec 1 2026.
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_id

Matches 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.json

All 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 an OfferPhaseDetails type that replaces the flat offerPhase field. OfferPhaseDetails is a structured object with richer phase information (phase type, cycle counts, and pricing details). The flat offerPhase field is deprecated; read from offerPhaseDetails in 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 (always a full refund; --revoke also removes the entitlement)
gpc purchases orders refund <order-id> --yes
gpc purchases orders refund <order-id> --revoke --yes

# Partial subscription refunds go through the v2 revoke instead
gpc purchases subscription revoke <token> --refund-type prorated --yes

orders refund takes only --revoke: Google's orders.refund is a full refund and has no partial or prorated form. purchases subscription revoke is where the choice lives, via --refund-type full|prorated|item (default prorated); --refund-type item also needs --product-id.

New in v0.9.47: --type 0 (default) returns only in-app purchase voids. --type 1 includes subscription voids. --include-partial-refunds includes quantity-based partial refunds.

All write operations support --dry-run.

Money commands need --yes in CI (v1.0.0-rc.1+)

purchases orders refund, purchases orders review-refund, purchases subscription revoke and external-transactions refund move money and cannot be undone. When they run without a terminal (CI, piped stdin, --no-interactive) they now stop with CONFIRMATION_REQUIRED (exit 2) unless --yes is passed. Earlier versions confirmed them automatically, so a pipeline could issue a refund by default. --dry-run sends nothing and is exempt. In an interactive shell the prompt is unchanged.

Read: references/purchase-verification.md for server-side verification patterns and best practices.

F. Chargeback disputes -- purchases orders review-refund (v0.9.96+)

When a user disputes a charge with their bank, Play sends a pendingRefundReviewNotification RTDN carrying a pendingRefundToken and the disputed orderId. You have 24 hours to answer. Play decides the outcome; your preference and evidence are inputs to that decision, not the decision itself.

# 1. Pull the token out of the notification (full token only in JSON output)
TOKEN=$(gpc rtdn decode "<base64-payload>" --output json \
  | jq -r .pendingRefundReviewNotification.pendingRefundToken)

# 2. Answer the dispute
gpc purchases orders review-refund "GPA.1234-5678-9012-34567" \
  --app com.example.app \
  --pending-refund-token "$TOKEN" \
  --preference decline \
  --sample-content-provided \
  --consumption-percent 82 \
  --usage-events-file ./usage-events.json

# Take no side, no evidence
gpc purchases orders review-refund "GPA.1234-5678-9012-34567" \
  --pending-refund-token "$TOKEN" \
  --preference neutral \
  --no-sample-content-provided
FlagRequiredNotes
--pending-refund-tokenyesToken from the pendingRefundReviewNotification
--preferenceyesapprove, decline, or neutral
--sample-content-provided / --no-sample-content-providedyes (exactly one)Whether a free sample, trial, or functionality info was offered before purchase. Google has no default, so GPC makes you state it
--consumption-percentnoHow much was consumed, 0-100, decimals allowed (sent as milliunits)
--usage-events-filenoJSON array of consumption usage events, max 1,000

usage-events.json uses RFC 3339 timestamps, and location.regionCode is a required CLDR region code whenever a location is supplied:

[
  {
    "consumptionTime": "2026-08-30T10:15:00Z",
    "consumptionItemDescription": "Opened chapter 4",
    "obfuscatedAccountId": "user-account-id",
    "ipAddress": "203.0.113.10",
    "location": { "regionCode": "US" }
  }
]

Bad input is rejected locally as ORDER_REVIEW_REFUND_INVALID (exit 2) before anything is sent. An empty or whitespace-only --consumption-percent counts as not provided, so a typo never claims "0% consumed" on your behalf. --dry-run previews the request.

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 test

Notification 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.

New in v0.9.96: gpc rtdn decode recognises pendingRefundReviewNotification (a chargeback sent for your review) and surfaces its orderId and pendingRefundToken -- the full token is printed only with --output json. It also no longer crashes on a partial chargeback notification. Answer within 24 hours with gpc purchases orders review-refund (section 5F).

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 --json

Reports: 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 --json

The 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.csv

Verification

  • gpc subscriptions list returns your subscriptions
  • gpc iap list returns your in-app products
  • gpc purchases get <product-id> <token> returns v1 purchase details for a valid token
  • gpc purchases product get-v2 <token> returns v2 purchase details with line items
  • gpc purchases orders get <order-id> returns order details
  • gpc pricing convert --from USD --amount 9.99 --json returns regional prices
  • All --dry-run commands show what would change without modifying data
  • JSON output works on all commands (--json flag)

Failure modes / debugging

SymptomLikely CauseFix
PRODUCT_NOT_FOUNDInvalid product ID or SKUVerify with gpc subscriptions list or gpc iap list
INVALID_PURCHASE_TOKENToken expired, already consumed, or wrong appVerify token matches the app and product
PURCHASE_NOT_ACKNOWLEDGEDPurchase not acknowledged within 3 daysAcknowledge immediately; if >3 days, purchase was auto-refunded
SUBSCRIPTION_NOT_FOUNDWrong subscription ID in cancel/deferUse gpc purchases subscription get <token> to find the correct ID
INVALID_JSON in create/updateMalformed product JSON fileValidate JSON structure against the schema reference
PERMISSION_DENIED on purchasesService account lacks financial permissionsGrant "View financial data" and "Manage orders" in Play Console
--update-mask errorInvalid field path in update maskCheck API docs for valid field names; omit flag to replace all fields
iap sync deletes unexpected productsDirectory missing some product filesUse --dry-run first; sync deletes products not in the directory
Missing --purchase-option on otp offers get/create/update/deletev0.9.96 routes these through Play's batch offer endpoints, which need a concrete purchase optionRun gpc otp offers list <product-id> to find it; - works only on list
API_ALREADY_EXISTS on otp offers createThat offer ID already exists; the batch endpoint is an upsert, so GPC probes and refuses rather than overwritingUse gpc otp offers update instead
ORDER_REVIEW_REFUND_INVALID--sample-content-provided unanswered, --consumption-percent outside 0-100, or --usage-events-file is not a JSON array of objectsPass one of --sample-content-provided / --no-sample-content-provided, use a plain decimal, and make the file an array

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

Comments

Markdown supported