SwiftyStoreKit icon indicating copy to clipboard operation
SwiftyStoreKit copied to clipboard

Verify Receipt fails for Apple Reviewer and keeps on rejecting app (Error: SwiftyStoreKit.ReceiptError error 2).

Open abhirav opened this issue 4 years ago • 28 comments

Bug Report

After successful purchase, Verify Receipt Works for us, but not for Apple reviewer. They are getting below error and keeps on rejecting app. (SwiftyStoreKit.ReceiptError error 2).

To Reproduce

SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
                    if case .success(let receipt) = result {
                        ...
                        
                    } else if case .error(let error) = result {
                        // Always comes here and shows error
                    }

Expected behavior Should come in success block and proceed further. Purchase is successful, verify receipt is failing.

Platform Information

  • OS: iOS 13.5.1
  • Purchase Type: auto-renewable subscription
  • Environment: Sandbox and app review
  • SwiftyStoreKit version: 0.13.3

Screenshots image

abhirav avatar Jun 15 '20 04:06 abhirav

Was there any update to this, I am having a similar issue and can not reproduce what the Apple reviewers are seeing.

wicheda avatar Jun 16 '20 07:06 wicheda

It appears that a lot of developers are having the same issue with the library. I'm going to start looking into this and will get back to you as soon as I have more information. In the meantime, community investigation and support would be mighty helpful.

Sam-Spencer avatar Jun 16 '20 14:06 Sam-Spencer

Iam having the same issue. Even I didn't make any changes on my code before regarding auto renewable subscription. It is working on my phone, but they are getting this error from their side. I hope this is solved the soonest

sam961 avatar Jun 16 '20 20:06 sam961

This is urgent and high priority issue. I'm getting consistent app rejections from Apple because they are unable to complete the receipt verification due to SwiftyStoreKit.ReceiptError error 2 requestBodyEncodeError.

The error occurs right after the following IAP popup during the receipt verification. attachment-5650459476598387394Screenshot-0616-075457 (1)

My guess is that the error is in the URLs for AppleReceiptValidator:

	public enum VerifyReceiptURLType: String {
		case production = "https://buy.itunes.apple.com/verifyReceipt"
		case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
	}

Apple does not want the client/app to call the App Store server verifyReceipt endpoint directly. More information: https://developer.apple.com/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store

What is interesting is that the error does not occur during debug/testing and the AppStore production environment. Only during the app review Sandbox environment.

I even downgrade to SwiftyStoreKit 0.15.0 and I still get the same Apple app rejection.

wimbledon avatar Jun 17 '20 05:06 wimbledon

@Sam-Spencer, @RomanPodymov, do you have any ideas?

wimbledon avatar Jun 17 '20 05:06 wimbledon

This is quite a perplexing case, my only suspicion is that perhaps Apple blocks or has a different internal redirect to the sandbox verify service for its testers?

This was a critical issue for an app that needed an update, so for now I have used local validation with:

https://github.com/tikhop/TPInAppReceipt

which seems to work nicely. I haven't submitted yet but am hopeful it will go through.

wicheda avatar Jun 17 '20 11:06 wicheda

Hello everybody. I get the following response from Apple when they try to verify subscriptions:

"We found that your in-app purchase products exhibited one or more bugs when reviewed on iPhone running iOS 13.5.1 on Wi-Fi."

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."

Screenshots image

I tried using SwiftyStoreKit 0.15.0 and 0.16.0, the result is identical - rejected.

AdAvAn avatar Jun 17 '20 14:06 AdAvAn

I have the same problem!

I still get information from apple:

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.

janczakb avatar Jun 18 '20 18:06 janczakb

Is there a workaround already? I need this urgently

janczakb avatar Jun 19 '20 09:06 janczakb

@janczakb , we needed an update approved urgently, I have managed to get Apple approval now by verifying receipts locally. In other words you can use SwiftyStoreKit for everything else except receipt verification.

This is quite a perplexing case, my only suspicion is that perhaps Apple blocks or has a different internal redirect to the sandbox verify service for its testers?

This was a critical issue for an app that needed an update, so for now I have used local validation with:

https://github.com/tikhop/TPInAppReceipt

