Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

IAP Badger: a unified approach to in-app purchases
Started by happymongoose Sep 13 2015 09:59 AM

409 replies to this topic
release lua plugin iap in app purchases monetization inventory
[TOPIC CONTROLS]
« Page 4 of 17 2 3 4 5 6 »
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#76

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Just for information for anyone following this thread, IAP Badger has now been released as a Corona plug-in, so you no longer need to download the code from Github.

 

You just need to activate the plug-in and include it in your project as described in the documentation.



[TOPIC: post.html]
#77

robin.jones

[GLOBAL: userInfoPane.html]
robin.jones
  • Observer

  • 2 posts
  • Corona SDK

Just to say thank you for this great bit of kit!

 

I was just looking at doing my first IAP and this is absolutely brilliant.  It's taken me about a day to work out how to use it but that is more around the vagaries of Google and Apple than IAP Badger- that was dead easy.

 

Goodness knows how long it would have taken me "going native".

 

Thanks again


  • happymongoose likes this

[TOPIC: post.html]
#78

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Thanks Robin - glad you found it useful  :)



[TOPIC: post.html]
#79

juris82

[GLOBAL: userInfoPane.html]
juris82
  • Observer

  • 3 posts
  • Corona SDK

Hi,

I integrated IAP Badger in my game and it is working fine on simulator.

It is working on device in debug mode, when downloaded from Google Play alpha testing chanel. (It shows msg that purchase succesfull).

But nothing happens when I comment out debugMode=true.

Is that the only thing I need to do before building app for production? Am I missing something?

 

PS

Thanks for IAP Badger, it really helps testing iap.



[TOPIC: post.html]
#80

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Hi there,

 

In debug mode, IAP Badger never contacts a real store - this is true for both the simulator and the device.  Once you have everything running in this mode, the next thing to do is comment out debugMode=true in the iapOptions table you pass to iap.init.

 

Now you need to make sure everything is set up correctly in Google Play Console (which is a tutorial in itself...) and that you have published your products.  It can take a couple of hours for changes you make in the console to work through their servers across the world.

 

Finally, you need to retest your app.  IAP Badger won't give you any pop up messages or print any text to the console; if you have any problems, to check for any errors, you'll need to connect your device to your computer via USB and check the Android error log using adb logcat, where errors for Google Play are output by Corona's store code.

 

Assuming your app works, double check you've commented our any reference to debugMode, debugStore and doNotLoadInventory when you call iap.init, and you're ready to publish :)



[TOPIC: post.html]
#81

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

Hey guys  :)

 

I might be wrong but it seems that there is some kind of bug or delay in purchaseListener function, that lets me to buy nonconsumable product twice in debug test mode...

 

Here is my code:

 

function purchaseListener(product, event )
     -- print("DO 1")
     if(product == "removeAds") then
      -- print("DO 2")
    --Give the user a message saying the purchase was successful
       removeAdsIcon:removeEventListener ( "tap", wasTapped )
       removeAdsIcon:removeEventListener("tap", buyUnlock)
       removeAdsIcon.alpha = 0
       adsYouPurchased.alpha = 1
      adsYouPurchased:addEventListener("tap", wasTapped)
         --Save the inventory change
         iap.saveInventory()
         native.showAlert("Info", "Your purchase was successful", {"Okay"})
    return true

  elseif(product == "buyLevel2") then
      -- print("DO 3")
      level2:removeEventListener("tap", buyUnlock)
      -- print("DO 5")
      level2:setSequence("blink")
      level2:play()
        iap.saveInventory()
        level2:addEventListener("tap", goSomewhere)
        native.showAlert("Info", "Your purchase was successful", {"Okay"})
   end
end
       
       buyUnlock=function(event)
       --Tell IAP to initiate a purchase
       iap.purchase(event.target.product, purchaseListener)
    end

Now what happens here:

- removeAdsIcon is a png (display.newImageRect)

- level2 is NOT a png, it is a Sprite (display.newSprite)

 

When I test above code in Windows Corona Simulator 2015.2731 (debug mode) it goes well for buying removeAdsIcon (i.e. you can click only once to remove Ads (because this line is executed: "removeAdsIcon:removeEventListener("tap", buyUnlock)" )

 

The problem is that you can click TWICE to buy level2 (I repeat level2 is a Sprite :) )

 

My question is, why "level2:removeEventListener("tap", buyUnlock)" line is executed only after two times clicking on level2 ?

 

