Working In-App Purchases for iOS

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Working In-App Purchases for iOS

Post by spill »

I recently released a game that uses my own implementation of in-app purchasing for löve on iOS. I figured other people might benefit from looking at or using my code. This is a modification of the löve 0.10.1 source code. The basic structure is a pair of new files (InAppPurchase.h/m) that define an IAPResponder (which is instantiated in ios.mm) that is used to call Apple's purchasing API and answer queries about things the user has purchased, and modifications to love.system (System.h/cpp, wrap_System.cpp) to include 3 new functions: love.system.hasPurchase(productIdentifier), love.system.makePurchase(productIdentifier), and love.system.restorePurchases(). Also, I think the project requires the Foundation and AVFoundation frameworks. Finally, Apple requires any game with in-app purchases to include a button that users can press to restore purchases, so if you do include this code, be sure to have a user-visible button that calls love.system.restorePurchases().

Known issues:
  • • It would be nice if the API allowed for a callback when the purchase is completed, or to hang the game until the purchase is completed and return the result, but it gets somewhat complicated because Apple's purchasing API is asynchronous.

    • Because purchasing is not instantaneous (it depends on the user entering their credentials, as well as network delay as the phone contacts Apple to confirm the purchase), it's possible to call love.system.makePurchase(), and then immediately afterwards call love.system.hasPurchase() and get an incorrect result. I work around this by requiring the user to push a button when they've finished making the purchase, and then delaying for a short time as an extra safety buffer.

    • There is no way to return a list of all purchases the user owns. This is easy enough to work around by just keeping a list of product identifiers in the game's code, and querying each one individually.

    • There is no support for consumable purchases (i.e. purchases that can be made many times, instead of just once). This is directly related to the lack of support for callbacks/hanging the game while the purchase is underway.
Here are all the relevant files:
love_ios_IAP.zip
(16.39 KiB) Downloaded 246 times
Also, I found this tutorial to be very helpful during the process of getting IAP to work.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Working In-App Purchases for iOS

Post by raidho36 »

Is there a reason it's not a dynamically loaded library and a FFI wrapper? I don't feel like using a whole custom build just to get this one orthogonal feature.
User avatar
zorg
Party member
Posts: 3444
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Working In-App Purchases for iOS

Post by zorg »

Now i might be wrong on the following, but here's my ideas:
spill wrote:• It would be nice if the API allowed for a callback when the purchase is completed, or to hang the game until the purchase is completed and return the result, but it gets somewhat complicated because Apple's purchasing API is asynchronous.
spill wrote:• Because purchasing is not instantaneous (it depends on the user entering their credentials, as well as network delay as the phone contacts Apple to confirm the purchase), it's possible to call love.system.makePurchase(), and then immediately afterwards call love.system.hasPurchase() and get an incorrect result. I work around this by requiring the user to push a button when they've finished making the purchase, and then delaying for a short time as an extra safety buffer.
I don't think hanging is a good idea, since no feedback (or rather, my app is hanging feedback) is a pretty bad design choice in my opinion.
It should be easier to use an async API to do synchronous things than the other way around; if anything, this would need something like an inner polling loop that would check the "far-end" whether the purchase was valid or not... or at least if it went through; and then call a callback function one can define, with a parameter whether the purchase worked or not... or something. Still, it would mean that it would need a network handling loop embedded, which may cause some concern... but yeah, IAP, can't really work without it.
spill wrote:• There is no way to return a list of all purchases the user owns. This is easy enough to work around by just keeping a list of product identifiers in the game's code, and querying each one individually.
If it can be done without any chance of meaningful tampering, then yeah, easy fix.
spill wrote:• There is no support for consumable purchases (i.e. purchases that can be made many times, instead of just once). This is directly related to the lack of support for callbacks/hanging the game while the purchase is underway.
Even with a polling loop, you'd need to block sending the same type of purchase until the last one of its kind didn't signal back from the remote server; though i'd implement that in a non-disruptive way, like having the function return false if it can't send the purchase.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Working In-App Purchases for iOS

Post by spill »

raidho36 wrote:Is there a reason it's not a dynamically loaded library and a FFI wrapper? I don't feel like using a whole custom build just to get this one orthogonal feature.
Doing IAP requires the use of native Apple APIs and frameworks, and I don't know whether it would be possible to access those through FFI (granted, I don't have any experience with FFI). I went with the source code modification, as it was the simplest approach for me. Also, if you're planning to distribute a game on the app store, you'll need to do a custom build anyway to fuse the game and change all of the game-specific options like the app icon, etc.
zorg wrote:I don't think hanging is a good idea, since no feedback (or rather, my app is hanging feedback) is a pretty bad design choice in my opinion.
It's how love.window.showMessageBox() works. The lua code hangs on the line where showMessageBox() was called until the user selects one of the options, at which point the lua function returns the result and code execution resumes there. For purchasing, I could easily see it working the same way, except with an extra step that after the user confirms the purchase and enters their credentials, it shows another dialog box saying "completing purchase..." with a "cancel" button the user could press to abort the process. Then, the lua function would return the result of the purchasing attempt (e.g. "success", "canceled", "failed"). The way my code works now, it calls the Apple API to open up the purchasing dialog, and then the lua code immediately continues running in the background while the dialog box is still open, which is a bit wonky.
zorg wrote:Still, it would mean that it would need a network handling loop embedded, which may cause some concern...
So, behind the scenes, Apple is already doing the networking async callback stuff anyway, using SKPaymentTransactionObserver. In theory, it should be possible to insert a "love.purchase" event into the SDL event queue once the app gets the transaction callback. Right now, the way I'm implementing it is by updating NSUserDefaults to store which purchases have been made when the Objective C callback is called, and querying NSUserDefaults when the user calls love.system.hasPurchase(). I opted to not go with the SDL event queue option because it adds a lot of complexity to the code if the Objective C callback has to have access to the SDL event queue (and deal with the concurrency issues there).
User avatar
zorg
Party member
Posts: 3444
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Working In-App Purchases for iOS

