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¶
- Open Nutri-E app on iPhone
- Tap More tab (bottom right)
- Scroll to Developer Tools section
- Tap Device Info
- Tap "Copy Device ID" button
- 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¶
- Go to App Store Connect
- Navigate to Users and Access (top menu)
- Click Sandbox → Testers (left sidebar)
- Click + to add new tester
- Fill in:
- First Name: Test
- Last Name: User
- Email:
nutrie.test1@icloud.com(must be unique, not a real email) - Password: Create a password you'll remember
- Country/Region: Your country
- App Store Territory: Same as country
- 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¶
- Still in App Store Connect, navigate to your app
- Go to General → App Information
- Scroll down to App Store Server Notifications
- Click Edit next to Production Server URL:
- Enter:
https://nutrie-openai-worker.invotekas.workers.dev/api/subscription/webhook - Click Save
- Click Edit next to Sandbox Server URL:
- Enter:
https://nutrie-openai-worker-sandbox.invotekas.workers.dev/api/subscription/webhook - 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¶
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!
- On iPhone, go to Settings → App Store
- Tap your Apple ID at the top
- Tap Sign Out
- Confirm sign out
✅ You're now ready to test with sandbox account!
Step 6: Make Test Purchase¶
- Open Nutri-E app
- Navigate to More → Subscriptions (or wherever your subscription view is)
- Tap Subscribe on any tier (e.g., Pro Monthly)
- iOS will prompt you to sign in
- Sign in with your sandbox test account:
- Email:
nutrie.test1@icloud.com - Password: [your sandbox password]
- 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:
Note: Sandbox subscriptions expire in 5 minutes (1 month in production)!
Step 8: Test API Rate Limit¶
- In the app, try to use an AI feature (Food photo or Supplement scan)
- Check Terminal 3 logs, you should see:
✅ User now has Pro tier quota (50/day instead of 10/day)!
Step 9: Test Cancellation¶
- On iPhone, go to Settings → App Store → tap your Apple ID
- Tap Subscriptions
- Find Nutri-E subscription
- Tap Cancel Subscription
- Confirm cancellation
In Terminal 1, you should see:
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:
In Terminal 2, the subscription should disappear (empty response).
Try making an AI request again - logs should show:
✅ 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:
✅ 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:
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¶
Sign Back Into App Store¶
- Settings → App Store
- Sign in with your real Apple ID
List All Sandbox Subscriptions¶
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:
- Test production webhook (same process, use real Apple ID)
- Monitor production:
wrangler tail nutrie-openai-worker - Set up alerts for webhook failures
- 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