├── meteor-apple-purchasing-tests.js ├── package.js ├── meteor-apple-purchasing.js └── README.md /meteor-apple-purchasing-tests.js: -------------------------------------------------------------------------------- 1 | Tinytest.add('example', function (test) { 2 | test.equal(true, true); 3 | }); 4 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'poetic:meteor-apple-purchasing', 3 | version: '0.0.1', 4 | // Brief, one-line summary of the package. 5 | summary: 'Meteor package for running apple in app purchasing inside of your mobile application.', 6 | // URL to the Git repository containing the source code for this package. 7 | git: 'https://github.com/poetic/meteor-apple-purchases', 8 | // By default, Meteor will default to using README.md for documentation. 9 | // To avoid submitting documentation, set this field to null. 10 | documentation: 'README.md' 11 | }); 12 | 13 | Package.onUse(function(api) { 14 | api.versionsFrom('1.2.1'); 15 | api.use('ecmascript'); 16 | api.addFiles('meteor-apple-purchasing.js'); 17 | api.export('IAP'); 18 | }); 19 | 20 | Package.onTest(function(api) { 21 | api.use('ecmascript'); 22 | api.use('tinytest'); 23 | api.use('poetic:meteor-apple-purchasing'); 24 | api.addFiles('meteor-apple-purchasing-tests.js'); 25 | }); 26 | -------------------------------------------------------------------------------- /meteor-apple-purchasing.js: -------------------------------------------------------------------------------- 1 | IAP = { isLoading: false, }; 2 | 3 | IAP.registerProducts = function(products){ 4 | IAP.list = []; 5 | products.forEach( function(product){ 6 | if(typeof(product) === 'string'){ 7 | IAP.list.push(product); 8 | } 9 | }); 10 | }; 11 | 12 | IAP.onLoad = function(callback){ 13 | IAP.onLoaded = callback; 14 | }; 15 | 16 | IAP.registerBuyCallback = function(callback){ 17 | IAP.buyCallback = callback; 18 | }; 19 | 20 | IAP.load = function () { 21 | // Check availability of the storekit plugin 22 | if (!window.storekit) { 23 | console.log("In-App Purchases not available"); 24 | return; 25 | } 26 | 27 | // Make sure its not already loading or calling this function would cause an 28 | // objective C null referenced object error 29 | if(IAP.isLoading) { return; } 30 | 31 | IAP.isLoading = true; 32 | // Initialize 33 | storekit.init({ 34 | debug: true, // Enable IAP messages on the console 35 | ready: IAP.onReady, 36 | purchase: IAP.onPurchase, 37 | restore: IAP.onRestore, 38 | error: IAP.onError 39 | }); 40 | }; 41 | 42 | IAP.onReady = function () { 43 | // Once setup is done, load all product data. 44 | storekit.load(IAP.list, function (products, invalidIds) { 45 | IAP.products = products; 46 | IAP.loaded = true; 47 | IAP.isLoading = false; 48 | for (var i = 0; i < invalidIds.length; ++i) { 49 | console.log("Error: could not load " + invalidIds[i]); 50 | } 51 | if(typeof(IAP.onLoaded) === 'function'){ 52 | IAP.onLoaded(products, invalidIds); 53 | } else { 54 | console.warn('you did not Pass a onLoaded function which is ok your products are available under IAP.products'); 55 | } 56 | }); 57 | }; 58 | 59 | IAP.onPurchase = function (transactionId, productId, receipt) { 60 | if(typeof(IAP.buyCallback) === 'function'){ 61 | IAP.buyCallback(transactionId, productId, receipt); 62 | } else { 63 | console.warn('You have not registered a on buy callback if you want to process reciepts please do so with IAP.registerBuyCallback(myCallback)'); 64 | } 65 | }; 66 | 67 | IAP.onError = function (errorCode, errorMessage) { 68 | alert('Error: ' + errorMessage); 69 | }; 70 | 71 | IAP.onRestore = function () {}; 72 | 73 | IAP.onError = function (err) { 74 | console.log('an error occured in IAP', err); 75 | }; 76 | 77 | IAP.buy = function (productId) { 78 | storekit.purchase(productId); 79 | }; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # meteor-apple-purchases 2 | A boilerplate of the code required to setup apple purchasing inside your app. This is a wrapper for the javascript code required in order to make an apple store in app purchase. There are many steps outside of this repo for setting up the products for your app. 3 | 4 | # helpful links 5 | [Apple store docs](https://developer.apple.com/in-app-purchase/) 6 | 7 | # Trouble Shooting 8 | 9 | There are MANY things you have to have working before your products array will work so I decided to include a list for those who get stuck. 10 | 11 | * Checklist 12 | 1. Create a unique App ID 13 | 2. Enable in-app purchasing for this App ID 14 | 3. Generate and install a new provisioning profile on your device 15 | 4. Update the bundle ID and code signing profile in Xcode 16 | 5. Check that your project’s .plist Bundle ID match your App ID 17 | 6. Complete your contract, tax, and banking Information. You need a VALID contract 18 | 19 | If you miss ANY of these steps then your products array will come back empty please ensure all valid settings are correct before reporting any issues. 20 | 21 | # Installation 22 | 23 | Add the cordova plugin Meteor 1.2 and Below: 24 | `meteor add cordova:cc.fovea.cordova.purchase@https://github.com/j3k0/cordova-plugin-purchase.git#3bd13373f6dd0d54128554a8458b4043fbefe45f` 25 | 26 | Meteor 1.3 27 | `meteor add cordova:cc.fovea.cordova.purchase@https://github.com/mlmassey/cordova-plugin-purchase.git#cfbb0ca93032275d1f3e79b72be9f2c7c397b093` 28 | 29 | Add the meteor package 30 | `meteor add poetic:meteor-apple-purchasing` 31 | 32 | # Usage 33 | 34 | Using the IAP interface is fairly simple once all the steps apple requires are completed. 35 | 36 | Workflow looks like: 37 | Register Products -> Register Callbacks -> Load -> Buy -> Cash Those Paychecks $$$ 38 | 39 | A very simple example would be using a template events and onRendered functions to accomplish this workflow. If you have more than one template using the products then you should probably use Meteor.startup to register and load. 40 | 41 | A sample using Meteor's boilerplate. 42 | 43 | HTML 44 | 45 | ``` 46 | 47 | In App Purchase Test 48 | 49 | 50 | 51 |

