StoreKit

RSS for tag

Support in-app purchases and interactions with the App Store using StoreKit.

StoreKit Documentation

Posts under StoreKit subtopic

Post

Replies

Boosts

Views

Activity

Apple In App Purchase Validation through Server Notification
We are currently experiencing an issue where our server is not receiving Apple’s server notification (webhook) for in-app purchase confirmations. This notification is critical as it helps us confirm the purchase status and fulfill the corresponding services to the users. Despite the successful completion of purchases within the app, the lack of webhook notifications prevents us from tracking and processing these payments on our backend. In addition to resolving this issue, we are also concerned about the security aspect of receiving server notifications. Specifically, we want to ensure that any requests or notifications we receive are indeed coming from Apple, and not subject to potential man-in-the-middle (MIM) attacks. We are looking for information or best practices on how to validate that the request originates from a legitimate Apple source, ensuring the integrity of the communication and safeguarding our system from spoofed or malicious requests. Key questions: Server Notification Issue: Why might we not be receiving Apple's payment confirmation notifications, and what steps can we take to troubleshoot this issue? Request Validation: What security mechanisms or validation techniques can we implement to confirm that the server notifications are genuinely from Apple, ensuring no interference from MIM attacks? Does Apple provide any headers, tokens, or signatures in the server notification that we can use to verify the origin? Are there any known methods or configurations to ensure secure receipt of in-app purchase confirmations? We are looking for guidance and possible solutions to ensure a secure and reliable payment validation process for our in-app purchases. Any support in this regard would be highly appreciated.
0
0
478
Oct ’24
OSX Subscriptions not working in Sandbox
I am working on the integration of subscriptions in my OSX application. The subscription flow works perfectly but after the purchase it is not creating _MASReceipt folder in Contents folder of application so I cannot send the receipt to apple to verify this purchase. when I checked the logs it showing below error. Anyone who is familiar to this issue please help. Error writing receipt (13401 bytes) using privileged service to /private/var/folders/xw/yd038cts3b94qmtvlxzb1sy80000gp/T/AppTranslocation/9B8BB321-1C16-4F41-93EA-E27675791E79/d/test.app Error Domain=NSCocoaErrorDomain Code=642 "You can’t save the file “_MASReceipt” because the volume “9B8BB321-1C16-4F41-93EA-E27675791E79” is read only." UserInfo={NSFileOriginalItemLocationKey=file:///private/var/folders/xw/yd038cts3b94qmtvlxzb1sy80000gp/T/AppTranslocation/9B8BB321-1C16-4F41-93EA-E27675791E79/d/Advanced%20Uninstall%20Manager.app/Contents/_MASReceipt, NSURL=file:///private/var/folders/xw/yd038cts3b94qmtvlxzb1sy80000gp/T/AppTranslocation/9B8BB321-1C16-4F41-93EA-E27675791E79/d/abc.app/Contents/_MASReceipt, NSFileNewItemLocationKey=file:///System/Library/Caches/com.apple.appstored/abc.app/_MASReceipt/, NSUnderlyingError=0x7fdc618ded90 {Error Domain=NSCocoaErrorDomain Code=642 "You can’t save the file “_MASReceipt
0
0
354
Oct ’24
Selling the same subscription in multiple apps
I want to sell the same subscription in multiple apps so that if someone subscribes in one they show up as having a subscription in the other. Apple's documentation states "To get started, use App Store Connect to create a separate and equivalent auto-renewable subscription for each app that offers the multi-app subscription so that users can subscribe from any app." (https://developer.apple.com/documentation/storekit/in-app_purchase/original_api_for_in-app_purchase/subscriptions_and_offers/offering_a_subscription_across_multiple_apps) But I'm unable to create two subscriptions with the same Product ID. I could create two subscriptions that are equivalent but with different Product IDs that are treated as the same by our server, which would accomplish the main goal, but I believe this risks someone subscribing to both subscriptions if they do so directly through the App Store, for example using a promo code link, because they're still two different subscriptions to Apple. Thanks!
0
0
389
Oct ’24
促销优惠一直拉起不成功
<SKPaymentQueue: 0x30217e480>: Payment completed with error: Error Domain=ASDServerErrorDomain Code=3904 "优惠已不再提供" UserInfo={storefront-country-code=CHN, AMSServerErrorCode=3904, client-environment-type=Sandbox, NSLocalizedFailureReason=优惠已不再提供}
1
0
290
Oct ’24
How to use 'appStoreReceiptURL' to obtain players' paid purchase information and the purchased version
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?
0
0
608
Oct ’24
Issues with Subscription Validation using Apple APIs and Server Notifications
Hello, We’re implementing subscription validation on our server using Apple’s latest APIs and have run into a few issues and uncertainties. We are transitioning away from the deprecated /verifyReceipt endpoint and are now using the /inApps/v1/subscriptions/{transactionId} API. However, we have questions about how to handle transaction validation, particularly around older transactions and notification behavior in the sandbox environment. Additionally, we are facing challenges with Apple Server Notifications, especially regarding when we should expect to be notified of subscription cancellations or expirations. Below are the specific questions we hope the community can help us with: Question 1: We are using the inApps/v1/subscriptions/{transactionId} API to retrieve the subscription status and validate subscriptions on our server side, since the /verifyReceipt endpoint has been deprecated. This API always returns the last transaction for a subscription. Should we only validate the latest transaction, and is it guaranteed that even if we provide an older transaction ID, the API will always return the most up-to-date transaction information? https://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses/ https://developer.apple.com/documentation/appstorereceipts/verifyreceipt Question 2: In the sandbox environment, we encountered an empty response when trying to validate a transaction ID. We are attempting to validate older transactions made by the user to keep our system and store subscriptions in sync. Could you provide guidance on why this might be happening and how we can validate older transactions? Question 3: We are using Apple Server Notifications to track subscription renewals and changes. However, when a user disables auto-renewal, we do not receive a webhook notification when the subscription actually expires or is canceled. We only get notified when the user changes their renewal preferences. Should we expect a separate notification when the subscription fully expires or is canceled, or is this behavior expected? https://developer.apple.com/documentation/appstoreservernotifications/notificationtype
0
0
357
Oct ’24
IPv6 Support for “verifyReceipt API” and “App Store Server API
We are currently upgrading our server to IPv6, and when we tested it, we found that the following API, which verifies receipts when charging, resulted in an error. verifyReceipt API https://buy.itunes.apple.com/verifyReceipt https://sandbox.itunes.apple.com/verifyReceipt This is currently a deprecated API, so when we tried curling the URL to the "App Store Server API" and using nslookup, we also got an error. App Store Server API https://api.storekit.itunes.apple.com https://api.storekit-sandbox.itunes.apple.com The error was that the address could be retrieved from the domain in IPv4, but the address could not be retrieved in IPv6. "https://www.apple.com" and other sites could be retrieved without any problems in IPv6. Is this API not compatible with IPv6? If this is not possible, the only way to verify receipts and the legitimacy of charges seems to be to perform verification on the device, but as this requires a lot of work, I would like to know if there is a better way.
0
0
363
Oct ’24
Issue with In-app system disclosure sheet
We are utilizing the StoreKit external purchase link within the app and have configured the necessary keys in the entitlements according to the documentation. The Info.plist file has also been updated with the required key and a single destination URL, following the guidelines from this documentation. However, when we click the link in the app, it redirects to the default browser, and the in-app system disclosure sheet does not appear. Should the in-app disclosure sheet appear automatically, or do we need to design and implement it ourselves?
0
1
384
Oct ’24
About NotificationHistory startDate
Hello folks. Hope you have a great day. I have a question on your Get Notification History API. Reference : https://developer.apple.com/documentation/appstoreserverapi/get_notification_history According to NotificationHistoryRequest document( https://developer.apple.com/documentation/appstoreserverapi/notificationhistoryrequest ) startDate parameter allows past 180 days from current date. However my test result is little bit different. Could you help me this case? I'll give detailed request/response during testing. Request body {"startDate":1714364482159,"endDate":1729048882326,"notificationType":"REFUND","notificationSubtype":null,"transactionId":null,"onlyFailures":null} Response Header server: daiquiri/5 date: Wed, 16 Oct 2024 03:21:38 GMT content-type: application/json content-length: 110 x-apple-jingle-correlation-key: SBDV5UOM7YGOJCYS2M4S2X67IE x-apple-request-uuid: 90475ed1-ccfe-0ce4-8b12-d3392d5fdf41 b3: 90475ed1ccfe0ce48b12d3392d5fdf41-06ee3a4acafcff41 x-b3-traceid: 90475ed1ccfe0ce48b12d3392d5fdf41 x-b3-spanid: 06ee3a4acafcff41 apple-seq: 0.0 apple-tk: false apple-originating-system: CommerceGateway x-responding-instance: CommerceGateway:20104::: apple-timing-app: 8 ms strict-transport-security: max-age=31536000; includeSubDomains x-daiquiri-instance: daiquiri:47578001:st44p00it-hyhk15014801:7987:24RELEASE221:daiquiri-amp-commerce-clients-ext-002-st body {"errorCode":4000012,"errorMessage":"Invalid request. The start date is earlier than the allowed start date."} As you see, request sent at Wed, 16 Oct 2024 03:21:38 GMT, and start date is 1714364482159 which is Mon, 29 April 2024 04:21:22 (milli second). The difference between the two dates is 170 days. Could you help me this this case? If I misunderstood on your API, please let me know. Thank you.
2
0
451
Oct ’24
iOS Version Requirements for Implementing Win-Back Offer
I am currently exploring the implementation of win-back offer in my app to encourage lapsed subscribers to re-subscribe. I plan to use the automatic win-back offer sheet that display to eligible customers upon app launch, as described in the documentation. However, I am unclear about the specific iOS version requirements needed to support this feature effectively. Could someone clarify the minimum iOS, iPadOS, and macOS versions required for these automatic win-back offer sheet?
1
0
398
Oct ’24
Unable to add subscription
We have subscriptions for our app that are in the "waiting for review" state. The subscriptions page has the message "Your first subscription must be submitted with a new app version. Create your subscription, then select it from the app’s In-App Purchases and Subscriptions section on the version page before submitting the version to App Review." However I don't see any section in the app to enter this. It seems to be a similar issue to what is being described at https://stackoverflow.com/questions/73098652/app-store-connect-in-app-purchase-and-subscriptions-section-missing?rq=2, but I have all requirements fulfilled; all compliance and tax forms have been completed, and I have made changes to my subscriptions in response to app review. I did add these subscriptions to my app in a previous round in the approval process, and I thought that was why it wasn't appearing now. However, my app is now live and users are not seeing the subscriptions options when they try to enter the app (which had previously worked in sandbox mode)
2
0
1.1k
Oct ’24
Is subscription autoRenewStatus in StoreKit2 working correctly with Sandbox testing?
It seems that subscription status gives different results with XCode testing and Sandbox testing. I am using StoreKit2 to implement an IAP of an autorenewable subscription. I want to determine whether the subscription has been cancelled, so that the UI reflects that the subscription will stop after the expiry date and not be renewed. the 'willAutoRenew' property of the subscription status renewalInfo seems to do exactly what is required, and works fine in XCode testing. My setup is very similar to the StoreKit demo associated with the WWDC21 session available here: https://developer.apple.com/documentation/storekit/in-app_purchase/implementing_a_store_in_your_app_using_the_storekit_api/ To demonstrate its use, add: print(renewalInfo.willAutoRenew) after line 79 of the SubscriptionsView in the demo project. When you run the app, and purchase a Standard Navigation assistance subscription, the console shows 'true'. If you then cancel the subscription in XCode (Debug:StoreKit:Manage Transactions), the console will show 'false' as expected So far so good. My problem is that when I move to Sandbox testing, and cancel the subscription in another way (eg using the .manageSubscriptionsSheet view modifier, or in Settings:App Store:Sandbox Account), the willAutoRenew property remains true, even though the subscription is in fact cancelled (ie it disappears after the expiry date) Does anyone know a workaround to determine cancellation status?
7
0
5.3k
Oct ’24
Disabling Streamlined Purchasing
We have an application that currently supports iOS 16, and in order to disable Streamlined Purchasing, the latest approved binary must include the necessary StoreKit APIs. The StoreKit API includes the PurchaseIntent, which is available from iOS 16.4. Our question is: Can we disable Streamlined Purchasing for our app? If yes, how will it work for devices running iOS versions earlier than 16.4?
0
0
416
Oct ’24
Why do we receive multiple CONSUMPTION_REQUEST notifications?
Hello, We have received three CONSUMPTION_REQUEST notifications for the same transactionId. We sent a response to each CONSUMPTION_REQUEST notification to the API and received a 202 Accepted response. Why do we receive multiple CONSUMPTION_REQUEST notifications even though we have sent a ConsumptionRequest to the Apple Store Server API? Do we need to respond to each CONSUMPTION_REQUEST for the same transactionId? Thanks
0
1
263
Oct ’24
Mac App Store receipt validation exit(173) Not Available on Sequoia
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?
2
3
941
Oct ’24
Unexpected App Store Server Notification offerIdentifier
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?
2
0
472
Oct ’24
In-App Purchases
The in-app payment system is confusing. In-app payment is initially reviewed without being attached (however, in-app purchase is applied) Once the review is complete (up to in-app payment), the in-app payment module is attached for testing It is reviewed with the attached module Isn't this the logic? If there is another logic, please let me know. Thank you.
0
0
260
Oct ’24
503 Service Unavailable Error with Sandbox iTunes API
Hello, I am currently integrating with the iTunes Sandbox environment, specifically with APIs like: https://sandbox.itunes.apple.com/inApps/v1/subscriptions/12345 However, I consistently receive a 503 - Http/1.1 Service Unavailable error in response. Below is the typical response I get: <html><body><b>Http/1.1 Service Unavailable</b></body></html> I've tried sending requests from different IP addresses and servers, but all attempts result in the same 503 error. Could anyone provide guidance on whether this is a temporary issue with the sandbox environment or if there is something I need to adjust in my setup? Any help would be greatly appreciated! Thank you.
1
0
376
Oct ’24
Failed to receive notifications for Apple's recurring monthly charges and refunds.
We have not been receiving notifications for Apple's recurring monthly charges and user refunds since 4 PM on October 10th. apple Id:1325419855 server notification endpoint: https://xyks-open.yuanfudao.com/leo-cm-third-pay/api/apple-iap/pay-sub-notify/2 Server notification version: version1 Additionally, we have another app, which is also using server notification version1, and it is able to receive notifications.
0
0
200
Oct ’24