×
A new build of Corona SDK is now available to subscribers. Not a subscriber? Subscribe now.
CoronaSDK 2012.821 | Released: 23 May 2012, 2:01am | What's New | Download Now

InAppDemo

InAppDemo

Features demonstrated:
Corona In App Purchase support

Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
local ui = require("ui")
local store = require("store")   -- Available in Corona build #261 or later
 
local isSimulator = "simulator" == system.getInfo("environment")
 
display.setStatusBar( display.HiddenStatusBar )
 
-- Unbuffer console output for debugging 
io.output():setvbuf('no')  -- Remove me for production code
 
local titlecard
function showTitle()
    if isSimulator then
        local myAlert = native.showAlert(  "iTunes Store not available in Corona Simulator", 
                "Offline testing: see console for output.", 
                { "OK" } )
        end
    titlecard = display.newImage( "titlecard.png", true )
    titlecard.x = display.contentCenterX
    titlecard.y = display.contentCenterY
end
 
local bg
local validProducts, invalidProducts = {}, {}
local descriptionArea
 
-------------------------------------------------------------------------------
--  Product IDs should match the In App Purchase products set up in iTunes Connect.
--  We cannot get them from the iTunes store so here they are hard coded;
--  your app could obtain them dynamically from your server.
-------------------------------------------------------------------------------
local listOfProducts = 
{
    -- These Product IDs must already be set up in your store
    -- We'll use this list to retrieve prices etc. for each item
    -- Note, this simple test only has room for about 4 items, please adjust accordingly
    
    -- The iTunes store will not validate bad Product IDs 
        "com.anscamobile.NewExampleInAppPurchase.ConsumableTier1",
        "com.anscamobile.NewExampleInAppPurchase.NonConsumableTier1",
        "com.anscamobile.NewExampleInAppPurchase.SubscriptionTier1",
}
 
