stubkit

Google Play Billing

Google Play RTDN, with purchase state you can trust.

A managed Real-Time Developer Notifications endpoint — Pub/Sub ingestion, service-account lookups against the Play Developer API, idempotent events, and a normalised lifecycle stream that speaks the same shape as Apple and Stripe.

Self-hosted RTDN is more infra than your app should own

  • You need your own Google Cloud project, Pub/Sub topic, service account, and push endpoint with authenticated HTTPS — just to receive the webhook.
  • The RTDN payload is notification-only; you still have to call the Play Developer API to fetch the authoritative purchase state on every event.
  • Google sends duplicate notifications regularly. Without idempotency, you double-grant and double-revoke entitlements.
  • Subscription state transitions are subtle (onHold vs paused vs inGracePeriod) — mistranslating them produces revenue bugs that are very hard to debug.

What Stubkit handles for you

  • A managed Pub/Sub topic and push endpoint per app — no Google Cloud project required.
  • Authoritative purchase state verified via the Play Developer API on every event, using the service account you upload once.
  • Idempotent ingestion keyed on purchaseToken, with a durable audit log.
  • Normalised lifecycle events (subscription.activated / renewed / canceled / refunded / paused) identical in shape across Apple, Google, and Stripe.
  • Sandbox vs production environment isolation out of the box.

See it in action

Ship the integration in minutes with the Stubkit SDK — the receipt flow, retries, and idempotency are handled server-side.

// Stubkit ingests Google Play RTDN messages through a Pub/Sub push
// subscription, looks up the authoritative purchase state via the Google
// Play Developer API, and emits normalised lifecycle events to your server.

import { Stubkit } from '@stubkit/js';

const stubkit = new Stubkit({ apiKey: process.env.STUBKIT_SECRET_KEY });

// Same handler, same event shape, regardless of whether the purchase
// originated on Apple, Google, or Stripe.
app.post('/webhooks/stubkit', async (req, res) => {
  const event = stubkit.verifyWebhook(req.body, req.headers['stubkit-signature']);

  if (event.type === 'subscription.activated') {
    await grantEntitlement(event.data.user_id, event.data.entitlement);
  } else if (event.type === 'subscription.canceled') {
    await revokeEntitlement(event.data.user_id, event.data.entitlement);
  }
  res.sendStatus(200);
});

Common questions

What is Google Play Real-Time Developer Notifications (RTDN)?+

RTDN is Google Play's push channel for subscription and one-time purchase lifecycle events. Instead of polling the Play Developer API, Google pushes a Pub/Sub message to a topic you own whenever a subscription changes state — renewal, hold, paused, canceled, expired, recovered, or deferred.

Does Stubkit provide its own Pub/Sub topic?+

Yes. Each app gets a managed Pub/Sub subscription URL. Paste the package name and topic ID into the Play Console, and Stubkit's push handler receives every notification directly — no Google Cloud project or service account management on your side.

How is purchase state verified?+

On every RTDN message, Stubkit calls the Google Play Developer API with a per-account service account to fetch the authoritative purchase record. The RTDN payload alone is not treated as authoritative — the subscription state is always reconciled against Google's API response before updating your entitlement.

What about duplicate notifications?+

Every RTDN event is keyed on Google's purchaseToken with a unique constraint, so duplicate messages (which Google regularly sends) are de-duplicated server-side. Your backend never sees the same event twice.

How do subscription pauses and deferrals work?+

Stubkit tracks the full lifecycle — onHold, paused, inGracePeriod, revoked, restarted — and translates it into entitlement-level granted / revoked state with a reason field. You receive a single normalised event per lifecycle transition.

Can I test with the Play Billing Library test tracks?+

Yes. Every Stubkit app has a sandbox environment wired up to internal test tracks, so test subscriptions are processed identically to production but kept in a separate event stream. Switching between environments is a single dashboard toggle.

Ship the integration, not the infrastructure.

Free forever on the starter tier. Full SDK support for iOS, Android, web, and server environments.

Start free

Already integrated? See pricing