1. So I click first time, and level2 native Text view pops up for buying level2 (I buy it and level2 starts to blink).

2. Second time when I click native Text view pops up for buying level2 (again!), and at the same time I go to level 2 (remember that it was opened/started to blink after first click on it - which is OK !) ?

 

Waiting your reply.

Many thanks.

Ivan 



[TOPIC: post.html]
#82

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Just to clarify, your actions and console output from your debug statements would be:

 

[Tap level2]

DO 1 

DO 3 

DO 5

[pop up appears, level2 blinks]

[Tap level2 again]

DO 1

DO 3

DO 5

[pop up appears, level2 starts at the same time]



[TOPIC: post.html]
#83

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

It looks like this:

 

[Tap level2 - I click successful ]

DO 1 

DO 3 

DO 5

[pop up appears, level2 blinks]

[Tap level2 again]

DO 1

DO 3

Runtime error

[pop up appears, level2 starts at the same time]

 

Pop up window offering buying level2 stays when level 2 is shown.

And I have a new pop up window showing Corona error:

 

"attempt to call method 'removeEventListener' (a nil value)

stace traceback: menu.lua:276: in function 'postStoreTransactionCallbackListener'

iap_badger.lua:1141: in function 'storeTransactionCallback'

iap_badger.lua:1556: in function <iap_badger.lua:1529>

 

I have even tried to create separate function for following code (that is outside of 'function purchaseListener', and I

call it from within purchaseListener):

level2:removeEventListener("tap", buyUnlock)
 -- print("DO 5")
level2:setSequence("blink")
level2:play()
level2:addEventListener("tap", goSomewhere)

Surprisingly buying "removeAds" works like a charm.

 

Buying level2 doesn`t work... 

Buying level3 also doesn`t work (tried this morning)... 

 

 

Thank you.

Ivan


Edited by ivan888, 18 November 2015 - 12:44 AM.


[TOPIC: post.html]
#84

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Could you show me the code where you initiate your level2 sprite and attach the listener?  It looks like there may an additional event listener acting on the object.



[TOPIC: post.html]
#85

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

Following code is below above mentioned code.

 

local sheetInfo_level2  = require("level2")
local myImageSheet_level2  = graphics.newImageSheet( "images/level2.png", sheetInfo_level2:getSheet() )
local sequenceData_level2  = {
 
            {
 
    name="blink",                                  -- name of the animation
    sheet=myImageSheet_level2,                           -- the image sheet
    start=sheetInfo_level2:getFrameIndex("level21"), -- first frame
    count=2,                                      -- number of frames
    time=300,                                    -- speed
    loopCount= 0                                 -- repeat
            },            
}
 
level2 = display.newSprite( grp, myImageSheet_level2, sequenceData_level2 )
level2.x = centerX + (screenWidth*0.25)
level2.y = level1.y
level2.id="level2"
level2.product ="buyLevel2"
level2:addEventListener("tap", buyUnlock)
grp:insert(level2) -- grp is display.newGroup called in 'scene:create'
 
Looks like you cannot easily remove tap listener because purchase function is ongoing or something...
 
Sometimes if I am really fast and I click successful second time before game proceeds to level2 (I get no errors then). And then I go back from level2 to main, and third time when I click listener is removed (everything works OK).
I think that is because I am calling this in menu.lua 'scene:show' :
if (iap.getInventoryValue("buyThatLevel2")==true) then
level2:removeEventListener("tap", buyUnlock)
level2:setSequence("blink")
level2:play()
level2:addEventListener("tap", goSomewhere)
level2:addEventListener ( "tap", wasTapped )
else
    --Otherwise add a tap listener to the button that unlocks the game
    level2:addEventListener("tap", buyUnlock)
end
So basically everything works OK only if you get a chance to read the inventory during menu.lua initialization.
 
 
************************************************************************
NEW TEST THAT PASSES:
 
1) Cleared Corona Sandbox.
2) Bought level2.
3) Did not clicked on level2 (second time).
4) Went to options.lua (which is basically new scene/lua file - i.e. menu.lua was cleared).
5) Went back to menu.lua, and level2 is purchased (no pop up window when I click on it the second time).
************************************************************************
 
But when I try my first test (staying all the time in menu.lua), level2 is offered 2 times - original problem.
What am I missing here?

Edited by ivan888, 18 November 2015 - 06:23 AM.


[TOPIC: post.html]
#86

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

