Rejection by Apple: Guideline 3.1.1 - In-App Purchase ...

May I know your version of Corona? @luaykanaan

@614751320: build 3175

Just fyi:

I had the same notice and the same response from Apple. I sent them a note saying I had no idea what they were talking about and that I only use Apple. At least for me, they approved it with no further communication.

Got my app approved too!

I just pushed an update with a message noting the false positive about “AliPay”

and the review team accepted it after about 1 hour (from review start).

Thanks to the team of Corona and fellow developers for their feedback :wink:

Got my app approved too. 

Same problem with my app!

Apple rejected my update where I only push some bug fixes (no change to my single and only IAP).

I thought it was a mistake on their side (still waiting for a response after 48h…).

@d.mach,

Do you use 'IAP Badger" to handle your in-app purchase too ?

Maybe the plugin was updated with some bad code detected by Apple review process.

Hope this get fixed quickly!

(I’ll post the response from Apple if I get any)

Thx for your reply. I’m not using “IAP Badger”… just checking if the device is giving back “Android” “Amazon” and if not loading the Apple App Store stuff. Our tests (on device) did work fine.

Thanks for the info.

If it is not ‘IAP Badger’, I guess the problem come from Apple review process or Corona SDK then (using build 2017.3160 btw).

The same version of my app that got rejected by Apple for release was validated for beta.

I tested it and everything works just fine too… So it might be something in the core code

that got detected as “another form of payment”, idk.

Does anybody else got the same issue ?

Any more info from Corona team maybe ?

The best thing for you to do, if you are only using Apple IAP and no other payment systems is to write back to your reviewer and have them explain what is wrong and that you’re only using IAP items.

Without any details, there isn’t anything to go on.

Rob

Thanks @Rob,

So no special change on iOS IAP from Corona I guess.

Waiting for Apple reply then… (I’ll keep you updated)

I did exactly what Rob is suggesting and got back this:

Hello,

Thank you for your reply. Our team has confirmed this app contains non Apple payment methods.

To continue review, it would be appropriate to remove all non Apple payment methods, then resubmit a new binary for review.

Note: Continuing to hide functionality within your app or other dishonest acts may result in the removal of your apps from the App Store and termination of your Apple Developer Program membership and all associated memberships.

Thank you,

App Store Review

Can you post your build.settings?

