My server receives App Store Server Notification v2 notifications. Recently, it has been receiving notifications for offer codes under offers that have been deactivated. The custom code value may be the same in the active offer as the deactivated offer.
When a user redeems the custom offer code for a deactivated offer, the notification payload's offerIdentifier value now contains the UUID associated with that offer in the URL in App Store Connect
(like https://appstoreconnect.apple.com/apps/my-app-id/distribution/subscriptions/my-subscription-id/pricing/offer-codes/offer-uuid)
The notification payload contains:
{"offerIdentifier": "offer-uuid"}
instead of
{"offerIdentifier": "<My offer identifier from App Store Connect>"}
Is it possible for a deactivated offer to be redeemed? If not, is this a known issue?
We need the actual offer identifier to understand which offers users are redeeming. Is this replacement with a UUID a known issue?
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
代码块
让购买结果=尝试等待购买(产品,选项:[选项])
//处理支付结果
开关购买结果{
案例让.success(验证结果):
如果案例让.verified(交易)=验证结果{
await transaction.finish()case .userCancelled:
自我.取消回调?()
案例.pending:
/// 交易可能在未来成功,通过Transaction.updates进行通知。
打印(“苹果支付中待定”)
默认:
打破
}
} 抓住 {
自我失败回电话?(”产品购买失败:\(错误)")
打印(“产品购买失败:\(错误)”)
}
凭证相关信息如下:
transactionid:1230000065994257
appAccountToken:D613C126-4142-4BFF-9960-00AE3F5A6F83
"jwsInfo": ["header": "eyJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlFTURDQ0E3YWdBd0lCQWdJUWZUbGZkMGZOdkZXdnpDMVlJQU5zWGpBS0JnZ3Foa2pPUFFRREF6QjFNVVF3UWdZRFZRUURERHRCY0hCc1pTQlhiM0pzWkhkcFpHVWdSR1YyWld4dmNHVnlJRkpsYkdGMGFXOXVjeUJEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURUxNQWtHQTFVRUN3d0NSell4RXpBUkJnTlZCQW9NQ2tGd2NHeGxJRWx1WXk0eEN6QUpCZ05WQkFZVEFsVlRNQjRYRFRJek1Ea3hNakU1TlRFMU0xb1hEVEkxTVRBeE1URTVOVEUxTWxvd2daSXhRREErQmdOVkJBTU1OMUJ5YjJRZ1JVTkRJRTFoWXlCQmNIQWdVM1J2Y21VZ1lXNWtJR2xVZFc1bGN5QlRkRzl5WlNCU1pXTmxhWEIwSUZOcFoyNXBibWN4TERBcUJnTlZCQXNNSTBGd2NHeGxJRmR2Y214a2QybGtaU0JFWlhabGJHOXdaWElnVW1Wc1lYUnBiMjV6TVJNd0VRWURWUVFLREFwQmNIQnNaU0JKYm1NdU1Rc3dDUVlEVlFRR0V3SlZVekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRUZFWWUvSnFUcXlRdi9kdFhrYXVESENTY1YxMjlGWVJWLzB4aUIyNG5DUWt6UWYzYXNISk9OUjVyMFJBMGFMdko0MzJoeTFTWk1vdXZ5ZnBtMjZqWFNqZ2dJSU1JSUNCREFNQmdOVkhSTUJBZjhFQWpBQU1COEdBMVVkSXdRWU1CYUFGRDh2bENOUjAxREptaWc5N2JCODVjK2xrR0taTUhBR0NDc0dBUVVGQndFQkJHUXdZakF0QmdnckJnRUZCUWN3QW9ZaGFIUjBjRG92TDJObGNuUnpMbUZ3Y0d4bExtTnZiUzkzZDJSeVp6WXVaR1Z5TURFR0NDc0dBUVVGQnpBQmhpVm9kSFJ3T2k4dmIyTnpjQzVoY0hCc1pTNWpiMjB2YjJOemNEQXpMWGQzWkhKbk5qQXlNSUlCSGdZRFZSMGdCSUlCRlRDQ0FSRXdnZ0VOQmdvcWhraUc5Mk5rQlFZQk1JSCtNSUhEQmdnckJnRUZCUWNDQWpDQnRneUJzMUpsYkdsaGJtTmxJRzl1SUhSb2FYTWdZMlZ5ZEdsbWFXTmhkR1VnWW5rZ1lXNTVJSEJoY25SNUlHRnpjM1Z0WlhNZ1lXTmpaWEIwWVc1alpTQnZaaUIwYUdVZ2RHaGxiaUJoY0hCc2FXTmhZbXhsSUhOMFlXNWtZWEprSUhSbGNtMXpJR0Z1WkNCamIyNWthWFJwYjI1eklHOW1JSFZ6WlN3Z1kyVnlkR2xtYVdOaGRHVWdjRzlzYVdONUlHRnVaQ0JqWlhKMGFXWnBZMkYwYVc5dUlIQnlZV04wYVdObElITjBZWFJsYldWdWRITXVNRFlHQ0NzR0FRVUZCd0lCRmlwb2RIUndPaTh2ZDNkM0xtRndjR3hsTG1OdmJTOWpaWEowYVdacFkyRjBaV0YxZEdodmNtbDBlUzh3SFFZRFZSME9CQllFRkFNczhQanM2VmhXR1FsekUyWk9FK0dYNE9vL01BNEdBMVVkRHdFQi93UUVBd0lIZ0RBUUJnb3Foa2lHOTJOa0Jnc0JCQUlGQURBS0JnZ3Foa2pPUFFRREF3Tm9BREJsQWpFQTh5Uk5kc2twNTA2REZkUExnaExMSndBdjVKOGhCR0xhSThERXhkY1BYK2FCS2pqTzhlVW85S3BmcGNOWVVZNVlBakFQWG1NWEVaTCtRMDJhZHJtbXNoTnh6M05uS20rb3VRd1U3dkJUbjBMdmxNN3ZwczJZc2xWVGFtUllMNGFTczVrPSIsIk1JSURGakNDQXB5Z0F3SUJBZ0lVSXNHaFJ3cDBjMm52VTRZU3ljYWZQVGp6Yk5jd0NnWUlLb1pJemowRUF3TXdaekViTUJrR0ExVUVBd3dTUVhCd2JHVWdVbTl2ZENCRFFTQXRJRWN6TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd0hoY05NakV3TXpFM01qQXpOekV3V2hjTk16WXdNekU1TURBd01EQXdXakIxTVVRd1FnWURWUVFERER0QmNIQnNaU0JYYjNKc1pIZHBaR1VnUkdWMlpXeHZjR1Z5SUZKbGJHRjBhVzl1Y3lCRFpYSjBhV1pwWTJGMGFXOXVJRUYxZEdodmNtbDBlVEVMTUFrR0ExVUVDd3dDUnpZeEV6QVJCZ05WQkFvTUNrRndjR3hsSUVsdVl5NHhDekFKQmdOVkJBWVRBbFZUTUhZd0VBWUhLb1pJemowQ0FRWUZLNEVFQUNJRFlnQUVic1FLQzk0UHJsV21aWG5YZ3R4emRWSkw4VDBTR1luZ0RSR3BuZ24zTjZQVDhKTUViN0ZEaTRiQm1QaENuWjMvc3E2UEYvY0djS1hXc0w1dk90ZVJoeUo0NXgzQVNQN2NPQithYW85MGZjcHhTdi9FWkZibmlBYk5nWkdoSWhwSW80SDZNSUgzTUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDQVFBd0h3WURWUjBqQkJnd0ZvQVV1N0Rlb1ZnemlKcWtpcG5ldnIzcnI5ckxKS3N3UmdZSUt3WUJCUVVIQVFFRU9qQTRNRFlHQ0NzR0FRVUZCekFCaGlwb2RIUndPaTh2YjJOemNDNWhjSEJzWlM1amIyMHZiMk56Y0RBekxXRndjR3hsY205dmRHTmhaek13TndZRFZSMGZCREF3TGpBc29DcWdLSVltYUhSMGNEb3ZMMk55YkM1aGNIQnNaUzVqYjIwdllYQndiR1Z5YjI5MFkyRm5NeTVqY213d0hRWURWUjBPQkJZRUZEOHZsQ05SMDFESm1pZzk3YkI4NWMrbGtHS1pNQTRHQTFVZER3RUIvd1FFQXdJQkJqQVFCZ29xaGtpRzkyTmtCZ0lCQkFJRkFEQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqQkFYaFNxNUl5S29nTUNQdHc0OTBCYUI2NzdDYUVHSlh1ZlFCL0VxWkdkNkNTamlDdE9udU1UYlhWWG14eGN4ZmtDTVFEVFNQeGFyWlh2TnJreFUzVGtVTUkzM3l6dkZWVlJUNHd4V0pDOTk0T3NkY1o0K1JHTnNZRHlSNWdtZHIwbkRHZz0iLCJNSUlDUXpDQ0FjbWdBd0lCQWdJSUxjWDhpTkxGUzVVd0NnWUlLb1pJemowRUF3TXdaekViTUJrR0ExVUVBd3dTUVhCd2JHVWdVbTl2ZENCRFFTQXRJRWN6TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd0hoY05NVFF3TkRNd01UZ3hPVEEyV2hjTk16a3dORE13TVRneE9UQTJXakJuTVJzd0dRWURWUVFEREJKQmNIQnNaU0JTYjI5MElFTkJJQzBnUnpNeEpqQWtCZ05WQkFzTUhVRndjR3hsSUVObGNuUnBabWxqWVhScGIyNGdRWFYwYUc5eWFYUjVNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVFzd0NRWURWUVFHRXdKVlV6QjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkpqcEx6MUFjcVR0a3lKeWdSTWMzUkNWOGNXalRuSGNGQmJaRHVXbUJTcDNaSHRmVGpqVHV4eEV0WC8xSDdZeVlsM0o2WVJiVHpCUEVWb0EvVmhZREtYMUR5eE5CMGNUZGRxWGw1ZHZNVnp0SzUxN0lEdll1VlRaWHBta09sRUtNYU5DTUVBd0hRWURWUjBPQkJZRUZMdXczcUZZTTRpYXBJcVozcjY5NjYvYXl5U3JNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEZ1lEVlIwUEFRSC9CQVFEQWdFR01Bb0dDQ3FHU000OUJBTURBMmdBTUdVQ01RQ0Q2Y0hFRmw0YVhUUVkyZTN2OUd3T0FFWkx1***5UmhIRkQvM21lb3locG12T3dnUFVuUFdUeG5TNGF0K3FJeFVDTUcxbWloREsxQTNVVDgyTlF6NjBpbU9sTTI3amJkb1h0MlFmeUZNbStZaGlkRGtMRjF2TFVhZ002QmdENTZLeUtBPT0iXX0", "payload": "eyJ0cmFuc2FjdGlvbklkIjoiMTIzMDAwMDA2NTk5NDI1NyIsIm9yaWdpbmFsVHJhbnNhY3Rpb25JZCI6IjEyMzAwMDAwNjU5OTQyNTciLCJidW5kbGVJZCI6ImNvbS5taWd1LmNsb3VkYXZwIiwicHJvZHVjdElkIjoibWlndS52aXNpb24uTW92aWUuOCIsInB1cmNoYXNlRGF0ZSI6MTc0NDgwNzYzMjAwMCwib3JpZ2luYWxQdXJjaGFzZURhdGUiOjE3NDQ4MDc2MzIwMDAsInF1YW50aXR5IjoxLCJ0eXBlIjoiTm9uLVJlbmV3aW5nIFN1YnNjcmlwdGlvbiIsImRldmljZVZlcmlmaWNhdGlvbiI6IjdIdUtPRUhRdVd2L0hKZjdLdlBzQnJOWUNoc2V3c3k3enpPZ2k1YjE3UW8wVnd2clhhQ3B5TTNmZTN3cFBqRUwiLCJkZXZpY2VWZXJpZmljYXRpb25Ob25jZSI6ImQ3YzgwOWI2LTFjNDMtNDIwOC1iZWVmLWVhZDUwYzY1ZGIwZCIsImFwcEFjY291bnRUb2tlbiI6ImQ2MTNjMTI2LTQxNDItNGJmZi05OTYwLTAwYWUzZjVhNmY4MyIsImluQXBwT3duZXJzaGlwVHlwZSI6IlBVUkNIQVNFRCIsInNpZ25lZERhdGUiOjE3NDU4MjM1MTU5OTUsImVudmlyb25tZW50IjoiUHJvZHVjdGlvbiIsInRyYW5zYWN0aW9uUmVhc29uIjoiUFVSQ0hBU0UiLCJzdG9yZWZyb250IjoiQ0hOIiwic3RvcmVmcm9udElkIjoiMTQzNDY1IiwicHJpY2UiOjgwMDAsImN1cnJlbmN5IjoiQ05ZIiwiYXBwVHJhbnNhY3Rpb25JZCI6IjcwNDQxMzM2NTEzNjUyNzAzMyJ9", "signature": "SXieZGabBt6xHoSaBsZ1k4AexqkNYzwZel0BEhGqc3mxrd4kzOR5wERRATXySqbqfT3WJzkDAsr9jmCdoz_7-g"], "status": "normal", "transactionId": "1230000065994257"]","Band_Phone_Num":"18653588566","Platform":"124","Oper_Time":"1745823519","verification_time":"1745823519115"},"ISP":"移动","OETM":"1745823519116","CLIENTID":"","CPURATE":"0.257","AMBERUDID":"1f72113ecc704ce4a4cc135e8af71ee6","ANAME":"","MEMRATE":"0.02346919","CITY":"北京","PROMOTION":"\\","CLIENTIP":"192.168.31.74","CLIENTIPV6":"fe80::4e3:40a8:51c3:dbf5","DB":"Apple","APN":"com.migu.cloudavp","ETM":"2025-04-28 14:58:39 116"}
请帮我查一下 是这个订单没关闭成功吗?为什么出现购买新的产品 返回的永远是这个支付凭证。
Topic:
App & System Services
SubTopic:
StoreKit
With the imminent suspension of SHA-1 on App Store receipts, we desperately need an objective C code sample demonstrating how to calculate the same SDH-256 hash on device to compared with the hash from the App Store receipt.
The forced migration to SHA256 for app store receipts this month mean we have to rewrite our on device receipt validation code. However there is no documentation or objC sample code on how to validate the SHA256 hash from MAS receipts. Thje documentation at
https://developer.apple.com/documentation/technotes/tn3138-handling-app-store-receipt-signing-certificate-changes/ does not give any detail on how to validate SHA256. All my 100+ hours of experimentation and trial and error attempting to create a matching SHA256 has on device have failed.
We desperately need some ObjC Sample code to validate the SHA256 hash on device. Our existing SHA1 code is still working but we expect SHA1 hashes to disappear from MAS Receipts any day now.
Tnanks for any advice !
Background
We sell a suite of iPadOS/macOS apps that share a single auto-renewable subscription using this architecture.
Per “Offering a Subscription Across Multiple Apps” we require users to sign in before purchasing so we can propagate the entitlement and avoid duplicate subscriptions across apps.
To enforce that sign-in step we plan to turn off Streamlined Purchasing in App Store Connect.
Question
We also want to distribute subscription offer codes (for promotion, retention, appeasing dissatisfied customers, etc.).
After Streamlined Purchasing is turned off, will customers still be able to redeem offer codes outside the app (App Store “Redeem Code” UI or redemption URL)?
If outside-app redemption remains possible, it bypasses our sign-in gate and could let the same customer buy the suite twice (once via each app).
Is there an approved method to limit offer-code redemption to the in-app flow only, or otherwise prevent such duplicate subscriptions?
If no such limitation exists, what best-practice workaround does Apple recommend for multi-app suites that must turn off Streamlined Purchasing yet still wish to use offer codes without duplication risk?
Environment
StoreKit 2; server-side receipt validation & cross-app entitlement propagation.
Apps support the in-app presentCodeRedemptionSheet flow.
We expect to use both one-time-use and custom offer codes.
I am shown as being subscribed to our service in the Subscriptions list in settings yet when going to the Storekit2 page in my app it shows me as NOT being subscribed and is unresponsive. I select Restore Subscription, that grays briefly, asks for a password, then returns to blue and nothing else happens. Bouncing back and forth between monthly and yearly likewise gives no response.
The Transaction.currentEntitlements seems to be empty so it thinks the user is not subscribed.
I have unsubscribed, and resubscribed via the Settings page to no avail.
Topic:
App & System Services
SubTopic:
StoreKit
Hello. My newly released app includes a 1 day free trial. I've done this by creating a non-consumable in-app purchase priced at 0. I consider the free trial active if there's a transaction (from Transaction.currentEntitlements) for that product such that transaction.originalPurchaseDate is less than 24 hours ago. This works fine locally in the simulator and also in TestFlight, however it does not seem to work in the actual app from the App Store. The user can "purchase" it fine; they see the purchase sheet with the product name and the $0.00 price, and when they double press the side button it all seems to work. However, the app then behaves as if it didn't work. The free trial product is no longer available though.
One thing is that I didn't follow the naming convention “XX-day Trial”. Could that be the problem? If so, is that meant to be for the product reference name?
Topic:
App & System Services
SubTopic:
StoreKit
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
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
macOS
StoreKit
App Store Receipts
Mac App Store
Hi apple team,
I'm using Apple Root Certificates from https://www.apple.com/certificateauthority/ for communicating with App Store Server Library for receipt validation API.
Apple Computer, Inc Root certificate from the website is Not Valid After: Monday, 10 February 2025 at 01:18:14 Central European Standard Time.
When we can expect update of this certificate.
Thank you
I'm testing subscription downgrade and upgrade cycles in sandbox, and seeing a behavior I can't explain.
Code is generally similar to WWDC sample code for StoreKit2, including listening to incoming transactions.
Three entitlement levels, say, Standard, Plus, Pro
Corresponding subscription plans all same duration (quarterly)
I go through this purchase sequence quickly,
Purchase the Pro plan
Before the Pro plan expires, purchase the Standard plan
Still before the Pro plan expires, purchase the Plus plan
Transaction.currentEntitlements after each of the purchases (logged in transaction listener),
After step 1: { Pro plan }
After Step 2: { (only) Pro plan }
After Step 3: { Plus plan , Pro plan }
I was expecting to see {Standard, Pro } in entitlements after step 2 (user entitlement calculated from the set is still correct -- Pro). If that's not the case then why after step 3 entitlements includes both Plus and Pro plans?
Regarding App Store Server Notifications V2,we are currently using Notifications V2 in a production environment.
It is set up so that if the server receives the notification successfully, it returns 200 after about 30 seconds, and if an error occurs, it returns 400 or 500.
However, the notification is being resent multiple times from Apple's server, at 1 hour, 12 hours, and 24 hours.
Is it necessary to return the notification using Apple's API?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
App Store Server Notifications
App Store Server API
I have implemented IAP. The purchases are successful. The refresh receipt is working fine, which then calls the requestDidFinish(_ request: SKRequest) delegate. I'm fetching the receipt url through 'Bundle.main.appStoreReceiptURL'. When I convert the receipt data in base64 string and send it to app store's sandbox api and try to validate the receipt, it fails giving status code : 21002.
Hi, all!
I am wondering about something about App Receipts. I'm using App Receipt hash to key some information server-side. I'm curious however, if that doesn't actually have a possible flaw. Is it possible to get a new receipt that would yield a different hash for the same transaction? Reinstalling the app, perhaps? Installing the app on a new phone? Basically, I want to make sure this hash is something I can rely on. If the user can get a new hash for the same purchase, that's obviously problematic.
Thanks!
I use [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]. Works on my App which is in the store (compiled pre-iOS 26).
If I compile the same App now, same codebase with Xcode Version 26.0, restore does not work. Nothing happens. Tested on real device (iOS 26).
Documentation says its deprecated, but my deployment target is iOS 12.
Anyone has similar issues? Any recommendations?
Topic:
App & System Services
SubTopic:
StoreKit
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 all,
Posting here before I put in a support ticket to see if there are any ideas.
The previous beta issues seem to have been resolved, but now we are having intermittent problems with sandbox purchases. We do not know if this will affect real purchases. This is happening on beta 9 in both public and dev channels for us, most often on iPad Pro 4th. gen (Though idk if that is relevant).
Sometimes running TestFlight builds on iOS 26 beta 9 devices we will have attempts to make sandbox purchases just go into a black hole.
We do not get a "Do you want to buy this" popup, or the credentials screen. It just pauses for a bit in the section of our code that would be akin to:
let result = try await product.purchase( options: [.appAccountToken(accountUUID) ] )
Then wait a couple seconds, and then nothing. The game returns to normal flow as if it was a pending purchase, but nothing more ever happens. We have not been able to get a local debug build to do this, so it's hard for us to tell if it is going into the pending bucket, the userCancelled bucket, or the unverified bucket, etc.
If we take a device in this state and remove the app and reinstall from TestFlight we will get a credentials popup on the first attempt after install to buy, and after putting in our info we will get the " "You've already purchased this In-App Purchase...", but nothing ever his our listener and we return to the broken state.
Has anyone else seen issues like this?
P.S. Our StoreKit logic code is currently widely distributed, so if it was reproducible in the live version on iOS 18 we would know about it.
Thanks, Chris
Topic:
App & System Services
SubTopic:
StoreKit
The same store kit configuration file works in iOS and iPadOS, but not in macOS for the same multi platform application project with a single scheme.
Here’s a more detailed write up with the sample code and screenshots. When the simple app is run on
https://www.reddit.com/r/SwiftUI/s/KJsYcggWOa
EDIT: I’m using Xcode 16.4
New subscriptions have been failing to renew in the sandbox for 3 days. I am seeing multiple posts and comments from people that appear to be experiencing the same issue. But I haven't seen any feedback from Apple representatives.
I really do not want to launch a new app without seeing functioning renewals in the sandbox.
Is there somewhere else we are intended to seek assistance?
When creating a subscription charging system for an iOS app, I am trying to change the display to yen, dollars, or euros depending on the user's country.
I am using [priceLocale] of [SKProduct] in [StoreKit] to obtain currency information linked to the Apple account from the App Store and change the display.
The smartphone I am testing on uses an Apple account created in Japan, and the nationality of the App Store is also set to Japan, so I expect the display to be in yen.
As a result, the TestFligh version displayed dollars, but the official release version displayed yen.
Why doesn't the TestFligh version display yen?
I've been struggling to work with the Storekit framework and specifically to find the current Storefront used by the user of the app.
Context : My app needs to behave differently depending on the country of the user.
For me relying on Locale.current.region?.identifier does not seem very reliable, the user can change it really easily.
I'm trying to use the Storekit framework like so :
if let storefront = await StoreKit.Storefront.current{
return storefront.countryCode
}
As per Apple's Storekit documentation :
Use current to determine a customer's current storefront region and offer in-app products suitable for that region. You maintain your own list of product identifiers and the storefronts in which you make them available.
But I just can't find out what I need to change in my current configuration to get another country. The code keeps returning my original storefront (which is France)
I've tried login in with a sandbox user defined on another country. Changed all settings on my device to another country. Changed my Apple's account region as described here. Also tried to logout from everything.
The only thing that works is setting a local .storekit file as described here and changing the default storefront.
Is Xcode overriding the default storefront when building on debug or TestFlight? does anyone know how can I test different storefronts with sandbox users without the local storekit file ?
Thank you in advance.
I am an app developer, and I have implemented in-app purchases in my application. When a user completes a purchase, Apple displays a success popup. After the user taps "OK", I send the receiptData to my server to add points to their account.
However, I have encountered cases where users either exit the app before tapping "OK" or experience network issues, preventing the receipt from being sent to my server. As a result, they do not receive their points.
Later, some users send me a receipt from Apple Pay, claiming that the payment was successful. These receipts include details such as the orderId, email, and other transaction information. However, I am not certain whether the user actually completed the payment but encountered an issue, or if they are providing a fraudulent receipt.
My question:
How can I verify the authenticity of these receipts? Is there an official way to check if a given Apple Pay invoice corresponds to a real in-app purchase in my app?
Any guidance or best practices would be greatly appreciated!