stubkit

App Store Server Notifications V2

A production-ready Apple Server Notifications V2 endpoint.

Point App Store Connect at Stubkit and get JWS-verified, WWDR-pinned, idempotent subscription events — normalised alongside Google Play and Stripe into one lifecycle stream.

Rolling your own ASSN V2 handler is a part-time job

  • JWS signatures must be verified against Apple's root CA chain with the correct WWDR intermediate. Miss a cert rotation and you silently reject legitimate payloads.
  • Apple retries aggressively. Without an idempotency strategy, you double-count renewals and trigger duplicate entitlement grants.
  • You need separate sandbox and production URLs, separate pipelines for StoreKit 2 testing, and a way to reconcile V1 and V2 formats for legacy transactions.
  • Timeouts, 5xx responses, and network blips all need retries — usually via a queue you now have to build, monitor, and pay for.

Stubkit ships the full webhook infrastructure

  • JWS verification with WWDR G6 certificate pinning (and automatic rotation tracking).
  • Idempotent event ingestion keyed on transaction_id, with a persistent audit log.
  • Separate sandbox and production endpoints, so QA data never contaminates your analytics.
  • Exponential-backoff retries on transient failures, backed by a durable queue.
  • Normalised outbound webhooks to your own backend — the same shape for Apple, Google, and Stripe events.

See it in action

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

// Stubkit exposes an Apple Server Notifications V2 URL for each app.
// Paste it into App Store Connect → App Information → App Store Server
// Notifications → Production Server URL (and Sandbox Server URL).
//
// Your backend doesn't receive Apple webhooks directly — you receive
// normalised Stubkit lifecycle events instead, which include Apple, Google,
// and Stripe in the same shape.

// Listen for entitlement changes in your own backend:
import { Stubkit } from '@stubkit/js';

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

// Verify a Stubkit webhook signature, then react to it
app.post('/webhooks/stubkit', async (req, res) => {
  const event = stubkit.verifyWebhook(req.body, req.headers['stubkit-signature']);

  switch (event.type) {
    case 'subscription.activated':
    case 'subscription.renewed':
      await grantPremium(event.data.user_id);
      break;
    case 'subscription.canceled':
    case 'subscription.refunded':
      await revokePremium(event.data.user_id);
      break;
  }
  res.sendStatus(200);
});

Common questions

What is Apple Server Notifications V2?+

A push-based webhook channel from the App Store to your server, issued for every subscription lifecycle event — initial purchase, renewal, cancellation, refund, price change, grace period, and billing retry. V2 replaces the legacy V1 payload with JWS-signed JSON for tamper resistance.

Do I register my own endpoint or Stubkit's?+

You register Stubkit's endpoint in App Store Connect (one URL for sandbox, one for production). Stubkit verifies the JWS signature, decodes it, stores the event with an idempotency key on the Apple transaction_id, and then fires a normalised webhook to your own backend.

What if Apple retries a notification I already processed?+

Idempotency is built in. Apple's transaction_id is a unique constraint in our event store, so duplicate notifications (common during App Store outages) are silently de-duplicated before they reach your entitlement pipeline.

How does sandbox testing work?+

Every Stubkit app has both a sandbox and a production webhook URL. Sandbox events are kept in a separate event stream so your QA data never contaminates live analytics. Sandbox StoreKit 2 testing integrates out of the box.

What happens during an Apple outage?+

Stubkit queues inbound notifications and processes them with exponential-backoff retries when Apple recovers. Our entitlement cache continues serving the last-known-good state, so paywalls and entitlement reads keep working even if the push channel is down.

How do I migrate from a self-hosted V2 endpoint?+

Change the URL in App Store Connect to Stubkit's, then leave both endpoints active for a week to verify parity. Stubkit's audit log gives you a transaction-by-transaction comparison so you can confirm nothing is missed before turning off your legacy handler.

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