I'm having an issue with In-App purchases on iOS. I have 5 In-App Purchases in a game, all of which work exactly as expected when tested on an iPhone 6 (iOS 8.3). When I go to test on iPad Air 2 (iOS 8.2), all IAPs fail immediately. Has anyone else experienced this problem? Is there some code that is specific to iPad that I have to add?
EDIT: Strangely, updating the iPad to iOS 8.3 fixed the problem. Any ideas as to why this issue is occurring? Should I change my app to only support iOS 8.3 and above?
To test the app, I'm using TestFlight, the same network connection, and the same Apple ID.
The code I'm using for In-App Purchases is Below:
func inApp() {
if (SKPaymentQueue.canMakePayments())
{
var productID:NSSet = NSSet(object: product_id);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as Set<NSObject>);
productsRequest.delegate = self;
productsRequest.start();
}else{
displayAlert()
}
}
func buyProduct(product: SKProduct){
var payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}
func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
var count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as! SKProduct
if (validProduct.productIdentifier == product_id) {
println(validProduct.localizedTitle)
println(validProduct.localizedDescription)
println(validProduct.price)
buyProduct(validProduct);
} else {
println(validProduct.productIdentifier)
}
} else {
displayAlert()
}
}
func request(request: SKRequest!, didFailWithError error: NSError!) {
self.displayAlert()
}
func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
if product_id == "com.shv.FrenzyTenLives" {
defaults.setInteger(10, forKey: "totalLives")
} else if product_id == "com.shv.FrenzyFiveLives" {
defaults.setInteger(5, forKey: "totalLives")
} else if product_id == "com.shv.FrenzyInfiniteLives" {
defaults.setBool(true, forKey: "infiniteLives")
} else if product_id == "com.shv.FrenzyShield" {
defaults.setInteger(5, forKey: "shieldValue")
} else if product_id == "com.shv.FrenzyRemoveAds" {
defaults.setBool(true, forKey: "adsRemoved")
adBanner.hidden = true
}
break;
case .Failed:
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
self.displayAlert()
break;
case .Restored:
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
if product_id == "com.shv.FrenzyTenLives" {
defaults.setInteger(10, forKey: "totalLives")
} else if product_id == "com.shv.FrenzyFiveLives" {
defaults.setInteger(5, forKey: "totalLives")
} else if product_id == "com.shv.FrenzyInfiniteLives" {
defaults.setBool(true, forKey: "infiniteLives")
} else if product_id == "com.shv.FrenzyRemoveAds" {
defaults.setBool(true, forKey: "adsRemoved")
adBanner.hidden = true
}
break;
default:
break;
}
}
}
}
Usually, when one device will not perform in app purchase, it's a setting under restrictions.
If you've checked and disabled that restriction or all restrictions - you can often fix this by signing out of the App Store entirely and powering down the device. When it starts cleanly, you can log in again. When you sign in, be sure to buy something (a free song of the week, a free app or even a paid app) to get through the verification questions. Once that's done, recheck an IAP (in-app purchase) to be sure things are functional.
As a last resort, you might need to contact Apple Support for Apple ID - but most times you can fix this without needing their help.
You should check the following things.
Make sure you can answer “Yes” to each of these questions:
Have you enabled In-App Purchases for your App ID?
Have you checked Cleared for Sale for your product?
Does your project’s .plist Bundle ID match your App ID?
Have you generated and installed a new provisioning profile for the new App ID?
Have you configured your project to code sign using this new provisioning profile?
Are you using the full product ID when when making an SKProductRequest?
Have you waited several hours since adding your product to iTunes Connect?
Have you tried deleting the app from your device and reinstalling?
Is your device jailbroken? If so, you need to revert the jailbreak for IAP to work.
If you answered “No” to any one of these questions, there’s your problem.
You should visit following links definitely you will get solution.
https://www.innofied.com/in-app-purchase-working-ios-solution/
Without a definition of "fail immediately" my best guess is that you were not logged in to a valid sandbox testing account on the iPad and in the midst of updating the iOS version this got reconciled, which is why it works on 8.3.
The easiest way to get IAP testing to work is to log out of iTunes:
Settings -> App and iTunes Store -> Tap apple ID and log out.
Once you've logged out, try to make an IAP and you will be prompted to log in - once you enter valid test account credentials, the IAP will download - if it doesn't, post your log files here. Note that you can find or create testing accounts inside iTunes Connect.
IAP is an extremely complicated subject with numerous points of failure - knowing that your IAPs work on one device but not another points to a configuration issue specific to the device on which the IAP failed.
Related
I have added auto renewable subscription to my iOS app. I have used a sandbox user to test the app and it worked fine. After that I logged out of the previous sandbox account and logged in with another sandbox account. Now my app sends receipts with two original transaction ids to validate from the server. It seems like my previous sandbox user data has not completely wiped off. Does anyone else experiencing the same issue? Any thoughts on this?
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction: AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchased, self.productToPurchase, trans)
}
case .failed:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
let errorCode = (trans.error as? SKError)?.code
if (errorCode == .paymentCancelled) {
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchaseCancelled, self.productToPurchase, trans)
}
} else {
if let completion = self.purchaseProductcompletion {
completion(PurchaseHandlerStatus.purchaseFailed, self.productToPurchase, trans)
}
}
case .restored:
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
totalRestoredPurchases += 1
default:
break
}
}
}
}
To avoid this annoying issue, you should finish all pending transactions from the old sandbox account.
When you debug in-app purchases and/or change Apple ID too often, some transactions may stay in the queue (for example, if you broke execution until transaction is finished). And these transactions will try to finish at every next app launch. In this case you may see a system alert prompting to enter your Apple ID password and this may also lead to sandbox receipt will not immediately update/ may not match logged in sandbox account.
To fix this issue, make sure you finish all pending transactions when launching the app.
SKPaymentQueue.default().finishTransaction(transaction)
I have never integrate IAP before, I am trying to integrate IAP in my iOS App first time, I have search lots of articles on internet, I followed all of them to resolve my issue, but I don't have any idea how to solve it.
I have full filled Agreement Tax and Banking, now Its status is Active.
I have added new user in sandbox tester.
I have choose Non-Renewing Subscription in features menu, now It's status is Ready To Submit,
I have again and again removed application in device (iPhone XR and iPad 5).
I have been waited for a long time, approx 5 to 7 days for activation.
I have already enabled In-App Purchase flag from capabilities.
My Development Profile is also consider with In-App Purchase.
I have sign out (iTunes account) my all device (iPhone XR and iPad 5).
My Product ID is relevant with my Bundle ID.
My Code is below...
func IAPintegrate(){
if (SKPaymentQueue.canMakePayments()) {
let productId : NSSet = NSSet(objects: IAP_DEV_PRODUCT_ID)
productsRequest = SKProductsRequest(productIdentifiers: productId as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
}
I have also added Delegate methods,
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print(response.products)
let count : Int = response.products.count
if (count>0) {
let validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == IAP_DEV_PRODUCT_ID) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
self.buyProduct(product: validProduct)
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}
still my product count is always 0 please help me what should i do ?
Check whether you are getting any product identifier strings that is invalid in didReceive response method.
print("Invalid product identifiers \(response.invalidProductIdentifiers)")
The source of the problem is shown in your third screen shot. Your in-app purchases are Ready To Submit. That means they have not yet been submitted and approved. Therefore, as far as the store is concerned, they don't exist.
Hello I am having getting my App published on the App Store as they keep insisting my in-app purchases are not set up correctly.
Upon testing them myself in the sandbox, everything worked correctly.
This is the message they sent me, I pasted my code below.
Thank you for taking the time to help me out!
Guideline 2.1 - Performance - App Completeness
We found that your in-app purchase products exhibited one or more bugs
when reviewed on iPhone and iPad running iOS 12 on Wi-Fi.
Specifically, your in app purchase buttons do not work.
Next Steps
When validating receipts on your server, your server needs to be able
to handle a production-signed app getting its receipts from Apple’s
test environment. The recommended approach is for your production
server to always validate receipts against the production App Store
first. If validation fails with the error code “Sandbox receipt used
in production,” you should validate against the test environment
instead.
class IAPService: NSObject {
private override init() {}
static let shared = IAPService()
var products = [SKProduct]()
let paymentQueue = SKPaymentQueue.default()
func getProducts() {
let products: Set = [IAPProduct.consumable.rawValue,
IAPProduct.nonConsumable.rawValue]
let request = SKProductsRequest(productIdentifiers: products)
request.delegate = self
request.start()
paymentQueue.add(self)
}
func purchase(product: IAPProduct) {
for p in products {
if p.productIdentifier == product.rawValue {
let payment = SKPayment(product: p)
paymentQueue.add(payment)
print("Adding product to payment queue")
}
}
}
func restorePurchase() {
print("Restoring purchases")
paymentQueue.restoreCompletedTransactions()
}
func givePurchasedProduct(productID: String) {
if productID.range(of: "Zap") != nil {
NotificationCenter.default.post(name: Notification.Name.init("zapPurchased"), object: nil)
} else if productID.range(of: "Ads") != nil {
NotificationCenter.default.post(name: Notification.Name.init("noAdsPurchased"), object: nil)
}
}
}
extension IAPService: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
self.products = response.products
for product in response.products {
print(product.localizedTitle)
}
}
}
extension IAPService: SKPaymentTransactionObserver {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
print(transaction.transactionState.status(), transaction.payment.productIdentifier)
switch transaction.transactionState {
case .purchasing, .deferred: break // do nothing
case .purchased:
queue.finishTransaction(transaction)
givePurchasedProduct(productID: transaction.payment.productIdentifier)
case .restored:
self.restorePurchase()
queue.finishTransaction(transaction)
case .failed:
queue.finishTransaction(transaction)
}
}
}
}
extension SKPaymentTransactionState {
func status() -> String {
switch self {
case .deferred:
return "deferred"
case .failed:
return "failed"
case .purchased:
return "purchased"
case .purchasing:
return "purchasing"
case .restored:
return "restored"
}
}
}
App review is very strict when it comes to Apple. Speaking from experience, I have had this problem many times. Your code seems fine to me till it goes to the givePurchasedProduct function.
Okay so things i noticed:
Your app processes the payment and we get return "purchased" if nothing goes wrong
If the case was case .purchased: then we invoke the givePurchasedProduct
On your function. you separate the purchase to see if it's either a Zap purchase or it was to remove the ads
However. this line is confusing me-- Why would you use range when contains where introduced recently.
if productID.contains("Zap") {
// No Zapp? it has to be Ads then
NotificationCenter.default.post(name: Notification.Name.init("zapPurchased"), object: nil)
} else {
NotificationCenter.default.post(name: Notification.Name.init("noAdsPurchased"), object: nil)
}
Side notes. You might have forgot:
To import Foundation
I don't know what goes behind the notification observers since the code is not included. But. It's not delivering
There's more to it. Receipt Validating is a headache, but when it's needed. It's relaxation and more security to your app.
If you're validating the receipt. these question and it's answers helped me a lot. please see:
Implementing Receipt Validation in Swift 3
Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+
Bonus. With SwiftyStoreKit. Receipt validating is just like tapping a button:
Use this method to (optionally) refresh the receipt and perform validation in one step.
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in
switch result {
case .success(let receipt):
print("Verify receipt success: \(receipt)")
case .error(let error):
print("Verify receipt failed: \(error)")
}
}
Now on the other hand. to the reviewers the purchased content is not delivering. So they think it's purchase validating.
How do you validate the purchase? deliver the content? please update your question. I'm sure i'll be helpful
Good Luck
I think there is no problem with your iOS code. From the Apple's response, they say that, your server is pointing to production environment of Apple InApp purchase and validating the receipts received from test environment of Apple InApp purchase used within App.
Apple has 2 environments for InApp purchases - Test & Production. Both the environments behave same. When you run the app on your iPhone to test by your QA or while you are debugging, it connects to Test environment. You are not charged in real when using Test environment. But the receipts generated are almost same as that of real production environment
Now when you submit the app to store, it will automatically make purchases from Production environment. Users are charged and real receipts are generated.
Your app is sending those receipts to server I think and your server is using the wrong InApp server environment to verify the receipts.
On your server make sure that the Apple InApp purchase environment URL is appropriately picked based on your InApp purchase receipt. If you or your team is testing the app, your server has to use Test URL and when the app is submitted to production, your server has to use Production URL of InApp Purchase.
I am using Xcode 8.0, Swift 3.0 and testing in app purchases in my iPad. I want to test in app purchases using sandbox user.
There is no account added in device's Setting
The Problem is I am not getting product list in response of product request code.
Please take a look on my code:
let PRODUCT_ID_MY_PRODUCT = "com.company.ProjectName.MyProduct"
// The ProducID in this code and ProducID on iTunes are the SAME. ✔️
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if productID == nil {
productID = PRODUCT_ID_MY_PRODUCT
}
SKPaymentQueue.default().add(self)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
startPurchases()
}
func startPurchases() {
if (SKPaymentQueue.canMakePayments())
{
let productIDs = NSSet(object: self.productID!)
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productIDs as! Set<String>)
productsRequest.delegate = self
productsRequest.start()
}
}
// Delegate Methods for SKProductsRequest
func productsRequest (_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
let count : Int = response.products.count
// THE PROBLEM IS HERE.. I AM GETTING COUNT IS ZERO.. MEANS response.products returning null ARRAY
if (count>0) {
let validProducts = response.products
for aProduct in validProducts {
print(aProduct.productIdentifier)
}
} else {
DispatchQueue.main.async(execute: {
UIAlertView(title: "Purchase !", message: "Product not available", delegate: nil, cancelButtonTitle: "OK").show()
return
})
}
}
So..... That's the problem: I am getting response.products null (no data in array) so Please help me to find the solution. You can see the comments in code:
// THE PROBLEM IS HERE.. I AM GETTING COUNT IS ZERO.. MEANS response.products returning null ARRAY
I created products over iTunes Connect. You can see the image below. All the products are in "Ready to Submit" state.
There is some warning on iTunes
Your first In-App Purchase must be submitted with a new app version.
Select it from the app’s In-App Purchases section and click Submit.
Once your binary has been uploaded and your first In-App Purchase
has been submitted for review, additional In-App Purchases can be
submitted using the table below.
And
I also created Sendbox user for testing In-App Purchases. See the image below:
Did I miss something? Or what is the error? And where is error? I want to test in app purchases using sandbox user
I fixed this. There are some points need to careful about. See below:
Make sure your developer account did the paid application contract. see image below:
Create Products on the iTunes Connect.
Implement In-App-Purchases code and configuration settings.
Create one build with Distribution profile.
Upload build on store. Add build to current version. Add In-App-Purchases to the version on iTunes Connect.
Then try to test, if still not then submit app once then cancel it. then after try to test on your device.
Make sure when you test with sandbox user, you need to sign-out from your already logged in account from device settings, and login with sandbox ID.
some screenshots might be helpful.
please check these settings
capabilities --> In-App purchase --> set to "ON"
and at developer.apple.com--> enable In-App purchase for App ID.
and please test app on Device instead of simulator.
I have been google this issue for couple hours. I have attempted something like:
confirm Bundle Identifier and product id are absolutely correct
remove and reinstall app on both Simulator and real devices
I have try both 'productID' and 'com.company.project.productID'
The IAP in iTunes connect should be configured correct as well
The only thing I'm not sure is that I haven't fill Banking and Tax things in my iTunes connect. Actually, I just filled the Banking info, but it needs 24 hours to update.
My codes look like this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Set IAPS
if(SKPaymentQueue.canMakePayments()) {
println("IAP is enabled, loading")
var productID:NSSet = NSSet(objects: "ProductID")
var request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as Set<NSObject>)
request.delegate = self
request.start()
} else {
println("please enable IAPS")
}
}
func productsRequest(request: SKProductsRequest!, didReceiveResponse response: SKProductsResponse!) {
println("product request")
var myProduct = response.products
for product in myProduct {
println("product added")
println(product.productIdentifier)
println(product.localizedTitle)
println(product.localizedDescription)
println(product.price)
list.append(product as! SKProduct)
}
}
This code is from a example, it was work very well in my simulator. But when I replace the ProductID with my own productID (of course bundleID as well), the SKProductsrequest become return nothing.
I have no idea why, hope some one can help me. Thx!
I was having this issue all week - no matter what I tried code wise and tweaking my build & provisioning settings, I got no products back.
What finally fixed it for me was changing the bundle id for my app xcode from company.appname to com.company.appname. Meanwhile you want to refer to your in-app purchase IDs withthe format com.company.appname.thingforpurchase1 etc.
In trying to solve this, I re-did the process of setting up automatic provisioning within the project and build settings & I re-added my developer account to xcode again. After doing this, the above syntax worked for me. Note that on itunes connect, my apps' bundle ID is still specified as just company.appname, but for some reason in xcode it needs the com. at the start too.
There is one other thing i've had to do differently with Swift 2.0 vs Swift 1.x - the compiler will no longer accept the product id's are no longer an NSSet - instead the request seems to want identifiers be given as a Set<String> - see this in the example below.
Note this solution was developed with and worked in swift 2.0 and xcode 7 beta.
Ex:
func requestProductInfo() {
print("about to fetch product info")
if SKPaymentQueue.canMakePayments() {
let request = SKProductsRequest(productIdentifiers:
Set(productIDs))
let productIdentifiers: NSSet = NSSet(array: productIDs) // NSset might need to be mutable
let productRequest : SKProductsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
//we set our class as its delegate (so we can handle the response)& trigger the request.
productRequest.delegate = self
productRequest.start()
print("Fetching Products");
}
else {
print("can't make purchases")
}
}
The problem was US Tax Form and Agreement. Once its approved, everything it will work.