App Store Server API

RSS for tag

Call this REST API from your server to request and provide information about your customers' in-app purchases.

Posts under App Store Server API tag

44 Posts

Post

Replies

Boosts

Views

Activity

Requesting Code-Level Support: Node.js Script to Fetch US Education Apps with "Exam Prep" by Revenue
Hi Apple Developer Support, I’m working on a Node.js script to fetch all apps listed under the Education category and Reference subcategory from the US App Store, where the app name includes "exam prep". My goal is to list these apps in descending order of their earned revenue (including both paid apps and those with subscriptions). To proceed, I’m looking for guidance or code-level support on: Accessing App Store metadata programmatically via Apple-approved APIs. Filtering apps by category, subcategory, and keywords in the app name. Sorting the result set by revenue (paid and subscription earnings). Is there any recommended API or service (official or via App Store Connect) that I should be using to achieve this? Appreciate any direction, documentation, or sample code that can help. Thanks in advance!
0
0
139
Apr ’25
App Store Server API JWT Authentication Issue
Issue Description I am experiencing persistent 401 Unauthorized errors when attempting to access the App Store Server API using JWT authentication. Despite following Apple's documentation and regenerating keys, I am unable to successfully authenticate. Implementation Details I'm implementing JWT authentication for the App Store Server API to retrieve transaction information from the following endpoint: https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionID} My JWT generation code (in PHP/Laravel) follows Apple's documentation: php$kid = '6W6H649LJ4'; $header = [ "alg" => "ES256", "kid" => $kid, "typ" => "JWT" ]; $iss = 'b8d99de7-b43b-4cbb-aada-546ec784e249'; // App Store Connect API Key Issuer ID $bid = 'com.gitiho.learnCourse'; // Bundle ID $payload = [ "iss" => $iss, "iat" => time(), "exp" => time() + 3600, "aud" => "appstoreconnect-v1", "bid" => $bid ]; $pathFileAuthKeyP8 = "AuthKey_6W6H649LJ4.p8"; $contentFileAuthKey = \File::get(base_path($pathFileAuthKeyP8)); $alg = "ES256"; $jwt = \Firebase\JWT\JWT::encode($payload, $contentFileAuthKey, $alg, null, $header); Steps Taken to Troubleshoot Verified that the Issuer ID is correct and in UUID format Confirmed that the Key ID matches the private key filename Regenerated the key with proper App Store Server API permissions Ensured the private key file is properly formatted with correct headers and footers Verified that the JWT is being properly encoded using the ES256 algorithm Confirmed the bundle ID is correct for our application Checked that the API endpoint URL is correct Additional Information This implementation previously worked correctly We started experiencing 401 errors recently without changing our implementation We are using the Firebase JWT library for PHP to encode the JWT Request Could you please help identify what might be causing these authentication failures? Is there any recent change in the authentication requirements or endpoint URLs that might be affecting our integration? Thanks for support me.
0
0
107
Apr ’25
StoreKit 2: jwsRepresentation Validation, Rate-Limit Relief, and Send Consumption Info Effectiveness
Hi everyone, We operate an online game where all in-app assets are stored server-side and require a logged-in account (no device binding). I’d like guidance on four areas: Do we really need deviceVerification / deviceVerificationNonce? – Because every purchase is tied to an account and we enforce a global transactionId UNIQUE constraint, replay or cross-account reuse appears infeasible. Under these conditions, is omitting device verification acceptable, or are there situations where Apple still recommends it? Permanent rate-limit increase for the App Store Server API – During anniversary events we saw bursts of ~18 000 requests per hour, breaching the current hourly cap on the App Store Server API (verifyTransaction, getNotificationHistory, etc.). Is there a formal process to request a long-term rate-limit expansion (or an alternative tier) from Apple? When is an App Store Server API call required for a StoreKit 2 jwsRepresentation? Docs say “call the API if you’re unsure,” but there’s no clear cut-off. Because we fully validate the JWS signature plus the entire certificate chain (including CRL/OCSP checks) on our server, local cryptographic validation seems sufficient for consumables. For subscriptions we still plan to hit the API to fetch the latest status. Does this separation match Apple’s best practice? If Apple does recommend hitting the API for consumables as well, we’d like a concrete rule of thumb—e.g. “if the item price is USD 50 or higher, always use the API.” Is establishing such thresholds consistent with Apple’s intent? Refund-risk reduction from Send Consumption Info – Adapty reports a 40–60 % refund-rate drop for subscriptions when using Send Consumption Info (blog reference). Can we expect similar reduction for consumable IAP in social/online games? Any real-world results would be helpful. Thanks in advance for any guidance!
0
0
197
Apr ’25
How to Best Practices for Implementing In-App Purchases (IAP) in Mobile Apps and Backend Systems
Hello everyone, I’d like to ask for your input regarding best practices for implementing In-App Purchases (IAP) across both the frontend and backend. Here’s our current flow: -Frontend (Mobile) The user opens a specific page. We initiate a payment request using react-native-iap. After the user completes the payment, we send the purchase data (receipt) to our backend. Backend: Accept the purchase receipt from the app. Validate the receipt with Apple’s server. (GET https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}) If the receipt is valid and the response indicates success, we mark the payment status as PAID. We store the transaction ID in our payment module. The Issue: We recently encountered a situation where Apple returned a valid receipt, so we marked the transaction as PAID. However, later we realized that the payment status was actually Pending. { transactionId: '70002676245699', originalTransactionId: '70002676245639', bundleId: '', productId: '', purchaseDate: 1745560404000, originalPurchaseDate: 1745560404000, quantity: 1, type: 'Consumable', inAppOwnershipType: 'PURCHASED', signedDate: 1745981078460, environment: 'Production', transactionReason: 'PURCHASE', storefront: 'SGP', storefrontId: '', price: 5000, currency: 'SGD', appTransactionId: '' } This raised a few questions: Does a Pending status always resolve to Paid, or is there a risk that Apple may later mark it as Failed or Unpaid? Is there a specific field in Apple's receipt response that reliably indicates whether the purchase is truly active? Should we hold off on granting access or product delivery until the status transitions from Pending to Paid? We’d really appreciate any insights or recommendations on how to handle this edge case to avoid granting access prematurely. Thanks in advance!
0
0
213
Apr ’25
External Purchase: status 401
Good morning, I am configuring in backend the sending of reports regarding purchases made in app with external platform (Stripe) as per documentation. To be clear I am talking about ExternalPurchase. However, when I make the call it returns "Apple responded with status 401". I verified the token on jwt.io as per documentation and it is working. I don't understand where I am going wrong. Below I share the code with you: const express = require("express"); const bodyParser = require("body-parser"); const jwt = require("jsonwebtoken"); const fs = require("fs"); const app = express(); const https = require("https"); const APPLE_KEY_ID = "XXXXX"; const APPLE_ISSUER_ID = "xxx-xxx-xxx-xx-xxxxxx"; const APPLE_PRIVATE_KEY = fs.readFileSync("AuthKey_xxxxx.p8", "utf8"); const APPLE_AUDIENCE = "appstoreconnect-v1"; function generateAppleJwt() { const now = Math.floor(Date.now() / 1000); const payload = { iss: APPLE_ISSUER_ID, iat: now, exp: now + (5 * 60), aud: APPLE_AUDIENCE }; return jwt.sign(payload, APPLE_PRIVATE_KEY, { algorithm: "ES256", header: { alg: "ES256", kid: APPLE_KEY_ID, typ: "JWT" } }); } app.post('/webhook', bodyParser.json({ type: 'application/json' }), async (req, res) => { let eventType = req.body.type; const relevantEvents = [ "invoice.paid" ]; if (relevantEvents.includes(eventType)) { try { const data= req.body.data; const platform = data.object.subscription_details.metadata.platform; if (platform === "IOS") { const token = generateAppleJwt(); const applePayload = { appAppleId: "xxxx", bundleId: 'com.xxx.xxx.test', externalPurchaseId: data.object.id, purchaseTime: new Date(data.object.created * 1000).toISOString(), purchaseAmount: { amount: (data.object.total / 100).toFixed(2), currencyCode: data.object.currency.toUpperCase() }, purchaseLocation: { isoCountryCode: "IT" } }; const jsonString = JSON.stringify(applePayload); const agent = new https.Agent({ keepAlive: false }); const response = await fetch( "https://api.storekit-sandbox.apple.com/externalPurchase/v1/reports", { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "Accept-Encoding": "identity", }, body: JSON.stringify(applePayload), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `Apple responded with status ${response.status}: ${errorText}` ); } console.log("✅ Notifica inviata ad Apple con successo"); } else { if(!canSendNotification){ console.log("Non è una Sub. Nessuna notifica inviata."); }else{ console.log("Customer non iOS. Nessuna notifica inviata."); } } } catch (err) { console.error("Errore durante l’invio ad Apple:"); if (err.response) { console.error("Status:", err.response.status); console.error("Headers:", err.response.headers); console.error("Data:", err.response.data); } else { console.error("Message:", err.message); } } } res.status(200).send("OK"); }); exports.checkSubStripe = functions.https.onRequest(app);
0
0
127
Jun ’25
External Purchase: Error 401
Good morning, I am configuring in backend the sending of reports regarding purchases made in app with external platform (Stripe) as per documentation. To be clear I am talking about ExternalPurchase. However, when I make the call it returns "Apple responded with status 401". I verified the token on jwt.io as per documentation and it is working. I don't understand where I am going wrong. Below is the code: const express = require("express"); const bodyParser = require("body-parser"); const jwt = require("jsonwebtoken"); const fs = require("fs"); const app = express(); const https = require("https"); const APPLE_KEY_ID = "xxx"; const APPLE_ISSUER_ID = "xxx-xxx-xxx-xxx-xxx"; const APPLE_PRIVATE_KEY = fs.readFileSync("AuthKey_xxx.p8", "utf8"); const APPLE_AUDIENCE = "appstoreconnect-v1"; function generateAppleJwt() { const now = Math.floor(Date.now() / 1000); const payload = { iss: APPLE_ISSUER_ID, iat: now, exp: now + (5 * 60), aud: APPLE_AUDIENCE }; return jwt.sign(payload, APPLE_PRIVATE_KEY, { algorithm: "ES256", header: { alg: "ES256", kid: APPLE_KEY_ID, typ: "JWT" } }); } app.post('/webhook', bodyParser.json({ type: 'application/json' }), async (req, res) => { let eventType = req.body.type; const relevantEvents = [ "invoice.paid" ]; if (relevantEvents.includes(eventType)) { try { const data= req.body.data; const platform = data.object.subscription_details.metadata.platform; if (platform === "IOS") { const token = generateAppleJwt(); const applePayload = { appAppleId: "xxx", bundleId: 'com.xxx.xxx.test', externalPurchaseId: data.object.id, purchaseTime: new Date(data.object.created * 1000).toISOString(), purchaseAmount: { amount: (data.object.total / 100).toFixed(2), currencyCode: data.object.currency.toUpperCase() }, purchaseLocation: { isoCountryCode: "IT" } }; const jsonString = JSON.stringify(applePayload); const agent = new https.Agent({ keepAlive: false }); const response = await fetch( "https://api.storekit-sandbox.apple.com/externalPurchase/v1/reports", { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "Accept-Encoding": "identity", }, body: JSON.stringify(applePayload), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `Apple responded with status ${response.status}: ${errorText}` ); } console.log("✅ Notifica inviata ad Apple con successo"); } else { if(!canSendNotification){ console.log("Non è una Sub. Nessuna notifica inviata."); }else{ console.log("Customer non iOS. Nessuna notifica inviata."); } } } catch (err) { console.error("Errore durante l’invio ad Apple:"); if (err.response) { console.error("Status:", err.response.status); console.error("Headers:", err.response.headers); console.error("Data:", err.response.data); } else { console.error("Message:", err.message); } } } res.status(200).send("OK"); }); exports.checkSubStripe = functions.https.onRequest(app);
0
0
109
Jun ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
0
1
284
Jun ’25
Is there an API provided by the App Store Server API for canceling subscriptions?
Is there an App Store Server API available that allows cancellation of specific subscriptions by specifying transaction_id or similar identifiers? Background of these questions: We occasionally suspend user accounts due to violations of our service terms and conditions. In such cases, we would like to forcibly cancel their subscriptions if possible. However, we could not find relevant information in the documentation, which is why we are reaching out with these questions. Please let us know.
0
0
94
Jul ’25
StoreKit appAccountToken Not Preserved During Apple ID Email Migration
I'm encountering an issue with the App Store Server API where the appAccountToken is not preserved when users migrate their Apple ID email addresses. I've submitted Feedback Assistant ticket FB18709241 but wanted to check if anyone else has experienced this and get community input on best practices. The Issue When a user migrates their Apple ID from one email to another (e.g., from olduser@example.com to newuser@icloud.com), the App Store creates a new subscription transaction with a different originalTransactionId, but the appAccountToken is not carried forward from the original transaction. What I'm Seeing note: these values are fake When querying /inApps/v1/subscriptions/{originalTransactionId} with the either post-migration transaction ID or the pre-migration transaction ID, the API returns both transactions: Pre-migration transaction (status: 2 - inactive): originalTransactionId: "12345678910111" Contains: "appAccountToken": "abc123-def456-ghi789" Post-migration transaction (status: 1 - active): originalTransactionId: "67891011121314" Missing: appAccountToken entirely The Problem The appAccountToken is our only way to link App Store subscriptions to user accounts. Without it on the new transaction: Users lose access to premium features despite having valid subscriptions Server-side renewal notifications can't be matched to user accounts Manual support intervention is required for each affected user Questions for the Community Has anyone else encountered this issue with Apple ID migrations? What's the recommended approach for handling this scenario? Is there an alternative mechanism to maintain the subscription-to-user linkage across migrations? Questions for Apple Engineers Is this the expected behavior, or should the appAccountToken be preserved? Are there any planned improvements to handle this migration scenario? What's the best practice for developers to handle this case? Interestingly, both the old and new transaction IDs return the same JSON response from the App Store Server API, suggesting Apple maintains internal linkage between these transactions, but the appAccountToken isn't carried forward to the active transaction. Any insights or similar experiences would be greatly appreciated! Thank you!! Feedback Assistant: FB18709241
0
3
196
Jul ’25
App Store Server Notification implementation in multiple environments
Hey everyone, We're looking for the best way to handle App Store Server Notifications in our development setup and would appreciate some guidance. Our Setup: We use a single App Store Connect account for development, which supports multiple environments (e.g., staging1, staging2). Our production app lives in a separate account, so that's not an issue. The Challenge: We have only one configurable sandbox notification URL. This makes it difficult to route notifications to the correct development server (staging1 vs. staging2 vs development) when a sandbox event occurs. We're considering using a proxy server to catch all notifications and then forward them to the appropriate environment. However, we're not sure how to determine the correct destination. Our Questions: What's the recommended approach for managing a single sandbox notification URL across multiple development environments? If a proxy is the best method, which parameter in the responseBodyV2 payload should we use to route the notification? How can we differentiate between our various dev environments? Is it possible to add custom properties to the App Store Server Notification V2 body to facilitate routing? Any advice or best practices you've implemented would be greatly appreciated. Thanks!
0
0
144
Jul ’25
App Store Server Notification implementation in multiple environments
Hey everyone, We're looking for the best way to handle App Store Server Notifications in our development setup and would appreciate some guidance. Our Setup: We use a single App Store Connect account for development, which supports multiple environments (e.g., staging1, staging2). Our production app lives in a separate account, so that's not an issue. The Challenge: We have only one configurable sandbox notification URL. This makes it difficult to route notifications to the correct development server (staging1 vs. staging2 vs developments) when a sandbox event occurs. We're considering using a proxy server to catch all notifications and then forward them to the appropriate environment. However, we're not sure how to determine the correct destination. Our Questions: What's the recommended approach for managing a single sandbox notification URL across multiple development environments? If a proxy is the best method, which parameter in the responseBodyV2 payload should we use to route the notification? How can we differentiate between our various dev environments? Is it possible to add custom properties to the App Store Server Notification V2 body to facilitate routing? Any advice or best practices you've implemented would be greatly appreciated.
0
0
118
Jul ’25
Getting 401 Unauthenticated when calling StoreKit In-App API with JWT
Hello, I am trying to call the StoreKit In-App API, but I am consistently receiving a 401 Unauthenticated error. Here is what I have done so far: JWT creation (via https://jwt.io): Header: { "alg": "ES256", "kid": "**********", "typ": "JWT" } Payload: { "iss": "********-e662-43d2-be42-012d0138ce39", "aud": "appstoreconnect-v1", "iat": 1757389187, "exp": 1757390987 } Private Key (.p8): -----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- Algorithm used: ES256 This generates the JWT successfully. API Call: I then include the JWT in the request header as follows: Authorization: Bearer ************ Endpoint: https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/2000001003740262 Response: Status: 401 Unauthenticated Request ID: S3KCYHDVRMDKUT3TZVTY3QRRWM.0.0 Has anyone else faced this issue? Is there something incorrect with how I’m generating the JWT (e.g., iat/exp values, formatting, or encoding)? Do I need to generate separate keys for Sandbox and Production, or is the same key valid for both? Any guidance would be appreciated. Thanks in advance!
0
1
184
Sep ’25
Advanced Commerce API – 5000000 "Unknown error" when calling Migrate API
We've been approved for the Advanced Commerce API. After receiving the approval, we completed the initial setup requirements (submitted a generic product ID and shared our subscriptions page deep link). When testing the Migrate Subscription to Advanced Commerce endpoint in the sandbox, we receive a 5000000 error. What could be the reason/s for it? There is no information, and the API call we make includes all required parameters. Could it be related to the fact that our new generic product ID status is "Missing Metadata"? Is there a way to understand what the issue is on Apple's side?
0
0
128
Oct ’25
URGENT: Severe Performance Issue with App Store Connect API (In-App Purchases Retrieval)
Hello Apple Support Team, We are currently experiencing a critical business issue caused by a sudden performance degradation in the App Store Connect API. This problem is blocking key parts of our production systems at wappier Optimizing game performance - website: https://wappier.com/ Since October 15, requests to the following endpoint have been taking extremely long to complete when fetching more than approximately 200 in-app purchase items. In many cases, these requests never complete even after several hours, making it impossible for us to retrieve all required data. Endpoint: https://api.appstoreconnect.apple.com/v1/apps/1252850847/inAppPurchasesV2 Parameters: { "include": "appStoreReviewScreenshot,inAppPurchaseLocalizations", "cursor": "Jd6I", "limit": "200" } We confirm that: This behavior started suddenly on October 15. No changes were made on our end. We found no corresponding updates or API change announcements in your release notes. Requests for fewer than 200 in-app products continue to perform normally. This issue is blocking our operations, as our systems depend on timely responses from this endpoint for in-app purchase data synchronization. We kindly ask your team to investigate this issue urgently and provide us guidance, and if possible, revert any recent change introduced around October 15 that could have impacted this endpoint’s performance. Thank you very much for your prompt attention and support. wappier Dev Team
0
2
208
Oct ’25
Authentication Error with App Store Server API (NOT_AUTHORIZED) while Using JWT for Subscription Validation
Hello Apple Developer Community, We are currently facing an authentication issue when calling the App Store Server API for subscription validation. Despite following Apple’s documentation and verifying all credentials, we consistently receive a NOT_AUTHORIZED error response. GET https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/appTransactions/{transactionId} Environment: Sandbox and Production (both tested, same result) Our Setup: Key ID: {Your Key ID} Issuer ID: {Your Issuer ID} Bundle ID: {Your Bundle ID} JWT Header: { "alg": "ES256", "kid": "" } JWT Payload: { "iss": "", "iat": , "exp": <timestamp + 5 minutes>, "aud": "appstoreconnect-v1", "bid": "" } Authorization Header: Authorization: Bearer Troubleshooting Steps Already Taken: Verified that .p8 key, Key ID, Issuer ID, and Bundle ID are all correctly configured and match the App Store Connect details. Confirmed that the system clock is accurate (UTC). Used appropriate endpoint (sandbox or production) based on environment. Ensured that the JWT is short-lived (under 5 minutes). Added the “Bearer” prefix correctly in the header. Tested JWT generations using Python. Issue: All requests return: { "errorCode": "NOT_AUTHORIZED" } Questions: Are there any additional claims or headers required for the subscriptions endpoint? Are there specific permissions or roles needed for the API key in App Store Connect? Is there a way to get more detailed logs or diagnostics for this NOT_AUTHORIZED response? Does the App Store Server API require a different aud or bid structure for certain endpoints? We already contacted Apple Developer Support, but they suggested posting here for engineering-level guidance. Any insight or examples of a working JWT + request for this endpoint would be greatly appreciated.
0
0
253
Nov ’25
Clarification on production App Store Server API activation timing and expected 401/404 behavior after release
We are following up on the previous discussion regarding the 401 Unauthorized response from the production App Store Server API when using sandbox transaction IDs before release. (Reference: https://developer.apple.com/forums/thread/806452) From the Apple staff’s response, we understand that: “Until you have a release in production, access to the production APIs is not allowed. Once you have a release in production this will be unlocked.” We would like to confirm a few technical details related to this point: Q1. Activation timing of production API access At what exact point does access to the production App Store Server API become available? (A) Immediately after App Review approval (status: “Ready for Sale”), but before the app is publicly available on the App Store (B) Only after the app is actually published and visible on the App Store This timing is important for our backend logic that verifies transactions during the review and initial release phases. Q2. Fallback logic between pre-release and post-release Before release, we plan to implement a fallback mechanism that retries sandbox verification when receiving HTTP 401 from the production endpoint. After the app is officially released, we expect that calling the production API with a sandbox transactionId should instead return HTTP 404 Not Found. Is this understanding correct? Should our fallback condition switch from detecting 401 → sandbox (pre-release) to 404 → sandbox (post-release)? Q3. Transition behavior immediately after release When the app transitions from “Ready for Sale” to actually being live on the App Store, is the production API immediately available for real transactions? Or does it take some time (e.g., a few hours) before production access becomes stable? We would appreciate confirmation of: The exact timing when production API access becomes active Whether the 401→404 transition is the expected design behavior Any recommended best practices for handling this transition safely in production systems Thank you for your continued support and clarification.
0
0
118
Nov ’25
prorated refund and upgrade of tier
Hi all, I'm encountering an issue with auto-renewable subscription upgrades in the App Store. Here's my setup: Context: Plan A: Base Plan (yearly auto-renewable subscription) Plan B: Pro Plan (monthly auto-renewable subscription) B is configured as an upgrade from A. Issue: When a user with an active Plan A subscription upgrades to Plan B, I correctly receive an App Store Server Notification v2 with DID_CHANGE_RENEWAL_PREF and UPGRADE subtype. According to Apple's documentation, a prorated refund is issued automatically in this scenario, and no separate REFUND event is sent, the refund information should be retrievable through the upgrade event itself. Testing in Sandbox: In my sandbox tests, Plan A has a 1-hour duration and Plan B has a 5-minute duration. After the user upgrades to Plan B, I immediately cancel the subscription to prevent auto-renewal. Expected vs. Actual Behavior: After the 5 minutes expire, Plan A still appears as the active current entitlement. I initially thought this might be because the prorated refund hadn't been processed yet. However, even after waiting the full hour (the original duration of Plan A), it continues to show as an active entitlement—which shouldn't be the case. As a result, when I attempt to restore purchases, Plan A is still identified as valid and the subscription gets reactivated. Question: Is this behavior expected in the sandbox environment, or am I missing something in how the prorated refund and entitlement expiration should be handled?
0
0
358
Dec ’25
Requesting Code-Level Support: Node.js Script to Fetch US Education Apps with "Exam Prep" by Revenue
Hi Apple Developer Support, I’m working on a Node.js script to fetch all apps listed under the Education category and Reference subcategory from the US App Store, where the app name includes "exam prep". My goal is to list these apps in descending order of their earned revenue (including both paid apps and those with subscriptions). To proceed, I’m looking for guidance or code-level support on: Accessing App Store metadata programmatically via Apple-approved APIs. Filtering apps by category, subcategory, and keywords in the app name. Sorting the result set by revenue (paid and subscription earnings). Is there any recommended API or service (official or via App Store Connect) that I should be using to achieve this? Appreciate any direction, documentation, or sample code that can help. Thanks in advance!
Replies
0
Boosts
0
Views
139
Activity
Apr ’25
App Store Server API JWT Authentication Issue
Issue Description I am experiencing persistent 401 Unauthorized errors when attempting to access the App Store Server API using JWT authentication. Despite following Apple's documentation and regenerating keys, I am unable to successfully authenticate. Implementation Details I'm implementing JWT authentication for the App Store Server API to retrieve transaction information from the following endpoint: https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionID} My JWT generation code (in PHP/Laravel) follows Apple's documentation: php$kid = '6W6H649LJ4'; $header = [ "alg" => "ES256", "kid" => $kid, "typ" => "JWT" ]; $iss = 'b8d99de7-b43b-4cbb-aada-546ec784e249'; // App Store Connect API Key Issuer ID $bid = 'com.gitiho.learnCourse'; // Bundle ID $payload = [ "iss" => $iss, "iat" => time(), "exp" => time() + 3600, "aud" => "appstoreconnect-v1", "bid" => $bid ]; $pathFileAuthKeyP8 = "AuthKey_6W6H649LJ4.p8"; $contentFileAuthKey = \File::get(base_path($pathFileAuthKeyP8)); $alg = "ES256"; $jwt = \Firebase\JWT\JWT::encode($payload, $contentFileAuthKey, $alg, null, $header); Steps Taken to Troubleshoot Verified that the Issuer ID is correct and in UUID format Confirmed that the Key ID matches the private key filename Regenerated the key with proper App Store Server API permissions Ensured the private key file is properly formatted with correct headers and footers Verified that the JWT is being properly encoded using the ES256 algorithm Confirmed the bundle ID is correct for our application Checked that the API endpoint URL is correct Additional Information This implementation previously worked correctly We started experiencing 401 errors recently without changing our implementation We are using the Firebase JWT library for PHP to encode the JWT Request Could you please help identify what might be causing these authentication failures? Is there any recent change in the authentication requirements or endpoint URLs that might be affecting our integration? Thanks for support me.
Replies
0
Boosts
0
Views
107
Activity
Apr ’25
StoreKit 2: jwsRepresentation Validation, Rate-Limit Relief, and Send Consumption Info Effectiveness
Hi everyone, We operate an online game where all in-app assets are stored server-side and require a logged-in account (no device binding). I’d like guidance on four areas: Do we really need deviceVerification / deviceVerificationNonce? – Because every purchase is tied to an account and we enforce a global transactionId UNIQUE constraint, replay or cross-account reuse appears infeasible. Under these conditions, is omitting device verification acceptable, or are there situations where Apple still recommends it? Permanent rate-limit increase for the App Store Server API – During anniversary events we saw bursts of ~18 000 requests per hour, breaching the current hourly cap on the App Store Server API (verifyTransaction, getNotificationHistory, etc.). Is there a formal process to request a long-term rate-limit expansion (or an alternative tier) from Apple? When is an App Store Server API call required for a StoreKit 2 jwsRepresentation? Docs say “call the API if you’re unsure,” but there’s no clear cut-off. Because we fully validate the JWS signature plus the entire certificate chain (including CRL/OCSP checks) on our server, local cryptographic validation seems sufficient for consumables. For subscriptions we still plan to hit the API to fetch the latest status. Does this separation match Apple’s best practice? If Apple does recommend hitting the API for consumables as well, we’d like a concrete rule of thumb—e.g. “if the item price is USD 50 or higher, always use the API.” Is establishing such thresholds consistent with Apple’s intent? Refund-risk reduction from Send Consumption Info – Adapty reports a 40–60 % refund-rate drop for subscriptions when using Send Consumption Info (blog reference). Can we expect similar reduction for consumable IAP in social/online games? Any real-world results would be helpful. Thanks in advance for any guidance!
Replies
0
Boosts
0
Views
197
Activity
Apr ’25
How to Best Practices for Implementing In-App Purchases (IAP) in Mobile Apps and Backend Systems
Hello everyone, I’d like to ask for your input regarding best practices for implementing In-App Purchases (IAP) across both the frontend and backend. Here’s our current flow: -Frontend (Mobile) The user opens a specific page. We initiate a payment request using react-native-iap. After the user completes the payment, we send the purchase data (receipt) to our backend. Backend: Accept the purchase receipt from the app. Validate the receipt with Apple’s server. (GET https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}) If the receipt is valid and the response indicates success, we mark the payment status as PAID. We store the transaction ID in our payment module. The Issue: We recently encountered a situation where Apple returned a valid receipt, so we marked the transaction as PAID. However, later we realized that the payment status was actually Pending. { transactionId: '70002676245699', originalTransactionId: '70002676245639', bundleId: '', productId: '', purchaseDate: 1745560404000, originalPurchaseDate: 1745560404000, quantity: 1, type: 'Consumable', inAppOwnershipType: 'PURCHASED', signedDate: 1745981078460, environment: 'Production', transactionReason: 'PURCHASE', storefront: 'SGP', storefrontId: '', price: 5000, currency: 'SGD', appTransactionId: '' } This raised a few questions: Does a Pending status always resolve to Paid, or is there a risk that Apple may later mark it as Failed or Unpaid? Is there a specific field in Apple's receipt response that reliably indicates whether the purchase is truly active? Should we hold off on granting access or product delivery until the status transitions from Pending to Paid? We’d really appreciate any insights or recommendations on how to handle this edge case to avoid granting access prematurely. Thanks in advance!
Replies
0
Boosts
0
Views
213
Activity
Apr ’25
External Purchase: status 401
Good morning, I am configuring in backend the sending of reports regarding purchases made in app with external platform (Stripe) as per documentation. To be clear I am talking about ExternalPurchase. However, when I make the call it returns "Apple responded with status 401". I verified the token on jwt.io as per documentation and it is working. I don't understand where I am going wrong. Below I share the code with you: const express = require("express"); const bodyParser = require("body-parser"); const jwt = require("jsonwebtoken"); const fs = require("fs"); const app = express(); const https = require("https"); const APPLE_KEY_ID = "XXXXX"; const APPLE_ISSUER_ID = "xxx-xxx-xxx-xx-xxxxxx"; const APPLE_PRIVATE_KEY = fs.readFileSync("AuthKey_xxxxx.p8", "utf8"); const APPLE_AUDIENCE = "appstoreconnect-v1"; function generateAppleJwt() { const now = Math.floor(Date.now() / 1000); const payload = { iss: APPLE_ISSUER_ID, iat: now, exp: now + (5 * 60), aud: APPLE_AUDIENCE }; return jwt.sign(payload, APPLE_PRIVATE_KEY, { algorithm: "ES256", header: { alg: "ES256", kid: APPLE_KEY_ID, typ: "JWT" } }); } app.post('/webhook', bodyParser.json({ type: 'application/json' }), async (req, res) => { let eventType = req.body.type; const relevantEvents = [ "invoice.paid" ]; if (relevantEvents.includes(eventType)) { try { const data= req.body.data; const platform = data.object.subscription_details.metadata.platform; if (platform === "IOS") { const token = generateAppleJwt(); const applePayload = { appAppleId: "xxxx", bundleId: 'com.xxx.xxx.test', externalPurchaseId: data.object.id, purchaseTime: new Date(data.object.created * 1000).toISOString(), purchaseAmount: { amount: (data.object.total / 100).toFixed(2), currencyCode: data.object.currency.toUpperCase() }, purchaseLocation: { isoCountryCode: "IT" } }; const jsonString = JSON.stringify(applePayload); const agent = new https.Agent({ keepAlive: false }); const response = await fetch( "https://api.storekit-sandbox.apple.com/externalPurchase/v1/reports", { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "Accept-Encoding": "identity", }, body: JSON.stringify(applePayload), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `Apple responded with status ${response.status}: ${errorText}` ); } console.log("✅ Notifica inviata ad Apple con successo"); } else { if(!canSendNotification){ console.log("Non è una Sub. Nessuna notifica inviata."); }else{ console.log("Customer non iOS. Nessuna notifica inviata."); } } } catch (err) { console.error("Errore durante l’invio ad Apple:"); if (err.response) { console.error("Status:", err.response.status); console.error("Headers:", err.response.headers); console.error("Data:", err.response.data); } else { console.error("Message:", err.message); } } } res.status(200).send("OK"); }); exports.checkSubStripe = functions.https.onRequest(app);
Replies
0
Boosts
0
Views
127
Activity
Jun ’25
External Purchase: Error 401
Good morning, I am configuring in backend the sending of reports regarding purchases made in app with external platform (Stripe) as per documentation. To be clear I am talking about ExternalPurchase. However, when I make the call it returns "Apple responded with status 401". I verified the token on jwt.io as per documentation and it is working. I don't understand where I am going wrong. Below is the code: const express = require("express"); const bodyParser = require("body-parser"); const jwt = require("jsonwebtoken"); const fs = require("fs"); const app = express(); const https = require("https"); const APPLE_KEY_ID = "xxx"; const APPLE_ISSUER_ID = "xxx-xxx-xxx-xxx-xxx"; const APPLE_PRIVATE_KEY = fs.readFileSync("AuthKey_xxx.p8", "utf8"); const APPLE_AUDIENCE = "appstoreconnect-v1"; function generateAppleJwt() { const now = Math.floor(Date.now() / 1000); const payload = { iss: APPLE_ISSUER_ID, iat: now, exp: now + (5 * 60), aud: APPLE_AUDIENCE }; return jwt.sign(payload, APPLE_PRIVATE_KEY, { algorithm: "ES256", header: { alg: "ES256", kid: APPLE_KEY_ID, typ: "JWT" } }); } app.post('/webhook', bodyParser.json({ type: 'application/json' }), async (req, res) => { let eventType = req.body.type; const relevantEvents = [ "invoice.paid" ]; if (relevantEvents.includes(eventType)) { try { const data= req.body.data; const platform = data.object.subscription_details.metadata.platform; if (platform === "IOS") { const token = generateAppleJwt(); const applePayload = { appAppleId: "xxx", bundleId: 'com.xxx.xxx.test', externalPurchaseId: data.object.id, purchaseTime: new Date(data.object.created * 1000).toISOString(), purchaseAmount: { amount: (data.object.total / 100).toFixed(2), currencyCode: data.object.currency.toUpperCase() }, purchaseLocation: { isoCountryCode: "IT" } }; const jsonString = JSON.stringify(applePayload); const agent = new https.Agent({ keepAlive: false }); const response = await fetch( "https://api.storekit-sandbox.apple.com/externalPurchase/v1/reports", { method: "PUT", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", "Accept-Encoding": "identity", }, body: JSON.stringify(applePayload), } ); if (!response.ok) { const errorText = await response.text(); throw new Error( `Apple responded with status ${response.status}: ${errorText}` ); } console.log("✅ Notifica inviata ad Apple con successo"); } else { if(!canSendNotification){ console.log("Non è una Sub. Nessuna notifica inviata."); }else{ console.log("Customer non iOS. Nessuna notifica inviata."); } } } catch (err) { console.error("Errore durante l’invio ad Apple:"); if (err.response) { console.error("Status:", err.response.status); console.error("Headers:", err.response.headers); console.error("Data:", err.response.data); } else { console.error("Message:", err.message); } } } res.status(200).send("OK"); }); exports.checkSubStripe = functions.https.onRequest(app);
Replies
0
Boosts
0
Views
109
Activity
Jun ’25
Mismatch between App Store Server API `expiresDate` (July 23) and iOS UI “Expires on” date (July 22) for 1-month subscription
Hi everyone, I’m seeing a consistent one-day discrepancy between the expiresDate returned by the App Store Server API and the “Expires on” date shown in the iOS Settings / App Store subscription list. I’d like to confirm whether this behavior is expected or if I’m misunderstanding the way Apple rounds dates. Reproduction steps Step Action Result 1 Purchase a 1-month auto-renewable subscription on 23 June 2025 14:00 JST (UTC+9) Transaction succeeds 2 Immediately fetch the transaction with GET /inApps/v1/subscriptions/{transactionId} Response contains "expiresDate": "2025-07-23T05:00:00Z" (= 23 July 2025 14:00 JST) 3 On the same device open Settings › Apple ID › Subscriptions (or App Store › Account › Subscriptions) UI shows Expires on: 22 July 2025 The same happens for every monthly renewal and on multiple devices. Region is Japan, device time zone Asia/Tokyo. What I understand so far (and my hypothesis) Apple’s docs say a monthly subscription renews “on the same calendar date” of the next month, so renewal in this example is 23 July. If the renewal is scheduled for 23 July at 14:00 JST, the subscription is fully usable until the end of 22 July in calendar terms, because the new billing period starts the moment the 23rd begins in Apple’s canonical time zone. Therefore, it might be intentional for the UI to display 22 July—i.e., “you can keep using it through the 22nd; on the 23rd it renews.” This hypothesis makes sense internally, yet it still looks confusing to end users who read “Expires on 22 July” and assume access ends at 00:00 on the 22nd, a whole day earlier than in reality. Questions Is showing the day before the renewal date the official/expected behavior? If so, could Apple clarify that the “Expires on” label represents the last full calendar day rather than the exact expiry timestamp? Which value should we surface in-app when telling users “Your subscription is valid until …”? The server’s expiresDate (precise to the second, converted to user time zone), or A UI-style date that’s one day earlier, matching Settings / App Store? Does Apple have a public document describing this rounding/visual convention? Have other developers encountered user confusion about the apparent 1-day “shortening” and, if so, how did you word your in-app messaging? Any insight from Apple engineers or fellow developers would be greatly appreciated. Thank you!
Replies
0
Boosts
1
Views
284
Activity
Jun ’25
Is there an API provided by the App Store Server API for canceling subscriptions?
Is there an App Store Server API available that allows cancellation of specific subscriptions by specifying transaction_id or similar identifiers? Background of these questions: We occasionally suspend user accounts due to violations of our service terms and conditions. In such cases, we would like to forcibly cancel their subscriptions if possible. However, we could not find relevant information in the documentation, which is why we are reaching out with these questions. Please let us know.
Replies
0
Boosts
0
Views
94
Activity
Jul ’25
StoreKit appAccountToken Not Preserved During Apple ID Email Migration
I'm encountering an issue with the App Store Server API where the appAccountToken is not preserved when users migrate their Apple ID email addresses. I've submitted Feedback Assistant ticket FB18709241 but wanted to check if anyone else has experienced this and get community input on best practices. The Issue When a user migrates their Apple ID from one email to another (e.g., from olduser@example.com to newuser@icloud.com), the App Store creates a new subscription transaction with a different originalTransactionId, but the appAccountToken is not carried forward from the original transaction. What I'm Seeing note: these values are fake When querying /inApps/v1/subscriptions/{originalTransactionId} with the either post-migration transaction ID or the pre-migration transaction ID, the API returns both transactions: Pre-migration transaction (status: 2 - inactive): originalTransactionId: "12345678910111" Contains: "appAccountToken": "abc123-def456-ghi789" Post-migration transaction (status: 1 - active): originalTransactionId: "67891011121314" Missing: appAccountToken entirely The Problem The appAccountToken is our only way to link App Store subscriptions to user accounts. Without it on the new transaction: Users lose access to premium features despite having valid subscriptions Server-side renewal notifications can't be matched to user accounts Manual support intervention is required for each affected user Questions for the Community Has anyone else encountered this issue with Apple ID migrations? What's the recommended approach for handling this scenario? Is there an alternative mechanism to maintain the subscription-to-user linkage across migrations? Questions for Apple Engineers Is this the expected behavior, or should the appAccountToken be preserved? Are there any planned improvements to handle this migration scenario? What's the best practice for developers to handle this case? Interestingly, both the old and new transaction IDs return the same JSON response from the App Store Server API, suggesting Apple maintains internal linkage between these transactions, but the appAccountToken isn't carried forward to the active transaction. Any insights or similar experiences would be greatly appreciated! Thank you!! Feedback Assistant: FB18709241
Replies
0
Boosts
3
Views
196
Activity
Jul ’25
Get Transaction History V1 has been marked as deprecated. Will this interface be discontinued?
Get Transaction History V1 has been marked as deprecated. Will this API be discontinued? If so, when will it be discontinued? https://developer.apple.com/documentation/appstoreserverapi/get-transaction-history-v1
Replies
0
Boosts
0
Views
72
Activity
Jul ’25
Get Transaction History V1 has been marked as deprecated. Will this interface be discontinued?
Get Transaction History V1 has been marked as deprecated. Will this interface be discontinued? https://developer.apple.com/documentation/appstoreserverapi/get-transaction-history-v1
Replies
0
Boosts
0
Views
90
Activity
Jul ’25
App Store Server Notification implementation in multiple environments
Hey everyone, We're looking for the best way to handle App Store Server Notifications in our development setup and would appreciate some guidance. Our Setup: We use a single App Store Connect account for development, which supports multiple environments (e.g., staging1, staging2). Our production app lives in a separate account, so that's not an issue. The Challenge: We have only one configurable sandbox notification URL. This makes it difficult to route notifications to the correct development server (staging1 vs. staging2 vs development) when a sandbox event occurs. We're considering using a proxy server to catch all notifications and then forward them to the appropriate environment. However, we're not sure how to determine the correct destination. Our Questions: What's the recommended approach for managing a single sandbox notification URL across multiple development environments? If a proxy is the best method, which parameter in the responseBodyV2 payload should we use to route the notification? How can we differentiate between our various dev environments? Is it possible to add custom properties to the App Store Server Notification V2 body to facilitate routing? Any advice or best practices you've implemented would be greatly appreciated. Thanks!
Replies
0
Boosts
0
Views
144
Activity
Jul ’25
App Store Server Notification implementation in multiple environments
Hey everyone, We're looking for the best way to handle App Store Server Notifications in our development setup and would appreciate some guidance. Our Setup: We use a single App Store Connect account for development, which supports multiple environments (e.g., staging1, staging2). Our production app lives in a separate account, so that's not an issue. The Challenge: We have only one configurable sandbox notification URL. This makes it difficult to route notifications to the correct development server (staging1 vs. staging2 vs developments) when a sandbox event occurs. We're considering using a proxy server to catch all notifications and then forward them to the appropriate environment. However, we're not sure how to determine the correct destination. Our Questions: What's the recommended approach for managing a single sandbox notification URL across multiple development environments? If a proxy is the best method, which parameter in the responseBodyV2 payload should we use to route the notification? How can we differentiate between our various dev environments? Is it possible to add custom properties to the App Store Server Notification V2 body to facilitate routing? Any advice or best practices you've implemented would be greatly appreciated.
Replies
0
Boosts
0
Views
118
Activity
Jul ’25
Getting 401 Unauthenticated when calling StoreKit In-App API with JWT
Hello, I am trying to call the StoreKit In-App API, but I am consistently receiving a 401 Unauthenticated error. Here is what I have done so far: JWT creation (via https://jwt.io): Header: { "alg": "ES256", "kid": "**********", "typ": "JWT" } Payload: { "iss": "********-e662-43d2-be42-012d0138ce39", "aud": "appstoreconnect-v1", "iat": 1757389187, "exp": 1757390987 } Private Key (.p8): -----BEGIN PRIVATE KEY----- -----END PRIVATE KEY----- Algorithm used: ES256 This generates the JWT successfully. API Call: I then include the JWT in the request header as follows: Authorization: Bearer ************ Endpoint: https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/2000001003740262 Response: Status: 401 Unauthenticated Request ID: S3KCYHDVRMDKUT3TZVTY3QRRWM.0.0 Has anyone else faced this issue? Is there something incorrect with how I’m generating the JWT (e.g., iat/exp values, formatting, or encoding)? Do I need to generate separate keys for Sandbox and Production, or is the same key valid for both? Any guidance would be appreciated. Thanks in advance!
Replies
0
Boosts
1
Views
184
Activity
Sep ’25
Advanced Commerce API – 5000000 "Unknown error" when calling Migrate API
We've been approved for the Advanced Commerce API. After receiving the approval, we completed the initial setup requirements (submitted a generic product ID and shared our subscriptions page deep link). When testing the Migrate Subscription to Advanced Commerce endpoint in the sandbox, we receive a 5000000 error. What could be the reason/s for it? There is no information, and the API call we make includes all required parameters. Could it be related to the fact that our new generic product ID status is "Missing Metadata"? Is there a way to understand what the issue is on Apple's side?
Replies
0
Boosts
0
Views
128
Activity
Oct ’25
URGENT: Severe Performance Issue with App Store Connect API (In-App Purchases Retrieval)
Hello Apple Support Team, We are currently experiencing a critical business issue caused by a sudden performance degradation in the App Store Connect API. This problem is blocking key parts of our production systems at wappier Optimizing game performance - website: https://wappier.com/ Since October 15, requests to the following endpoint have been taking extremely long to complete when fetching more than approximately 200 in-app purchase items. In many cases, these requests never complete even after several hours, making it impossible for us to retrieve all required data. Endpoint: https://api.appstoreconnect.apple.com/v1/apps/1252850847/inAppPurchasesV2 Parameters: { "include": "appStoreReviewScreenshot,inAppPurchaseLocalizations", "cursor": "Jd6I", "limit": "200" } We confirm that: This behavior started suddenly on October 15. No changes were made on our end. We found no corresponding updates or API change announcements in your release notes. Requests for fewer than 200 in-app products continue to perform normally. This issue is blocking our operations, as our systems depend on timely responses from this endpoint for in-app purchase data synchronization. We kindly ask your team to investigate this issue urgently and provide us guidance, and if possible, revert any recent change introduced around October 15 that could have impacted this endpoint’s performance. Thank you very much for your prompt attention and support. wappier Dev Team
Replies
0
Boosts
2
Views
208
Activity
Oct ’25
Authentication Error with App Store Server API (NOT_AUTHORIZED) while Using JWT for Subscription Validation
Hello Apple Developer Community, We are currently facing an authentication issue when calling the App Store Server API for subscription validation. Despite following Apple’s documentation and verifying all credentials, we consistently receive a NOT_AUTHORIZED error response. GET https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/appTransactions/{transactionId} Environment: Sandbox and Production (both tested, same result) Our Setup: Key ID: {Your Key ID} Issuer ID: {Your Issuer ID} Bundle ID: {Your Bundle ID} JWT Header: { "alg": "ES256", "kid": "" } JWT Payload: { "iss": "", "iat": , "exp": <timestamp + 5 minutes>, "aud": "appstoreconnect-v1", "bid": "" } Authorization Header: Authorization: Bearer Troubleshooting Steps Already Taken: Verified that .p8 key, Key ID, Issuer ID, and Bundle ID are all correctly configured and match the App Store Connect details. Confirmed that the system clock is accurate (UTC). Used appropriate endpoint (sandbox or production) based on environment. Ensured that the JWT is short-lived (under 5 minutes). Added the “Bearer” prefix correctly in the header. Tested JWT generations using Python. Issue: All requests return: { "errorCode": "NOT_AUTHORIZED" } Questions: Are there any additional claims or headers required for the subscriptions endpoint? Are there specific permissions or roles needed for the API key in App Store Connect? Is there a way to get more detailed logs or diagnostics for this NOT_AUTHORIZED response? Does the App Store Server API require a different aud or bid structure for certain endpoints? We already contacted Apple Developer Support, but they suggested posting here for engineering-level guidance. Any insight or examples of a working JWT + request for this endpoint would be greatly appreciated.
Replies
0
Boosts
0
Views
253
Activity
Nov ’25
Clarification on production App Store Server API activation timing and expected 401/404 behavior after release
We are following up on the previous discussion regarding the 401 Unauthorized response from the production App Store Server API when using sandbox transaction IDs before release. (Reference: https://developer.apple.com/forums/thread/806452) From the Apple staff’s response, we understand that: “Until you have a release in production, access to the production APIs is not allowed. Once you have a release in production this will be unlocked.” We would like to confirm a few technical details related to this point: Q1. Activation timing of production API access At what exact point does access to the production App Store Server API become available? (A) Immediately after App Review approval (status: “Ready for Sale”), but before the app is publicly available on the App Store (B) Only after the app is actually published and visible on the App Store This timing is important for our backend logic that verifies transactions during the review and initial release phases. Q2. Fallback logic between pre-release and post-release Before release, we plan to implement a fallback mechanism that retries sandbox verification when receiving HTTP 401 from the production endpoint. After the app is officially released, we expect that calling the production API with a sandbox transactionId should instead return HTTP 404 Not Found. Is this understanding correct? Should our fallback condition switch from detecting 401 → sandbox (pre-release) to 404 → sandbox (post-release)? Q3. Transition behavior immediately after release When the app transitions from “Ready for Sale” to actually being live on the App Store, is the production API immediately available for real transactions? Or does it take some time (e.g., a few hours) before production access becomes stable? We would appreciate confirmation of: The exact timing when production API access becomes active Whether the 401→404 transition is the expected design behavior Any recommended best practices for handling this transition safely in production systems Thank you for your continued support and clarification.
Replies
0
Boosts
0
Views
118
Activity
Nov ’25
IAP应用内购买怎么配置升级的?
会员等级进行升级?需要按天计算费用,像爱奇艺这样是怎么做的?
Replies
0
Boosts
0
Views
229
Activity
Jan ’26
prorated refund and upgrade of tier
Hi all, I'm encountering an issue with auto-renewable subscription upgrades in the App Store. Here's my setup: Context: Plan A: Base Plan (yearly auto-renewable subscription) Plan B: Pro Plan (monthly auto-renewable subscription) B is configured as an upgrade from A. Issue: When a user with an active Plan A subscription upgrades to Plan B, I correctly receive an App Store Server Notification v2 with DID_CHANGE_RENEWAL_PREF and UPGRADE subtype. According to Apple's documentation, a prorated refund is issued automatically in this scenario, and no separate REFUND event is sent, the refund information should be retrievable through the upgrade event itself. Testing in Sandbox: In my sandbox tests, Plan A has a 1-hour duration and Plan B has a 5-minute duration. After the user upgrades to Plan B, I immediately cancel the subscription to prevent auto-renewal. Expected vs. Actual Behavior: After the 5 minutes expire, Plan A still appears as the active current entitlement. I initially thought this might be because the prorated refund hadn't been processed yet. However, even after waiting the full hour (the original duration of Plan A), it continues to show as an active entitlement—which shouldn't be the case. As a result, when I attempt to restore purchases, Plan A is still identified as valid and the subscription gets reactivated. Question: Is this behavior expected in the sandbox environment, or am I missing something in how the prorated refund and entitlement expiration should be handled?
Replies
0
Boosts
0
Views
358
Activity
Dec ’25