-------------------------------------------------------------------------------
-- Process and display product information obtained from store.
-- Constructs a button for each item
-------------------------------------------------------------------------------
function unpackValidProducts()
 
    -- Utility to build a buy button
    function newBuyButton (index)
        --      Handler for buy button 
        local buttonDefault = "buttonBuy.png"
        local buttonOver = "buttonBuyDown.png"
        local buyThis = function ( product )
            print ("Ka-ching! Purchasing " ..product)
            -- Purchase the item
            if store.canMakePurchases then
                store.purchase( {product} )
            else
                native.showAlert("Store purchases are not available, please try again later", 
                            { "OK" } )
            end
        end
        function buyThis_closure ( index )            
            -- Closure wrapper for buyThis() to remember which button
            return function ( event )
                    buyThis (validProducts[index].productIdentifier)             
                return true
            end        
        end
        local hideDescription = function ( )
            descriptionArea.text = "Select a product..."
        end
        local describeThis = function ( description )
            -- Display product description for testing
            print ("About this product:  " ..description)
            -- TODO wrap if necessary
            descriptionArea.text = description
            timer.performWithDelay( 2000, hideDescription)  
        end
        function describeThis_closure ( index )            
            -- Closure wrapper for describeThis() to remember which button
            return function ( event )
                    describeThis (validProducts[index].description)              
                return true
            end        
        end
        local formatted = string.format("%.2f", validProducts[index].price)
        local label = validProducts[index].title .. "  " ..formatted
        local myButton = ui.newButton{ 
                 default = buttonDefault, over = buttonOver,
                 onPress = describeThis_closure (index), onRelease = buyThis_closure (index),
                 text = "", textColor = {2, 0, 127, 255}, font="Marker Felt", size = 14, emboss = false
        }
        myButton:setReferencePoint(display.CenterLeftReferencePoint)
        myButton:setText(label)
        return myButton
    end
 
    -- Utility to build a restore button
    function newRestoreButton ()
        local buttonDefault = "buttonRestore.png"
        local buttonOver = "buttonRestoreDown.png"
        local restore = function ( product )
            -- Ask the iTunes Store to initiate restore transaction sequence
            print ("Restoring " )
            store.restore()
        end
        local hideDescription = function ( )
            descriptionArea.text = "Select a product..."
        end
        local describeThis = function ()
            -- Display info in description area
            print ("Test restore feature")
            descriptionArea.text = "Test restore feature"
            timer.performWithDelay( 2000, hideDescription)  
        end
            local label = "Test restore"
            local myButton = ui.newButton{ 
                     default = buttonDefault, over = buttonOver,
                     onPress = describeThis, onRelease = restore,
                     text = "", textColor = {255, 255, 1, 255}, font="Marker Felt", size = 14, emboss = false
            }
            myButton:setReferencePoint(display.CenterLeftReferencePoint)
            myButton:setText(label) 
            return myButton
    end
 
    print ("Loading product list")
    if not validProducts then
        native.showAlert( "In App features not available", "initStore() failed", { "OK" } )        
    else
        print ("Product list loaded")
        print( "Country: " .. system.getPreference( "locale", "country" ) )  
        -- Show store UI
        titlecard:removeSelf()
        bg = display.newImage( "storebg.png", true )
        bg.x = display.contentCenterX
        bg.y = display.contentCenterY        
        descriptionArea = native.newTextBox (10, 0.7*display.contentHeight, display.contentCenterX - 20, display.contentCenterY - 10)
        descriptionArea.text = "Select a product..."
        descriptionArea:setTextColor (2, 0, 127)
        descriptionArea.size = 18
        descriptionArea.hasBackground = false
        local buttonSpacing = 5
        print( "Found " .. #validProducts .. " valid items ")
        -- display the valid products in buttons 
        for i=1, #validProducts do            
            -- Debug:  print out product info 
            print ("Item " .. i .. ": " .. validProducts[i].productIdentifier 
                            .. " (" .. validProducts[i].price .. ")")
            print (validProducts[i].title .. ",  ".. validProducts[i].description)
 
            -- create and position product button
            local myButton = newBuyButton(i)
            myButton.x = display.contentWidth - myButton.width - buttonSpacing
            myButton.y = i * buttonSpacing + (2 * i - 1) * myButton.height / 2
        end
        -- create and position Restore button
        local myButton = newRestoreButton()
        myButton.x = display.contentWidth - myButton.width - buttonSpacing
        myButton.y = display.contentHeight - myButton.height / 2 - buttonSpacing
        
        for i=1, #invalidProducts do
            -- Debug:  display the product info 
            native.showAlert( "Item " .. invalidProducts[i] .. " is invalid.",
                            { "OK" } )
            print("Item " .. invalidProducts[i] .. " is invalid.")
        end
 
    end
end
 
-------------------------------------------------------------------------------
-- Handler to receive product information 
-- This callback is set up by store.loadProducts()
-------------------------------------------------------------------------------
function loadProductsCallback( event )
    -- Debug info for testing
        print("In loadProductsCallback()")
        print("event, event.name", event, event.name)
        print(event.products)
        print("#event.products", #event.products)
    io.flush()  -- remove for production
 
    -- save for later use
    validProducts = event.products
    invalidProducts = event.invalidProducts    
    unpackValidProducts ()
    
end
 
-------------------------------------------------------------------------------
-- Handler for all store transactions
-- This callback is set up by store.init()
-------------------------------------------------------------------------------
function transactionCallback( event )
    local infoString  
        print("transactionCallback: Received event ", event.name)
--[[
    -- Also available for your app to use:
        print("transaction", event.transaction)
        print("state", event.transaction.state)
        print("errorType", event.transaction.errorType)
        print("errorString", event.transaction.errorString)
--]]
--      print("testing", store.transactionStatePurchased, store.transactionErrorPaymentCancelled, store.transactionStateFailed )
 
        if event.transaction.state == "purchased" then
        infoString = "Transaction successful!"
                print (infoString)
        descriptionArea.text = infoString
        elseif  event.transaction.state == "restored" then
        -- Reminder: your app must store this information somewhere
        -- Here we just display some of it
        infoString = "Restoring transaction:" ..
                            "\n   Original ID: " ..event.transaction.originalTransactionIdentifier ..
                            "\n   Original date: "..event.transaction.originalDate
                print (infoString)
        descriptionArea.text = infoString
                print("productIdentifier", event.transaction.productIdentifier)
                print("receipt", event.transaction.receipt)
                print("transactionIdentifier", event.transaction.transactionIdentifier)
                print("date", event.transaction.date)
                print("originalReceipt", event.transaction.originalReceipt)
 
        elseif event.transaction.state == "cancelled" then
        infoString = "Transaction cancelled by user."
                print (infoString)
        descriptionArea.text = infoString
 
        elseif event.transaction.state == "failed" then        
        infoString = "Transaction failed, type: ", 
            event.transaction.errorType, event.transaction.errorString
                print (infoString)
        descriptionArea.text = infoString
        else
        infoString = "Unknown event"
                print (infoString)
        descriptionArea.text = infoString
        end
 
        -- Tell the store we are done with the transaction.
        -- If you are providing downloadable content, do not call this until
    -- the download has completed.
        store.finishTransaction( event.transaction )
end
 
-------------------------------------------------------------------------------
-- Setter upper 
-------------------------------------------------------------------------------
function setupMyStore (event)
    store.loadProducts( listOfProducts, loadProductsCallback )
    print ("After store.loadProducts, waiting for callback")
    
    if isSimulator then
        -- No Store, so no callbacks, so exercise our GUI "manually"  
        validProducts[1] = {}
        validProducts[1].title = "Lemonade refill"
        validProducts[1].description = "A wonderful dummy product for testing"
        validProducts[1].price = 0.99
        validProducts[1].productIdentifier = "com.lemonadestand.consumable.001"
        validProducts[2] = {}
        validProducts[2].title = "Drinking glass"
        validProducts[2].description = "Makes lemonade easier to drink."
        validProducts[2].price = 1.99
        validProducts[2].productIdentifier = "com.lemonadestand.nonconsumable.001"
        validProducts[3] = {}
        validProducts[3].title = "Daily dose"
        validProducts[3].description = "Made fresh daily!"
        validProducts[3].price = 19.99
        validProducts[3].productIdentifier = "com.lemonadestand.subscription.001"
        unpackValidProducts()   
    end
end
 
 
-------------------------------------------------------------------------------
-- Main
-------------------------------------------------------------------------------
 
-- Show title card
showTitle ()
 
-- Connect to store at startup
store.init (transactionCallback )
print ("After init")
 
-- Hide title card, run store
timer.performWithDelay (1000, setupMyStore)
 
collectgarbage()