My question is simple, I do not have much experience in writing swift code, I am only doing it to create a small executable that I can call from my python application which completes Subcription Management.
I was hoping someone with more experience could point out my flaws along with giving me tips on how to verify that the check is working for my applicaiton. Any inight is appreciated, thank you.
import Foundation
import StoreKit
class SubscriptionValidator {
static func getReceiptURL() -> URL? {
guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL else {
print("No receipt found.")
return nil
}
return appStoreReceiptURL
}
static func validateReceipt() -> Bool {
guard let receiptURL = getReceiptURL(),
let receiptData = try? Data(contentsOf: receiptURL) else {
print("Could not read receipt.")
return false
}
let receiptString = receiptData.base64EncodedString()
let validationResult = sendReceiptToApple(receiptString: receiptString)
return validationResult
}
static func sendReceiptToApple(receiptString: String) -> Bool {
let isSandbox = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
let urlString = isSandbox ? "https://sandbox.itunes.apple.com/verifyReceipt" : "https://buy.itunes.apple.com/verifyReceipt"
let url = URL(string: urlString)!
let requestData: [String: Any] = [
"receipt-data": receiptString,
"password": "0b7f88907b77443997838c72be52f5fc"
]
guard let requestBody = try? JSONSerialization.data(withJSONObject: requestData) else {
print("Error creating request body.")
return false
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = requestBody
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let semaphore = DispatchSemaphore(value: 0)
var isValid = false
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil,
let jsonResponse = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let status = jsonResponse["status"] as? Int else {
print("Receipt validation failed.")
semaphore.signal()
return
}
if status == 0, let receipt = jsonResponse["receipt"] as? [String: Any],
let inApp = receipt["in_app"] as? [[String: Any]] {
for purchase in inApp {
if let expiresDateMS = purchase["expires_date_ms"] as? String,
let expiresDate = Double(expiresDateMS) {
let expiryDate = Date(timeIntervalSince1970: expiresDate / 1000.0)
if expiryDate > Date() {
isValid = true
}
}
}
}
semaphore.signal()
}
task.resume()
semaphore.wait()
return isValid
}
}
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
We use the Transaction Info API for Apple Pay verification, but due to a surge in player payment requests during major game version updates, we've encountered a large backlog of orders and are unable to process all verifications in time. We're wondering if anyone knows the process for requesting an increase in the QPS limit? QAQ
Topic:
App & System Services
SubTopic:
StoreKit
I have a macOS app where I am testing the in app subscription purchase using Xcode.
When I trigger the purchase using this code, a dialog is shown in the upper right of my screen, which cannot be moved and just has a "cancel" button. How can I accept the test purchase?
let result = try await product.purchase()
I am testing the subscription flow in my iOS app. Initially, everything was working fine when following the official StoreKit and sandbox testing documentation. After a successful subscription, the “You’re all set” popup always displayed the environment as “sandbox.” However, after some changes, possibly upgrading macOS to the latest version, upgrading Xcode, or regenerating certificates, I can no longer connect to the sandbox testing environment. The subscription success popup now always shows the environment as “xcode.”
By default, the iOS app should run in the sandbox on macOS, so I didn’t set the “Enable App Sandbox” option to “Yes” in the Xcode build settings. When I try enabling it, Xcode throws the following error:
“Failed to verify code signature of /var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.n3J0tr/extracted/Payload/XXXX.app : 0xe8008015 (A valid provisioning profile for this executable was not found.) Please ensure that your app is signed by a valid provisioning profile.”
Additionally, if “Enable App Sandbox” is set to “No,” the app installs successfully on a real device, but there is no prompt to trust an untrusted developer certificate, which usually appears for such certificates.
I’m not sure if this information will be useful to others, but I’ve been stuck on this issue for a while, and it’s preventing me from moving forward with my work. Any help to resolve this would be greatly appreciated. Thank you!
Topic:
App & System Services
SubTopic:
StoreKit
The application is developed with Xamarin Framework and it is live now.
The customer installed the app and purchased the annual subscription.
And for some reason, they uninstall and reinstall the application on the same device.
Now user wants to restore the subscription. In the application, there is an option to Restore the subscription. But restore API not return purchase details.
But when clicking the subscription button instead of restoring the subscription, it says you subscribed to this plan".
is there any possibility of not getting VerifyRecipt even after a successful purchase?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store
StoreKit Test
App Store Connect
I am currently using StoreKit2 to set up the in-app purchase subscription flow, and I have already configured the subscription products in App Connect. I created a StoreKit Configuration file in Xcode and used it in the scheme. However, after completing the purchase, the transaction.jsonRepresentation data returns a transactionId of 0. After checking the documentation, I found that I need to disable the StoreKit Configuration and enable Sandbox Testing. But after disabling the StoreKit Configuration, I can't retrieve the real product data using Product.products(for: productIds). I can confirm that the ProductId I provided is real and matches the data configured in App Connect. Could you please help me identify the issue?
I read the documentation and it told I had to prepare the product on App Store connect and once it is at the state "Ready to submit" I could access it on a phone where I am connected with an Icloud account in the developper list of the apple development account.
This is what I've done but when I try to fetch in my flutter code the product with the id I set in App Store connect it says "No product found"
Here is where I fetch the product:
Future purchaseProduct(String productId) async {
try {
Set<String> _pIds = {productId};
final ProductDetailsResponse response =
await _iap.queryProductDetails(_pIds);
if (response.productDetails.isEmpty) {
throw 'Product not found';
}
final ProductDetails productDetails = response.productDetails.first;
final PurchaseParam purchaseParam =
PurchaseParam(productDetails: productDetails);
_iap.buyConsumable(purchaseParam: purchaseParam);
} catch (e) {
Services.debugLog('Error purchasing product: $e');
throw e;
}
}
I checked the product ID and it does not seems to be the problem. Is there some other steps I need to do ?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store Connect
In-App Purchase
Debugging
While reviewing the Apple Documentation, I came across a potential issue in one of the examples that I believe is worth addressing.
The example appears to compare strings instead of integers, which could lead to unexpected behavior in production environments. Specifically, in the line where originalMajorVersion (a string) is compared with newBusinessModelMajorVersion (also a string) using <:
if originalMajorVersion < newBusinessModelMajorVersion
This comparison performs a lexicographical check rather than evaluating the numerical values of the strings. As a result, strings like "10" would incorrectly be considered less than "2", which is not the desired behaviour when comparing version numbers.
I have reported this via the Feedback assistant (FB16432337) but at the time of posting this there has been no reply at all (23 days)
Supporting business model changes by using the app transaction
do {
// Get the appTransaction.
let shared = try await AppTransaction.shared
if case .verified(let appTransaction) = shared {
// Hard-code the major version number in which the app's business model changed.
let newBusinessModelMajorVersion = "2"
// Get the major version number of the version the customer originally purchased.
let versionComponents = appTransaction.originalAppVersion.split(separator: ".")
let originalMajorVersion = versionComponents[0]
if originalMajorVersion < newBusinessModelMajorVersion {
// This customer purchased the app before the business model changed.
// Deliver content that they're entitled to based on their app purchase.
}
else {
// This customer purchased the app after the business model changed.
}
}
}
catch {
// Handle errors.
}
Topic:
App & System Services
SubTopic:
StoreKit
I am trying to implement in-app purchases in Apple TV.
I added a "non-consumable" product and started testing in Sandbox, but it did not work properly.
While I am trying to fetch the product from the appstore, it won't give any responses like success or failure.
So that our app gets rejected in the App Store.
Please provide me the steps to implement in-app purhcase in Apple tvos using Swift.
Note: The same code is working fine in iOS.
We are running auto-renewing subscriptions with StoreKit2 and the “get all subscription statuses” API is behaving unexpectedly.
record the originalTransactionId from the iPhone to the server side when purchasing a subscription with Storekit2.
query the get all subscription statuses API from the server side with the originalTransactionId recorded.
get all subscription statuses returns a response, but there is no data in the response that matches the originalTransactionId.
I have an error on my system because I have built my system on the assumption that all subscriptions including originalTransactionId will be returned.
Please allow me to confirm the Server Notifications V2 specification.
I am aware that if withdrawal an Apple account that has a subscription, the subscription will eventually be cancelled.
Regarding Server Notifications V2 notifications with a notificationType of EXPIRED, am I correct in thinking that they will be sent when the subscription expires even if the Apple account is withdrawal?
I've noticed that CONSUMPTION_REQUEST notifications sometimes have a signedTransactionInfo which corresponds not to the latest transaction, but to an earlier transaction in a subscription.
Is this expected? I thought signedTransactionInfo was always the latest subscription information?
Are there any other notification types for which signedTransactionInfo can be out of date?
Signed renewal info from 'Get Subscription Statuses' or in server notifications never has the offerType or offerDiscountType even when the corresponding transaction does have those values set.
Our offer is a free trial.
Do these properties refer to something different in JWSRenewalInfoDecodedPayload than they do in transactions?
I'm trying to determine whether a subscription (identified by originalTransactionId) is currently in a free trial based on server notifications. The status doesn't tell us if the subscription is currently in free trial and the signedTransactionInfo may be for an older transaction.
I am working on a banking application (includes iPhone and iPad) which includes add to wallet feature.
During the implementation I saw one document it is mentioned that for iPad app, the app must be extended to support Apple pay functionality.
Details from document says "Card Issuers with an iOS mobile banking app must support Card Issuer iOS Wallet extension functionality to enable Card issuer mobile app customers to provision new cards directly from the iOS Wallet app with all eligible Apple iOS devices. If the Card Issuer has a dedicated iPad App, that App must be extended to support Apple Pay functionality. "
Is wallet extension implementation required for Apple pay to work in iPhone and iPad?
Is wallet extension a mandatory implementation for Add to Apple Wallet feature to work and approved by Apple?
I am little confused in this.
Anyone who integrated Apple pay or done add to Apple Wallet feature recently without wallet extension faced any rejection?
Any help would be much appreciated.
Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You may be connecting to a server masquerading as a "auth-sandbox.itunes.apple.com", which threatens the security of your confidential information. "
有人遇到这个问题吗,在支付的时候提示未知错误,具体的错误信息如下:
交易失败,outTradeNo:2025022631999900326, productId:com.f6car.p0001, error:Err-or -Domain=SKErrorDomain Code=0 "发生未知错误" UserInfo={NSLocalizedDescription=发生未知错误, NSUnderlyingError=0x302f50120 {Error Domain=ASDServerErrorDomain Code=3512 "无效的应用程序外部版本。" UserInfo={NSLocalizedFailureReason=无效的应用程序外部版本。}}}
寻求解决方案,感谢.
Hello. I launched my new mobile app Drop Pin Location to promote your business or brand on the go, on January 12, 2023. How can i market and campaign to get more daily users?
Topic:
App & System Services
SubTopic:
StoreKit
Tags:
Subscriptions
App Store Server Notifications
Marketing
I ran into a problem. When using Storekit1 to purchase an SKU, the user payment was successful, but StoreKit1 did return paymentCancelled to my App. I would like to know under what circumstances this problem may occur? How do I fix it? Thank you
users download app with Streamlined Purchasing ,but the logic of checking subscription doesn't work. there the code:
func checkSubscriptionStatus() async {
for await entitlement in Transaction.currentEntitlements {
guard case .verified(let transaction) = entitlement else { continue }
if transaction.productID == monthlyProductID || transaction.productID == yearlyProductID {
if transaction.revocationDate == nil && !transaction.isUpgraded {
let activeSubscribed = transaction.expirationDate ?? .distantFuture > .now
if activeSubscribed {
hasActiveSubscription = activeSubscribed
// other operation
}
}
}
}
}
A customer of mine signed up for a free trial. I got a apple server notification with notification type DID_RENEW. What does that mean? Does that mean that they will be charged the subscription price now?