Files
honeyDueAPI/docs/SUBSCRIPTION_WEBHOOKS.md
Trey t c58aaa5d5f Add Apple/Google IAP validation and subscription webhooks
- Add Apple App Store Server API integration for receipt/transaction validation
- Add Google Play Developer API integration for purchase token validation
- Add webhook endpoints for server-to-server subscription notifications
  - POST /api/subscription/webhook/apple/ (App Store Server Notifications v2)
  - POST /api/subscription/webhook/google/ (Real-time Developer Notifications)
- Support both StoreKit 1 (receipt_data) and StoreKit 2 (transaction_id)
- Add repository methods to find users by transaction ID or purchase token
- Add configuration for IAP credentials (APPLE_IAP_*, GOOGLE_IAP_*)
- Add setup documentation for configuring webhooks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 13:58:37 -06:00

8.6 KiB

Subscription Webhook Setup

This document explains how to configure Apple App Store Server Notifications and Google Real-time Developer Notifications for server-to-server subscription status updates.

Overview

The webhook endpoints allow Apple and Google to notify your server when subscription events occur:

  • New subscriptions
  • Renewals
  • Cancellations
  • Refunds
  • Expirations
  • Grace period events

Webhook Endpoints:

  • Apple: POST /api/subscription/webhook/apple/
  • Google: POST /api/subscription/webhook/google/

Apple App Store Server Notifications v2

1. Configure in App Store Connect

  1. Log in to App Store Connect
  2. Navigate to My Apps > Select your app
  3. Go to App Information (under General)
  4. Scroll to App Store Server Notifications
  5. Enter your webhook URLs:
    • Production Server URL: https://your-domain.com/api/subscription/webhook/apple/
    • Sandbox Server URL: https://your-domain.com/api/subscription/webhook/apple/ (or separate staging URL)
  6. Select Version 2 for the notification version

2. Environment Variables

Set these environment variables on your server:

# Apple IAP Configuration (optional but recommended for validation)
APPLE_IAP_KEY_PATH=/path/to/AuthKey_XXXXX.p8    # Your App Store Connect API key
APPLE_IAP_KEY_ID=XXXXXXXXXX                      # Key ID from App Store Connect
APPLE_IAP_ISSUER_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  # Issuer ID from App Store Connect
APPLE_IAP_BUNDLE_ID=com.your.app.bundleid       # Your app's bundle ID
APPLE_IAP_SANDBOX=true                           # Set to false for production

3. Generate App Store Connect API Key

To enable server-side receipt validation:

  1. Go to App Store Connect > Users and Access > Keys
  2. Click Generate API Key
  3. Give it a name (e.g., "Casera IAP Server")
  4. Select App Manager role (minimum required for IAP)
  5. Download the .p8 file - you can only download it once!
  6. Note the Key ID and Issuer ID

4. Apple Notification Types

Your server will receive these notification types:

Type Description Action
SUBSCRIBED New subscription Upgrade user to Pro
DID_RENEW Subscription renewed Extend expiry date
DID_CHANGE_RENEWAL_STATUS Auto-renew toggled Update auto_renew flag
DID_FAIL_TO_RENEW Billing failed User may be in grace period
EXPIRED Subscription expired Downgrade to Free
REFUND User got refund Downgrade to Free
REVOKE Access revoked Downgrade to Free
GRACE_PERIOD_EXPIRED Grace period ended Downgrade to Free

Google Real-time Developer Notifications

Google uses Cloud Pub/Sub to deliver subscription notifications. Setup requires creating a Pub/Sub topic and subscription.

1. Create a Pub/Sub Topic

  1. Go to Google Cloud Console
  2. Select your project (or create one)
  3. Navigate to Pub/Sub > Topics
  4. Click Create Topic
  5. Name it (e.g., casera-subscriptions)
  6. Note the full topic name: projects/your-project-id/topics/casera-subscriptions

2. Create a Push Subscription

  1. In the Pub/Sub Topics list, click on your topic
  2. Click Create Subscription
  3. Configure:
    • Subscription ID: casera-subscription-webhook
    • Delivery type: Push
    • Endpoint URL: https://your-domain.com/api/subscription/webhook/google/
    • Acknowledgment deadline: 60 seconds
  4. Click Create
  1. Go to Google Play Console
  2. Select your app
  3. Navigate to Monetization > Monetization setup
  4. Under Real-time developer notifications:
    • Enter your topic name: projects/your-project-id/topics/casera-subscriptions
  5. Click Save
  6. Click Send test notification to verify the connection

