{ "id": "111571", "key": "AC-2710", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "12217", "key": "AC", "name": "Appcelerator - INBOX", "projectCategory": { "id": "10000", "description": "", "name": "Customer Service" } }, "resolution": { "id": "7", "description": "", "name": "Invalid" }, "resolutiondate": "2013-04-04T23:36:35.000+0000", "created": "2013-03-23T13:29:20.000+0000", "labels": [ "storekit" ], "versions": [], "issuelinks": [], "assignee": { "name": "shossain", "key": "shossain", "displayName": "Shak Hossain", "active": false, "timeZone": "America/Los_Angeles" }, "updated": "2016-03-08T07:41:49.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "14550", "name": "Appcelerator Modules", "description": "Please enter tickets related to Modules here." }, { "id": "14548", "name": "Titanium SDK & CLI", "description": "Please enter tickets related to the MobileSDK here." } ], "description": "Ti.Storekit's requestProducts don't return Ti.Storekit.Product object, returns product's description instead of Ti.Storekit.Product, so i cannot call purchase method in next step.\r\n\r\nFor example, In the followings code,\r\n{code}\r\npurchaseBtn.addEventListener('click', function(e) {\r\n\t\tstoreKit.requestProducts(['product1'], function (evt) {\r\n\t\t\tif (evt.success) {\r\n\t\t\t\tTi.API.info(evt);\r\n\t\t\t\tTi.API.info(JSON.stringify(evt));\r\n\r\n\t\t\t\tproduct = evt.products[0];\r\n\t\t\t\t\r\n\t\t\t}...\r\n{code}\r\n\r\nevt.products is:\r\n{code}\r\n{\r\n products = (\r\n \"You can to be use Full Function\"\r\n );\r\n source = \"[object TiStorekitProductRequest]\";\r\n success = 1;\r\n type = callback;\r\n}\r\n{code}\r\n{code}\r\n{\"type\":\"callback\",\"products\":[{}],\"source\":{},\"success\":true}\r\n{code}\r\nNot object.\r\n\r\nAppStore server returns Product's information normally.\r\n\r\nI attached sample code project, and capture result with AppStore.", "attachment": [ { "id": "36606", "filename": "IaPTest.zip", "author": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "created": "2013-03-23T13:29:20.000+0000", "size": 2154866, "mimeType": "application/zip" }, { "id": "36605", "filename": "sandbox_In-App-Purchase.xml", "author": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "created": "2013-03-23T13:29:20.000+0000", "size": 9356, "mimeType": "text/xml" } ], "flagged": false, "summary": "Ti.Storekit: requestProducts don't return Ti.Storekit.Product object", "creator": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "subtasks": [], "reporter": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "environment": "SDK: 3.0.2GA, Ti.Storekit: 1.6.4, iOS: 6.1, OSX 10.8.3", "comment": { "comments": [ { "id": "245838", "author": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Seiji,\nThe JIRA platform is here to report known bugs. If you have questions on how to implement a feature such as Ti.Storekit please refer to the Q&A: http://developer.appcelerator.com/questions/newest\nRegards,\nCarter", "updateAuthor": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-04-04T23:36:25.000+0000", "updated": "2013-04-04T23:36:25.000+0000" }, { "id": "245855", "author": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "body": "Hi,\n\nI already submitted Q&A: http://developer.appcelerator.com/question/149502/tistorekit-requestproducts-dont-return-tistorekitproduct-return-description\nYour support team recommends here, if I think this is a BUG.\nI think this a BUG happens on unknown conditions. Because it happens in 100% on my environment.\n\nRegards,\n-Seiji", "updateAuthor": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "created": "2013-04-05T00:43:04.000+0000", "updated": "2013-04-05T00:43:04.000+0000" }, { "id": "246440", "author": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Please reattachthe sample app. The one you have uploaded is the default Ti App, I think this might have been a mistake. I will then take another look at this.\n-Carter", "updateAuthor": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-04-08T17:33:37.000+0000", "updated": "2013-04-08T17:33:37.000+0000" }, { "id": "246444", "author": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "body": "This not default app. Please see ui/common/FirstView.js. This is very simple sample app.\nI have tried on my devices. of cause same result.\nIf you teach me your UDID, I can send you sample app of this code.", "updateAuthor": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "created": "2013-04-08T17:43:07.000+0000", "updated": "2013-04-08T17:43:07.000+0000" }, { "id": "246451", "author": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Seiji,\nI am not quite sure I understand your problem/code. In order to get the description you need to call Ti.StoreKit.Product.description and I dont see you doing that. Also can you clarify what your second piece of code is referring too? Is this the printed value of evt.products? Because I do not see where you printed that out in your code. All the information you need on how to set up Ti.StoreKit is available to you in the example folder within the module when you download it. I will attach it here as well again for you. Apple has not made an recent changes and this problem only seems to be unique to you at this time. This leads me to believe you are making an error somewhere. Please use the example app.js as a reference on how to use the StoreKit and your issue should go away. Any questions or comments, feel free to ask.\nRegards,\nCarter \n\napp.js\n{code}\n/*\n Learn the basics of Storekit with this example.\n\n Before we can do anything in our app, we need to set up iTunesConnect! This process can be a little painful, but I will\n guide you through it so you don't have to figure it out on your own.\n\n Follow these steps:\n\n 1) Log in to your Apple Developer account at https://itunesconnect.apple.com/\n 2) Click \"Manage Your Applications\".\n 3) If you have already set up your app, click on its icon. If not, click \"Add New App\" and set up your app.\n 4) Click \"Manage In-App Purchases\".\n 5) Click \"Create New\".\n 6) Click \"Select\" beneath \"Non-Consumable\".\n 7) In \"Reference Name\" type \"Soda Pop\", and in \"Product ID\" type \"DigitalSodaPop\".\n 8) Click \"Add Language\", and fill out all of the fields. (What you enter here isn't important to this example.)\n 9) Select a Price Tier, and upload a Screenshot. For testing purposes, using your app's splash screen is easiest.\n 10) Click \"Save\".\n 11) Click \"Create New\" again, and this time click \"Select\" beneath \"Auto-Renewable Subscriptions\".\n 12) Click \"Create New Family\" and fill out all of the required fields.\n 13) When asked, enter \"MonthlySodaPop\" for the Product ID, and save the product.\n\n iTunesConnect is now set up with at least two products with the IDs \"DigitalSodaPop\" and \"MonthlySodaPop\".\n\n Now we're ready to use Storekit in our app. We're going to do 5 different activities:\n\n 1) Checking if the user can make purchases.\n 2) Tracking what the user has purchased in the past.\n 3) Buying a single item.\n 4) Buying a subscription.\n 5) Restoring past purchases.\n\n Look at the JavaScript below to understand how we do each of these.\n\n Then, when you are ready to test the app, follow these steps:\n\n 1) Storekit works in two different environments: \"Live\" and \"Sandbox\". It automatically uses the \"Sandbox\" when you\n run your app in Xcode. This means that \"Deploy to Device\" in Titanium Studio will connect to the \"Live\" environment!\n Using a test account in \"Live\" will ruin the test account. And paying for items with a live account will quickly\n suck up your hard earned cash. So be careful!\n 2) Log in to your Apple Developer account at https://itunesconnect.apple.com/\n 3) Click \"Manage Users\" and create a \"Test User\".\n 4) Run your app in the simulator from Titanium Studio at least once.\n 5) In your app's directory, open up the build/iphone/yourProject.xcodeproj in Xcode.\n 6) In the top left of Xcode, change the \"Scheme\" to target your device.\n 7) Hit \"Run\" to test the Storekit module in the sandbox!\n\n */\n\nvar Storekit = require('ti.storekit');\n\n/*\n If you decide to perform receipt verification then you need to indicate if the receipts should be verified\n against the \"Sandbox\" or \"Live\" server. If you are verifying auto-renewable subscriptions then you need\n to set the shared secret for the application from your iTunes Connect account.\n */\n\nStorekit.receiptVerificationSandbox = true;\nStorekit.receiptVerificationSharedSecret = \"\";\n\nvar verifyingReceipts = false;\n\n\nvar win = Ti.UI.createWindow({\n\tbackgroundColor:'#fff'\n});\n\n/*\n We want to show the user when we're communicating with the server, so let's define two simple\n functions that interact with an activity indicator.\n */\nvar loading = Ti.UI.createActivityIndicator({\n\tbottom:10, height:50, width:50,\n\tbackgroundColor:'black', borderRadius:10,\n\tstyle:Ti.UI.iPhone.ActivityIndicatorStyle.BIG\n});\nvar loadingCount = 0;\nfunction showLoading()\n{\n\tloadingCount += 1;\n\tif (loadingCount == 1) {\n\t\tloading.show();\n\t}\n}\nfunction hideLoading()\n{\n\tloadingCount -= 1;\n\tif (loadingCount == 0) {\n\t\tloading.hide();\n\t}\n}\nwin.add(loading);\n\n/*\n Now let's define a couple utility functions. We'll use these throughout the app.\n */\nvar tempPurchasedStore = {};\n\n/**\n * Keeps track (internally) of purchased products.\n * @param identifier The identifier of the Ti.Storekit.Product that was purchased.\n */\nfunction markProductAsPurchased(identifier)\n{\n\tTi.API.info('Marking as purchased: ' + identifier);\n\t// Store it in an object for immediate retrieval.\n\ttempPurchasedStore[identifier] = true;\n\t// And in to Ti.App.Properties for persistent storage.\n\tTi.App.Properties.setBool('Purchased-' + identifier, true);\n}\n\n/**\n * Checks if a product has been purchased in the past, based on our internal memory.\n * @param identifier The identifier of the Ti.Storekit.Product that was purchased.\n */\nfunction checkIfProductPurchased(identifier)\n{\n\tTi.API.info('Checking if purchased: ' + identifier);\n\tif (tempPurchasedStore[identifier] === undefined)\n\t\ttempPurchasedStore[identifier] = Ti.App.Properties.getBool('Purchased-' + identifier, false);\n\treturn tempPurchasedStore[identifier];\n}\n\n/**\n * Requests a product. Use this to get the information you have set up in iTunesConnect, like the localized name and\n * price for the current user.\n * @param identifier The identifier of the product, as specified in iTunesConnect.\n * @param success A callback function.\n * @return A Ti.Storekit.Product.\n */\nfunction requestProduct(identifier, success)\n{\n\tshowLoading();\n\tStorekit.requestProducts([identifier], function (evt) {\n\t\thideLoading();\n\t\tif (!evt.success) {\n\t\t\talert('ERROR: We failed to talk to Apple!');\n\t\t}\n\t\telse if (evt.invalid) {\n\t\t\talert('ERROR: We requested an invalid product!');\n\t\t}\n\t\telse {\n\t\t\tsuccess(evt.products[0]);\n\t\t}\n\t});\n}\n\n/**\n * Purchases a product.\n * @param product A Ti.Storekit.Product (hint: use Storekit.requestProducts to get one of these!).\n */\nfunction purchaseProduct(product)\n{\n\tshowLoading();\n\tStorekit.purchase(product, function (evt) {\n\t\thideLoading();\n\t\tswitch (evt.state) {\n\t\t\tcase Storekit.FAILED:\n\t\t\t\tif (evt.cancelled) {\n\t\t\t\t\talert('Purchase cancelled');\n\t\t\t\t} else {\n\t\t\t\t\talert('ERROR: Buying failed! ' + evt.message);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Storekit.PURCHASED:\n\t\t\tcase Storekit.RESTORED:\n\t\t\t\tif (verifyingReceipts) {\n\t\t\t\t\tStorekit.verifyReceipt(evt, function (e) {\n\t\t\t\t\t\tif (e.valid) {\n\t\t\t\t\t\t\talert('Thanks! Receipt Verified');\n\t\t\t\t\t\t\tmarkProductAsPurchased(evt.productIdentifier);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\talert('Sorry. Receipt is invalid');\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\talert('Thanks!');\n\t\t\t\t\tmarkProductAsPurchased(product.identifier);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t});\n}\n\n/**\n * Restores any purchases that the current user has made in the past, but we have lost memory of.\n */\nfunction restorePurchases()\n{\n\tshowLoading();\n\tStorekit.restoreCompletedTransactions();\n}\nStorekit.addEventListener('restoredCompletedTransactions', function (evt) {\n\thideLoading();\n\tif (evt.error) {\n\t\talert(evt.error);\n\t}\n\telse if (evt.transactions == null || evt.transactions.length == 0) {\n\t\talert('There were no purchases to restore!');\n\t}\n\telse {\n\t\tfor (var i = 0; i < evt.transactions.length; i++) {\n\t\t\tif (verifyingReceipts) {\n\t\t\t\tStorekit.verifyReceipt(evt.transactions[i], function (e) {\n\t\t\t\t\tif (e.valid) {\n\t\t\t\t\t\tmarkProductAsPurchased(e.productIdentifier);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tTi.API.error(\"Restored transaction is not valid\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmarkProductAsPurchased(evt.transactions[i].productIdentifier);\n\t\t\t}\n\t\t}\n\t\talert('Restored ' + evt.transactions.length + ' purchases!');\n\t}\n});\n\n/*\n 1) Can the user make payments? Their device may be locked down, or this may be a simulator.\n */\nif (!Storekit.canMakePayments)\n\talert('This device cannot make purchases!');\nelse {\n\n\t/*\n\t 2) Tracking what the user has purchased in the past.\n\t */\n\tvar whatHaveIPurchased = Ti.UI.createButton({\n\t\ttitle:'What Have I Purchased?',\n\t\ttop:10, left:5, right:5, height:40\n\t});\n\twhatHaveIPurchased.addEventListener('click', function () {\n\t\talert({\n\t\t\t'Single Item':checkIfProductPurchased('DigitalSodaPop') ? 'Purchased!' : 'Not Yet',\n\t\t\t'Subscription':checkIfProductPurchased('MonthlySodaPop') ? 'Purchased!' : 'Not Yet'\n\t\t});\n\t});\n\twin.add(whatHaveIPurchased);\n\n\t/*\n\t 3) Buying a single item.\n\t */\n\trequestProduct('DigitalSodaPop', function (product) {\n\t\tvar buySingleItem = Ti.UI.createButton({\n\t\t\ttitle:'Buy ' + product.title + ', ' + product.formattedPrice,\n\t\t\ttop:60, left:5, right:5, height:40\n\t\t});\n\t\tbuySingleItem.addEventListener('click', function () {\n\t\t\tpurchaseProduct(product);\n\t\t});\n\t\twin.add(buySingleItem);\n\t});\n\n\t/*\n\t 4) Buying a subscription.\n\t */\n\trequestProduct('MonthlySodaPop', function (product) {\n\t\tvar buySubscription = Ti.UI.createButton({\n\t\t\ttitle:'Buy ' + product.title + ', ' + product.formattedPrice,\n\t\t\ttop:110, left:5, right:5, height:40\n\t\t});\n\t\tbuySubscription.addEventListener('click', function () {\n\t\t\tpurchaseProduct(product);\n\t\t});\n\t\twin.add(buySubscription);\n\t});\n\n\t/*\n\t 5) Restoring past purchases.\n\t */\n\tvar restoreCompletedTransactions = Ti.UI.createButton({\n\t\ttitle:'Restore Lost Purchases',\n\t\ttop:160, left:5, right:5, height:40\n\t});\n\trestoreCompletedTransactions.addEventListener('click', function () {\n\t\trestorePurchases();\n\t});\n\twin.add(restoreCompletedTransactions);\n\n\t/*\n\t 6) Receipt verification.\n\t */\n\tvar view = Ti.UI.createView({\n\t\tlayout:'horizontal',\n\t\ttop:210,\n\t\tleft:10,\n\t\twidth:'auto',\n\t\theight:'auto'\n\t});\n\tvar verifyingLabel = Ti.UI.createLabel({\n\t\ttext:'Verify receipts:',\n\t\theight:Ti.UI.SIZE || 'auto',\n\t\twidth:Ti.UI.SIZE || 'auto'\n\t});\n\tvar onSwitch = Ti.UI.createSwitch({\n\t\tvalue:false,\n\t\tisSwitch:true,\n\t\tleft:4,\n\t\theight:Ti.UI.SIZE || 'auto',\n\t\twidth:Ti.UI.SIZE || 'auto'\n\t});\n\tonSwitch.addEventListener('change', function (e) {\n\t\tverifyingReceipts = e.value;\n\t});\n\tview.add(verifyingLabel);\n\tview.add(onSwitch);\n\twin.add(view);\n}\n\nwin.open();\n{code}", "updateAuthor": { "name": "clathrop", "key": "clathrop", "displayName": "Carter Lathrop", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-04-08T18:24:39.000+0000", "updated": "2013-04-08T18:24:39.000+0000" }, { "id": "246501", "author": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "body": "Hello Carter,\nI'm sorry, This case is my misunderstanding.\nIt seemed that evt.products is invalid in json. However evt.products has normally data actually.\nMy app works fine now.\n\nI apologize to your team, and Thanks for your support so much.\n\n-Seiji", "updateAuthor": { "name": "snakaya", "key": "snakaya", "displayName": "Seiji Nakaya", "active": true, "timeZone": "Asia/Tokyo" }, "created": "2013-04-08T21:07:36.000+0000", "updated": "2013-04-08T21:07:36.000+0000" } ], "maxResults": 6, "total": 6, "startAt": 0 } } }