I can't double check from where I am at the moment, but I am 99% sure that, for some reason, your buyUnlock tap listener is being called twice when you tap on your level2 object.  (Which is why the code is creating an exception on line 276; the first time your purchase listener is called, your listener successfully removes the tap listener for buyUnlock; the second time, it attempts to remove the same listener, which no longer exists, raising the exception.)

 

An easy test for this would be to include two print statements in your buyUnlock function, alongside you other debug prints:

function buyUnlock(event)
   print("Entering buyUnlock")
   iap.purchase(event.target.product, purchaseListener)
   print("Leaving buyUnlock")
end

Try the change and see what console output you get now.



[TOPIC: post.html]
#87

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

You were right!

This code was redundant (' level2:addEventListener("tap", buyUnlock) '), I deleted it and now everything is fine.

I will put you on Credits page.

Thank you!

 

I have two last questions:

1) Is your code enough or I should add additional separate functions for:

- no internet connection

- lost internet connection

- unable to connect to the store or timeout ?

 

2) Does Apple/Google mandate "Restore Purchases" button, or I could restore them automatically when user is online and enters menu.lua (of course if he/she has deleted app previously) ?

 

Many, many thanks!  :)

Ivan


Edited by ivan888, 18 November 2015 - 02:22 PM.


[TOPIC: post.html]
#88

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

IAP Badger is just a wrapper class for Corona's native store code, which I believe handles the above situations for you.  I've not tried it myself, but I'm guessing any attempts to make a purchase without a connection will just come back as a 'fail' event; and when you initiate a restore purchases call, you can (and have to) specify a timeout function anyway, which is initiated after a set period of time whether there is an internet connection or not.

 

If anyone knows differently to this, I'd be happy to update the code base to reflect this.

 

Hope that helps / makes sense.

 

Many thanks for the credit  :)



[TOPIC: post.html]
#89

juris82

[GLOBAL: userInfoPane.html]
juris82
  • Observer

  • 3 posts
  • Corona SDK

Happymongoose, thanks for advice!

 

Everything was correct with code, but license key was not correct, because I missed brackets!

I installed adb logcat as you said and saw that licensing is not working. USB debugging is really cool! :)

I believe, there is no way to find it out without adb logcat.

Of course adb logcat Corona:v *:s (I found it on corona forums) is the best way for corona debugging.



[TOPIC: post.html]
#90

juris82

[GLOBAL: userInfoPane.html]
juris82
  • Observer

  • 3 posts
  • Corona SDK

One more dumb question.

User makes one purchase to unlock content. What is best practice to "keep in mind" that purchase- should I need to check if user has bought it through google store next and every time when user lounches app? Or save some data on device?



[TOPIC: post.html]
#91

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

In practice, using IAP Badger's inventory should be enough to cope with most situations, assuming you are saving the inventory after purchases etc.  The flow I follow is:

 

* when the app is launched (not restored), on Android devices I initiate a product restore (Apple tell you explicitly not to do this).

* I offer a product restore button in the settings menu on Android and iOS

 

The above will handle non-consumables.  In terms of consumables, it is your responsibility to store purchases on the device, as you'll receive no information from the App Stores about consumables during the restore process.  Again, IAP Badger's inventory is designed to handle this for you.



[TOPIC: post.html]
#92

benyemi

[GLOBAL: userInfoPane.html]
benyemi
  • Observer

  • 23 posts
  • Corona SDK

Hey HappyMongoose,

 