4. Create a Service Account for Validation

  1. Go to Google Cloud Console > IAM & Admin > Service Accounts
  2. Click Create Service Account
  3. Name it (e.g., casera-iap-validator)
  4. Grant roles:
    • Pub/Sub Subscriber (for webhook handling)
  5. Click Done
  6. Click on the service account > Keys > Add Key > Create new key
  7. Select JSON and download the key file
  1. Go to Google Play Console > Setup > API access
  2. Link your Google Cloud project
  3. Under Service accounts, grant your service account access:
    • View financial data (for subscription validation)

6. Environment Variables

# Google IAP Configuration
GOOGLE_IAP_SERVICE_ACCOUNT_PATH=/path/to/service-account.json
GOOGLE_IAP_PACKAGE_NAME=com.your.app.packagename

7. Google Notification Types

Your server will receive these notification types:

Type Code Type Description Action
1 SUBSCRIPTION_RECOVERED Recovered from hold Re-upgrade to Pro
2 SUBSCRIPTION_RENEWED Subscription renewed Extend expiry date
3 SUBSCRIPTION_CANCELED User canceled Mark as canceled, will expire
4 SUBSCRIPTION_PURCHASED New purchase Upgrade to Pro
5 SUBSCRIPTION_ON_HOLD Account hold User may lose access soon
6 SUBSCRIPTION_IN_GRACE_PERIOD In grace period User still has access
7 SUBSCRIPTION_RESTARTED User resubscribed Re-upgrade to Pro
12 SUBSCRIPTION_REVOKED Subscription revoked Downgrade to Free
13 SUBSCRIPTION_EXPIRED Subscription expired Downgrade to Free

Testing

Test Apple Webhooks

  1. Use the Sandbox environment in App Store Connect
  2. Create a Sandbox tester account
  3. Make test purchases on a test device
  4. Monitor your server logs for webhook events

Test Google Webhooks

  1. In Google Play Console > Monetization setup
  2. Click Send test notification
  3. Your server should receive a test notification
  4. Check server logs for: "Google Webhook: Received test notification"

Local Development Testing

For local testing, use a tunnel service like ngrok:

# Start ngrok
ngrok http 8000

# Use the ngrok URL for webhook configuration
# e.g., https://abc123.ngrok.io/api/subscription/webhook/apple/

Security Considerations

Apple Webhook Verification

The webhook handler includes optional JWS signature verification. Apple signs all notifications using their certificate chain. While the current implementation decodes the payload without full verification, you can enable verification by:

  1. Downloading Apple's root certificates from https://www.apple.com/certificateauthority/
  2. Calling VerifyAppleSignature() before processing

Google Webhook Security

Options for securing Google webhooks:

  1. IP Whitelisting: Google publishes Pub/Sub IP ranges
  2. Push Authentication: Configure your Pub/Sub subscription to include an authentication header
  3. OIDC Token: Have Pub/Sub include an OIDC token that your server verifies

General Security

  • Always use HTTPS for webhook endpoints
  • Validate the bundle ID / package name in each notification
  • Log all webhook events for debugging and audit
  • Return 200 OK even if processing fails (to prevent retries flooding your server)

Troubleshooting

Apple Webhooks Not Arriving

  1. Verify the URL is correct in App Store Connect
  2. Check your server is reachable from the internet
  3. Ensure you're testing with a Sandbox account
  4. Check server logs for any processing errors

Google Webhooks Not Arriving

  1. Verify the topic name format: projects/PROJECT_ID/topics/TOPIC_NAME
  2. Check the Pub/Sub subscription is active
  3. Test with "Send test notification" in Play Console
  4. Check Cloud Logging for Pub/Sub delivery errors

User Not Found for Webhook

This typically means:

  • The user made a purchase but the receipt/token wasn't stored
  • The transaction ID or purchase token doesn't match any stored data

The handler logs these cases but returns success to prevent Apple/Google from retrying.


Database Schema

Relevant fields in user_subscriptions table:

apple_receipt_data TEXT    -- Stores receipt/transaction ID for Apple
google_purchase_token TEXT -- Stores purchase token for Google
cancelled_at TIMESTAMP     -- When user canceled (will expire at end of period)
auto_renew BOOLEAN         -- Whether subscription will auto-renew

These fields are used by webhook handlers to:

  1. Find the user associated with a transaction
  2. Track cancellation state
  3. Update renewal preferences