can't quite get iAP working, stuck..

Alright so using @Rob Miracle nice tutorial here: 

https://coronalabs.com/blog/2013/09/03/tutorial-understanding-in-app-purchases/

I understand most of the code and what’s it doing- but some reason I can’t get this to remove ads.  I know I’m missing something obvious as I’m new.  What do I need to change to make a purchase that removes ads.

I made the iAP in iTunes… and everything in iTunes is setup right, this is my fourth app.

The question is in the code, I’ve changed this part:

local function removeAds( event ) if event.phase == "began" then    event.target:setFillColor(224)   elseif event.phase == "ended" then      event.target:setFillColor(255)      if system.getInfo("targetAppStore") == "amazon" or system.getInfo("targetAppStore") == "google" then         store.purchase( "com.acme.superrunner.upgrade" )      else         store.purchase( { "com.acme.superrunner.upgrade" } )      end   end   return true end

To resemble my ‘removeAds’ in-app product ID for my game:

local function removeAds( event )    if event.phase == "ended" then       if system.getInfo("targetAppStore") == "amazon" or system.getInfo("targetAppStore") == "google" then          store.purchase( "com.my.address.myiap" )       else          store.purchase( { "com.my.address.myiap" } )       end    end    return true end

local noAds = display.newImageRect("noAds.png", 125, 125)noAds.x, noAds.y = display.contentWidth/2 + noAds.width\*1.25, display.contentHeight - 81 noAds:addEventListener( "touch", removeAds )

Everything else in the other bits of example code above that is the same as example - actually I think there’s one more place to put just my Product ID (without the iAP id) …

Then where I normally call the interstitial advertisements, I have a small chunk of code that goes:

if randomBanner \<= 25 and&nbsp;(mySettings.isPaid == false or mySettings.isPaid == nil) then ads.show( "interstitial" ) end

What am I missing? :ph34r:  Nothing comes up when I press my button, nothing about iAP and the interstitials keep showing.  It does recognize I’ve touched button as I’ve used Print commands to confirm.   

I would print out the value of system.getInfo(“targetAppStore”) and make sure it’s what you expect.  Are you testing this on a device or in the Corona Simulator?  What does your store.init() code look like?  Your store listener function code?

Rob

@Rob Miracle

Thanks for the reply- all the code resembles what you have on your guide at the website I posted originally. I’m new so didn’t grasp there’s extra code bits needed?

