How to Configure App Store Server Notifications V2 in App Store Connect
This guide provides a step-by-step setup for App Store Server Notifications V2, covering endpoint registration in App Store Connect, JWS payload decryption, and executing test notifications without needing external SaaS.
These notifications are essential because they give your server real-time information about subscription status changes, refunds, and other purchase events. Without them, you'd have to poll Apple's API regularly, which is less efficient and prone to missing critical, time-sensitive updates.
Registering Your Server Endpoint in App Store Connect
The first step to receiving App Store Server Notifications V2 is to tell Apple where to send them. You'll need a publicly accessible HTTPS endpoint for this. This must be a URL that Apple can reach, typically on port 443, and it needs a valid TLS certificate from a trusted root authority. Apple will not send notifications to endpoints using self-signed certificates.
- Log into App Store Connect.
- Navigate to Users and Access from the main menu.
- In the sidebar, under the "Integrations" section, select App Store Server Notifications.
- Click the + button or "Add URL" to register a new endpoint.
- Enter your Production URL. For example,
https://api.your-app.com/apple-notifications. - If you have a separate environment for sandbox testing, also enter a Sandbox URL. This is optional but highly recommended for development.
- You can register up to five unique URLs in App Store Connect. If you need more, you might consider a single endpoint that routes to different internal services.
Apple will attempt to validate your URL immediately. Ensure your server is already running and ready to respond to a simple GET request, even if it just returns a 200 OK status. If Apple cannot reach your endpoint or the TLS certificate is invalid, it will show an error.
Understanding the JWS Payload Structure
App Store Server Notifications V2 use JSON Web Signatures (JWS) to ensure the integrity and authenticity of the data. This means the notification you receive isn't plain JSON; it's a compact, URL-safe string divided into three parts by dots: header.payload.signature.
eyJhbGciOiJFUzI1NiIsIng1YyI...
This entire string is the signedPayload. The actual notification data you care about is embedded within the payload section, which is Base64Url encoded. The header contains information about the signing algorithm and the certificate chain used to sign the payload, while the signature confirms the payload hasn't been tampered with.
When you receive a notification, it will be a JSON object containing a top-level key named signedPayload. This is the JWS string you need to parse and verify. For instance, a basic notification will look something like this:
{
"signedPayload": "eyJhbGciOiJFUzI1NiIsIng1YyI6WyJ...[rest of JWS string]..."
}
It's crucial to understand that directly decoding the signedPayload without verification is risky. Always verify the signature first to ensure the notification genuinely came from Apple and hasn't been altered.
Decrypting and Verifying the JWS Payload
To access the actual notification data, you need to decrypt and verify the JWS payload. This involves several steps:
-
Extract the Certificate Chain: The JWS
headercontains anx5cfield, which is an array of Base64-encoded X.509 certificates. The first certificate in this chain is Apple's signing certificate. - Download Apple's Root Certificates: For complete trust, you'll also need Apple's root certificates to verify the chain of trust for the signing certificate. You can find these on Apple's Certificate Authority page. The "Apple Root CA - G3" is typically the one you'll need.
-
Use a JWS Library: Do not try to implement JWS parsing and verification yourself. Use a well-tested library for your programming language.
- Node.js:
orjosenode-jose - Python:
python-jose - Ruby:
json-jwt - PHP:
web-token/jwt-framework - Java:
nimbus-jose-jwt
- Node.js:
Here's a conceptual outline of the verification process:
// Pseudocode for JWS verification
function verifyAndDecodeJWS(signedPayload) {
// 1. Parse the JWS string to get header, payload, and signature
const { header, protectedHeader, payload, signature } = parseJWS(signedPayload);
// 2. Extract Apple's signing certificate from the x5c header field
const appleSigningCert = header.x5c[0];
// 3. Verify the signature using the extracted certificate
// This step ensures the payload hasn't been tampered with.
if (!verifySignature(signedPayload, appleSigningCert)) {
throw new Error("JWS signature verification failed.");
}
// 4. Decode the payload (Base64Url decode)
const decodedPayload = base64UrlDecode(payload);
// 5. Parse the decoded payload as JSON
const notificationData = JSON.parse(decodedPayload);
// 6. Optionally, verify the certificate chain up to Apple's root CA
// (This adds an extra layer of security, ensuring the signing cert is valid)
if (!verifyCertificateChain(header.x5c, appleRootCACert)) {
throw new Error("Certificate chain verification failed.");
}
return notificationData;
}
Once successfully verified and decoded, the notificationData will be a JSON object containing details like notificationType (e.g., DID_RENEW, REFUND), subtype, and a data object with specific transaction information.
Executing Test Notifications Without External SaaS
App Store Connect provides a built-in way to send test notifications to your configured endpoints for verifying your server setup and JWS decryption logic without making actual purchases.
Step-by-Step Test Notification Checklist:
- Ensure your endpoint is running: Before initiating a test, double-check that your server is live and listening for requests at the URL you registered in App Store Connect.
- Log into App Store Connect: Access your developer account.
- Navigate to Server Notifications: Go to Users and Access > Integrations > App Store Server Notifications.
- Select an endpoint: Choose either your Production URL or Sandbox URL from the list. If you haven't set up a Sandbox URL, the production one will receive sandbox tests.
- Send Test Notification: Below your registered URLs, you'll see a section for "Test Notifications." Click the Send Test Notification button.
- Monitor your server logs: Immediately after clicking, check your server logs for an incoming request to your endpoint. You should see the JWS payload.
-
Verify Decryption: Your server should attempt to verify and decrypt the JWS payload. If successful, you'll see the parsed JSON notification. The
notificationTypefor test notifications is typicallyTEST. - Check Delivery History: Back in App Store Connect, below the "Send Test Notification" button, there's a "Delivery History" section. This shows recent notification attempts, including their status (e.g., "Succeeded," "Failed," with HTTP status codes). If your server returned a non-200 status, it will be reflected here.
Repeat this process whenever you make changes to your notification processing logic. This built-in testing mechanism is a powerful way to ensure your app's backend is always in sync with Apple's purchase events.
Practical Example: Handling Notifications for a Habit Tracker App
Imagine you ship a habit tracker app that offers a premium subscription. You need to know when a user subscribes, renews, or cancels their subscription to manage their premium features correctly on your backend. App Store Server Notifications V2 streamline this.
Your server endpoint (e.g., https://api.myhabitapp.com/apple-webhooks) receives a signedPayload. After successful JWS decryption, you might see a notification like this:
{
"notificationType": "DID_RENEW",
"subtype": "AUTO_RENEWED",
"data": {
"bundleId": "com.myhabitapp",
"environment": "Production",
"appAppleId": 1234567890,
"signedTransactionInfo": "eyJhbGciOiJFUzI1NiIsImtpZCI6...", // another JWS
"signedRenewalInfo": "eyJhbGciOiJFUzI1NiIsImtpZCI6..." // another JWS
},
"version": "2.0",
"signedDate": 1678886400000
}
In this example:
- The
notificationTypeisDID_RENEW, indicating a subscription renewal. - The
subtypeisAUTO_RENEWED, meaning it happened automatically. - Inside
data, you findsignedTransactionInfoandsignedRenewalInfo. These are *also* JWS strings that you need to verify and decode separately to get the actual transaction and renewal details (e.g., purchase date, expiration date, product ID).
Your backend logic would then:
- Decode
signedTransactionInfoto update the user's subscription status in your database, recording the new expiration date. - Decode
signedRenewalInfoto track the renewal history and potentially offer proactive support if issues arise. - If the
notificationTypewasGRACE_PERIOD_EXPIRED, you might revoke premium access until the subscription is resolved. - If it was
REFUND, you'd immediately revoke premium access and issue any necessary credits on your end.
Using a privacy policy generator or an app privacy label generator can also help ensure you're compliant with data handling related to subscription information.
FAQ
What's the difference between App Store Server Notifications V1 and V2?
V2 notifications provide a more robust and secure system. They use JWS for payload integrity and authenticity, offer more granular notification types and subtypes (like specific refund reasons or billing issues), and include more detailed transaction and renewal information in a structured format, making them easier to process reliably.
Do I need to handle all notification types sent by Apple?
No, you only need to handle the notification types relevant to your app's business logic. For example, if your app doesn't have consumable in-app purchases, you might not need to process notifications related to those. However, you should always be prepared to receive and ignore unknown types gracefully, and definitely process subscription lifecycle events if your app uses subscriptions.
What if my server misses an App Store Server Notification V2?
Apple will retry sending notifications multiple times over a period if your server returns a non-200 HTTP status code. However, it's good practice to also have a fallback mechanism, such as regularly fetching transaction history for your users via the App Store Server API, to reconcile any potential discrepancies and catch missed notifications.
Can I use a self-signed certificate for my endpoint URL?
No, Apple requires that your endpoint URL uses a valid TLS certificate issued by a trusted certificate authority. Self-signed certificates will cause validation errors in App Store Connect and prevent notifications from being sent to your server.
What port should my endpoint listen on for App Store Server Notifications V2?
Your App Store Server Notification V2 endpoint must listen on port 443, the standard port for HTTPS traffic. Apple expects all communications with your endpoint to be secured via HTTPS.
Next Steps: Integrate and Monitor
With your endpoint registered and JWS decryption logic in place, the next step is to integrate the parsed notification data into your app's backend to manage user entitlements and features. Remember to monitor your server logs and the App Store Connect delivery history for any issues. If you're also working on your app's visual presence, LaunchShots' free tools can help you design compelling App Store screenshots and generate other marketing assets.
Make your App Store screenshots free
LaunchShots is a free, in-browser screenshot maker. No signup, no watermark.
Open the app →
Comments (0)