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.
App Store Server API
RSS for tagCall this REST API from your server to request and provide information about your customers' in-app purchases.
Posts under App Store Server API tag
56 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
We have encountered an issue when verifying transactions using the Get Transaction Info API.
We tested the behavior in both the sandbox and production environments and observed the following results.
When calling the production endpoint:
https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
with a transactionId generated in the sandbox environment, the API returns HTTP 401 Unauthorized.
However, based on the documentation and common understanding, we expected HTTP 404 Not Found in this case.
Using the same JWT token, if we call the sandbox endpoint:
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId},
we receive HTTP 200 OK with the expected response body.
We have also confirmed that the same behavior occurs when using the Get Transaction History API — it works correctly in the sandbox environment but returns 401 in production.
Could you please confirm whether this behavior (receiving 401 instead of 404) is expected by design, or if it indicates a potential issue?
If this is not the intended behavior, we would appreciate any guidance or instructions to resolve it.
Thank you very much for your technical support.
「Get Transaction Info」APIを用いてトランザクションの検証を行ったところ、以下の問題が発生しました。
サンドボックス環境および本番環境の両方で検証を行い、次の結果を確認しています。
本番環境エンドポイント https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId}
に対して サンドボックス環境で生成された transactionId を使用すると、HTTP 401 Unauthorized が返却されます。
(一般的には、この場合 404 Not Found が返る想定であると理解しています。)
同一のJWTトークン を用いて サンドボックス環境のエンドポイント
https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId}
を呼び出した場合は、HTTP 200 OK が返り、期待通りのレスポンスボディを受け取ることができています。
また、同様の挙動が Get Transaction History を使用した場合にも発生することを確認しています。
サンドボックス環境では正常に動作しますが、本番環境では401が返却されます。
この挙動(401が返却されること)は仕様上想定されたものか、または何らかの問題によるものかご確認をお願いいたします。
もし想定外の挙動である場合は、解決に向けたご案内をいただけますと幸いです。
本件について、技術的なサポートをお願いいたします。
よろしくお願いいたします。
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.
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
StoreKit
In-App Purchase
App Store Server API
We got Advanced Commerce API and the generic product identifiers approved. When I was try to migrate a sandbox subscription to ACA enabled subscription I hit an error
Request payload
(Hid some info but the requestReferenceId is the real)
{
"descriptors": {
"description": "Migrated",
"displayName": "Migration"
},
"items": [
{
"sku": "product_1mo_999",
"description": "description",
"displayName": "Product"
}
],
"requestInfo": {
"requestReferenceId": "3b0b8e67-d8a0-45f4-8f6d-06bffa9a2c08"
},
"storefront": "USA",
"targetProductId": "com.company.generic.subscription",
"taxCode": "C003-00-1"
}
Response
{
"errorCode": 5000000,
"errorMessage": "An unknown error occurred."
}
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
App Store Server API
Advanced Commerce API
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
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
In-App Purchase
App Store Server 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?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
App Store Server API
Advanced Commerce API
Hi, I have deployed my app on Test Flight, I have two subscriptions, monthly and yearly. User can have one of them at a time and upgrade, downgrade to the other. Upgrade, downgrade, cancel from the Apple Settings worked fine in the sandbox environment when testing locally. Now when I have deployed the app on TestFlight, I was able to purchase the subscription successfully from my app. Now when I want to cancel my subscription from the Apple Settings it gives me the following error after confirming cancellation, 'Your request is temporarily unable to be processed. Please try again later.' Also the other subscription offer (yearly) is also not shown to which I could upgrade, even though in the sandbox I was able to upgrade downgrade from the settings. Another thing I have noticed is that the app Icon or name is not shown anywhere in settings with the subscription. Instead of app icon only empty square is shown. Even though app icon shows fine everywhere else.
Can someone please help me figure out this issue?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
In-App Purchase
TestFlight
App Store Server API
One of our apps has 85% stuck in Billing Retry -- We are so confused. All the users are from the US, and have a one-week free trial.
We had 1,000 subscriptions expire from this issue.
So any help would be so appreciated.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
StoreKit
App Store Connect
App Store Server API
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.
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!
Topic:
Developer Tools & Services
SubTopic:
Apple Developer Program
Tags:
App Store Connect API
App Store Server API
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.
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!
Topic:
Community
SubTopic:
Apple Developers
Tags:
Subscriptions
App Store Server Notifications
App Store Server API
App Store Server Library
Get Transaction History V1 has been marked as deprecated. Will this interface be discontinued?
https://developer.apple.com/documentation/appstoreserverapi/get-transaction-history-v1
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Server API
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
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Server API
Hello. I'm currently implementing Apple Notification v2 to prepare for refunds for in-app purchases, but I'm not receiving requests from Apple servers to my backend server.
I've applied HTTPS (TLS 1.2) and correctly registered production/sandbox notification URLs on App Store Connect.
After requesting a test notification, when I check the status of testNotificationToken, I receive an UNSUCCESSFUL_HTTP_RESPONSE_CODE as follows: {"signedPayload":"......":[{"atteptDate":1752128001970,"sendAttemptResult":"UNSUCCESSFUL_HTTP_RESPONSE_CODE"}]}
The endpoint for receiving notifications is set to accept POST requests with application/json format, and it responds with 200 (OK) without any content. However, Apple notifications are not coming through.
Could anyone help me with this issue?
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Server API
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
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
StoreKit
App Store Server Notifications
App Store Server API
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.
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!
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store
StoreKit
App Store Server API
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);
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Connect API
App Store Server API
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);
Topic:
App Store Distribution & Marketing
SubTopic:
App Store Connect API
Tags:
App Store Connect API
App Store Server API