Post by zorg »

spill wrote:It's how love.window.showMessageBox() works. The lua code hangs on the line where showMessageBox() was called until the user selects one of the options, at which point the lua function returns the result and code execution resumes there. For purchasing, I could easily see it working the same way, except with an extra step that after the user confirms the purchase and enters their credentials, it shows another dialog box saying "completing purchase..." with a "cancel" button the user could press to abort the process. Then, the lua function would return the result of the purchasing attempt (e.g. "success", "canceled", "failed"). The way my code works now, it calls the Apple API to open up the purchasing dialog, and then the lua code immediately continues running in the background while the dialog box is still open, which is a bit wonky.
Okay, but one could just not use the modal messagebox function, and just implement a visual one with löve itself, that way it may not block.
spill wrote:In theory, it should be possible to insert a "love.purchase" event into the SDL event queue once the app gets the transaction callback. ... I opted to not go with the SDL event queue option because it adds a lot of complexity to the code if the Objective C callback has to have access to the SDL event queue (and deal with the concurrency issues there).
Understandable.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Working In-App Purchases for iOS

Post by raidho36 »

spill wrote: Doing IAP requires the use of native Apple APIs and frameworks, and I don't know whether it would be possible to access those through FFI (granted, I don't have any experience with FFI).
Why wouldn't it? All it does is loads native binaries from library files, the same way a native executable file does. Also, using native libraries from FFI code amounts to loading one with ffi.load, declaring relevant header info via ffi.cdef, and calling declared functions via ffi module interface.
User avatar
spill
Prole
Posts: 27
Joined: Thu May 07, 2015 1:53 am
Contact:

Re: Working In-App Purchases for iOS

Post by spill »

zorg wrote:Okay, but one could just not use the modal messagebox function, and just implement a visual one with löve itself, that way it may not block.
Actually no, Apple doesn't let you do that. Presumably in order to prevent apps from making deceptive or confusing purchase dialogs (or stealing the user's credentials, maybe?), the only way to make a purchase through Apple involves using their native purchase modal dialog. There's no way to request a purchase without popping one up. It also fills in the region/currency-specific pricing, which is something you wouldn't want to do by hand.
radho36 wrote:Why wouldn't it? All it does is loads native binaries from library files, the same way a native executable file does. Also, using native libraries from FFI code amounts to loading one with ffi.load, declaring relevant header info via ffi.cdef, and calling declared functions via ffi module interface.
For starters, the framework that the purchasing uses is not included in löve by default, so you'd have to run a custom build anyway. Furthermore, ffi is for C only, right? The APIs that I'm using are in Objective C (also Swift). I'm not sure if it's possible or impossible, but I know it's not straightforward.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Working In-App Purchases for iOS

Post by raidho36 »

If you can implement it in C++ framework source, then you can implement it in C++ library. Also pretty sure the whole thing is already a library (which goes by the name StoreKit I suppose) and all you need to do is load it using FFI, and maybe add some Lua wrapper code.
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Working In-App Purchases for iOS

Post by bartbes »

spill wrote:I opted to not go with the SDL event queue option because it adds a lot of complexity to the code if the Objective C callback has to have access to the SDL event queue (and deal with the concurrency issues there).
Love's event queue is thread-safe, and you don't have to predeclare events either, that might be a good option.
spill wrote:
radho36 wrote:[...]ffi[...]
For starters, the framework that the purchasing uses is not included in löve by default, so you'd have to run a custom build anyway. Furthermore, ffi is for C only, right? The APIs that I'm using are in Objective C (also Swift). I'm not sure if it's possible or impossible, but I know it's not straightforward.
Well, it's possible, but it's definitely not easier. Since you'll likely be compiling LÖVE anyway, I don't see what there is to gain from using the ffi. And then there's the fact that jit is disabled on iOS, so the ffi will probably be slower, too.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Working In-App Purchases for iOS

Post by raidho36 »

I just don't favor using custom builds, is all. Maybe this can be implemented as core functionality instead, given that it's a fairly standard feature for mobile apps.

Also there really is no reason for mocking people like that.
Post Reply

Who is online

Users browsing this forum: No registered users and 62 guests