settings = { splashScreen = { ios = { enable = false, image = "images/splashscreen.jpg" }, android = { enable = false, image = "images/splashscreen.jpg" } }, orientation = { default = "landscapeRight", supported = { "landscapeRight","landscapeLeft" }, }, android = { versionCode="10", versionName="1.0", UIStatusBarHidden = true, usesPermissions = { "com.android.vending.BILLING", "com.android.vending.CHECK\_LICENSE", "android.permission.INTERNET", --"android.permission.CAMERA", "android.permission.WRITE\_EXTERNAL\_STORAGE", "android.permission.ACCESS\_NETWORK\_STATE", "android.permission.READ\_PHONE\_STATE", }, }, iphone = { plist = { MinimumOSVersion = "7.0", NSPhotoLibraryUsageDescription = "This app would like to access the photo library.", NSPhotoLibraryAddUsageDescription = "This app would like to add the photo library.", UIStatusBarHidden=true, skipPNGCrush = true, UILaunchStoryboardName = "LaunchScreen", CFBundleIconFile = "Icon.png", CFBundleIconFiles = { "Icon.png", "Icon@2x.png", "Icon-60.png", "Icon-60@2x.png", "Icon-60@3x.png", "Icon-72.png", "Icon-72@2x.png", "Icon-76.png", "Icon-76@2x.png", "Icon-167.png", "Icon-Small-40.png", "Icon-Small-40@2x.png", "Icon-Small-40@3x.png", "Icon-Small-50.png", "Icon-Small-50@2x.png", "Icon-Small.png", "Icon-Small@2x.png", "Icon-Small@3x.png", }, --launch image files table UILaunchImages = { { -- iPhone 4 Portrait ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{320, 480}" }, { -- iPhone 4 LandscapeLeft ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{320, 480}" }, { -- iPhone 4 LandscapeRight ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{320, 480}" }, { -- iPhone 5 Portrait ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-568h", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{320, 568}" }, { -- iPhone 5 LandscapeLeft ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-568h", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{320, 568}" }, { -- iPhone 5 LandscapeRight ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-568h", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{320, 568}" }, { -- iPad Portrait ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-Portrait", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{768, 1024}" }, { -- iPad LandscapeLeft ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-Landscape", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{768, 1024}" }, { -- iPad LandscapeRight ["UILaunchImageMinimumOSVersion"] = "7.0", ["UILaunchImageName"] = "Default-Landscape", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{768, 1024}" }, { -- iPhone 6 Portrait ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-667h", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{375, 667}" }, { -- iPhone 6 LandscapeLeft ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-667h", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{375, 667}" }, { -- iPhone 6 LandscapeRight ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-667h", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{375, 667}" }, { -- iPhone 6 Plus Portrait ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-736h", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{414, 736}" }, { -- iPhone 6 Plus LandscapeLeft ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-Landscape-736h", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{414, 736}" }, { -- iPhone 6 Plus LandscapeRight ["UILaunchImageMinimumOSVersion"] = "8.0", ["UILaunchImageName"] = "Default-Landscape-736h", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{414, 736}" }, { -- iPad Pro Portrait ["UILaunchImageMinimumOSVersion"] = "9.0", ["UILaunchImageName"] = "Default-Portrait-1336", ["UILaunchImageOrientation"] = "Portrait", ["UILaunchImageSize"] = "{1024, 1366}" }, { -- iPad Pro Landscape Right ["UILaunchImageMinimumOSVersion"] = "9.0", ["UILaunchImageName"] = "Default-Landscape-1336", ["UILaunchImageOrientation"] = "LandscapeRight", ["UILaunchImageSize"] = "{1024, 1366}" }, { -- iPad Pro Landscape Left ["UILaunchImageMinimumOSVersion"] = "9.0", ["UILaunchImageName"] = "Default-Landscape-1336", ["UILaunchImageOrientation"] = "LandscapeLeft", ["UILaunchImageSize"] = "{1024, 1366}" }, }, CFBundleLocalizations = { "English", "German", "French", "Spanish", "Italian", "Norwegian", "Dutch", "Danish", "Swedish", "Finnish", "Russian", "Portuguese", "Turkish", "Japanese", "Chinese", "Korean", "Vietnamese", "Czech", "Polish", "Hindi" }, UIPrerenderedIcon = true, UIApplicationExitsOnSuspend = false, CFBundleDisplayName = "NAME", CFBundleName = "xxx", CFBundleIdentifier = "xxx", CFBundleVersion = "1.3", -- Version Code CFBundleShortVersionString = "1.4", -- Version Name }, }, plugins = { -- key is the name passed to Lua's 'require()' ["CoronaProvider.analytics.flurry"] = { publisherId = "com.coronalabs" }, --[[] ["plugin.google.iap.v3"] = { publisherId = "com.coronalabs", supportedPlatforms = { android=true } }, ["plugin.google.play.services"] = { publisherId = "com.coronalabs" }, --]] }, }

You are commenting out the Google IAP which shouldn’t be included anyway in an iOS build, so I’m confused by this. I’ll ask engineering.

On an unrelated note. I don’t recommend setting these in your build.settings:

CFBundleDisplayName = “NAME”,
CFBundleName = “xxx”,
CFBundleIdentifier = “xxx”,
CFBundleVersion = “1.3”, – Version Code
CFBundleShortVersionString = “1.4”, – Version Name

You should let Corona provide values for them which most are taken from the “Build” dialog. It’s too easy for a change to get missed and always having to keep the version’s up to date when we provide a safe, form-based way to manage these.

Rob

If it can help, here is my build settings:

Note: didn’t change since last Apple validation

settings = { orientation = { default = "landscapeRight", supported = { "landscapeRight", "landscapeLeft" } }, android = { usesPermissions = { "android.permission.INTERNET", "android.permission.ACCESS\_NETWORK\_STATE", --"android.permission.WRITE\_EXTERNAL\_STORAGE", "com.android.vending.BILLING", "com.android.vending.CHECK\_LICENSE", }, googlePlayGamesAppId = "XXXXXXXXXXXX", -- hidden --largeHeap = true }, iphone = { iCloud = true, xcassets = "Images.xcassets", plist = { MinimumOSVersion = "8.0", NSAppTransportSecurity = { NSAllowsArbitraryLoads = true }, UILaunchStoryboardName = "LaunchScreen", UIStatusBarHidden = true, UIPrerenderedIcon = true, -- set to false for "shine" overlay CFBundleDisplayName = "Switchero", CFBundleName = "Switchero" } }, splashScreen = { enable = false }, plugins = { ["CoronaProvider.native.popup.social"] = { publisherId = "com.coronalabs", supportedPlatforms = { android = true } }, ["CoronaProvider.native.popup.activity"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone = true } }, ["plugin.appodeal"] = { publisherId = "com.coronalabs" }, ["plugin.google.iap.v3"] = { publisherId = "com.coronalabs", supportedPlatforms = { android = true } }, ["plugin.iap\_badger"] = { publisherId = "uk.co.happymongoose" }, ["plugin.gpgs"] = { publisherId = "com.coronalabs", supportedPlatforms = { android=true } }, ["CoronaProvider.gameNetwork.apple"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true } }, ["plugin.iCloud"] = { publisherId = "com.coronalabs", supportedPlatforms = { iphone=true } } }, excludeFiles = { all = { 'Icon.png', 'Images.xcassets' }, ios = { 'Icon-\*dpi.png', 'audio/musics/\*.ogg', 'audio/sounds/\*.ogg' }, android = { 'LaunchScreen.storyboardc', 'audio/musics/\*.m4a', 'audio/sounds/\*.m4a' }, win32 = { 'Icon\*.png', 'Icon\*.icns', 'audio/musics/\*.ogg', 'audio/sounds/\*.ogg' }, macos = { 'Icon\*.png', 'Icon\*.ico', 'audio/musics/\*.ogg', 'audio/sounds/\*.ogg' } }, }

Thx Rob!

Can you please explain this a little bit more? Where do I change the Version Code or Version Name if not including it like this then?

One of the two fields comes from the build dialog:

The “Version” provides one of the two values. The other, which is required to be unique, we make up a safe, unique value. The field from the dialog is the visual version to the user in the store.

Other values are pulled from the Application Name and the provisioning profile.  As long as you update your public version number in the build dialog, this minimizes the work you have to do.

Rob

Regarding the problem I noticed we have called the purchase like this in the code:

if system.getInfo("platformName") == "Android" then                             \_G.store.purchase(product)                         else                             \_G.store.purchase(product)                         end

but it should be this:

if system.getInfo("platformName") == "Android" then                             \_G.store.purchase(product)                         else                             -- Apple                             \_G.store.purchase({product})                         end

As you can see the brackets for the Apple purchase were missing. Could this maybe cause the problem?

We tested the first code on device and everything worked fine, so we missed this.

What do you think?

@d.mach: no, I don’t believe it is. I recently (about a month ago) changed my iap code and removed the braces and it is working fine on both ios and android. The docs also has them without braces now. So, as said, i don’t believe it is.

However, i am preparing for an ios update next week. To avoid a similar rejection, i followed @rob’s advice and created two separate build.settings files (one for each platform). The one for ios excludes the google iap plugin and the amazon iap plugin. I know it is a workaround and not a solution. I also believe it is against the nature of corona (one code base for all platforms). But what can we do?!

We have changed store.purchase() to be smart and if a table is needed, convert it to a table or if a table is provided and the store wants a single item pass the first table item as a string. So that shouldn’t matter.

There isn’t any reason that Apple should be detecting any other billing. We simply can’t include anything Google/Android with an iOS build.  

I’ll bring this up with Engineering to see if they have any thoughts.

Rob