Welcome to In App Purchase!

52 | 53 | {{> hello}} 54 | 55 | 56 | 59 | ``` 60 | 61 | JS 62 | 63 | ``` 64 | Template.hello.events({ 65 | 'click .purchase': function (e) { 66 | IAP.buy(IAP.products[0].id); 67 | } 68 | }); 69 | 70 | Template.hello.onRendered( function(){ 71 | IAP.registerProducts(['product1', 'product2']); 72 | IAP.onLoad(checkProducts); 73 | IAP.load(); 74 | }); 75 | 76 | function checkProducts(products, invalid){ 77 | console.log('valid products:', products); 78 | console.log('invalid products:', invalid); 79 | } 80 | ``` 81 | 82 | # After purchase 83 | 84 | In some cases you may want to run a function after a purchase was successful such as unlocking features are changing the user's account settings. In this case you need to register a `IAP.buyCallback` as well. 85 | 86 | your callback function passed should expect 3 arguments `transactionId, productId, receipt` 87 | ``` 88 | IAP.buyCallback( function(transactionId, productId, receipt){ 89 | switch(productId){ 90 | case 'product1': unlockFeatures('product1'); break; 91 | case 'product2': addCoins('100'); break; 92 | default: console.warn('a productId was passed that you did not handle'); 93 | } 94 | }); 95 | ``` 96 | --------------------------------------------------------------------------------