Hello!
I am wondering what the best way is to fetch previous introductory offers from the App Store Connect API. I have tried hitting this endpoint:
https://developer.apple.com/documentation/appstoreconnectapi/list_all_introductory_offers_for_a_subscription
which seemed to only return the current, active introductory offer.
I am looking to gather all introductory offers for my subscription products, including the following attributes:
Start date
End date
Customer price
Territory
Currency
Introductory offer mode
Number of periods
Thank you.
StoreKit
RSS for tagSupport in-app purchases and interactions with the App Store using StoreKit.
Post
Replies
Boosts
Views
Activity
Hi there, I am here to ask for help regarding IAP and Apple Pay. So in my app, there are user two types, fan and athlete where a fan can ask the user text questions, or video questions or can directly tip the athlete(note: these questions are under the name of tip).
I was using Stripe for payment but Apple rejected that and forced us to implement IAP by mentioning that as the question/tip is video/text based so you have to remove Stripe and use IAP.
Therefore, I am here to ask whether I should use IAP or Apple Pay because in IAP we have limitations for predefined products while what if a fan wanted to tip a dynamic amount (here can we use Apple Pay for tipping)
Please share your thoughts on what is the best way to tip while asking a question and free tip.
One thing more to add we want to implement a bidding feature in the app as well where fans can bid dynamic amounts that IAP lacks).
Thank you
We are doing a Swift Package to manage our iap with storekit 2.
This packages also contains the unit tests for the code.
(as well as all the required files .storekit, certificate, ...)
when calling SKTestSession.buyProduct(identifier: options:) we catch an error : Error Domain=SKErrorDomain Code=0 "(null)"
Note when trying exactly the same code but in a project instead of a package that works perfectly.
Any idea what could it be?
The code is really simple :
var testSession: SKTestSession!
override func setUpWithError() throws {
testSession = try SKTestSession(configurationFileNamed: "Products")
}
func testExample() async throws {
do {
let productID = "nonConsumable.crystal.tier1"
try await testSession.buyProduct(identifier: productID)
} catch {
XCTFail("An error occured during purchase : \(error)")
}
}
}
I wrote a StoreKit unit test and set the renewal rate to .oneRenewalEveryTwoSeconds for the test session. But now my App expires and renews every two seconds when running normally, even though the StoreKit Configuration file is set to "Real Time." Changing it to anything else doesn't change the renewal rate. I've tried cleaning the build folder.
If, however, I set my SKTestSession explicitly to .realTime and run the test again, then my app behaves. However, again, it doesn't matter what I set the Configuration Settings to.
Anyone know where this information is stored?
Hello,
I'm curious to know if in the situation that I build an app where people can book and pay for vacations (Hotel + Flights) and I have suppliers for those vacations making the payments to the hotels and the airlines, the booking would be subject to an in app purchases tax?
Basically apps like booking.com, airbnb, expedia etc... does Apple let this kind of apps to use their own payment processor like stripe?
SKAN / SkAdNetwork Error
ERROR: We never received any http request from the "skan.ourdomain.com" as expected.
ERROR: All the attributions(YES! ALL OF THE INSTALLS) in the Google Analytics / Singular / AppsFlyer / Twitter / Facebook, shows the source are "direct organic".
We Did These
We use Google Ads and Twitter Ads and Facebook Ads to promote our apps. We spent enough money, and got thousands of paid installs from these ads.
We set the NSAdvertisingAttributionReportEndpoint to "skan.ourdomain.com".
We call the SKAdNetwork.updatePostbackConversionValue(1). in "AppDelegate" and "Subscribe" source codes.
And from our app logs, we see there are SKAN_UPDATE_CONVERSION_VALUE_OK.
Source Code
if #available(iOS 15.4, *) {
SKAdNetwork.updatePostbackConversionValue(1) { err in
if let err = err {
Tracker.shared.reportEvent(.SKAN_UPDATE_CONVERSION_VALUE_FAIL, name: err.localizedDescription, value: 1)
} else {
Tracker.shared.reportEvent(.SKAN_UPDATE_CONVERSION_VALUE_OK)
}
}
} else {
SKAdNetwork.registerAppForAdNetworkAttribution()
Tracker.shared.reporxtEvent(
.SKAN_UPDATE_CONVERSION_VALUE_OLD_VERSION, name: "AppDelegate")
}
Hello! I would like to add In-app purchase capability to my visionOS app. However, unlike in iOS project, I can't add this capability to visionOS project. On Apple's In-app purchase page there is an indicator that it is supported by Apple Vision Pro.
Please help me on how can I add paid subscriptions to my visionOS app.
Best regards, Vladyslav.
Background
We are currently working on two apps: a Church Management System (ChMS) and a Giving App. Both apps are free to use. The Giving App will incorporate a Stripe payment gateway for users to make donations to churches.
The Question:
Now, here's the scenario: If a church desires a custom-branded app developed specifically for their ChMS (will be hosted in their developer account), we plan to charge a one-time development fee for this service. We would like to facilitate the payment within the app itself. However, we're exploring the possibility of using a third-party payment service (such as Stripe) rather than relying on the Apple in-app payment system.
Key Points:
Both apps are free to use.
The Giving App will have Stripe integration for donations to churches.
Churches interested in a custom-branded ChMS app will be required to pay a one-time development fee. We will develop the app and add it to their developer account.
We want to know if it's feasible to collect this development fee within the app using a third-party payment service and not give it as in-app purchase.
Common Sign-Up:
It's worth mentioning that the sign-up process is common for both apps. Users can sign up on the ChMS app, and the same logins can be used for the Giving App.
Specific Questions:
Can we use a third-party payment service (like Stripe) to collect the one-time development fee within the app without utilizing the Apple in-app payment system?
Any insights on potential challenges or best practices related to this approach?
Considering the common sign-up, are there considerations we should be aware of for such purchases?
I'd love to hear from the community based on your experiences. Any advice, tips, or shared experiences would be incredibly helpful.
Thank you
Hi,
I've been trying to renew my developer subscription for 3 days now. I've updated my payment method too. Only UPI is accepted. When ever I try to renew the subscription from the "Manage subscription" section on my App Store. it keeps failing. Can someone help me out here. tried raising support ticket few times no response.
Need some help here.
I've got an iPhone 11 PM on 16.6.1 and I'm trying to test Family Sharing IAPs. However, I can't seem to test via the Sandbox environment (which I need to validate receipt handling for Family Sharing). The app is running locally on my device and was built straight from Xcode.
When I tap to make a purchase in my app (which uses StoreKit2, if that makes any difference) a sheet pops up with a purchase button which, when tapped, immediately completes and I get the following dialog box:
"Your purchase was successful"
[Environment: Xcode]
How do I get my app to use the Sandbox environment? All documentation suggestions when I tap my purchase button I should simply be presented with a login modal and then, after the purchase has completed, be able to see my Sandbox credentials under Settings -> App Store. At the moment no dialog is presented (the purchase completes immediately) and the entire "Sandbox Account" section is missing from Settings -> App Store. Any help will be greatly appreciated!
We are trying to distribute an app for our digital ministry. It is a religious Bible app.
We offer our app 100% free of charge, however, we do accept voluntary donations. These donations do not result in any sort of in-app content incentives for the user, they strictly go to supporting our ministry.
However, every time we explain this to the App Review Team, this is their response:
Tips or donations may be optional, if they are connected to or associated with receiving digital content, they must use in-app purchase in accordance with guideline 3.1.1 To resolve this issue, please revise your app to use in-app purchase to pay for this type of transaction or remove these transactions from your app.
We am stuck, because we do not know what to do. We want to keep the donation in the app, however, using In-App purchases tarnishes the image of 100% free.
The iOS App Sandbox plays a pivotal role in ensuring the secure testing of In-App Purchases (IAPs) without impacting real users. To utilize the sandbox, set up your app's IAPs in App Store Connect. Begin by importing the StoreKit framework and fetching product information using SKProductsRequest. Once products are retrieved, initiate purchases through SKPaymentQueue. Implement the SKPaymentTransactionObserver to handle transaction updates, distinguishing between successful, failed, and restored purchases. When testing, remember to use test user accounts and sign in to the App Store with a test account on your device. This ensures a controlled environment for validating IAPs during development.
URL - https://www.controlf5.in/ios-mobile-app-development-company/
swift
Copy code
import StoreKit
class YourViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {
func fetchProducts() {
let productIdentifiers: Set<String> = ["com.yourapp.product1", "com.yourapp.product2"]
let request = SKProductsRequest(productIdentifiers: productIdentifiers)
request.delegate = self
request.start()
}
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let products = response.products
// Handle retrieved products
}
func request(_ request: SKRequest, didFailWithError error: Error) {
// Handle error
}
func purchaseProduct(product: SKProduct) {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
// Handle successful purchase
case .failed:
// Handle failed purchase
case .restored:
// Handle restored purchase
default:
break
}
}
}
}
How can I add listening for transactions which use a an offer code to purchase an auto renewable subscription. I implemented listening for transactions using the example code included in the WWDC21-10114 session. Code works when user purchases a subscription, but not when they use the promo code, then purchase the discounted subscription.
I've seen several posts (mostly from 3+ years ago) asking about implementing a "home-rolled" free trial for subscriptions in iOS. I'm still a bit confused though as there doesn't seem to be a definitive answer.
I'm wanting to try to increase registration -> free trial conversion by offering users a time-based free trial from my backend rather than going through StoreKit. It will be clear up front that it's a subscription service and has a cost, but they won't need to agree to the auto payment after X days.
Is anyone currently doing this without getting rejected or is this still considered a no-no by Apple?
This is re-posted from this Stack Overflow post.
I am looking at validating the purchase of a paid app from Mac AppStore. Based on this WWDC video about StoreKit 2, I am attempting to this with AppTransaction. I have not found meaningful high-level documentation about this specific use case beyond that.
My approach is to first get the "cached" AppTransaction by calling AppTransaction.shared. If that is not there I proceed to getting it from Apple, via AppTransaction.refresh(). If they don't have it, or when the network is down, the user automagically gets the familiar "log in to your store account" UI that has been around as long as the Mac AppStore.
Once I have the AppTransaction I use it to verify we are on the right device, using code like this, where the returned Bool represents validation success:
guard let deviceVID = AppStore.deviceVerificationID?.uuidString.lowercased() else { return false }
let nonce = appTransaction.deviceVerificationNonce.uuidString.lowercased()
let combo = nonce + deviceVID
let digest = SHA384.hash(data: Data(combo.utf8))
return (digest == appTransaction.deviceVerification)
My first question is: Does that look like the right approach? Is there something else I should do, or check?
My second question is around testing this approach. Refreshing the AppTransaction in the sandbox invariably yields a valid item, even if the app version does not yet exist in AppStoreConnect. This is also the case when I log out in the App Store app on the Mac. This makes me think it is using my AppleID which I am logged into in System Settings. Does that sound right?
I would like to be able to remove / delete the cached AppTransactions - where might I find those on the system?
Thanks for everyone's help!
I wanted to test some purchasing situations in Testflight. So I pressed purchase button in my app. Then when product was being loaded, I closed the app.
I expected that purchase popup would show failure process like real purchase. But in my case,it was shown with purchase button and it was able to press. Purchase was completed outside of the app(In home screen). Receipt was remaining in pending state
Is this the correct purchase process?
Since verifyReceipt is marked as deprecated, there is appstore server library; e.g. this is the java version:
https://github.com/apple/app-store-server-library-java/tree/main
According to https://developer.apple.com/videos/play/wwdc2023/10143/, client (game) sends receipt to server for validation and server extracts transactionId with ReceiptUtility and asks for transaction history to appstore to receive signed transactions. During this request, server can filter to receive only consumables; e.g:
TransactionHistoryRequest request = new TransactionHistoryRequest()
.sort(TransactionHistoryRequest.Order.DESCENDING)
.revoked(false)
.productTypes(List.of(
TransactionHistoryRequest.ProductType.CONSUMABLE));
Working in sanbox env, everything seems fine, I can make a successful in app purchase; but I receive below from transaction history request:
APIException{httpStatusCode=404, apiError=4040010, apiErrorMessage='Transaction id not found.'}
But when I use getTransactionInfo with the same extracted transactionId, I receive a successful response like below:
TransactionInfoResponse{signedTransactionInfo='eyJ....
I have 2 questions here:
1- why am i getting error in TransactionHistoryRequest
2- what is the correct way to validate an in app purchase server side without verifyReceipt?
Thanks in advance
PS: Here are sample codes for TransactionHistoryRequest and getTransactionInfo:
TransactionHistoryRequest
String encodedKey = Files.readString(filePath);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client =
new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
String receipt = "MIIUW......";
ReceiptUtility receiptUtil = new ReceiptUtility();
String transactionId = receiptUtil.extractTransactionIdFromAppReceipt(receipt);
System.out.println("extracted txId is " + transactionId);
if (transactionId != null) {
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;
System.out.println("revision is " + revision);
response = client.getTransactionHistory(transactionId, revision, request);
transactions.addAll(response.getSignedTransactions());
} while (response.getHasMore());
System.out.println(transactions);
}
getTransactionInfo
String encodedKey = Files.readString(filePath);
System.out.println("encoded key is " + encodedKey);
String receipt = "MIIUW...";
ReceiptUtility receiptUtil = new ReceiptUtility();
String txId = receiptUtil.extractTransactionIdFromAppReceipt(receipt);
Environment environment = Environment.SANDBOX;
AppStoreServerAPIClient client =
new AppStoreServerAPIClient(encodedKey, keyId, issuerId, bundleId, environment);
try {
TransactionInfoResponse response = client.getTransactionInfo(txId);
String signed = response.getSignedTransactionInfo();
System.out.println(">>>" + signed.split("\\.").length);
String jws_payload = signed.split("\\.")[1];
String payload = new String(Base64.decodeBase64(jws_payload));
System.out.println(response);
System.out.println("payload is " + payload);
} catch (APIException | IOException e) {
e.printStackTrace();
}
I'm having trouble with "Subscriptions" on the Apple Store. Users can purchase multiple courses. Multiple courses can have the same price and use the same "Subscriptions". Suppose I have user A, course C1 costs 200 USD (PRODUCT_ID: 200_USD), course C2 costs 200 USD (PRODUCT_ID: 200_USD). When creating a registration, the json response from apple is all in the in_app section, has the same id_product_id, it is not possible to distinguish which element belongs to course C1, which element belongs to course C2. How do I know which renewal item belongs to course C1 or C2?
"in_app": [
{
"quantity": "1",
"product_id": "200_1jp",
"transaction_id": "2000000497632148",
"original_transaction_id": "2000000497632148",
"purchase_date": "2024-01-11 05:06:49 Etc/GMT",
"purchase_date_ms": "1704949609000",
"purchase_date_pst": "2024-01-10 21:06:49 America/Los_Angeles",
"original_purchase_date": "2024-01-11 05:06:57 Etc/GMT",
"original_purchase_date_ms": "1704949617000",
"original_purchase_date_pst": "2024-01-10 21:06:57 America/Los_Angeles",
"expires_date": "2024-01-11 05:21:49 Etc/GMT",
"expires_date_ms": "1704950509000",
"expires_date_pst": "2024-01-10 21:21:49 America/Los_Angeles",
"web_order_line_item_id": "2000000047929472",
"is_trial_period": "false",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED"
},
{
"quantity": "1",
"product_id": "200_1jp",
"transaction_id": "2000000497640048",
"original_transaction_id": "2000000497632148",
"purchase_date": "2024-01-11 05:22:32 Etc/GMT",
"purchase_date_ms": "1704950552000",
"purchase_date_pst": "2024-01-10 21:22:32 America/Los_Angeles",
"original_purchase_date": "2024-01-11 05:06:57 Etc/GMT",
"original_purchase_date_ms": "1704949617000",
"original_purchase_date_pst": "2024-01-10 21:06:57 America/Los_Angeles",
"expires_date": "2024-01-11 05:37:32 Etc/GMT",
"expires_date_ms": "1704951452000",
"expires_date_pst": "2024-01-10 21:37:32 America/Los_Angeles",
"web_order_line_item_id": "2000000047929473",
"is_trial_period": "false",
"is_in_intro_offer_period": "false",
"in_app_ownership_type": "PURCHASED"
},
Hello,
I want to provide in- app subscription option for my application services inside other apps.
Is this possible? Does Apple in app subscription guidelines allow this? Reading the guidelines this use case is not clear.
UseCase: I am embedding the services that my app provides inside other 3rd party apps (embed my application Framework/library within the other application) but want to provide in-app subscription option to use services provided by my library. Users can purchase this service by subscribing to monthly subscription option using Apple in-app subscription. Because there are multiple other 3rd party applications that will include my library and since these 3rd party applications in some cases are not from the same developer account I do not want to use individual in-app subscription for each application. Instead, I am looking for a way to create in-app subscription from my main application and provide the same purchase option within all other 3rd party apps that embed my application library.
Can this be done? Does Apple in-app subscription allow this use case?
I have an app that I distribute to beta testers via TestFlight. The app has auto-renewable subscriptions. In the sandbox environment the subscriptions expire at an accelerated rate; every hour. They renew up to 12 times. This means that my testers have to re-subscribe every 12 hours.
Is there a way to make the sandbox operate at realtime for certain users?