I did print(system.getinfo(“targetAppStore”) and it returned none. This was in the removeAds button function- but was on simulator.

I’m testing in both simulator and on iOS devices. But when I try to test on simulator says 'store Api is not supported on this platform , yada yada yada. And testing on iOS device nothing happens.

Are there extra functions that need to be added like the store listener and store.init() - I thought this was in the top part of the code in your example so I didn’t add anything extra - sorry I’m a newb!

IAP can only be tested on device, not in the simulator, but many people make that common mistake.

You have to have required the store module in main.lua and in every scene you make a store.* call from.  In main.lua you need the listener function and the code that calls store.init().   With out all these bits in place it won’t work.

Next because you’re testing for the value “targetAppStore” you actually have to set this value when you build your app for the device.  On the Android build form there is a “Store” option.  It defaults to none.  Because you’re not setting it, you’re calling the iOS version of store.purchase() which is passing invalid data to the android store.purchase().

Rob

Right… I do require the store module… I guess I’m a little confused, I’m using this example from your guide:

local store = require( "store" ) local utility = require( "utility" ) local mySettings local restoring mySettings = utility.loadTable("settings.json") if mySettings == nil then &nbsp; &nbsp;mySettings = {} &nbsp; &nbsp;mySettings.isPaid = false &nbsp; &nbsp;utility.saveTable(mySettings, "settings.json") end

local function transactionCallback( event ) &nbsp; &nbsp;print("In transactionCallback", event.transaction.state) &nbsp; &nbsp;local transaction = event.transaction &nbsp; &nbsp;local tstate = event.transaction.state &nbsp; &nbsp;-- &nbsp; &nbsp;--Google does not return a "restored" state when you call store.restore() &nbsp; &nbsp;--You're only going to get "purchased" with Google. This is a work around &nbsp; &nbsp;--to the problem. &nbsp; &nbsp;-- &nbsp; &nbsp;--The assumption here is that any real purchase should happen reasonably &nbsp; &nbsp;--quick while restores will have a transaction date sometime in the past. &nbsp; &nbsp;--5 minutes seems sufficient to separate a purchase from a restore. &nbsp; &nbsp;-- &nbsp; &nbsp;if store.availableStores.google and tstate == "purchased" then &nbsp; &nbsp; &nbsp; local tdate = math.floor( transaction.date /1000 ) &nbsp; &nbsp; &nbsp; local timeStamp = utility.makeTimeStamp(transaction.date,"ctime") &nbsp; &nbsp; &nbsp; if timeStamp + 360 \< os.time() then &nbsp;-- if the time stamp is older than 5 minutes, we will assume a restore. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("map this purchase to a restore") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tstate = "restored" &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("I think tstate is ", tstate) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;restoring = false &nbsp; &nbsp; &nbsp; end &nbsp; &nbsp;end &nbsp; &nbsp;if tstate == "purchased" then &nbsp; &nbsp; &nbsp; print("Transaction succuessful!") &nbsp; &nbsp; &nbsp; mySettings.isPaid = true &nbsp; &nbsp; &nbsp; utility.saveTable(mySettings, "settings.json") &nbsp; &nbsp; &nbsp; native.showAlert("Thank you!", "Your support is greatly appreciated!", {"Okay"}) &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;elseif &nbsp;tstate == "restored" then &nbsp; &nbsp; &nbsp; print("Transaction restored (from previous session)") &nbsp; &nbsp; &nbsp; mySettings.isPaid = true &nbsp; &nbsp; &nbsp; utility.saveTable(mySettings, "settings.json") &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;elseif tstate == "refunded" then &nbsp; &nbsp; &nbsp; print("User requested a refund -- locking app back") &nbsp; &nbsp; &nbsp; mySettings.isPaid = false &nbsp; &nbsp; &nbsp; utility.saveTable(mySettings, "settings.json") &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;elseif tstate == "revoked" then -- Amazon feature &nbsp; &nbsp; &nbsp; print ("The user who has a revoked purchase is", transaction.userId) &nbsp; &nbsp; &nbsp; --Revoke this SKU here: &nbsp; &nbsp; &nbsp; mySettings.isPaid = false &nbsp; &nbsp; &nbsp; utility.saveTable(mySettings, "settings.json") &nbsp; &nbsp;elseif tstate == "cancelled" then &nbsp; &nbsp; &nbsp; print("User cancelled transaction") &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;elseif tstate == "failed" then &nbsp; &nbsp; &nbsp; print("Transaction failed, type:", transaction.errorType, transaction.errorString) &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;else &nbsp; &nbsp; &nbsp; print("unknown event") &nbsp; &nbsp; &nbsp; store.finishTransaction( transaction ) &nbsp; &nbsp;end &nbsp; &nbsp;print("done with store business for now") end local function loadProductsListener( event ) &nbsp; &nbsp;print("In loadProductsListener") &nbsp; &nbsp;local products = event.products &nbsp; &nbsp;for i=1, #event.products do &nbsp; &nbsp; &nbsp; print(event.products[i].title) &nbsp; &nbsp; &nbsp; print(event.products[i].description) &nbsp; &nbsp; &nbsp; print(event.products[i].localizedPrice) &nbsp; &nbsp; &nbsp; print(event.products[i].productIdentifier) &nbsp; &nbsp;end &nbsp; &nbsp;for i=1, #event.invalidProducts do &nbsp; &nbsp; &nbsp; print(event.invalidProducts[i]) &nbsp; &nbsp;end end if system.getInfo("targetAppStore") == "amazon" then &nbsp; &nbsp;store = require "plugin.amazon.iap" &nbsp; &nbsp;store.init( transactionCallback ) &nbsp; &nbsp;print("The currently logged in user is: ", store.getUserId()) &nbsp; &nbsp;store.loadProducts({"com.my.address.myiap"}, loadProductsListener) &nbsp; &nbsp;store.restore() else &nbsp; &nbsp;if store.availableStores.apple and not mySettings.isPaid then &nbsp; &nbsp; &nbsp; timer.performWithDelay(1000, function() store.init( "apple", transactionCallback); end) &nbsp; &nbsp;end &nbsp; &nbsp;if store.availableStores.google and not mySettings.isPaid then &nbsp; &nbsp; &nbsp; timer.performWithDelay( 1000, &nbsp; &nbsp; &nbsp; function() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;store.init( "google", transactionCallback ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;restoring = true; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;store.restore(); &nbsp; &nbsp; &nbsp; end ) &nbsp; &nbsp;end end

All I’m trying to do is get this to work on iOS (when I test on device).  Below this chunk of code is:

local function removeAds( event ) if event.phase == "ended" then if system.getInfo("targetAppStore") == "amazon" or system.getInfo("targetAppStore") == "google" then store.purchase( "com.my.address.myiap.removeAds" ) else store.purchase( { "com.my.address.myiap.removeAds" } ) end end return true end local noAds = display.newImageRect("noAds.png", 125, 125) noAds.x, noAds.y = display.contentWidth/2 + noAds.width\*1.25, display.contentHeight - 81 noAds:addEventListener( "touch", removeAds )

I have to set:  “targetAppStore”  ?— … I thought the API and your functions auto-detects this?  I’m confused :huh: .  What exactly do I need to change in my above chunks of code (taken from your example) to get the: “mySettings.isPaid” set to true when my ‘no ads’ button is pressed…so I can use that to determine to show ads or not.  

I appreciate your help.  

Also on a related note, if everything was working and I click the button to remove ads - just testing on a local device, will the apple login appear like it would to confirm an in-app purchase if it were live…?

See the screen shot below:

@Rob Miracle

Right but I’m building for iOS - and don’t have that option in that screen.

I think it maybe related to my test environment.  The App hasn’t been published yet, and so the iAP in iTunesConnect status is “Ready to Submit” , it needs to go along side with the first time you publish for it to be fully active??

I’m not sure - this whole iAP is pretty new to me

You need to look in the device’s console log for messages that might clue you in as to what’s going on.   Apple is particularly tough to test IAP on. 

The app has to be build with a developer certificate/provisioning profile.  You have to use an registered test account for the sandbox to work.  You don’t have to have your IAP items approved, but they have to be setup an enabled.

Rob

Yes the developer stuff is setup and sandbox as I have gameNetwork stuff tested and working.

I guess the question remains, if I haven’t published the App yet and I setup the IAP as far as I can to the point it’s ‘ready to submit’ - is that the same as being enabled and being able to test?

Where to find the devices console log?

On another note- I been having adverts work both on an iPad test and iPhone test device. Now the ads are only working on the iPad and not the iPhone, and I haven’t changed anything to the code…

Agh and development was going oh so smoothly.

With your device tethered to your computer, go into Xcode.  Then Windows -> Devices (SHIFT-CMD-2). Select your device.  The console log is collapsed at the bottom.  Details are located here:

http://docs.coronalabs.com/guide/basics/debugging/index.html

As for the ads, I would suggest looking at the console log.

Rob

Ok pretty sure the ad issue was from a test limit and nothing related to code.

I’ll try to get some debug info on the iAp stuff

@Rob Miracle - 

Alright got some debug info:

 <Warning>: In transactionCallback failed

 <Warning>: Transaction failed, type: unknown Cannot connect to iTunes Store

 <Warning>: done with store business for now

Which is odd cause I am connected to the internet.  I can exit out of App and immediately start surfing the net.  Maybe I have something setup wrong with permissions or something in iTunes/Dev. account.  I thought I’ve double checked everything and can’t really find anything that would be wrong.

EDIT >>> found this:

"Make sure you have signed out of any production iTunes accounts on the device.

I was getting this error on my test phone which was logged in with my actual iTunes account. You cannot test apps using your production iTunes account, hence the error. I just wish Apple provided a better error so as to avoid this guesswork…"

Whoops that fixed it …err tested correctly.  Thanks…and sorry about that.

 

Whooohoooooo!!

One more question @Rob Miracle or whomever,

Nearly done…Just realized Apple requires the Restore for iap - so this is soley a question for iOS - if I setup a separate button that acts like the ‘noAds’ but change around a few things:

local function restoreIAP( event ) if event.phase == "ended" then store.restore( { "com.my.address.myiap.removeAds" } ) end return true end local restoreBtn = display.newImageRect("restore.png", 125, 125) restoreBtn.x, restoreBtn.y = display.contentWidth/2 + restoreBtn.width\*-1.25, display.contentHeight - 81 restoreBtn:addEventListener( "touch", restoreIAP )

Does Apple keep track of the purchase (I’m only using one) ?  It seemed like from your guides that this is true but not on google/amazon.  Or do I need some other code to determine if the user has previously bought it even for iOS??

Thanks!  Think this will be the last question… :ph34r:

First you just call:   store.restore()

You’re not restoring specific items.  When Apple sees the restore request it will trigger your IAP transaction call back function with a “restore” type, ones for each item being restored.  You can then process them (i.e. do your unlocks) based on the product ID it sends back to you.

Rob

@Rob Miracle

Thanks, Ok I changed to just store.restore() but it’s not working… not sure if it’s code or test environment setup… but this is what is displaying thru the Xcode debug:

iPhone itunescloudd[1551] <Notice>: (Note ) MC: User has completed cloud configuration. Not showing UI again.

Apr 14 19:17:16 iPhone itunescloudd[1551] <Warning>: Updating media purchase history for initial load…

Apr 14 19:17:16 iPhone accountsd[84] <Warning>: AIDA Notification plugin running

Apr 14 19:17:16 iPhone locationd[61] <Notice>: Gesture EnabledForTopCLient: 0, EnabledInDaemonSettings: 0

Apr 14 19:17:16 iPhone backupd[1727] <Warning>: INFO: Account changed (enabled=0, accountID=1069416152)

Apr 14 19:17:16 iPhone kernel[0] <Notice>: AppleKeyStore: operation failed (pid: 1727 sel: 23 ret: e00002f0)

Apr 14 19:17:17 iPhone syncdefaultsd[1721] <Notice>: (Note ) marked “com.me.keyvalueservice” topic as “enabled” on <APSConnection: 0x13de1c6d0>

Apr 14 19:17:17 iPhone CLTM[20] <Error>: CLTM: _subscription 0x0, _subscribedChannels 0x0

Apr 14 19:17:18 iPhone locationd[61] <Notice>: NETWORK: requery, 0, 0, 0, 0, 1, items, fQueryRetries, 0, fLastRetryTimestamp, 450744981.7

Apr 14 19:17:18 iPhone locationd[61] <Notice>: NETWORK: query, cells, 0, 0, 0, 0, wifis, 1

Apr 14 19:17:19 iPhone timed[59] <Notice>: (Note ) CoreTime: Received time 04/14/2015 23:17:19±15.00 from “LocationServer”

Apr 14 19:17:19 iPhone timed[59] <Notice>: (Note ) CoreTime: Want active time in 24.40min. Need active time in 45.23min. Remaining retry interval: 11.356794min.

Apr 14 19:17:20 iPhone locationd[61] <Notice>: NETWORK: requery, 0, 0, 0, 0, 1, items, fQueryRetries, 0, fLastRetryTimestamp, 450746238.5

Apr 14 19:17:20 iPhone locationd[61] <Notice>: NETWORK: query, cells, 0, 0, 0, 0, wifis, 1

Apr 14 19:17:20 iPhone timed[59] <Notice>: (Note ) CoreTime: Received time 04/14/2015 23:17:20±15.00 from “LocationServer”

Apr 14 19:17:20 iPhone timed[59] <Notice>: (Note ) CoreTime: Want active time in 24.39min. Need active time in 45.22min. Remaining retry interval: 11.326807min.

Apr 14 19:17:22 iPhone CLTM[20] <Error>: CLTM: _subscription 0x0, _subscribedChannels 0x0

Apr 14 19:17:23 iPhone kernel[0] <Notice>: 081126.373719 wlan0.A[16661] AppleBCMWLANCore::dumpWmeCounters():  per AC tx counters: 780576 110657 176509 24510, rx counters: 1839930 0 0 0 

Apr 14 19:17:23 iPhone kernel[0] <Notice>: 081126.373749 wlan0.A[16662] AppleBCMWLANCore::dumpWmeCounters():                AWDL: Tx 76 0 0 0      Rx: 30 0 0 0 

Apr 14 19:17:24 iPhone kernel[0] <Notice>: AppleARMPMUCharger: limiting USB input current to 1100 mA (measured 935 mA)

Apr 14 19:17:27 iPhone CLTM[20] <Error>: CLTM: _subscription 0x0, _subscribedChannels 0x0

Apr 14 19:17:32 iPhone CLTM[20] <Error>: CLTM: _subscription 0x0, _subscribedChannels 0x0

Apr 14 19:17:37 iPhone locationd[61] <Notice>: Gesture EnabledForTopCLient: 0, EnabledInDaemonSettings: 0

Apr 14 19:17:37 iPhone accountsd[84] <Warning>: AIDA Notification plugin running

Apr 14 19:17:37 iPhone CLTM[20] <Error>: CLTM: _subscription 0x0, _subscribedChannels 0x0

Apr 14 19:17:38 iPhone kernel[0] <Notice>: AppleKeyStore: operation failed (pid: 1728 sel: 23 ret: e00002f0)

Apr 14 19:17:38 iPhone backupd[1728] <Warning>: INFO: Account changed (enabled=0, accountID=1069416152)

Any ideas?  I think most of that is retrying connect, but " <Notice>: AppleKeyStore: operation failed (pid: 1727 sel: 23 ret: e00002f0) " seems suspect, not sure what it’s referring to tho

You will not get anything, if there is nothing to restore.   I don’t see anything in your log file that indicates an issue or looks like messages from your app.

Rob

If I understand correctly - ‘store.restore()’ will call the “restored” part in the ‘transactionCallback’ function.  Which will then set the mySettings.isPaid = true    ---- much like how the 'store.purchase({com.web.game.iap}) called the “purchased” part.

But it’s not.  If I delete everything… install the game.  Buy the iAP to remove the ads (it’ll even say you bought this before which is true)… everything works up to that point.  Ads will stop playing.

Now if I install the game on another device, or even delete this then reinstall.  It’s back to showing ads, as one would expect.  When I click the button that calls the ‘store.restore()’ theoretically that should remove the ads.  But it’s not restoring anything, ads still going even tho I’m logged in as the user that initially removed the ads.  It is going thru the login stuff tho

***** EDIT -> Never mind it started working, I think it was because I was in sandbox environment.  My iPAD is in the sandbox/developer account and my iPhone is not.  When I do these steps on iPAD it doesn’t work, when I do it on iPhone it does.  Go figure.  So I’m assuming it’s something with my test environment setup.

Thanks Rob for your help.

Did you find out more about the test environment problem?

I encounter a similar one with a new product you can read about here:

https://forums.coronalabs.com/topic/56541-problem-with-transactioncallback-when-calling-storepurchase-in-ios/

Well it’s kind of difficult- having sandbox accounts to test iAP ( I don’t believe you can use dev account? ) . But nothing ultimately was wrong with my code just how I was testing.

Sorry can’t shed some more light on the issue!

I would print out the value of system.getInfo(“targetAppStore”) and make sure it’s what you expect.  Are you testing this on a device or in the Corona Simulator?  What does your store.init() code look like?  Your store listener function code?

Rob