Skip to content

Apple Server Notifications Webhook Testing Guide

Complete guide to test the subscription webhook system end-to-end.

Prerequisites

✅ Both workers deployed: - Production: https://nutrie-openai-worker.invotekas.workers.dev - Sandbox: https://nutrie-openai-worker-sandbox.invotekas.workers.dev

✅ App built and installed on iPhone

Step 1: Get Your Device ID

  1. Open Nutri-E app on iPhone
  2. Tap More tab (bottom right)
  3. Scroll to Developer Tools section
  4. Tap Device Info
  5. Tap "Copy Device ID" button
  6. Device ID is now in your clipboard (e.g., BD843DDF-7116-4955-B9B3-9A486EB7ED64)

Save this Device ID - you'll need it for testing!

Step 2: Create Sandbox Test User

  1. Go to App Store Connect
  2. Navigate to Users and Access (top menu)
  3. Click SandboxTesters (left sidebar)
  4. Click + to add new tester
  5. Fill in:
  6. First Name: Test
  7. Last Name: User
  8. Email: nutrie.test1@icloud.com (must be unique, not a real email)
  9. Password: Create a password you'll remember
  10. Country/Region: Your country
  11. App Store Territory: Same as country
  12. Click Create

Save the email and password - you'll use these to sign in on iPhone!

Step 3: Configure Webhook URLs in App Store Connect

  1. Still in App Store Connect, navigate to your app
  2. Go to GeneralApp Information
  3. Scroll down to App Store Server Notifications
  4. Click Edit next to Production Server URL:
  5. Enter: https://nutrie-openai-worker.invotekas.workers.dev/api/subscription/webhook
  6. Click Save
  7. Click Edit next to Sandbox Server URL:
  8. Enter: https://nutrie-openai-worker-sandbox.invotekas.workers.dev/api/subscription/webhook
  9. Click Save

✅ Webhooks are now configured!

Step 4: Set Up Monitoring

Open 3 terminal windows:

Terminal 1: Monitor Sandbox Webhook

cd /Users/post/repos/github/nutri-e/cloudflare-worker-sandbox
wrangler tail nutrie-openai-worker-sandbox --grep "Apple webhook"

Terminal 2: Monitor KV Subscriptions

# Replace <device-id> with YOUR device ID from Step 1
watch -n 2 'wrangler kv key get --namespace-id="c823bfef717348c982b00c8a310b6e00" "<device-id>"'

Terminal 3: General Logs

wrangler tail nutrie-openai-worker-sandbox

Keep these running! You'll see webhook events in real-time.

Step 5: Sign Out of App Store on iPhone

IMPORTANT: You must sign out of your real Apple ID before testing with sandbox account!

  1. On iPhone, go to SettingsApp Store
  2. Tap your Apple ID at the top
  3. Tap Sign Out
  4. Confirm sign out

✅ You're now ready to test with sandbox account!

