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.

App Store Server API

Posts under App Store Server API tag

86 Posts
Sort by:
Post not yet marked as solved
1 Replies
388 Views
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.
Posted
by
Post marked as solved
1 Replies
464 Views
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!
Posted
by
Post not yet marked as solved
3 Replies
490 Views
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!! :)
Posted
by
Post not yet marked as solved
0 Replies
395 Views
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.
Posted
by
Post not yet marked as solved
0 Replies
454 Views
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?
Posted
by
Post not yet marked as solved
0 Replies
376 Views
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!! :)
Posted
by
Post not yet marked as solved
0 Replies
336 Views
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.
Posted
by
Post not yet marked as solved
1 Replies
475 Views
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?
Posted
by
Post not yet marked as solved
1 Replies
683 Views
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.
Posted
by
Post not yet marked as solved
4 Replies
1.6k Views
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)
Posted
by
Post not yet marked as solved
0 Replies
471 Views
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?
Posted
by
Post not yet marked as solved
0 Replies
450 Views
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,
Posted
by
Post not yet marked as solved
0 Replies
409 Views
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?
Posted
by
Post not yet marked as solved
0 Replies
395 Views
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
Posted
by
Post not yet marked as solved
0 Replies
428 Views
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
Posted
by
Post not yet marked as solved
1 Replies
377 Views
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?"
Posted
by
Post not yet marked as solved
2 Replies
373 Views
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.
Posted
by
Post not yet marked as solved
2 Replies
552 Views
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; } };
Posted
by