Thanks for your corona file, its been a God send. I am having a slight problem in my apple app. I am able to purchase my product when i click my buy button and if I delete the app and re-download the app, when I click the buy button again, the app restores my purchase. However Apple rejected my app because when I re-download it, the buy button needs to be pressed again (they're so nitpicky smh). So In order for the app to be approved, I need to make a conditional statement that automatically changes the text on my "buy" button to a "restore" as soon as I enter the app. Can you explain in detail how I can do this? Thank you



[TOPIC: post.html]
#93

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

Hi Benyemi,

 

I hope you are not right because my app (under review) will be rejected too!  :mellow:

 

As I understand:

- Apple suggests that you DO NOT check server purchases condition on each app launch!

- So what you suggested (Buy/Restore) is not possible without checking purchases condition on each app launch

- Checking purchases condition on each app launch would annoy users (they would have to sign in each time app launches)

 

I think you have to have Restore Purchases button somewhere in your app (like in Options or Settings).

When user clicks it all Buy buttons should disappear if transaction was successful, and if user indeed purchased that particular item

 

That should be sufficient I think...  :o   :D



[TOPIC: post.html]
#94

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Hi Benyemi,

 

My understanding is that ivan888 is correct.  Apple explicitly say not to keep checking their servers for IAP information when you launch your app.  You should have a separate Restore Purchases button somewhere in your app so users can recover non-consumables.

 

Google recommend the opposite and say you should initiate a restore purchases call every time the app launches.

 

Different strokes...



[TOPIC: post.html]
#95

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,400 posts
  • Enterprise

Apple does not want the user to experience a login/purchase prompt when the app starts. Just put a Restore Purchases button somewhere in your app and you will be good.

 

Rob



[TOPIC: post.html]
#96

azhar

[GLOBAL: userInfoPane.html]
azhar
  • Enthusiast

  • 54 posts
  • Corona SDK

sir, a quetion, how to get the item price? thx.



[TOPIC: post.html]
#97

ivan888

[GLOBAL: userInfoPane.html]
ivan888
  • Contributor

  • 397 posts
  • Corona SDK

Hi Azhar,
Item price is displayed to the customer when he/she attemtps to purchase the product.
You set the item price on itunnes connect when seting up your app.
Item price depends on App Store location (US, Germany,....).

Maybe IAP badger knows more...

[TOPIC: post.html]
#98

happymongoose

[GLOBAL: userInfoPane.html]
happymongoose
  • Contributor

  • 356 posts
  • Corona SDK

Hi Azhar,

 

ivan888 is correct in that when you attempt to make a purchase, iOS/Android will tell the user how much they will be charged.

 

However, IAP Badger can tell you the price of all the items in your catalogue in the users local currency, as well as a localised IAP product title and description.  It pulls the information from the console on the relevant app store (ie. iTunes Connect or Google Play Console).  At the moment, you cannot access the information on Amazon's console.  This means you can display IAP pricing information for the user before they hit the purchase button (which is fair, really - they will want to know how much something costs before committing to payment).

 

To do this, you make a call to iap.loadProducts().  You should do this as soon as possible in your app's execution, because the function can take several seconds (or longer) to contact the relevant server and download the product information.  Once this is complete, you call iap.loadProductsCatalogue() to get a table containing all the relevant information.  If the information is not yet back from the server, the function will return an empty {} table.

 

For debug purposes, you can hard code some values for your IAP products' pricing and descriptions to test you UI code.

 

Sample code for getting pricing information can be found in IAP Badger's documentation for iap.loadProducts() and iap.getLoadProductsCatalogue().



[TOPIC: post.html]
#99

azhar

[GLOBAL: userInfoPane.html]
azhar
  • Enthusiast

  • 54 posts
  • Corona SDK

Hi Azhar,

 

ivan888 is correct in that when you attempt to make a purchase, iOS/Android will tell the user how much they will be charged.

 

However, IAP Badger can tell you the price of all the items in your catalogue in the users local currency, as well as a localised IAP product title and description.  It pulls the information from the console on the relevant app store (ie. iTunes Connect or Google Play Console).  At the moment, you cannot access the information on Amazon's console.  This means you can display IAP pricing information for the user before they hit the purchase button (which is fair, really - they will want to know how much something costs before committing to payment).

 

To do this, you make a call to iap.loadProducts().  You should do this as soon as possible in your app's execution, because the function can take several seconds (or longer) to contact the relevant server and download the product information.  Once this is complete, you call iap.loadProductsCatalogue() to get a table containing all the relevant information.  If the information is not yet back from the server, the function will return an empty {} table.

 

For debug purposes, you can hard code some values for your IAP products' pricing and descriptions to test you UI code.

 

Sample code for getting pricing information can be found in IAP Badger's documentation for iap.loadProducts() and iap.loadProductsCatalogue().

 

I know, what I mean is the value of price so I can display the price before user click the trigger. Thx. I will try :D.



[TOPIC: post.html]
#100

azhar

[GLOBAL: userInfoPane.html]
azhar
  • Enthusiast

  • 54 posts
  • Corona SDK

I got an error the variable is nil. How to anticipate the delay? Thx


[topic_controls]
« Page 4 of 17 2 3 4 5 6 »
 
[/topic_controls]

Also tagged with one or more of these keywords: release, lua plugin, iap, in app purchases, monetization, inventory