I have some questions regarding the specifications of the receipt information that can be obtained from https://developer.apple.com/documentation/appstoreserverapi/get_transaction_info.
When a subscription is newly purchased, it is expected that the purchaseDate should reflect the time of purchase and should not be later than this time, such as an hour after the purchase. Is this understanding correct?
We observed a phenomenon where, when a user purchased a new subscription between 17:30 and 20:00 JST on November 3, 2024, the purchaseDate in the received receipt was delayed by one hour compared to the actual purchase time. Is this a specification or an issue?
When validating the receipt for a newly purchased subscription, if the purchaseDate reflects a time later than when the purchase was made, should I regard the user as having subscription rights at the time of validation?
App Store Receipts
RSS for tagValidate app and in-app purchase receipts with the App Store using App Store Receipts.
Posts under App Store Receipts tag
46 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I have 6 Mac App Store apps. They're all upfront paid, with no IAP, and they've all used the same on-device Mac App Store receipt validation code for years, which returns 173 in main() if there's not a valid receipt. Incidentally, the apps are entirely Objective-C.
I've just learned that if I compile an app with Xcode 16 and the macOS 15 SDK, I get the alert "exit(173) Not Available" when the app returns 173 on macOS 15 Sequoia. The rest of the alert text says, "The exit(173) API is no longer available. You can use Transaction.all or AppTransaction.shared to verify in-app purchases instead."
I have several questions:
Why was this done?
Where is this behavior change documented?
What are my options, given the above description of my apps?
The application has changed from paid purchase to free use. We need to obtain the previous player's purchase records to unlock the paid content.
//Here is the code for the client to obtain the player's payment information:
NSURL * receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]){
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptUrlString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
NSLog(@"requestAppStoreReceipt receiptUrlString = %@", receiptUrlString);
} else {
// 如果凭证为空,则再发一次凭证请求
SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
refreshReceiptRequest.delegate = self;
[refreshReceiptRequest start];
NSLog(@"requestAppStoreReceipt 如果凭证为空,则再发一次凭证请求!");
}
//The following is the server-side parsing code:
Path filePath = Path.of("SubscriptionKey_ABCDEFGHIJ.p8");
String encodedKey = Files.readString(filePath);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client = new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
String appReceipt = "MIIcs...";
ReceiptUtility receiptUtil = new ReceiptUtility();
String transactionId = receiptUtil.extractTransactionIdFromTransactionReceipt(transactionReceipt);
System.out.println(transactionId);
if (transactionId != null) {
long now = System.currentTimeMillis();
TransactionHistoryRequest request = new TransactionHistoryRequest()
.sort(TransactionHistoryRequest.Order.DESCENDING)
.revoked(false)
.productTypes(List.of(TransactionHistoryRequest.ProductType.CONSUMABLE));
HistoryResponse response = null;
List<String> transactions = new LinkedList<>();
do {
String revision = response != null ? response.getRevision() : null;
response = client.getTransactionHistory(transactionId, revision, request, GetTransactionHistoryVersion.V2);
transactions.addAll(response.getSignedTransactions());
} while (response.getHasMore());
Set<InputStream> rootCAs = Set.of(
new FileInputStream("AppleComputerRootCertificate.cer"),
new FileInputStream("AppleIncRootCertificate.cer"),
new FileInputStream("AppleRootCA-G2.cer"),
new FileInputStream("AppleRootCA-G3.cer")
);
Long appAppleId = 1234567899L; // appAppleId must be provided for the Production environment
System.out.println(transactions.size());
SignedDataVerifier signedPayloadVerifier = new SignedDataVerifier(rootCAs, bundleId, appAppleId, environment, true);
for (String notificationPayload : transactions) {
try {
AppTransaction payload = signedPayloadVerifier.verifyAndDecodeAppTransaction(notificationPayload);
System.out.println(payload);
} catch (VerificationException e) {
e.printStackTrace();
}
}
}
//Return result analysis:
JWSTransactionDecodedPayload{originalTransactionId='2000000641683476', transactionId='2000000641683476', webOrderLineItemId='null', bundleId='', productId='', subscriptionGroupIdentifier='null', purchaseDate=1719566962000, originalPurchaseDate=1719566962000, expiresDate=null, quantity=1, type='Consumable', appAccountToken=null, inAppOwnershipType='PURCHASED', signedDate=1728885967093, revocationReason=null, revocationDate=null, isUpgraded=null, offerType=null, offerIdentifier='null', environment='Sandbox', storefront='CHN', storefrontId='143465', transactionReason='PURCHASE', price=6000, currency='CNY', offerDiscountType='null', unknownFields=null}
We have develop according to the following document on our end:
https://developer.apple.com/documentation/foundation/nsbundle/1407276-appstorereceipturl#4098404
https://developer.apple.com/documentation/appstoreserverapi/get_transaction_info/
https://developer.apple.com/documentation/appstoreserverapi/data_types
We would like to know if the solutions in the document can be used to solve the problems we encountered?
Is there a problem with our method of parsing bills that prevents us from obtaining the necessary information?
Hello! Since October 2, we have observed a problem with the renewal of subscriptions. We have not changed anything, but the receipts no longer have the information whether a valid subscription exists after the subscription was actually automatically extended. The subscription status can then be queried correctly via verify Receipt. Has anything been changed to the receipts from Apple?
PS: we know that StoreKit1 is deprecated and we should switch to StoreKit2 asap.
Hello,
I have a customer who keeps getting an "app is damaged" error for a freshly downloaded app from the Mac App Store.
The logs show the following lines:
standard 12:58:40.390872+0200 storeuid Receipt Validation (at.EternalStorms.Yoink)
Signature Check: PASS
Bundle ID Check: PASS
Bundle Version Check: PASS
GUID Check: PASS
Expiration Check: PASS
standard 12:58:40.391649+0200 storelegacy StoreLegacy: Failed to perform in-line receipt renewal for application at path /Applications/Yoink.app : '(null)'
The Mac in question is running macOS 12 Monterey - curiously, the customer has another Mac with that same system version and there it works just fine.
What can be done to make this work again?
Thank you,
– Matthias
Hello,
I'd like to find out if macOS Sequoia's MAC Address randomization affects the data (specifically, MAC addresses) we receive from I/O Kit.
For context, I'd like to find out if it affects my Mac App Store receipt validation code in any way.
Thank you,
– Matthias
Hello,
I hope to find out more about how AppTransaction works on macOS, specifically about its internet connection requirements: if I use this to validate that the app is a legit purchase from the Mac App Store, I would not want it to have an always-on requirement just to validate.
Does AppTransaction require the user to always be online for AppTransaction.shared ?
When an app is downloaded from the Mac App Store, is the data needed for AppTransaction automatically embedded during that download, or is that data downloaded upon first launch of the app, therefore requiring an internet connection at launch time?
Once the data/receipt has been downloaded by AppTransaction, is it cached until the app's next update, or is it cleared at some time during the version's life and needs to be re-downloaded, therefore requiring an internet connection at launch?
Where is that receipt/data stored?
Also, if you don't mind me sneaking in this non-related but sort of related question, in terms of receipt validation:
Does macOS Sequoia's MAC address rotation feature affect receipt validation in any way when using IOKit?
Thank you kindly,
– Matthias
Since today, we are receiving reports from several customers on iPadOS17.x and iPadOS18 that their app runs in 'demo' mode. This can only happen if the purchase receipt is not found and this also shows in the log file they sent us. We can also reproduce it on an officially installed version of our app, but not when run from Xcode.
Is anyone else experiencing this?
Hello,
Our project did not approve because of the in-app purchase did not work, i need help! (Guideline 2.1 - Performance - App Completeness)
We've done it several times in TestFlight and sorry the in-app purchase connection doesn't work, before getting the final output the in-app purchase debugging is done correctly in editor unity, i wanted to make sure before testing the in-app purchase whether the bank account form Should it be approved?
Because of the bank account form, I could not find the code and branch, so I sent a ticket to financial support and sent the bank documents and I am waiting for the answer.
Regards,
Hello team,
It'd be very grateful if you give us some advice for our issue. It's about receipt verification in production environment. Details are as follows.
User tried to purchase consumable IAP in production environment.
Price is charged for IAP.
After that, tried to verify receipt using the receipt of the transaction, but it failed with an error message. "21004 The shared secret you provided does not match the shared secret on file for your account."
This error occurs only sometimes, not always. Most of purchase succeeded without this issue.
For your reference, we've sold subscription IAP but now stopped to sell subscription IAP since last year. And we've been transferred this app from other iOS team last year.
Our iOS developer account have configured both primary shared secret and app-specific shared secret for this app.
I've found the similar issue with ours.
https://forums.developer.apple.com/forums/thread/746202
Thank you.
Best regards
Dear Apple Development Team,
I would like to draw attention to certain aspects of working with Server Notifications for In-App Purchases that could be improved to enhance development convenience and API efficiency.
1. Lack of Information on Non-Consumable Purchases in Server Notifications
Currently, Server Notifications do not provide information about non-consumable purchases. This creates certain inconveniences when validating such purchases on the server. It would be extremely useful to have the ability to verify non-consumable purchases in the same way as subscriptions.
Moreover, there is currently no way to obtain information about the amounts paid for non-consumable purchases, even with additional API requests. This limitation significantly complicates financial reporting and analytics for apps that utilize non-consumable purchases. While we can obtain information about the amount paid by the user for a subscription, we have no equivalent capability for non-consumable purchases.
Adding this information to Server Notifications or providing an API endpoint to retrieve it would greatly improve our ability to track and analyze non-consumable purchase data without relying on client-side reporting.
2. Inconsistency in Token and Signature Handling
There is some inconsistency in the approaches to authentication and verification between various Apple APIs. For example:
When using Sign In with Apple, the approach with keyid is applied for JWT verification.
In Server Notifications for In-App Purchases, certificate information is repeatedly duplicated in each notification.
This leads to the need to implement different methods of JWT verification depending on the API being used. Additionally, the current approach with Server Notifications results in data redundancy: the useful payload is about 1.5 KB, while repetitive certificate information takes up about 17 KB in each notification.
Unifying authentication and verification approaches across different APIs could significantly simplify development and improve data processing efficiency.
We would appreciate consideration of these suggestions for API improvement. This could substantially simplify developers' work and increase the efficiency of integrating Apple services into applications.
Thank you for your attention to this matter.
It seems that starting from around 2024-08-20T10:28:00 UTC, both the "trialPeriod" and "introOfferPeriod" fields are set to true in the receipt data of free-trial subscriptions. Before that time only trialPeriod was set to true while introOfferPeriod was false.
Just want to confirm whether this is an expected and permanent change?
Thanks.
I want to get MacOS Version from Objective-C build for iOS Apps to run on Apple Silicon Mac.
Because AppStore In-App Purchase on Apple Silicon Mac had issues on MacOS version 13.0 and was fixed at version 13.3.1.
I need to know the exact version of the MacOS to prevent users from buying In-App Purchases.
I found that I can get iOS version that the app is emulating on and assume
MacOS version from iOS version. But I think it's not 100% guaranteed way, because no documentation says anything about it.
Is there a way to get MacOS Version in iOS Apps running on Apple Silicon Mac?
Hi,
Our application offers 3 non-renewing subscriptions. I have integrated the necessary code, and our App Store Account Paid Agreement status is Active. Transactions are successfully completed in the sandbox environment.
However, I am facing difficulties with server-side validation. The /verifyReceipt API is deprecated, and I need an alternative method for server-side validation. The Apple documentation is quite confusing, and I couldn't find any articles related to the new approach for server-side validation.
I would greatly appreciate it if you could provide a detailed guide on how to perform server-side validation with the new method.
Thank you in advance.
We have some questions regarding the receipt fields:
Our receipt validation refers to this document: Validating Receipts on the Device(https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device). We would like to know if preorder receipts can also be validated using the methods outlined in the above document. If so, may I ask which ASN.1 Field Type corresponds to the preorder_date_ms?
Additionally, we found other receipt fields in this document: Receipt Fields(https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1). If possible, it would be appreciated if someone could supplement the definitions of each ASN.1 Field Type.
Hi,
I'm using the App Store Server API for in-app purchase receipt validation. However I received 401 error status code.
My app is ready for submit in App Store Connect, but not yet published the first version.
The receipt is generated using StoreKit test configuration and follow the Sandbox testing instruction. It is generated on a real device using Sandbox Apple account registered in the App Sandbox tester section.
If I go back to use the deprecated verifyReceipt API sandbox endpoint, I get {'status': 21002} error instead.
Is it expected for an App that has not yet published in App Store?
If not, is there any way to test the in-app purchase server-side validation before the App is release?
My app has a single subscription group called Premium that contains two products with different billing periods - monthly and yearly. All tests are made in Apples's Sandbox environment with sandbox user.
I am currently testing a scenario, where the user is subscribed to the monthly product, subsequently cancels the subscription from the App Store subscriptions page, and before the subscription expires, he wants to re-enable it (same monthly product). If the subscription is re-enabled from the App Store subscriptions page, my server gets a notification about the changed subscription renewal status and everything works well. This scenario is documented at the bottom of the page here: https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox/testing_disabling_auto-renew#3780478
I however also want to support the scenario where the user can re-enable the subscription before it expires from the app itself. To do so, if the subscription is cancelled, but not yet expired, I show the user a button Re-activate. When this button is clicked, I initiate a purchase of the same monthly product, the IAP correctly shows the payment sheet and once I click Subscribe in the In App Purchase payment sheet, it goes through without any issues. However, my app subsequently receives a callback that the purchase failed:
<SKPaymentQueue: 0x280f1fa10>: Payment completed with error: Error Domain=ASDErrorDomain Code=500 "Unhandled exception" UserInfo={NSUnderlyingError=0x2803a4720 {Error Domain=AMSErrorDomain Code=301 "Invalid Status Code" UserInfo={NSLocalizedDescription=Invalid Status Code, AMSURL=https://sandbox.itunes.apple.com/WebObjects/MZBuy.woa/wa/inAppBuy?REDACTED, AMSStatusCode=500, NSLocalizedFailureReason=The response has an invalid status code}}, NSLocalizedFailureReason=An unknown error occurred, NSLocalizedDescription=Unhandled exception}
This seems like the purchase failed with invalid status, but strangely enough, my server receives a notification that the subscription renewal status was changed to AUTO_RENEW_ENABLED and if I check the App Store subscriptions, I can see that its not cancelled anymore. The subscription also gets renewed at the end of the billing period, where it would have otherwise been cancelled. So in other words, everything seems to work except the purchase error above.
My question is, what could be the reason for this? Perhaps Apple does not support re-enabling subscription directly from the App, but only from the App Store subscription page? Or perhaps its just a limitation of Apple's sandbox environment and I would not receive this payment error in production environment?
Hi,
I developped an app which is an alcohol level calculator (cf image for few screens). I did lots of modification to the app is accepted, but none works. I also asked to the verification team, but they can't help me more.
However similar app already exists like https://apps.apple.com/fr/app/alcootel-by-maaf/id6444157056 or https://apps.apple.com/fr/app/%C3%A9thylom%C3%A8tre/id688242393. I even copy the description of the first app.
So, could you help me about what I can change in order to the app is validated, or do the new rules make my app "out of the Apple laws".
Thank you
I need user installed app information like first install time (purchase to install)
last time use "Bundle.main.appStoreReceiptURL" to request Receipt was successful
but when i Reinstall the app by Xcode has ERROR:"Error reading receipt data: Error Domain=NSCocoaErrorDomain Code=260"
what wrong with ERROR?
Hello everyone,
I'm currently in the process of updating my code to remove the deprecated verifyReceipt method in line with the latest documentation and guidelines.
I have a question regarding the Receipt Usage example provided in the documentation here: https://github.com/apple/app-store-server-library-java?tab=readme-ov-file#receipt-usage
ReceiptUtility receiptUtil = new ReceiptUtility();
String transactionId = receiptUtil.extractTransactionIdFromAppReceipt(appReceipt);
My question is: Why does this method only return one transactionId? I had assumed that it might return a list of different transaction IDs present in the encoded receipt.
Thank you in advance for any assistance!
Best regards,
Maria