Hello everyone,
I have some questions regarding the behavior of the offerIdentifier property in the TransactionPayload from App Store Server Notifications.
When a user redeems an Offer Code, the offerIdentifier field is populated with the respective identifier. However, I am unsure how this field behaves in different scenarios, and I would appreciate any insights or clarification:
Does the offerIdentifier persist throughout the subscription lifecycle (from the initial purchase to expiration)?
Does it become null once the Offer Code benefits expire?
Is it only present at the time of purchase and omitted in subsequent notifications?
Additionally, I would like to understand the behavior of the offerIdentifier in the following scenario:
A user purchases a lower-tier subscription using an Offer Code. Later, they upgrade to a higher-tier plan, causing the Offer Code benefits to effectively expire.
What happens to the offerIdentifier in the transaction for the upgrade?
Will it still appear in transactions after the upgrade, or will it be null?
I couldn't find explicit details about these situations in the official documentation, so I hope someone here might have experience or knowledge to share.
Thank you in advance for your help!
App Store Server Notifications
RSS for tagMonitor subscription events in real time with server notifications from the App Store using App Store Server Notifications.
Posts under App Store Server Notifications tag
83 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
We integrated App Store Server notification, to get notified about CONSUMPTION_REQUESTS and REFUND notifications.
In the data, we noticed same transactionId have multiple REFUND decisions, usually REFUND_DECLINED and then REFUND. Why is that? Did user contact customer support ?
For the second (or later) REFUND decision, CONSUMPTION_REQUEST notifications are usually not sent, but thats not always the case. Sometimes, REFUND decision are the same. Sometimes, we get even 3 or more REFUND related notifications for same transactionId, e.g:
2024-12-02: REFUND_DECLINED
2024-12-05: REFUND_DECLINED
2024-12-12: REFUND
Do user request refund again ? Do they contact customer support ? But I can not explain why sometimes status it REFUND at first, but then later REFUND_DECLINED.
Thank you already in advance:)
Dear Apple Support,
We are currently in the process of migrating from Apple Server Notifications v1 to v2. As you know, once we switch from v1 to v2 in App Store Connect, this change is irreversible. Our team needs to ensure that our backend environment, developed in Python with Django and using the app-store-server-library==1.5.0, is fully prepared to handle all possible notification types before making that final switch.
While we can receive a basic test notification from Apple, it doesn’t provide the range of data or scenarios (e.g., INITIAL_BUY, RENEWAL, GRACE_PERIOD, etc.) that our code must be able to process, store in our database, and respond to accordingly. Without comprehensive, example-rich raw notification data, we are left guessing the structure and full scope of incoming v2 notifications.
Could you please provide us with a set of raw, fully representative notification payloads for every notification type supported in v2? With these examples, we can simulate the entire lifecycle of subscription events, verify our logic, and confidently complete our migration.
Thank you very much for your assistance.
I encountered a scenario involving a subscription and need to determine if it's a problem or an expected outcome. Here are the details:
My service received a notification from Apple of type DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. The status field received on the payload was equal to 1 - Active. (2024-12-19T15:34:53.801)
My service again received a DID_CHANGE_RENEWAL_STATUS with subtype AUTO_RENEW_DISABLED. But the status field received was 2 - Expired. (2024-12-19T23:34:57.527)
My service received an EXPIRED with subtype VOLUNTARY notification. (2024-12-19T23:35:01.669)
Is the event 2 an inconsistent event? Since we are receiving a notification that means the auto renew was disabled when the subscription was already expired.
Hello everyone,
I recently received an email from Apple titled "Notification of Apple Developer Program License Agreement (“DPLA”) violation:" stating that I may have violated Section 11.2 (Termination) of the agreement. The specific clause mentioned relates to engaging in or encouraging misleading, fraudulent, improper, or dishonest acts, such as manipulating App Store rankings or reviews.
I’m unsure what triggered this notification as I have always adhered to Apple’s guidelines. Recently, I ran a promotional campaign for my app, where it was made temporarily free, leading to a significant spike in downloads. I suspect this may have raised a flag, but everything was done transparently and without intent to violate any rules.
I’ve reached out to Apple Support for clarification, but I wanted to ask here if anyone has experienced something similar or could provide insights into resolving this.
I value my membership in the Apple Developer Program and want to ensure everything is compliant. Any advice would be greatly appreciated!
This documentation describes what kind of data we should be sending to Apple server, once we are receiving CONSUMPTION_REQUEST
https://developer.apple.com/documentation/appstoreserverapi/consumptionrequest
But, it doesn't describe what kind of data we are receiving, when we are receiving CONSUMPTION_REQUEST?
May I know, is such a document available?
Thank you.
Apple In-App Purchase sandbox environment can receive App Store Server Notifications v2, but the production environment is not receiving any notifications. I have verified that the configured URL is correct and it appears that Apple is not pushing messages in the production environment. Is there anyone who knows how to troubleshoot this issue
The documentation mentions the following:
Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.
This way, you can use one server environment to handle both Sandbox and Production environments. It is necessary to pass App Review.
However, I'm not manually hitting these URLs - I'm using Apple's libraries.
Specifically, the environment is used in SignedDataVerifier and AppStoreServerAPIClient.
(I can't link to these because, for some reason, the domain apple.github.io is not allowed. The documentation for these is only found there. You can find it quickly by searching these terms and the domain.)
Here is how SignedDataVerifier is being used:
const verifier = new SignedDataVerifier(
appleRootCertificates,
APPLE_ENABLE_ONLINE_CHECKS,
APPLE_ENVIRONMENT,
APPLE_BUNDLE_ID,
APPLE_APP_ID
)
const verifiedNotification: ResponseBodyV2DecodedPayload = await verifier.verifyAndDecodeNotification(signedPayload)
if (!verifiedNotification)
{
// Failure
return
}
Here is how AppStoreServerAPIClient is being used:
const appStoreServerAPIClient = new AppStoreServerAPIClient(
SIGNING_KEY,
APPLE_IAP_KEY_ID,
APPLE_IAP_ISSUER_ID,
APPLE_BUNDLE_ID,
APPLE_ENVIRONMENT
)
const statusResponse: StatusResponse = await appStoreServerAPIClient.getAllSubscriptionStatuses(originalTransactionId, [Status.ACTIVE])
In the source code for SignedDataVerifier.verifyAndDecodeNotification, I can see that it throws a VerificationException(VerificationStatus.INVALID_ENVIRONMENT) error .
So for SignedDataVerifier is it as simple as wrapping my code in a try/catch and checking that the error's status code is 21007? I'm unsure about this because if you scroll to the bottom of the linked source code file, you can see the enumeration VerificationStatus, but it's unclear if this member has a value of 21007.
The source code for AppStoreServerAPIClient only says that it throws an APIException if a response could not be processed, so I'm not too sure about how to handle this one.
i m integrate App Store Server Notifications for External Purchase Server API
The documentation says:
For external purchase tokens that are unreported after 10 days, Apple sends a server notification to your server
https://developer.apple.com/documentation/externalpurchaseserverapi#Receive-notifications-for-unreported-tokens
I have a doubt.
After I'm generating an external purchase token (sandbox), apple notify me 4 times in next 2-3h, even they say, they will notify me after 10 days for unreported tokens.
Mention: Response for notification call was 200
token_create: 13:18
notification_1: 13:58
notification_2: 14:38
notification_3: 15:08
notificaiton_4: 15:38
"notificationType": "EXTERNAL_PURCHASE_TOKEN",
"subtype": "UNREPORTED",
I have no clue what's happening
Hi,
With the upcoming changes to the Apple Push Notification service (APNs) server certificates — including the SHA-2 Root: USERTrust RSA Certification Authority certificate update — I wanted to clarify if we need to take any action with Firebase Cloud Messaging (FCM).
Since we’re using FCM to send push notifications to iOS devices, does Firebase also need to update its server certificates in response to these changes, or will Firebase handle the updates automatically? We understand that Apple recommends updating our Trust Store to include the new certificates for APNs, but we’re unsure if any action is needed on our end for FCM specifically.
Thanks in advance for the clarification!
I have a build, but Distribution won't add it because it doesn't see encryption info in the Info.plist file, but that's hidden in Xcode. How do I find/update it?
I need to decode a Base64-encoded receipt from an In-App Purchase and extract the transaction ID from it. However, I want to avoid using the verifyReceipt endpoint since it is deprecated.
Can anyone guide me on how to achieve this manually? Are there any specific libraries, steps, or code samples I should follow to parse the receipt data and retrieve the transaction ID?
Any help or insights would be greatly appreciated!
Is it possible to switch back to V1 if we find some issues? in sandbox and production.
I've found conflicting answers, here is the list of answers and when were they posted:
yes (not sure if only in sandbox mode), dec 2022 -> https://developer.apple.com/forums/thread/722115
no, aug 2024 -> https://forums.developer.apple.com/forums/thread/761272
yes, nov 2024 https://developer.apple.com/documentation/technotes/tn3180-reverting-app-store-server-notifications-v1
What is the right one?
Can we get v2 notifications without enabling it? Just by using this api https://developer.apple.com/documentation/appstoreserverapi/get_notification_history
Thanks for your time
I'm trying to call the Test Notification endpoint: https://developer.apple.com/documentation/appstoreserverapi/request_a_test_notification?language=objc
I'm creating a JWT bearer token using a script I run on the console with these 2 commands: npm install jsonwebtoken fs and node generate_jwt.js
Keys are taken from App Store Connect -> User and Access -> Integrations -> Keys -> In-App Purchase.
generate_jwt.js script:
const jwt = require('jsonwebtoken');
const fs = require('fs');
const keyId = 'MY_KEY_ID';
const issuerId = 'MY_ISSUER_ID';
const privateKey = `-----BEGIN PRIVATE KEY-----
MY_SECRET_KEY
-----END PRIVATE KEY-----`;
// Define JWT headers and claims
const token = jwt.sign({}, privateKey, {
algorithm: 'ES256',
expiresIn: '1h',
issuer: issuerId,
header: {
alg: 'ES256',
kid: keyId,
},
audience: 'appstoreconnect-v1'
});
console.log('JWT Token:', token);
When I run a POST https://api.storekit.itunes.apple.com/inApps/v1/notifications/test request on postman with Bearer token authentication, I get a 401 Unauthorized error.
How can I get the App Store Server Notifications again if my server is down? As far as I know, Apple sends a notification once. What if the server was unavailable? How to get this notification again?
Hello,
About a month ago me and my team allocated a sprint to migrate the App Store Server Notifications service and the StoreKit library from v1 to v2.
Once the implementation was completed, we started testing using the sandbox environment (which was supposed to provide greater stability and maturity in v2).
The testing session went well, we managed to test various scenarios without noticing any issue (successful payment, failed payment, renewal etc).
This week, before doing the production deployment, a final testing session was scheduled.
Out of nowhere, we started to encounter all kinds of strange behaviours such as:
StoreKit2 error (in Xcode)
Received error that does not have a corresponding StoreKit Error: Error Domain=ASDErrorDomain Code=825 "No transactions in response" UserInfo={NSDebugDescription=No transactions in response}
Purchase did not return a transaction: Error Domain=ASDErrorDomain Code=825 "No transactions in response" UserInfo={NSDebugDescription=No transactions in response}
In some cases the transaction id resulting from the payment was retuned "" or nil, causing our server to raise an exception.
Newly created sandbox account that was raising an error that says "there is already a subscription made on this apple id".
Delays in the payment processing
Today, without doing any code changes, we managed to test all the scenarios without encountering any issue.
Due to the oscillating experience we had in the sandbox environment, we are considering postponing the release and maintaining StoreKit 1 for now, but even in this case, the fact that App Store Connect does not offer the possibility of rolling back the Notification Service to v1, causes inconvenience.
Has anyone encountered something similar? Is migrating to v2 a good choice?
I'm using my sandbox account to subscribed to something. I am now trying to open the subscribed item in Settings > Apple ID > Subscriptions > Click on the active sandbox subscription > Cannot Connect.
Has anyone else encountered this? How were you able to get around it?
My server receives App Store Server Notification v2 notifications. Recently, it has been receiving notifications for offer codes under offers that have been deactivated. The custom code value may be the same in the active offer as the deactivated offer.
When a user redeems the custom offer code for a deactivated offer, the notification payload's offerIdentifier value now contains the UUID associated with that offer in the URL in App Store Connect
(like https://appstoreconnect.apple.com/apps/my-app-id/distribution/subscriptions/my-subscription-id/pricing/offer-codes/offer-uuid)
The notification payload contains:
{"offerIdentifier": "offer-uuid"}
instead of
{"offerIdentifier": "<My offer identifier from App Store Connect>"}
Is it possible for a deactivated offer to be redeemed? If not, is this a known issue?
We need the actual offer identifier to understand which offers users are redeeming. Is this replacement with a UUID a known issue?
Hello everyone,
We have an app that implements in-app purchases, and we're using the notification settings outlined in this Apple Developer guide. Everything is functioning correctly in TestFlight, and our server successfully receives notifications from Apple.
However, after releasing the app to production, we're no longer receiving these notifications, while the TestFlight version continues to work as expected.
Has anyone encountered a similar issue or have any suggestions on how to resolve this?
Thank you for your help!
I keep getting 401 Unauthorized error when fetching Apple's public keys.
In [14]: print(f"Error fetching public keys: {response.status_code} {response.text}")
Error fetching public keys: 401 Unauthenticated
I've verified that the Key ID, Issuer ID, and private key file are all correct, with the private key having admin access. The server time is correctly set to UTC. Given this, I can't identify what might be causing the issue. Any insights?
def generate_apple_developer_token():
# Load the private key in PEM format
with open(PRIVATE_KEY_FILE, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
# JWT header
headers = {
"alg": "ES256",
"kid": KEY_ID,
"typ": "JWT"
}
# JWT payload
payload = {
"iss": ISSUER_ID,
"iat": int(datetime.utcnow().timestamp()),
"exp": int((datetime.utcnow() + timedelta(minutes=10)).timestamp()),
"aud": "appstoreconnect-v1",
}
# Encode the header and payload as base64
header_base64 = base64.urlsafe_b64encode(json.dumps(headers).encode()).decode().rstrip("=")
payload_base64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
# Concatenate header and payload
message = f"{header_base64}.{payload_base64}".encode()
# Sign the message using ECDSA with SHA256
signature = private_key.sign(
message,
ec.ECDSA(hashes.SHA256())
)
# Convert the DER-encoded signature to raw format (r and s concatenated)
der_to_raw_ecdsa_format = lambda der: der[4:36] + der[-32:]
# Convert the signature to raw format (64 bytes)
signature_64 = der_to_raw_ecdsa_format(signature)
# Base64 URL-encode the signature
signature_base64 = base64.urlsafe_b64encode(signature_64).decode().rstrip("=")
# Concatenate header, payload, and signature to form the JWT
jwt_token = f"{header_base64}.{payload_base64}.{signature_base64}"
return jwt_token
def get_apple_public_keys():
try:
# Generate a fresh JWT
developer_token = generate_apple_developer_token()
# Set up headers with the authorization token
headers = {
"Authorization": f"Bearer {developer_token}"
}
# Fetch the public keys from Apple
response = requests.get('https://api.storekit.itunes.apple.com/in-app-purchase/publicKeys', headers=headers)
# Log the response if it's not successful
if response.status_code != 200:
print(f"Error fetching public keys: {response.status_code} {response.text}")
response.raise_for_status() # Raises an exception for 4xx/5xx errors
# Parse and return the public keys
response_data = response.json()
keys = response_data.get('keys')
if not keys:
print("No 'keys' found in the response from Apple.")
return []
return keys
except requests.exceptions.RequestException as e:
print(f"Error fetching Apple's public keys: {e}")
return []