My app is planning to develop a subscription product, for this type of in-app purchase do I need call finish() to complete the transaction?
And, does currentEntitlements() return the transaction after calling finish()?
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
Hello,
I have an app where the users can connect their vehicles to an API and record trips and so on.
Currently I have one subscription, where the user can add as many vehicles as he wants. But now I want (need) to change this to one vehicle per subscription.
So if the user want to add two vehicles, he need to subscribe two times to the same product.
How can I handle this?
Thanks,
Arangoool
Topic:
App & System Services
SubTopic:
StoreKit
I have an app that allows users to complete a video call and then bills them a per-minute charge for the length of the call. How can I automatically bill my app user using in-app purchase without prompting for approval?
I'm using Stripe on the web to complete an automatic purchase with the user's stored credit card. How do I accomplish the same thing on the web while on Apple device (or within my app) without prompting them for approval? They've already given approval by completing the call.
I am aware that I can create an in-app purchase product for the necessary "credits" but this will prompt them to complete the purchase which should instead be automatic. Thanks!
Hi I'm writing my first in-app purchase app, and I'm trying to do some testing with sandbox accounts. I wrote my subscription page use SwiftUI SubscriptionStoreView. And I read the documentation it says :
The sandbox account appears in Settings > App Store after the first time you use the device to attempt a purchase in a development-signed app.
But I have no idea how to make a sandbox purchasing. Every time I click the subscription button it just making a purchase in xcode environment. Did I missed anything? What can I do to make a sandbox pruchasing?
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?
I have 2 subscriptions, monthly and year
first:DID_CHANGE_RENEWAL_PREF + DOWNGRADE:
Customer downgrades a subscription within the same subscription group;
The current subscribe is year;if i change to month;when the subscribe year was expire,Automatic renewal will change to month
second:DID_CHANGE_RENEWAL_PREF+UPGRADE:
Customer upgrades a subscription within the same subscription group.
The current subscribe is month;if i change to year;I need to pay for the annual subscription immediately;and The subscription immediately switches to the annual
third:DID_CHANGE_RENEWAL_PREF subtype is None
Customer reverts to the previous subscription, effectively canceling their downgrade.
what this mean?
This is Test Env,month is five minutes;year is one hour
①My current subscription is an annual,
startTime:2024-10-02 15:04:58 ,expireTime:2024-10-02 16:04:58
②first DOWNGRADE to month,at 2024-10-02 15:14:16
②after 38 minutes,I change to the annual subscribe;at 2024-10-02 15:52:00
in the end,the Notification purchaseDate 2024-10-02 15:52:00;expiresDate 2024-10-02 16:52:00;
So
When I get NotificationType=DID_CHANGE_RENEWAL_PREF,NotificationSubType=None,Do I need to create a new subscription for the users?
Is it the latest notice of purchaseDate and expiresDate, for a year?
The appleNotification Payload as follows:
JWSTransactionDecodedPayload(
originalTransactionId='2000000731045285', transactionId='2000000731088945',
webOrderLineItemId='2000000076096676',
bundleId='app.xxxx',
productId='com.xxxx.365',
subscriptionGroupIdentifier='21514251',
purchaseDate=1727855520000, 2024-10-02 15:52:00
originalPurchaseDate=1727852699000, 2024-10-02 15:04:59
expiresDate=1727859120000, 2024-10-02 16:52:00
quantity=1,
type=<Type.AUTO_RENEWABLE_SUBSCRIPTION: 'Auto-Renewable Subscription'>, rawType='Auto-Renewable Subscription', appAccountToken='fa37b7a2-2b0b-43cb-8fda-a1fb21168efe', inAppOwnershipType=<InAppOwnershipType.PURCHASED: 'PURCHASED'>, rawInAppOwnershipType='PURCHASED',
signedDate=1727855526632, 2024-10-02 15:52:06
revocationReason=None,
rawRevocationReason=None,
revocationDate=None,
isUpgraded=None,
offerType=None,
rawOfferType=None,
offerIdentifier=None,
environment=<Environment.SANDBOX: 'Sandbox'>,
rawEnvironment='Sandbox',
storefront='CAN',
storefrontId='143455',
transactionReason=<TransactionReason.PURCHASE: 'PURCHASE'>, rawTransactionReason='PURCHASE',
currency='CAD',
price=14990,
offerDiscountType=None,
rawOfferDiscountType=None)
JWSRenewalInfoDecodedPayload(
expirationIntent=None,
rawExpirationIntent=None,
originalTransactionId='2000000731045285', autoRenewProductId='com.xxxx.365',
productId='com.xxxx.365',
autoRenewStatus=<AutoRenewStatus.ON: 1>,
rawAutoRenewStatus=1,
isInBillingRetryPeriod=None,
priceIncreaseStatus=None,
rawPriceIncreaseStatus=None,
gracePeriodExpiresDate=None,
offerType=None,
rawOfferType=None,
offerIdentifier=None,
signedDate=1727855526632, 2024-10-02 15:52:06
environment=<Environment.SANDBOX: 'Sandbox'>,
rawEnvironment='Sandbox',
recentSubscriptionStartDate=1727852698000, 2024-10-02 15:04:58
renewalDate=1727859120000, 2024-10-02 16:52:00
currency='CAD',
renewalPrice=14990,
offerDiscountType=None,
rawOfferDiscountType=None,
eligibleWinBackOfferIds=None)
Topic:
App & System Services
SubTopic:
StoreKit
Over 50 hours ago, we created an app update with 4 IAP:s. We use revenue cat for the payment features. All IAP:s are approved, and they were bundled into this version.
Still, none of the IAP:s are working. They work as intended on TestFlight. All of them are approved.
I have reached out with a case to apple developer support but have received no assistance. This is a really frustrating situation and there are no logs or any ability to understand what is going on.
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
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
Hi,
we had a case where one of our users wanted us to stop renewing his subscription in our app since he was unable to do it himself.
Is there a way for us to stop the auto renewal?
Kind regards
Ondřej Leitmančík
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.
I work as QA and we develop mobile farm game. Inside our game we have shop where user can buy coins or special prem.coins. And interface shows price with Apple Tax. Also we have battle pass, and when user want to buy battle pass the price shows without Apple tax.
Anybody knows what is wrong?
Pass price inside game > 9.99$
Price when user want to buy it > 11.99
Topic:
App & System Services
SubTopic:
StoreKit
I have got from App Review Team this same feedback during several months:
Guideline 2.1 - Performance - App Completeness
We are unable to complete the review of your app because one or more of your in-app purchase products have not been submitted for review.
Next Steps
To resolve this issue, please be sure to take action and submit your in-app purchases and upload a new binary in App Store Connect so we can proceed with our review.
I am unable to understand and implement what is meant by the feedback:
… take action …
What "action" to take?
… submit your in-app purchases …
How to "submit in-app purchases"? I know how to submit my app but how "submitting in-app purchases”?
My app contains no separate in-app purchases but several pathways through the app. The app's code contains three flags that act as signposts on user’s way through the app (Basic, Standard, Premium). The signposts are not extra content and features distributed on the App Store. They are existing parts in the app from the very beginning. They are activated according to the model user chooses (Basic, Standard, Premium) and loads down from the App Store.
I try to solve the problem since several months - without success. Links offer no solution. Links say what one can do but not how to do it. What is missing are a couple of lines of code I can adapt and integrate into my app's Objective-C code.
Best regards
gefa
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.
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
In-App Purchase
StoreKit Test
App Store Server API
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
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!
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
App Store Connect
Subscriptions
App Store Server Notifications
<SKPaymentQueue: 0x30217e480>: Payment completed with error: Error Domain=ASDServerErrorDomain Code=3904 "优惠已不再提供" UserInfo={storefront-country-code=CHN, AMSServerErrorCode=3904, client-environment-type=Sandbox, NSLocalizedFailureReason=优惠已不再提供}
Topic:
App & System Services
SubTopic:
StoreKit
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?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
In-App Purchase
StoreKit
App Store Receipts
App Store Server API
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
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
App Store
App Store Connect
App Store Server Notifications
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)