Step 6: Make Test Purchase

  1. Open Nutri-E app
  2. Navigate to MoreSubscriptions (or wherever your subscription view is)
  3. Tap Subscribe on any tier (e.g., Pro Monthly)
  4. iOS will prompt you to sign in
  5. Sign in with your sandbox test account:
  6. Email: nutrie.test1@icloud.com
  7. Password: [your sandbox password]
  8. Confirm the purchase (it's free in sandbox!)

Step 7: Watch the Magic! ✨

In Terminal 1 (webhook logs), you should see:

🍎 Apple webhook [SANDBOX]: SUBSCRIBED for <your-device-id>
✅ Subscription updated: <your-device-id> → pro (expires: 2025-10-08T00:31:00Z)

In Terminal 2 (KV monitor), you should see:

{
  "plan": "pro",
  "expiresAt": "2025-10-08T00:31:00Z",
  "updatedAt": "2025-10-08T00:26:00Z"
}

Note: Sandbox subscriptions expire in 5 minutes (1 month in production)!

Step 8: Test API Rate Limit

  1. In the app, try to use an AI feature (Food photo or Supplement scan)
  2. Check Terminal 3 logs, you should see:
    Device authenticated: <device-id>, usage: 0/50
    

✅ User now has Pro tier quota (50/day instead of 10/day)!

Step 9: Test Cancellation

  1. On iPhone, go to SettingsApp Store → tap your Apple ID
  2. Tap Subscriptions
  3. Find Nutri-E subscription
  4. Tap Cancel Subscription
  5. Confirm cancellation

In Terminal 1, you should see:

🍎 Apple webhook [SANDBOX]: DID_CHANGE_RENEWAL_STATUS for <device-id>

Important: Subscription stays active! Check Terminal 2 - subscription still in KV.

Step 10: Test Expiration (Wait ~5 Minutes)

Sandbox subscriptions expire in 5 minutes. Wait and watch...

In Terminal 1, after ~5 minutes you should see:

🍎 Apple webhook [SANDBOX]: EXPIRED for <device-id>
❌ Subscription removed: <device-id>

In Terminal 2, the subscription should disappear (empty response).

Try making an AI request again - logs should show:

Device authenticated: <device-id>, usage: 0/10

✅ User reverted to Free tier (10/day)!

Step 11: Test Expiration Enforcement

Manually add an expired subscription to test defense-in-depth:

cd /Users/post/repos/github/nutri-e/cloudflare-worker-sandbox

# Create expired subscription (expiresAt in the past)
wrangler kv key put \
  --namespace-id="c823bfef717348c982b00c8a310b6e00" \
  "<your-device-id>" \
  '{"plan":"pro","expiresAt":"2025-10-07T00:00:00Z","updatedAt":"2025-10-07T12:00:00Z"}'

Now make an AI request from the app. In Terminal 3 you should see:

⏰ Subscription expired for <device-id>, removing...
Device authenticated: <device-id>, usage: 0/10

✅ Worker detected expired subscription and auto-removed it!

Common Issues & Solutions

Issue: No webhook received after purchase

Check: 1. Webhook URLs saved correctly in App Store Connect 2. Worker is deployed: wrangler deployments list 3. Apple webhook delivery status (App Store Connect → Analytics)

Solution: - Re-save webhook URLs in App Store Connect - Check worker logs for errors: wrangler tail nutrie-openai-worker-sandbox

Issue: "Invalid device signature" error

Check: 1. DEVICE_SALT secret matches between app and worker 2. Device ID/Secret generated correctly

Solution:

# Verify secret is set
wrangler secret list
# Should show DEVICE_SALT and OPENAI_API_KEY

Issue: Subscription in KV but quota still 10/day

Check: 1. expiresAt date is in the future 2. Plan name matches tier map (basic/pro/premium/ultimate)

Solution:

# Check subscription data
wrangler kv key get --namespace-id="c823bfef717348c982b00c8a310b6e00" "<device-id>"

# Should show valid plan and future expiresAt

Sandbox Subscription Duration Reference

Production Sandbox Use Case
1 week 3 minutes Quick expiration test
1 month 5 minutes Standard testing
2 months 10 minutes Renewal testing
3 months 15 minutes Long-term testing
6 months 30 minutes Extended testing
1 year 1 hour Full lifecycle

Cleanup After Testing

Remove Test Subscription

wrangler kv key delete --namespace-id="c823bfef717348c982b00c8a310b6e00" "<device-id>"

Sign Back Into App Store

  1. Settings → App Store
  2. Sign in with your real Apple ID

List All Sandbox Subscriptions

wrangler kv key list --namespace-id="c823bfef717348c982b00c8a310b6e00"

Success Checklist

  • [x] Device ID copied from app
  • [x] Sandbox test user created
  • [x] Webhook URLs configured in App Store Connect
  • [x] Monitoring terminals running
  • [x] Test purchase completed
  • [x] Webhook received and logged
  • [x] Subscription stored in KV
  • [x] API quota increased to paid tier
  • [x] Cancellation webhook received
  • [x] Subscription stayed active after cancel
  • [x] Expiration webhook received after 5 min
  • [x] Subscription removed from KV
  • [x] API quota reverted to free tier
  • [x] Expiration enforcement tested

Next Steps

After sandbox testing succeeds:

  1. Test production webhook (same process, use real Apple ID)
  2. Monitor production: wrangler tail nutrie-openai-worker
  3. Set up alerts for webhook failures
  4. Add JWT signature verification (currently TODO)

Support

If issues persist: - Check Cloudflare dashboard for worker errors - Review Apple's webhook delivery logs - Contact Apple Developer Support for webhook issues