which seems to work nicely. I haven't submitted yet but am hopeful it will go through.

wicheda avatar Jun 19 '20 09:06 wicheda

@wicheda Could you send me an example of use? I will be very grateful

janczakb avatar Jun 19 '20 09:06 janczakb

Hi @janczakb , it depends exactly what verification logic you want. But you need to add the TPInAppReceipt library to your project as per their instructions. Then instead of calling SwiftyStoreKit.verifyReceipt , you add a new method, something like:

func validate() {
        // get the receipt from SwiftyStoreKit and validate
        if let receiptData = SwiftyStoreKit.localReceiptData {
            do {
                let receipt = try InAppReceipt.receipt(from: receiptData)
                validateReceipt(receipt: receipt)
            }
            catch {
                // error creating receipt from data?
            }
        }
        else {
            //no receipt, hence force a refresh
            SwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in
                switch result {
                case .success(let receiptData):
                    do {
                        let receipt = try InAppReceipt.receipt(from: receiptData)
                        self.validateReceipt(receipt: receipt)
                    }
                    catch {
                        print("Error validating receipt")
                    }
                case .error(let _):
                    print("Error fetching receipt")
                }
            }
        }
    }
    
    func validateReceipt(receipt:InAppReceipt) {
        
        // Verify all at once
        do {
            try receipt.verify()
        } catch IARError.validationFailed(reason: .hashValidation)
        {
            // Do smth
        } catch IARError.validationFailed(reason: .bundleIdentifierVefirication)
        {
            // Do smth
        } catch IARError.validationFailed(reason: .signatureValidation)
        {
            // Do smth
        } catch {
            // Do smth
        }
        
        // Check whether receipt contains any purchases
        let hasPurchases = receipt.hasPurchases

        // All auto renewable `InAppPurchase`s,
        let purchases: [InAppPurchase] = receipt.autoRenewablePurchases

        // all ACTIVE auto renewable `InAppPurchase`s,
        let activePurchases: [InAppPurchase] = receipt.activeAutoRenewableSubscriptionPurchases
        
        // Add your own logic here around exactly what you want to check
    }

So you'll need to call validate when you want to check after a purchase / restore and at the bottom of validateReceipt add whatever checking logic your app needs. I hope that helps.

wicheda avatar Jun 19 '20 09:06 wicheda

When testing my application by Apple testers, the "SwiftyStoreKit.purchaseProduct" method returns an "unknown" error

janczakb avatar Jun 19 '20 10:06 janczakb

Hi @janczakb , it sounds like you more likely have issues with your products set up then, rather than this specific issue around sandbox receipt validation? SwiftyStoreKit.purchaseProduct is working for me for Apple testers (or certainly was yesterday when it was approved).

wicheda avatar Jun 19 '20 10:06 wicheda

Guys, I had implemented the verified receipt from the server side and apple accepted it now....it seems now they are blocking verifying receipt from mobile

sam961 avatar Jun 22 '20 19:06 sam961

Please take a look at issue #550. Your help with this project is dearly needed! @abhirav, @sam961, @wicheda, @AdAvAn, @wimbledon

Sam-Spencer avatar Jun 23 '20 02:06 Sam-Spencer

I have a different case. I'm not verifying the receipt locally, but do it in the server but got the error SwiftyStoreKit.ReceiptError error 1 when the app is reviewed by Apple testers. I think this error is occurred in fetchReceipt() before the app sends the receipt to the API server for validation check.

cclaflin89 avatar Jul 07 '20 15:07 cclaflin89

I want to know if this problem is solved?

I have a different case. I'm not verifying the receipt locally, but do it in the server but got the error SwiftyStoreKit.ReceiptError error 1 when the app is reviewed by Apple testers. I think this error is occurred in fetchReceipt() before the app sends the receipt to the API server for validation check.

Hello,I have encountered this problem too, is it finally solved?

2856571872 avatar Sep 29 '20 03:09 2856571872

Why is SwiftyStoreKit.localReceiptData empty after successful purchase

I don’t see the problem on iOS, but it often appears on MacOS

2856571872 avatar Sep 29 '20 03:09 2856571872

