In App Purchase module :)

Monkey Targets Forums/iOS/In App Purchase module :)

anawiki(Posted 2011) [#1]
Hi all,
I finally made it :) The code for making in app purchases on appstore is working. It's surprising how much time one can waste getting such thing to work.

Let me just put some docs into the mod and I'll upload it somewhere for you all.

cheers
Roman


Uncle(Posted 2011) [#2]
Sounds great :) Will be useful to many Im sure.


NoOdle(Posted 2011) [#3]
Ohh this is awesome news, good job! :)


Aman(Posted 2011) [#4]
Great news. Can't wait to check it out


xlsior(Posted 2011) [#5]
Cool...

Just one word of warning though: Lodsys has been sueing several (small bit) app makers for adding this functionality to their programs, because they claim it violates one of their patents.
More info: http://news.cnet.com/8301-27076_3-20063253-248.html


MikeHart(Posted 2011) [#6]
And Apple dealed with Lodsys because IOS devs (if they use Apples API) are covered by Apples license of these patents.


pantson(Posted 2011) [#7]
are you sure Mike?
Rovio now added to the list of patent infringers by Lodsys
http://fosspatents.blogspot.com/2011/07/lodsys-sues-rovio-over-angry-birds-for.html
Looks like they are also going after Android platform too...

its a dangerous world out there.


MikeHart(Posted 2011) [#8]
You're right. It is more than ever very risky to add these features. Hell, even without stuff like that, you will never know which patent you are already codig against or even if someone will patent something tommorow that will bring you trouble.

Today I saw someone posting on twitter that it is the best not to publish into markets anymore, where patent you can patent software mechanics. Software patents are evil. EVIL!!! Apple might want to protect you, but Google stays quite. And even if they want, no on knows if they will be successful. I read alot today and so far I get the feeling that it is the best to pull my application from the net and quit developing software. Because there will be ALWAYS a patent that you would need to get a license for. And how to find out which ones you need? Impossible.


pantson(Posted 2011) [#9]
I fully disagree with patents that have no invention..
just because I draw a car and then the ground a meter beneath it, doesn't mean I own the patent for flying cars.

I agree that software patents are EVIL

On the original point..
great work anawiki
Sorry for hijacking this thread.

conversation moved to http://www.monkeycoder.co.nz/Community/posts.php?topic=1233


anawiki(Posted 2011) [#10]
One of the top USA publishers taught me a lesson that comes down to this: if you're not in the USA we can do nothing to you. I believe the same will apply here.

It sucks for what they do.


xzess(Posted 2011) [#11]
indeed!


Aman(Posted 2011) [#12]
@anawiki

Sorry man. Looks like that's not the case with this company. They sued Illusion Labs AB of Malmö, Sweden for their game Labyrinth on both android and iPhone. Wulven Games of Hanoi, Vietnam is also accused of infringement of Lodsys's patents with at least Shadow Era for iPhone.

If you are not in USA, the least they can do is prevent you from distributing your apps and games in countries where their patent is effective. That include at least North America and most of Europe which is nearly 90% of your targeted markets.

You have two choices: play safe or try your luck. The more your apps and games get successful the less luck you have.


Soap(Posted 2011) [#13]
So despite all this, I'm working on implementing this. My question is on implementation. After purchasing, what's the best way to check to see if the purchase when through? Right now the only thing that I can think of is to use a timer to reload the InitInAppPurchases() function then to call isProductPurchased()


MikeHart(Posted 2011) [#14]
I would let the user decide and implement its own logic. Just have InitInAppPurchases() and isPrductPurchased available for the user.


Soap(Posted 2011) [#15]
Is what I'm talking about the best/only method?


Soap(Posted 2011) [#16]
Ugh, for some reason it crashes when I try to implement it. I've added this function:
String getFailedTransactionError(void) {
return String(failedTransactionError);
}
But I'm pretty sure this wouldn't cause it to crash. failedTransactionError is a string that I use to check the progress. I don't think that this would cause it to crash. Testing it with a sandbox account it crashed after pressing the button to verify the payment info.
After that it says, "You've already purchased this In-App Purchase but it hasn't been downloaded. [Environment Sandbox]"
Any thoughts?


Soap(Posted 2011) [#17]
I found the problem. This is what I've had to do to get it working.
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{

//[[MKStoreManager sharedManager] provideContent:transaction.payment.productIdentifier
// forReceipt:transaction.transactionReceipt];

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}


MikeHart(Posted 2011) [#18]
I am glad you keep working on it.


Soap(Posted 2011) [#19]
NSException raise:format:arguments:] + 62
[NSException raise:format:] + 28
[NSString substringFromIndex:] + 86
According to this, there's a problem with the way the NSString is formated. I haven't worked with Objective C before so I don't know how to fix it.

I wonder however, if this problem comes from itunes connect. I haven't submitted a binary but submitting the IAP to open it up. I think my app was rejected. I'll have to see about that but I wonder if not receiving a proper receipt would cause it to crash.


Dabz(Posted 2011) [#20]
Would be great to this working, keep going! :)

Dabz


anawiki(Posted 2011) [#21]
I'm getting back to working on this module again. I regret that I got a brake. It seemed to work fine 2 months ago, now I have to find out the reasons what's wrong again :(


anawiki(Posted 2011) [#22]
Oh, iOS seems to be a bit like Windows when it comes to IAP. Reinstalling your app helps to solve issues. I lost one day trying to figure out why my IAP are returning invalid ID's, just to find out that after deleting app from iPad and reinstalling it through xcode solved this issue.

Don't you love this... :D


Dabz(Posted 2011) [#23]
Oh, so it is working, is there a guide/code or anything laid about for me to peek at, as I'm now 100% committed to going this route on iOS with my new game! :)

Dabz


anawiki(Posted 2011) [#24]
I'm making some changes to this module. While it's triggering purchase functions it fails to check "isProductPurchased" as Soap already found out. It looks like I found a solution, but before I upload new code I want to give it a bit more testing. I would just have to answer all those support emails, and believe me, IAP causes a lot of support issues (just check on net how many people struggle with IAP).


Dabz(Posted 2011) [#25]
Rightio... But, when you've sussed it, any chance you can throw a linky up! :)

Dabz


anawiki(Posted 2011) [#26]
Here's the latest version of IAP module. I tested it and it works with both consumables and non consumables. Make sure to set up everything in iTunes correctly and copy it to your code without mistakes.

http://innotech.pl/anawiki/4people/iap.zip

You need to init app store first like that:

InitInAppPurchases("com.mycompany.bundleid.", ["com.mycompany.bundleid.prod1", "com.mycompany.bundleid.prod2"])


and then you can call buyProduct("com.mycompany.bundleid.prod1")

and check if it was purchased with isProductPurchased("com.mycompany.bundleid.prod1")

isProductPurchased doesn't work for consumables.

There are also other functions that might be helpful for you, but I didn't have time to document them.

If you see "Problem in itunes configuration" in console, make sure your iPad is connected to internet, or try to delete app from device, clean build in xcode, make sure you have right bundleid in plist.

You are free to send me gifts, etc. ;-)


Dabz(Posted 2011) [#27]
Cor... Thanks for putting work into that, I'll have a looksy ASAP! :)

Dabz


Bladko(Posted 2011) [#28]
i think that this info about Store Kit should be written somewhere :)

it would be wise to just add StoreKit and GameKit to monkeys target template project for iOS as a standard i think

anyway great job !!


Alex(Posted 2011) [#29]
Hey!

Can somebody please give a simple three-line example on how to use consumables? Should I use buyProduct anyway?

Can I name my consumable product "com.mycompany.appname.productname". Product name is not numbers.

And what should I change here?
// CONFIGURATION STARTS -- Change this in your app
//#define kConsumableBaseFeatureId @"com.mycompany.myapp."
//#define kFeatureAId @"com.mycompany.myapp.featureA"
//#define kConsumableFeatureBId @"com.mycompany.myapp.005"
// consumable features should have only number as the last part of the product name
// MKStoreKit automatically keeps track of the count of your consumable product


By the way, the
Function consumeProduct:Bool(product$, quantity)
should be like
Function consumeProduct:Bool(product$)
. Yes?

My english is not so good, so I'd appreciate your help!


Spacebuddy(Posted 2011) [#30]
Have you started working on Game Center? :)


Alex(Posted 2011) [#31]
Here's Game Center module:
http://www.monkeycoder.co.nz/Community/posts.php?topic=1823

It works fine for me :)


Alex(Posted 2011) [#32]
And also it gves me this when I build in monkey:

/main.build/ios/main.mm: In function 'void -[MKStoreObserver completeTransaction:](MKStoreObserver*, objc_selector*, SKPaymentTransaction*)':
/main.build/ios/main.mm:3754: warning: 'MKStoreManager' may not respond to '-provideContent:forReceipt:'
/main.build/ios/main.mm:3754: warning: (Messages without a matching method signature
/main.build/ios/main.mm:3754: warning: will be assumed to return 'id' and accept
/main.build/ios/main.mm:3754: warning: '...' as arguments.)
/main.build/ios/main.mm: In function 'void -[MKStoreObserver restoreTransaction:](MKStoreObserver*, objc_selector*, SKPaymentTransaction*)':
/main.build/ios/main.mm:3766: warning: 'MKStoreManager' may not respond to '-provideContent:forReceipt:'
/main.build/ios/main.mm: In function 'void -[MKStoreManager buyFeature:](MKStoreManager*, objc_selector*, NSString*)':
/main.build/ios/main.mm:3998: warning: 'MKStoreManager' may not respond to '-canCurrentDeviceUseFeature:'
/main.build/ios/main.mm:4008: warning: 'MKStoreManager' may not respond to '-enableContentForThisSession:'
/main.build/ios/main.mm: In function 'void -[MKStoreManager provideContent:forReceipt:](MKStoreManager*, objc_selector*, NSString*, NSData*)':
/main.build/ios/main.mm:4078: warning: 'MKStoreManager' may not respond to '-verifyReceipt:'
/main.build/ios/main.mm: At global scope:
/main.build/ios/main.mm:4258: warning: property 'productsList' requires method '-productsList' to be defined - use @synthesize, @dynamic or provide a method implementation
/main.build/ios/main.mm: In function 'bool consumeProduct(String)':
/main.build/ios/main.mm:4345: warning: 'MKStoreManager' may not respond to '-consumeProduct:'


Everything compiles, but doesn't work.

Maybe someone had something like this?


anawiki(Posted 2011) [#33]
Warnings aren't that important, though I may fix them at some point. The mod works but you need to make sure that you init IAP with proper function and that you have your iTunes connect and other things set up correctly. If you don't, it will never give you right results. If it compiles on device, then it should display some comments, like "Product blah" or "Problem in configuration" etc. Do you get that?


Alex(Posted 2011) [#34]
No, no comments.
Okay, warnings are unimportant, so they are not causing my trouble.

Can you please explain, how to use consumables?
I didn't figured out how to use functions.
Should my code goes like this?

If double_btn.Pressed
	
	consumeProduct("com.company.app.001")
					
Endif

If canConsumeProduct("com.company.app.001")

	Coins = Coins * 2

EndIf



Or should I use isProductPurchased() or buyProduct() for consumables too?


anawiki(Posted 2011) [#35]
You need to use buyProduct() and it will give you results similar to buying non consumables.

The version on the server has a little bug with consumables, I will upload newer version later.

Do you use InitInAppPurchases("com.company.app.", ["com.company.app.001"]) in OnCreate() in your App?

(it's possible that Init function is named different, but you know what I mean)

And it's best to start with non-comsumables. Once you get that working, you'll be able to get consumables without problems.


Alex(Posted 2011) [#36]
I got buyProduct() working! Thanks!

I have a button in my app that allows you to double your game coins.


If double_btn.Pressed		
	
      *consumeProduct or buyProduct?*

Endif
			
If *what function to check if it was bought?*

      Coins = Coins * 2

Endif



Can you, please, write three lines example where should I use canConsumeProduct() and consumeProduct() functions? And what are they for?
I simply can't figure out their meaning.


anawiki(Posted 2011) [#37]
I uploaded new version to the same URL:

http://innotech.pl/anawiki/4people/iap.zip

It has fixed a bug with consumables.

You buy consumables via buyProduct, and if it's just one unit, you can proceed with it just like with non-consumable.

If q > 1 then you can use canConsumeProduct or consumeProduct function to handle consumables (but still you need to buy them first).

canConsumeProduct() checks if there are any units available (from already purchased. consumeProduct() "eats" them.


Alex(Posted 2011) [#38]
It works!

Thank you! =)


Xaron(Posted 2011) [#39]
Hmm could you please provide a Monkey example?

I mean let's say I have a lite version and want to buy a full version. I guess I have to do the following?

Somewhere during onCreate?
InitInAppPurchases( "com.mycompany.bundleid.", ["com.mycompany.bundleid.prod1"] )


After pressing the appropriate button:
buyProduct( "com.mycompany.bundleid.prod1" )


So what then? What exactly is "prod1" here? I mean I know my com.mycompany.bundleid but that is .prod1 after that?

If I have a lite version and an extra full version how do I let him/her buy that full version? Or is it better to have the full version integrated into the lite version and just enable the features?

Thanks!


Alex(Posted 2011) [#40]
I'd make a full version with restriction.
Create a product in iTunes connect.
"com.yourcompany.yourapp.fullversion" for example

In the code I'd make it like this:
(actually I did, instead of my code was for permanent coin doubling)


-------main cycle

If buyfullversion_btn.Pressed		
	
	buyProduct("com.yourcompany.yourapp.fullversion")
	CheckBuy = True
	
Endif
		
If CheckBuy And isProductPurchased("com.yourcompany.yourapp.fullversion")
	
	CheckBuy = False
	
	'Code to unlock full version
	FullVersion = True
	
Endif

-------main cycle



Edit: Oops. just made a mistake :/


anawiki(Posted 2011) [#41]
Why do you call buyProduct 2 times in your code?


Alex(Posted 2011) [#42]
Yes, my bad. Fixed.


siread(Posted 2012) [#43]
Am I right in thinking that this mod cannot return descriptions or prices of your products? So if you want to list items in an in-game store you would have to hard-code the description/prices?


anawiki(Posted 2012) [#44]
Itprobably does not have functions to return prices or descriptions, but it doesn't mean you can nnot add them to it. At the start of your game the mod lists products and prices in debug console, so for sure it is doable.


siread(Posted 2012) [#45]
Ok, thanks Anawiki. I'm currently getting this error:
"Problem in iTunes connect configuration for product: com.newstargames.newstarsoccer.fullversion"

After calling:
InitInAppPurchases("com.newstargames.newstarsoccer.", ["com.newstargames.newstarsoccer.fullversion"])

It's been over 12 hours since I set up my non-consumable products. I have new provisioning ids. I submitted an app then rejected it. Do I have to do anything with this section?

// CONFIGURATION STARTS -- Change this in your app
//#define kConsumableBaseFeatureId @"com.mycompany.myapp."
//#define kFeatureAId @"com.mycompany.myapp.featureA"
//#define kConsumableFeatureBId @"com.mycompany.myapp.005"
// consumable features should have only number as the last part of the product name
// MKStoreKit automatically keeps track of the count of your consumable product



anawiki(Posted 2012) [#46]
That section is irrelevant, I replaced it with InitInAppPurchases because I thought that data driven behavior is much more pro than in code settings.

Sign out of iStore on device. Delete app from device. Select Clean build in xcode. Reinstall. If it doesn't help and you have all correctly set up then you need to wait.


siread(Posted 2012) [#47]
I believe it is all set up correctly, although I can't really say that I understand all the certificates and provisioning profile stuff for iOS. :/

I'll give it some more hours.


siread(Posted 2012) [#48]
Still not working. I've decided to delete the old consumable product and create a new one. Have uploaded a new binary (won't cancel it just yet) so App and IAP are both 'Waiting for review'. Now I play the waiting game again. :(


siread(Posted 2012) [#49]
OMG, I think it's working. Instead of this...

InitInAppPurchases("com.newstargames.newstarsoccer.", ["com.newstargames.newstarsoccer.fullversion"])

I did this...

InitInAppPurchases("com.newstargames.newstarsoccer.", ["fullversion"])

I just spent 2 days figuring that out. Now to get the actual purchasing part sorted...


anawiki(Posted 2012) [#50]
Funny thing is that you typed it correctly in the forum post :D


siread(Posted 2012) [#51]
No I think you misunderstood. InitInAppPurchases only works if I use the short version (without the bundle id) for the product name:
InitInAppPurchases("com.newstargames.newstarsoccer.", ["fullversion"])

It's the same with buyProduct. Only this format works:
buyProduct("fullversion")

However, it crashed after attempting to buy:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSCFString substringFromIndex:]: Range or index out of bounds'

And now InitAppPurchases does nothing, when trying to buy it does nothing at all (no sign-in request, no error) and isProductPurchased("fullversion") returns 0.


anawiki(Posted 2012) [#52]
It crashes because it needs long version (the bug complains about lack of dot in the name). Don't know why long version doesn't work for you. At least long version works for me.


siread(Posted 2012) [#53]
I THINK I've sorted it. Basically I set up a new product id like this "noncon.fullgame". Then got results using the following methods...

InitInAppPurchases("com.newstargames.newstarsoccer.", ["noncon.fullgame"])
buyProduct("noncon.fullgame")

A dot in the product id seems to have fixed the crash. So did you set up your own product ids in iTunes Connect with the full bundle id or just with a single name?


siread(Posted 2012) [#54]
*sigh* I spoke too soon. Everytime I start the app I need to purchase a non-consumable again. I presume calling restorePurchasedProducts() after Init should mean that isProductPurchased() would return true, but it doesn't.

I'm nearly at the end of my tether with this. Has anyone used it successfully for a game actually on the store?


anawiki(Posted 2012) [#55]
Yes, I used it successfully for The Perfect Tree on iPad (and unreleased yet Avalon Legends Solitaire). I set up IAP with full bundle name and IAP name, like this:

IAP-non-con: com.anawiki.perfecttree.unlock
bundle id: com.anawiki.perfecttree

I call InitInAppPurchases in OnCreate(). To check if product was purchased I use isProductPurchased(). restorePurchase() should be used only if app was deleted from device and previously purchased. You should not call restore every time your app starts.


siread(Posted 2012) [#56]
Ahh, I give up. I only get any hint of success by using the short ids (init app returns the price ok) but then it causes nothing but trouble after that. Maybe I'll try again after releasing a straight forward, paid app.


siread(Posted 2012) [#57]
Aha! So you DO have to set it up the product id in iTunes with the full bundleid! Finally I think I have it working. :)

PS I lied earlier. I hadn't given up. ;)


EdzUp(Posted 2012) [#58]
does anyone have a simple code example for this as it would be a great help :) All I need is initialisation, checking purchases and purchasing an item :)


Xaron(Posted 2012) [#59]
I second that. Would love to see a simple example! Please! :)


siread(Posted 2012) [#60]
Very hectic times for me at the moment after releasing my game yesterday but I will try to write up a full description if no-one else does. :)


EdzUp(Posted 2012) [#61]
@siread: Thanks it will be a great help :)


Xaron(Posted 2012) [#62]
I appreciate it as well! :)


siread(Posted 2012) [#63]
Si's guide to using Anawiki's IAP module...

Ok, I'm assuming that you have your app and bundle id all set up in iTunes Connect. So the first thing to do is create an in-app-purchase product. There are two types, both of which can be used via the Monkey module...

Non-consumable
Non-consumable products are items that are purchased once, then the customer should never have to purchase them again. For instance my game has a career mode that you need to unlock to play, but once unlocked the customer can re-install the game, download on another device or whatever and so long as he is using the same iTunes username he should be able to access the career mode. (I'll go into how you restore consumable purchases at the end.)

Consumable
Consumable products are things like in-game currency, fish food, ammo, whatever. The customer can purchase the item, use it up in the game, then purchase some more. In New Star Soccer I have in-game currency called 'Star Bux' which you can buy in packs of 25, 50, 100, 250, 500, 1000. (Surprisingly the 1000 pack has sold the most units even though it costs the most! I will need to include some higher priced packs in the next version!)

Setting up your products
IMPORTANT! The product id must include your FULL bundle id. For instance, my bundle id is com.newstargames.newstarsoccer. Therefore my (non-consumable) career mode product id is com.newstargames.newstarsoccer.careermode. You need to enter that complete id when setting up the product in iTunes Connect (which is where I went wrong and was left completely stumped for days!)

The original code from Mugunth Kumar on which the IAP module is based can handle how many units that the customer has purchase and how many he has left to consume, but the IAP module cannot do this so you need to assume that they are buying one unit (ie one pack of 25 star bux, rather than 25 individual star bux). This is fine because your game code can simply add the number of units purchased to the game file keep track of how many units (star bux) he has left.

So when setting up a consumable product ID you need to do it this way:
com.newstargames.newstarsoccer.starbux_1.1

I also have:
com.newstargames.newstarsoccer.starbux_2.1
com.newstargames.newstarsoccer.starbux_3.1
com.newstargames.newstarsoccer.starbux_4.1
com.newstargames.newstarsoccer.starbux_5.1
com.newstargames.newstarsoccer.starbux_6.1

The .1 at the end is used by the mod to determine that this is a 1 unit product. The player will purchase it and consume it immediately, but of course his save file will be updated with 25 bux or whatever. (My game knows that com.newstargames.newstarsoccer.starbux_1.1 refers to the 25 bux pack. com.newstargames.newstarsoccer.starbux_2.1 refers to the 50 bux pack and so on.)

IMPORTANT! When you create your IAP do not upload a screenshot. This will mean that you can test your IAPs in Sandbox mode without having to upload a game binary. (You will need to create a test user in iTunes Connect. Also you need to go to Settings > Store in your iOS device and sign out of your normal iTunes account, then sign-in with your Test User account when prompted in the game.)

The Code
Ok, so you have your products set up. Obviously you need to:
Import iap


Then in OnCreate you need to do something like this:
#If TARGET="ios"
	'Initialise the mod with your bundle id and the list of products
	InitInAppPurchases("com.newstargames.newstarsoccer.", ["com.newstargames.newstarsoccer.careermode", 
"com.newstargames.newstarsoccer.starbux_1.1"])

	'gIAP_CareerMode is just a flag to record if the career mode is locked or not
	If isProductPurchased("com.newstargames.newstarsoccer.careermode")	
		gIAP_CareerMode = True
		TPlayer.SaveGlobalData()
	End
#End


Note: I am just showing the code for career mode and star bux 25, to keep it simple.

It is important that you set up InitAppPurchases precisely, so that your bundle id has a dot at the end, and your product ids include the full id, and your consumables products have .1 at the end.

Ok, so now you have to create the code to allow the player to purchase items. I have a shop screen where the player can browse the items. When they click BUY on an item you call the code:
buyProduct("com.newstargames.newstarsoccer.careermode")

Or
buyProduct("com.newstargames.newstarsoccer.starbux_1.1")


Your game should then wait whilst the mod does it's thing. The isPurchaseInProgress() call will return true if the mod is displaying the "Do you want to buy this item for 69p?" window of if it is connecting with iTunes, so whilst that is happening you should probably disable the game interface or at least show a spinning icon. I do this by drawing a spinning star and my button clicks do nothing if isPurchaseInProgress() returns true.

Function Update()
	If isPurchaseInProgress()
		spinner += 1.0				' Rotate spinning star
		If spinner > 360 Then spinner = 1
	Elseif spinner > 0
		' Purchase was initiated but is no longer in progress, so check for purchase
		If Not gIAP_CareerMode And isProductPurchased("com.newstargames.newstarsoccer.careermode")
			gIAP_CareerMode = True
			PlayMySound(sndCash, CHN_FX)
			TPlayer.SaveGlobalData()
			
		Elseif canConsumeProduct("com.newstargames.newstarsoccer.starbux_1") ' Note! .1 is not needed here
			' Consumable products should be consumed
			consumeProduct("com.newstargames.newstarsoccer.starbux_1")

			' And the "units" should be stored in the save file
			PlayMySound(sndCash, CHN_FX)
			player.UpdateBank(25)
		End
		
		spinner = 0	' Reset spinner
		RefreshShop()	' This will update the shop items and bank label to show what has been purchased
	End
End

Function Draw()
	If spinner > 0
		' Darken screen
		SetAlpha(0.5)
		SetColor(0,0,0)
		DrawRect(0, 0, gGuiW, gGuiH)
		
		' Draw spinning star
		SetAlpha(1)
		SetColor(255,255,255)
		DrawImage(imgStar128, gGuiW - 72, 72, spinner, 1, 1)
	End
End


And that is pretty much about it. The only other thing you need to worry about is when a customer has purchased a non-consumable product and has re-installed the game or is installing it on a new iOS device. Then you need to have a button to restore their purchases. I display a message explaining to the player what the restore button is for, and if they click proceed it calls restorePurchasedProducts(). This is a bit flakey, because calling isPurchaseInProgress() has no effect even though the mod is connecting to iTunes to check their purchase history. That means you can't display a spinner or disable the interface reliably because you don't know when the restore function completes. I just have an Update function that continually checks isProductPurchased("com.newstargames.newstarsoccer.careermode") until the player backs out of the screen. isProductPurchased will eventually return true if the customer has purchased that product in the past. As I say, not ideal but it does work at least.

Important! I should also point out that when you have finished coding and testing your game, make sure you upload screenshot to iTunes Connect for your products. Then go into 'View Details' for your app and make sure your IAPs are all included BEFORE you upload the binary.

I hope that all helps! Massive thanks to Anawiki for creating this mod, and to Mugunth Kumar who created the original storekit code. Oh, and you can check out my game here...
http://itunes.apple.com/us/app/new-star-soccer/id498973162

I will post some sales stats soon. :)


EdzUp(Posted 2012) [#64]
Thanks siread a rather comprehensive explanation :)


Fred(Posted 2012) [#65]
Implemented a working IAP system in one day !
It seems that I had to renew my provisionning profiles to make it work.

Many thanks to siread and anawiki !


benmc(Posted 2012) [#66]
I don't really understand the requirement that the product ID have your app ID in it. Mine has a dash, for example, which means that I cannot use it for an IAP product ID. Am I dead in the water? I'm guessing that you can't change your product ID for an update or everyone will lose their saved state data.

EDIT: I don't seem to be having any problems getting this to work if my IAP ID is not exactly the same as my Bundle ID. So far so good.


frank(Posted 2013) [#67]
Does anyone know if this code is still working or it should be updated? I would like to use it , but if someone already knows that it's not working...


York(Posted 2013) [#68]
I just updated my app for iOs 6 and the code runs. There is no update needed.


frank(Posted 2013) [#69]
Does someone with more iOS / iAP experience know what skerrorunknown means?

When testing my app, I click on the purchase, spinner comes up, but then it says, in the NSLog:

skerrorunknown

and

Error: LibMobileGestalt computeUniqueDeviceId

I have no clue...

Edit: Partly fixed, apparently I needed to make a Test User BEFORE trying the app (makes some sense :). So small part solved... See my next message.


frank(Posted 2013) [#70]
What IAP ID am I supposed to use? I see very conflicing stories online, some say

BUNDLEID.IAPREFERENCE

others say to never do that, but do

IAPREFERENCE

only.

I tried both. The error changed from:

Error: LibMobileGestalt computeUniqueDeviceId

to

Problem with your iTunes connect setup.

But I don't get what it could be, I tried all combinations and they all give that skerrorunknown and then the iTunes connect setup error.

Edit; I 'm reading the manual now and see I'm supposed to use the Product ID (again, makes sense) ; testing now.

Edit2: As quite so often it was a matter of reading and trying.

TL;DR to save other people from hassle:

- in Itunes connect make a Test User
- logout from the Appstore on your test device
- add one or more IAP products via Itunes connect to your app => Reference name can be anything, the Product ID is what you need
- Add the product IDs in your app like above and everything works like a charm

If you did any of the above wrong, you'll get that nice Unknown error; not sure why they just cannot tell me what is wrong in a normal error message I can use ...

For buying credits (over and over again), use Consumables, for switching off ads (for instance), use non-consumables.


SLotman(Posted 2013) [#71]
I'm just looking at IAP now, and found this thread... this looks promising, but one thing got me worried:


The original code from Mugunth Kumar on which the IAP module is based can handle how many units that the customer has purchase and how many he has left to consume, but the IAP module cannot do this so you need to assume that they are buying one unit (ie one pack of 25 star bux, rather than 25 individual star bux). This is fine because your game code can simply add the number of units purchased to the game file keep track of how many units (star bux) he has left.



This is a BIG problem for consumables! If a player buy 25 star bux, then go and install the game on another device, he won't have those credits anymore.

Is there a link to the original code, said to actually deal with this? Maybe the module could be updated to handle this as it should be, querying the server if the product can be consumed or not.


Shinkiro1(Posted 2013) [#72]
Just wanted to let everybody know that this module still works :)
I had nearly the same problems as frank but after a few hours of battling they go away.

Some useful info:
getPurchaseResult() will return a number between 0 and 4 which represents the last state of the transaction.
0 or 1: no purchase happend yet
2: transaction succeded
3: transaction failed
4: restore purchases (not sure what this does but i assume you can restore purchases from other devices)

Sometimes you just have to wait. Seriously! I was testing the same game without recompiling and suddenly it was all up and running.

Thanks to anawiki for this great module.
Also I wouldn't have done it without sireads tutorial :)


Xaron(Posted 2013) [#73]
Great. Hope so, Mark will have this soon in the official Monkey release. :)


Raul(Posted 2013) [#74]
hey, I tried to make a test and I receive this error:

** BUILD FAILED **


The following build commands failed:
Ld build/Debug-iphonesimulator/MonkeyGame.app/MonkeyGame normal i386
TRANS FAILED: Error executing 'xcodebuild -configuration Debug -sdk iphonesimulator', return code=16640
(1 failure)

If I remove the code for IAP is working fine..


rIKmAN(Posted 2013) [#75]
Did you delete the iOS .build folder when compiling with the new code from Monkey?


Raul(Posted 2013) [#76]
I did not even had a previously iOS build folder..


Raul(Posted 2013) [#77]
also before that error I receive this one:

/Users/User/Desktop/monkeySundao/Sundao/main.build/ios/main.mm:4354:1: warning: autosynthesized property 'productsList' will use synthesized instance variable '_productsList', not existing instance variable 'productsList' [-Wobjc-autosynthesis-property-ivar-name-match]
@implementation MKStoreManager
^
/Users/User/Desktop/monkeySundao/Sundao/main.build/ios/main.mm:4209:25: note: property declared here
@property (copy) NSSet *productsList;
** BUILD FAILED **




Shinkiro1(Posted 2013) [#78]
Open the project in xcode and try adding i386 and armv6 as valid architectures.
It's under the "Build Settings" tab in "Architectures" and then add them to "Valid Architectures".
Be sure to clean your product, delete the app from your device and then run again.

Further reading: http://stackoverflow.com/questions/3549478/iphone-compiler-fails-no-architectures-to-compile-for


Edit: And make sure the AppID you use for building has IAP rights/entitlements.


Raul(Posted 2013) [#79]
cannot do that.

All I have in Architectures in Standard (armv7)


Xaron(Posted 2013) [#80]
Then remove that line and add armv6 with your keyboard. :)


Raul(Posted 2013) [#81]
Actually the issue was for missing the StoreKit framework..

I added and tried to run on simulator. When I want to purchase an item I receive the message: IAP is not running in simulator. I assume I can only test on a real device, right?


Xaron(Posted 2013) [#82]
Yes, that's true.


Raul(Posted 2013) [#83]
I had the same skerrorunknown error.

I signed out from Settings->Store.
I created an IAP for my testing app from store.
I compiled the code from Monkey
I opened xcode, I added the StoreKit Framework..
I changed the product name to match the product ids from store..

and when I try to purcahse, in xcode console I receive the skerrorunknown.. and that the itunnes setup is wrong

EDIT:
My IAP has status: Waiting for Screenshot. I hope this is ok for sandboxing, right?


rIKmAN(Posted 2013) [#84]
In sireads mini-tutorial post above he says...


IMPORTANT! When you create your IAP do not upload a screenshot. This will mean that you can test your IAPs in Sandbox mode without having to upload a game binary. (You will need to create a test user in iTunes Connect. Also you need to go to Settings > Store in your iOS device and sign out of your normal iTunes account, then sign-in with your Test User account when prompted in the game.)


...so I assume waiting for screenshot is correct.

Have you setup the Test User in iTunes Connect?


Raul(Posted 2013) [#85]
@rIKmAN:
Yes I have it.

I talked to a friend of mine who is an xcode developer and he helped me.

I have to share here with you my experience:
-It doesnt matter if you have a screenshot or not of the IAP
-Also in the iap.monkey it says:
' IMPORTANT: If your app is not "Waiting for review" in iTunes Connect you won't be able to test In App Purchases. Once you get that status you can reject your binary and test peacefully. How weird! 

not more applicable. In fact the status of my application is now: Binary Rejected.

- The target iOS must be 6.0. Initially I tested on my friends iPhone 4 which has iOS.5. That's why I receive that Unkown Error message.
Anyway from September Apple will not support anymore iOS<6 so if it is working on 6.0 it is ok.

- Also to run the IAP in simulator, just open "MKStoreKit.cpp" and search for:
NSLog(@"You are running in Simulator MKStoreKit runs only on devices");

and remove the "#if TARGET_IPHONE_SIMULATOR"

This is usefull if you do not have always a device.. like me.

Hope this will help other, too.

BUT, there is also an issue here:
While trying to upload my app to the itunnes connect we received in that 'Package Maker and verification' an error which told us that we cannot continue because of using the 'uniqueID' I have no idea what should be the next step. I just removed the features from MKStoreKit.cpp


Alex(Posted 2014) [#86]
Does anyone tested the module on iOS 7?

I have this in debug:
2014-02-26 18:00:59.112 MonkeyGame[5111:60b] state failed
2014-02-26 18:00:59.114 MonkeyGame[5111:60b] SKErrorUnknown



Xaron(Posted 2014) [#87]
Not needed anymore. Monkey has its own cross platform in app stuff now!


Alex(Posted 2014) [#88]
Thanks Xaron!

I just leave this link here:
http://www.monkeycoder.co.nz/Community/posts.php?topic=7708