iaptest.monkey2 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. #rem
  2. IMPORTANT!
  3. This wont work 'as is'! You'll need to set up a bunch of stuff on GooglePlay/iTunes Connect developer portal such as app/products etc.
  4. #end
  5. Namespace myapp
  6. #Import "<iap>"
  7. #Import "<std>"
  8. #Import "<mojo>"
  9. #Import "<mojox>"
  10. Using iap..
  11. Using std..
  12. Using mojo..
  13. Using mojox..
  14. Class MyWindow Extends Window
  15. Field _purchases:JsonObject
  16. Field _products:Product[]
  17. Field _info:String
  18. Field _listView:ListView
  19. Field _openStoreItem:ListView.Item
  20. Field _restorePurchasesItem:ListView.Item
  21. Field _iap:IAPStore
  22. Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null )
  23. Super.New( title,width,height,flags )
  24. Layout="stretch"
  25. 'Create our IAP products.
  26. '
  27. _products=New Product[](
  28. New Product( "speed_boost",ProductType.Consumable ),
  29. New Product( "bullet_boost",ProductType.Consumable ),
  30. New Product( "ship_upgrade",ProductType.NonConsumable ) )
  31. 'Load purchases we've already bought.
  32. '
  33. LoadPurchases()
  34. 'Create a ListView to fill in with products later...
  35. '
  36. _listView=New ListView
  37. _listView.Layout="float"
  38. _listView.Gravity=New Vec2f( .5,.5 )
  39. _listView.ItemClicked=Lambda( item:ListView.Item )
  40. If Not _iap Or _iap.Busy Return
  41. If item=_restorePurchasesItem
  42. _iap.GetOwnedProducts()
  43. Return
  44. Endif
  45. If item=_openStoreItem
  46. OpenStore()
  47. Return
  48. Endif
  49. Local index:= _listView.IndexOfItem( item )
  50. Local product:=_products[ index ]
  51. 'it's actually harmless to let someone purchase a non consumable again, they'll
  52. 'only be charged for it once, but still...
  53. If product.Type=ProductType.NonConsumable And _purchases.GetNumber( product.Identifier )>0 Return
  54. _iap.BuyProduct( product )
  55. _info="Buying product "+product.Identifier
  56. End
  57. ContentView=_listView
  58. OpenStore()
  59. End
  60. Method MakePurchase( product:Product )
  61. Select product.Type
  62. Case ProductType.Consumable
  63. _purchases.SetNumber( product.Identifier,_purchases.GetNumber( product.Identifier )+1 )
  64. Case ProductType.NonConsumable
  65. _purchases.SetNumber( product.Identifier,1 )
  66. End
  67. End
  68. Method OpenStore()
  69. _iap?.CloseStore()
  70. _iap=Null
  71. 'Create IAPStore and various handlers.
  72. '
  73. _iap=New IAPStore
  74. GCCollect()
  75. GCCollect()
  76. 'This is called when OpenStore completes.
  77. '
  78. _iap.OpenStoreComplete=Lambda( result:Int,interrupted:Product[],owned:Product[] )
  79. Print "OpenStoreComplete result="+result
  80. Select result
  81. Case 0
  82. 'Handle interrupted purchases - these are purchases that the user was charged for but your app never
  83. 'got notified about coz it or the OS crashed or something...
  84. '
  85. 'This array is always empty on ios which apparently handles this edge case better?
  86. '
  87. For Local product:=Eachin interrupted
  88. MakePurchase( product )
  89. Next
  90. 'Handle owned products - these are non-consumables that have been bought by the user. Android tracks these for
  91. 'you (as long as you're logged in anyway) so you don't strictly need to save them, but you do on ios so for the sake of
  92. 'uniformity we will on android too.
  93. '
  94. 'On Android, you never really need to use GetOwnedProducts() because you are given this array of owned products when the store
  95. 'opens. So in a sense, android automatically 'restores purchases' whenever you open the store.
  96. '
  97. 'This array is always empty on ios. To restore non-consumables on ios you must call GetOwnedProducts in response to the push
  98. 'of a 'Restore Purchases' button or similar. Apps with IAP but no 'restore purchases' mechanism will be rejected from the
  99. 'app store. GetOwnedProducts may cause dialogs to appear etc as the user may need to log into itunes, which is why it's not
  100. 'just done by default when the store opens the way it is on android.
  101. '
  102. For Local product:=Eachin owned
  103. MakePurchase( product )
  104. Next
  105. SavePurchases()
  106. _info="OpenStore successful"
  107. Default
  108. _info="OpenStore error:"+result
  109. End
  110. UpdateListView()
  111. End
  112. 'This is called when BuyProduct completes.
  113. '
  114. _iap.BuyProductComplete=Lambda( result:Int,product:Product )
  115. Select result
  116. Case 0 'success!
  117. MakePurchase( product )
  118. SavePurchases()
  119. UpdateListView()
  120. _info="BuyProduct successful:"+product.Identifier
  121. Case 1 'cancelled!
  122. _info="BuyProduct cancelled:"+product.Identifier
  123. Default
  124. _info="BuyProduct error:"+result
  125. End
  126. End
  127. 'This is called when GetOwnProducts completes.
  128. '
  129. _iap.GetOwnedProductsComplete=Lambda( result:Int,owned:Product[] )
  130. Select result
  131. Case 0 'success!
  132. 'Make sure we really own all owned products.
  133. '
  134. For Local product:=Eachin owned
  135. MakePurchase( product )
  136. Next
  137. SavePurchases()
  138. UpdateListView()
  139. _info="GetOwnProducts successful"
  140. Default
  141. _info="GetOwnedProducts error:"+result
  142. End
  143. End
  144. _iap.OpenStore( _products )
  145. _info="Opening store..."
  146. End
  147. Method LoadPurchases()
  148. 'load purchases file from internal app storage.
  149. '
  150. _purchases=JsonObject.Load( "internal::purchases.json" )
  151. 'if not found, create new purchases file.
  152. '
  153. If Not _purchases
  154. _purchases=New JsonObject
  155. For Local product:=Eachin _products
  156. _purchases.SetNumber( product.Identifier,0 )
  157. Next
  158. SavePurchases()
  159. Endif
  160. End
  161. Method SavePurchases()
  162. _purchases.Save( "internal::purchases.json" )
  163. end
  164. 'populate listview with products
  165. '
  166. Method UpdateListView()
  167. _listView.RemoveAllItems()
  168. _restorePurchasesItem=Null
  169. _openStoreItem=Null
  170. If _iap?.Open
  171. For Local product:=Eachin _products
  172. 'products become valid when store is opened.
  173. '
  174. 'If Not product.Valid Continue
  175. _listView.AddItem( product.Title+" - "+product.Description+" for the low price of "+product.Price+" ("+_purchases.GetNumber( product.Identifier )+")" )
  176. Next
  177. _restorePurchasesItem=_listView.AddItem( "[RESTORE PURCHASES]" )
  178. Endif
  179. _openStoreItem=_listView.AddItem( "[REOPEN STORE]" )
  180. End
  181. Method OnRender( canvas:Canvas ) Override
  182. RequestRender()
  183. canvas.DrawText( "Hello World!",Width/2,Height/2,.5,.5 )
  184. canvas.DrawText( "IAP info:"+_info,0,0 )
  185. End
  186. Method OnMeasure:Vec2i() Override
  187. Return New Vec2i( 640,360 )
  188. End
  189. End
  190. Function Main()
  191. New AppInstance
  192. New MyWindow
  193. App.Run()
  194. End