`func buyProduct(product: SKProduct,orderId: String) {

    SwiftyStoreKit.purchaseProduct(product, quantity: 1, atomically: true, applicationUsername: orderId, simulatesAskToBuyInSandbox: false) { [weak self] (result) in
        switch result {
        case .success(let purchase):
            print("Purchase Success: \(purchase.productId)")
            self?.uploadReceipt(receiptData: nil)
        case .error(let error):
            var errorStr = error.localizedDescription
            switch error.code {
            case .unknown: errorStr = "Unknown error. Please contact support".languageSet()
            case .clientInvalid:
                errorStr = "The payment is cancelled".languageSet()
            case .paymentCancelled:
                errorStr = "Not allowed to make the payment".languageSet()
            case .paymentInvalid:
                errorStr = "The purchase identifier was invalid".languageSet()
            case .paymentNotAllowed:
                errorStr = "The device is not allowed to make the payment".languageSet()
            case .storeProductNotAvailable:
                errorStr = "The product is not available in the current storefront".languageSet()
            case .cloudServicePermissionDenied:
                errorStr = "Access to cloud service information is not allowed".languageSet()
            case .cloudServiceNetworkConnectionFailed:
                errorStr = "Could not connect to the network".languageSet()
            case .cloudServiceRevoked:
                errorStr = "User has revoked permission to use this cloud service".languageSet()
            default: print((error as NSError).localizedDescription)
            }
            
            let customerError = NSError(domain: "generateOrder", code: -1000, userInfo: [NSLocalizedDescriptionKey : errorStr])
            self?.failBlock?(customerError)
        }
    }
}


/// upload receipt to the server
func uploadReceipt(receiptData: Data?) {
    let data = (receiptData == nil) ? SwiftyStoreKit.localReceiptData : receiptData
    let receiptString = data?.base64EncodedString(options: []) ?? ""
    if !receiptString.isEmpty {
            // ... upload receipt
    }else {
        print("## receipt is empty")
        SwiftyStoreKit.fetchReceipt(forceRefresh: true) { [weak self] (result) in
            switch result {
                case .success(let receiptData):
                    self?.uploadReceipt(receiptData: receiptData)
                case .error(let error):
                    self?.failBlock?(error as NSError)
            }
        }
    }
    
}

`

This is my code

2856571872 avatar Sep 29 '20 03:09 2856571872

I think the best solution to this is to remove this buggy library and create a wrapper by yourself.

exclucive avatar Oct 23 '20 04:10 exclucive

Today mine app was also rejected because of this reason.

attachment Screenshot-0418-183246

Zeeshan0075 avatar Apr 19 '21 20:04 Zeeshan0075

Anyone so far got the solution? am facing the same issue. What am doing is if recceiptURL is not existing, I send a force fetch receipt to the apple server. But my build got rejected with prompting message "Receipt Error 2". The mentioned in the review message that first I need to hit to the production server and if that fails then I should go for sandbox server. Cannot produce the issue in local build and on testFlight. Anyone got the solution..?

Zulqarnain-tech avatar Dec 27 '21 13:12 Zulqarnain-tech

Bug Report

After successful purchase, Verify Receipt Works for us, but not for Apple reviewer. They are getting below error and keeps on rejecting app. (SwiftyStoreKit.ReceiptError error 2).

To Reproduce

SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
                    if case .success(let receipt) = result {
                        ...
                        
                    } else if case .error(let error) = result {
                        // Always comes here and shows error
                    }

Expected behavior Should come in success block and proceed further. Purchase is successful, verify receipt is failing.

Platform Information

  • OS: iOS 13.5.1
  • Purchase Type: auto-renewable subscription
  • Environment: Sandbox and app review
  • SwiftyStoreKit version: 0.13.3

Screenshots image

Did you get the solution? am facing the same issue.

Zulqarnain-tech avatar Dec 27 '21 13:12 Zulqarnain-tech

Screenshot-1004-104856

I am facing this issue. please help me

shahzadhussainios avatar Oct 06 '22 10:10 shahzadhussainios

Why is SwiftyStoreKit.localReceiptData empty after successful purchase

I don’t see the problem on iOS, but it often appears on MacOS

Did you get the solution? am facing the same issue.

shls77cxl avatar Apr 07 '24 02:04 shls77cxl