Post not yet marked as solved
Hi,
We are using V2 App Store Server Notifications but can't figure out where to get the following subscription info:
the price of the subscription (can't find anywhere)
billing period duration e.g. "P1M" for monthly subscription
Google Play billing has these properties so we expect that these properties also exist in the app store.
Hi there,
i trying to get notifications use curl:
curl -X POST https://api.storekit.itunes.apple.com/inApps/v1/notifications/history -H "Authorization: Bearer 88z9....E0N3Q" -d "startDate=1685577600&endDate=1696118400"
but Apple returned 415 HTTP code that means "Unsupported Media Type"
What i do wrong?
Thanx!
Post not yet marked as solved
If my user purchase and get transaction_id: 2000000395609292, and then another user just makeup a same transaction_id to call the App Store Server API
If in the old way, the receipt seems impossiable to makeup, how about now? Is that equally safe as before?
Or is there any way to protect transaction_id.
I am not that good at security, so please forgive me about missing any point. o(╥﹏╥)o
Looking forward to your response, sincerely!! :)
Post not yet marked as solved
here are some field in JWSTransactionDecodedPayload and JWSRenewalInfoDecodedPayload, miss this kind of desc: This field is present only for xxxxx
Such as
revocationReason in JWSTransactionDecodedPayload, this field is present only when transaction is refunded. And its possible value is 0 and 1, when this field not present, golang would still unmarshal to default 0 for int32 type
isInBillingRetryPeriod in JWSRenewalInfoDecodedPayload, this field is present only when transaction in billing-retry state(relative doc), and because this field type is boolean, so possible value is true and false, base on the old api desc,
true - The App Store is attempting to renew the subscription.
false - The App Store has stopped attempting to renew the subscription.
When this field not present, golang would still unmarshal to default false for bool type
priceIncreaseStatus in JWSRenewalInfoDecodedPayload, this field is present only when an auto-renewable subscription price increase that requires customer consent. Its possible value is
0 - The customer hasn’t yet responded to an auto-renewable subscription price increase that requires customer consent.
1 - The customer consented to an auto-renewable subscription price increase that requires customer consent, or the App Store has notified the customer of an auto-renewable subscription price increase that doesn’t require consent.
Post not yet marked as solved
Hello, I have a few questions about the App Store Server API.
According to the document, if 'OriginalTransactionIdNotFoundError(errorCode: 4040005)' is received as a response after calling the production environment API if there is no environment information, it is written to call the sandbox environment, but 'Get Transaction Info' / 'Get Transaction History' API call and when 'TransactionIdNotFoundError(errorCode: 4040010)' is received, can I call it in the sandbox environment?
Is the root certificate of the X.509 certificate chain on x5c claim in JWSDecodedHeader always AppleRootCA-G3?
If I add and recall the query parameter '?sort=DESCENDING' in the Get Transaction History API, will the first transaction information always have the largest expiration date?
Post not yet marked as solved
here is original thread
And I got a way to handle it:
By SDK upload introductory_price 😂
introductory_price > 0 that means it is Pay as you go or Pay up front
introductory_price = 0 means it is free trial
So here is another question, is there any api Apple could introduce for backend to query product price?
Looking forward to your response, sincerely!! :)
Post not yet marked as solved
As we transition from using verifyReceipt to the App Store Server API, I'd like to know how we can determine trial-related information such as is_trial_period and is_in_intro_offer_period. Specifically, can we differentiate these details using the offerType value (https://developer.apple.com/documentation/appstoreserverapi/offertype)?
Any guidance or suggestions would be greatly appreciated. Thank you.
Post not yet marked as solved
I have verified that my sandbox endpoint works for App Store Server Notifications (V2), by requesting a Test Notification.
When I issue a beginRefundRequest in my app for a Sandbox account, my backend does not receive a request. According to the docs this should work in Sandbox too: https://developer.apple.com/documentation/storekit/transaction/testing_refund_requests
What could possible be missing if the TEST notification works, but the REFUND notification doesn't?
Post not yet marked as solved
Hello, I'm trying to generate a JWT in C# for calling this endpoint -> https://developer.apple.com/documentation/appstoreserverapi/look_up_order_id
I have followed the instructions in the documentation, but I'm always getting "Unauthenticated" as a response.
The private key used is of type "In-App Purchase" as the documentation says.
Here you have a response with a Request ID:
Unauthenticated
Request ID: YGV3ELXFCFHA5IPRZEW76S66NQ.0.0
This is the code used to generate the JWT:
public string CreateNewApiToken()
{
const string audience = "appstoreconnect-v1";
var kid = "***";
var privateKey = "***";
var bundleId = "***";
var issuerId = "***";
var now = DateTime.UtcNow;
using var ecdsa = ECDsa.Create();
ecdsa?.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKey), out _);
var signingCredentials = new SigningCredentials(new ECDsaSecurityKey(ecdsa), SecurityAlgorithms.EcdsaSha256);
signingCredentials.Key.KeyId = kid;
var payload = new JObject
{
{ "iss", issuerId },
{ "iat", now.ToUnixTime() },
{ "exp", now.AddMinutes(30).ToUnixTime() },
{ "aud", audience },
{ "bid", bundleId}
};
var handler = new JsonWebTokenHandler();
var token = handler.CreateToken(payload.ToJson(), signingCredentials);
return token;
}
Thanks in advance.
Post not yet marked as solved
Hello!
Please let me know after I make a request for purchase information
GET https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/2000000411047647
I receive encrypted information in this format
{
"signedTransactionInfo":"eyJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlFTURDQ0E3YWdBd0lCQWdJUWZUbGZkMGZOdkZXdnpDMVlJQU5zWGpBS0JnZ3Foa2pPUFFRREF6QjFNVVF3UWdZRFZRUURERHRCY0hCc1pTQlhiM0pzWkhkcFpHVWdSR1YyWld4dmNHVnlJRkpsYkdGMGFXOXVjeUJEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURUxNQWtHQTFVRUN3d0NSell4RXpBUkJnTlZCQW9NQ2tGd2NHeGxJRWx1WXk0eEN6QUpCZ05WQkFZVEFsVlRNQjRYRFRJek1Ea3hNakU1TlRFMU0xb1hEVEkxTVRBeE1URTVOVEUxTWxvd2daSXhRREErQmdOVkJBTU1OMUJ5YjJRZ1JVTkRJRTFoWXlCQmNIQWdVM1J2Y21VZ1lXNWtJR2xVZFc1bGN5QlRkRzl5WlNCU1pXTmxhWEIwSUZOcFoyNXBibWN4TERBcUJnTlZCQXNNSTBGd2NHeGxJRmR2Y214a2QybpBQmhpVm9kSFJ3T2k4dmIyTnpjQzVoY0hCc1pTNWpiMjB2YjJOemNEQXpMWGQzWkhKbk5qQXlNSUlCSGdZRFZSMGdCSUlCRlRDQ0FSRXdnZ0VOQmdvcWhraUc5Mk5rQlFZQk1JSCtNSUhEQmdnckJnRUZCUWNDQWpDQnRneUJzMUpsYkdsaGJtTmxJRzl1SUhSb2FYTWdZMlZ5ZEdsbWFXTmhkR1VnWW5rZ1lXNTVJSEJoY25SNUlHRnpjM1Z0WlhNZ1lXTmpaWEIwWVc1alpTQnZaaUIwYUdVZ2RHaGxiaUJoY0hCc2FXTmhZbXhsSUhOMFlXNWtZWEprSUhSbGNtMXpJR0Z1WkNCamIyNWthWFJwYjI1eklHOW1JSFZ6WlN3Z1kyVnlkR2xtYVdOaGRHVWdjRzlzYVdONUlHRnVaQ0JqWlhKMGFXWnBZMkYwYVc5dUlIQnlZV04wYVdObElITjBZWFJsYldWdWRITXVNRFlHQ0NzR0FRVUZCd0lCRmlwb2RIUndPaTh2ZDNkM0xtRndjR3hsTG1OdmJTOWpaWEowYVdacFkyRjBaV0YxZEdodmNtbDBlUzh3SFFZRFZSME9CQllFRkFNczhQanM2VmhXR1FsekUyWk9FK0dYNE9vL01BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3Tm9BREJsQWpFQTh5Uk5kc2twNTA2REZkUExnaExMSndBdjVKOGhCR0xhSThERXhkY1BYK2FCS2pqTzhlVW85S3BmcGNOWVVZNVlBakFQWG1NWEVaTCtRMDJhZHJtbXNoTnh6M05uS20rb3VRd1U3dkJUbjBMdmxNN3ZwczJZc2xWVGFtUllMNGFTczVrPSIsIk1JSURGakNDQXB5Z0F3SUJBZ0lVSXNHaFJ3cDBjMm52VTRZU3ljYWZQVGp6Yk5jd0NnWUlLb1pJemowRUF3TXdaekViTUJrR0ExVRWUVFERER0QmNIQnNaU0JYYjNKc1pIZHBaR1VnUkdWMlpXeHZjR1Z5SUZKbGJHRj000OUJBTURBMmdBTUdVQ01RQ0Q2Y0hFRmw0YVhUUVkyZTN2OUd3T0FFWkx1***5UmhIRkQvM21lb3locG12T3dnUFVuUFdUeG5TNGF0K3FJeFVDTUcxbWloREsxQ"
}
Please tell me how I can decode the information signedTransactionInfo ?
and what is needed for this? (I'm trying to decode on Node.js)
Post not yet marked as solved
https://developer.apple.com/documentation/appstoreserverapi/get_transaction_info
Is there a status code that can identify whether transactionid is in the sandbox or production environment?
Post not yet marked as solved
As the Title, I had an app which is available only to specific businesses or organizations, how can we get the app version on the App store by the Lookup API for the APP?
I used the link for trying: (https://itunes.apple.com/lookup?bundleId=), but I got the below response:
{
"resultCount":0,
"results": []
}
Any suggestion?
Post not yet marked as solved
Dear Apple Developer Forum Community,
I hope this message finds you well. We are in the process of planning a migration from the existing "verify receipt" method to the new App Store Server API using the app-store-server library in Node.js.
While we are excited about the capabilities and advantages that the app-store-server library offers, we have noted that it is currently in a beta phase. As we strive to ensure a stable and reliable production environment for our application, we would like to inquire about the expected timeline for the app-store-server library to transition from beta to a production-ready release.
Our decision to adopt this library is contingent upon its readiness for production use, and we are keen to align our migration plans with its development roadmap. Any insights or updates regarding the library's readiness for production use would be greatly appreciated.
Thank you for your time and assistance. We look forward to your response and are eager to continue our journey of providing exceptional experiences to our users through the Apple ecosystem.
Sincerely,
Post not yet marked as solved
I am developing a mobile app that has a paid subscription. I am interested in triggering a push notification when users change their subscription status in the App Store - for example, if they were to turn off auto-renew on their subscription, that would trigger a push notification using APNS to offer a discount.
How would I go about sending a notification to a specific user based on the Server Notification payload? It is unclear which, if any, of the response body payload can be used to identify the user. Furthermore, I need a way to associate this identification with a device token so I can send an APNS push notification to the user.
I have had the notification toggle available on launch for a while. If I understand correctly, users toggling "Allow/Deny" for registerForRemoteNotifications() notifications only changes their local settings. My first instinct was to start storing user data when they consent or decline notifications on launch - I could store [some kind of user ID, notifications_allowed (bool), and the APNS device token]. That way, when receiving an App Store Server Notification, I can reference this table, then fetch their device token and send a push notification if they have agreed to notifications. However, this seems somewhat fallible because it doesn't track users who change their notification settings in their own setting later.
To summarize, my questions are the following:
How can I identify a user from an App Store Server Notification payload?
How could I store an identifier along with the APNS device token when users consent or decline push notifications that can later be used to associate their device with an App Store Server Notification?
Post not yet marked as solved
There are fake receipts used by hackers, which are the receipts before iOS7.The Receipt can be successfully verified with an incorrect password.Is this a bug?https://developer.apple.com/documentation/appstorereceipts/verifyreceipt --Do I need to completely drop compatibility for this?
FB number is https://feedbackassistant.apple.com/feedback/13205370
but the ticket's status is Investigation complete - Unable to diagnose with current information,but No reply message
Post not yet marked as solved
Hi everyone,
I'm trying to understand something for analytics purpose. We see a large number of transactions coming in Transaction.update that don't initiate from our app's paywalls.
When using AppStore.sync, does this send any restored transactions in Transaction.update? Or does it simply update what currentEntitlements will return. In other words, when I validate a transaction coming from Transaction.update, and the reason is .purchase, is it always a new purchase, or can it be an old purchase which is replayed?
If the answer to the above question is yes, how can we distinguish actual purchases from restored transactions when verifying a transaction?
Thanks!
Bruno
Post not yet marked as solved
I received the following notifications (in this order) for the subscription:
date, type, subtype
2023-10-09 10:49:17,SUBSCRIBED,INITIAL_BUY
2023-10-16 03:46:44,DID_CHANGE_RENEWAL_STATUS,AUTO_RENEW_DISABLED
2023-10-16 10:49:19,DID_FAIL_TO_RENEW,GRACE_PERIOD
2023-10-16 13:14:36,EXPIRED,BILLING_RETRY
What do these mean? Why is there a DID_FAIL_TO_RENEW GRACE_PERIOD notification when the user already disabled auto-renew? Why did it expire in 2.5 hours? Why isn't there a GRACE_PERIOD_EXPIRED notification?"
Post not yet marked as solved
Hi, recently our system received many CONSUMPTION_REQUEST after receive REFUND request for some transaction.
EX: we have one transaction wants to refund, and our system receives the first CONSUMPTION_REQUEST at 10th Oct and receives the REFUND request after two days(12th Oct), there is no CONSUMPTION_REQUEST coming between those time, and after that, we recieve many CONSUMPTION_REQUEST between 13th Otc to now.
Not sure if it this is what Apple excepted, because we are not found the document related to this.
Why we post this question is for our system, what we always faced is there is no CONSUMPTION_REQUEST coming after receive the REFUND request before.
Post not yet marked as solved
Hi, This is first time posting in here.
Now our app is using In-app purchase and i'm implementing server logics using app-store-server-api.
But from yesterday, i'm facing 'http status code 401 Unauthorized error' when calling some apis.
I did many trials to fix this error and issue new key for api in app store connect. But I can't resolve this error. Even can't know what is the reason.
I attach my real code for calling 'get transaction info' api. Please give some help for me!!!!
const jwt = require('jsonwebtoken');
const fs = require('fs');
const axios = require('axios');
getApiJWTToken = () => {
// header
const keyId = process.env.APP_STORE_KEY_ID;
const issuerId = process.env.APP_STORE_ISSUER_ID;
const appBundleId = process.env.APP_BUNDLE_ID;
const header = {
alg: 'ES256',
kid: keyId,
typ: 'JWT',
};
// payload
const issuedAt = Number(dayjs().unix());
const payload = {
iss: issuerId,
iat: issuedAt,
aud: 'appstoreconnect-v1',
bid: appBundleId,
};
// sign
const keyString = fs.readFileSync(filePath, 'utf-8');
const token = jwt.sign(payload, keyString, {
algorithm: 'ES256',
header: header,
expiresIn: '30m',
});
return btoa(token);
};
requestSingleTransactionInfo = async ({ transactionId }) => {
try {
// jwt token
const token = this.getApiJWTToken();
const envType = process.env.APP_STORE_SERVER_API_ENV;
const commonUrl = `/inApps/v1/transactions/${transactionId}`;
let baseUrl;
if (envType === 'Sandbox') {
// sandbox
baseUrl = 'https://api.storekit-sandbox.itunes.apple.com';
} else if (envType === 'Production') {
// production
baseUrl = 'https://api.storekit.itunes.apple.com';
}
const url = `${baseUrl}${commonUrl}`;
const response = await axios.get(url, {
headers: {
Authorization: `Bearer ${token}`,
},
});
const { signedTransactionInfo } = response.data;
const transactionInfo = await decodeTransaction(signedTransactionInfo);
console.log(transactionInfo);
return transactionInfo;
} catch (err) {
throw err;
}
};
Post not yet marked as solved
https://appstoreconnect.apple.com/iris/v1/apiAccesses server this api respone 500