From 2ea12d5c713538cf20d60516cf0c9f97668f4386 Mon Sep 17 00:00:00 2001 From: Negue Date: Sat, 22 Nov 2014 20:40:18 +0100 Subject: [PATCH 1/7] Add in-app-purchase Package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index fc3edb043b..061d4a227e 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "grunt-nodemon": "~0.3.0", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "icalendar": "git://github.com/lefnire/node-icalendar#master", + "in-app-purchase": "^0.2.0", "jade": "~1.7.0", "js2xmlparser": "~0.1.2", "lodash": "~2.4.1", From cc4812712a2a29e742db4ea5301bd7ce4faa0c84 Mon Sep 17 00:00:00 2001 From: Negue Date: Sun, 23 Nov 2014 01:50:03 +0100 Subject: [PATCH 2/7] dummy iap verify routes --- config.json.example | 3 +- src/controllers/payments/iap.js | 63 +++++++++++++++++++++++++++++++ src/controllers/payments/index.js | 7 +++- src/routes/payments.js | 3 ++ 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 src/controllers/payments/iap.js diff --git a/config.json.example b/config.json.example index d8bb67b62e..caedcf3ae9 100644 --- a/config.json.example +++ b/config.json.example @@ -41,5 +41,6 @@ "mode":"sandbox", "client_id":"client_id", "client_secret":"client_secret" - } + }, + "IAP_GOOGLE_KEYDIR": "/path/to/google/public/key/dir/" } diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js new file mode 100644 index 0000000000..f4c91b663f --- /dev/null +++ b/src/controllers/payments/iap.js @@ -0,0 +1,63 @@ +var iap = require('in-app-purchase'); +var async = require('async'); +var payments = require('./index'); + +/* +For google iap, you need to name your public key file as: +iap-sanbox or iap-live +*/ +iap.config({ + googlePublicKeyPath: "/path/to/google/public/key/dir/" +}); + +exports.androidVerify = function(req, res, next) { + console.info(req.body); + + var token = req.body.id; + var user = res.locals.user; + +iap.setup(function (error) { + if (error) { + console.error('something went wrong...'); + console.error(error); + + return; + } + /* + google receipt must be provided as an object + { + "data": "{stringified data object}", + "signature": "signature from google" + } + */ + // iap is ready + /*iap.validate(iap.GOOGLE, googleReceipt, function (err, googleRes) { + if (err) { + return console.error(err); + } + if (iap.isValidated(googleRes)) { + // yay good! + } + });*/ +}); + +/* + async.waterfall([ + function(response, cb) { + payments.buyGems(user, {customerId: response.id, paymentMethod: 'IAP'}); + + user.save(cb); + } + ], function(err, saved){ + if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors + res.send(200); + user = token = null; + });*/ +}; + +exports.iosVerify = function(req, res, next) { + console.info(req.body); + + var token = req.body.id; + var user = res.locals.user; +}; \ No newline at end of file diff --git a/src/controllers/payments/index.js b/src/controllers/payments/index.js index 176098b4c5..b0c59f643c 100644 --- a/src/controllers/payments/index.js +++ b/src/controllers/payments/index.js @@ -7,9 +7,9 @@ var moment = require('moment'); var isProduction = nconf.get("NODE_ENV") === "production"; var stripe = require('./stripe'); var paypal = require('./paypal'); -var User = require('mongoose').model('User'); var members = require('../members') var async = require('async'); +var iap = require('./iap'); function revealMysteryItems(user) { _.each(shared.content.gear.flat, function(item) { @@ -123,4 +123,7 @@ exports.paypalSubscribeSuccess = paypal.executeBillingAgreement; exports.paypalSubscribeCancel = paypal.cancelSubscription; exports.paypalCheckout = paypal.createPayment; exports.paypalCheckoutSuccess = paypal.executePayment; -exports.paypalIPN = paypal.ipn; \ No newline at end of file +exports.paypalIPN = paypal.ipn; + +exports.iapAndroidVerify = iap.androidVerify; +exports.iapIosVerify = iap.iosVerify; diff --git a/src/routes/payments.js b/src/routes/payments.js index c34d77b24b..8cd6b37ef7 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,4 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); +router.post("/iap/android/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapAndroidVerify); +router.post("/iap/ios/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapIosVerify); + module.exports = router; \ No newline at end of file From b59a70f1285a8d8ac10cc3229fcd7852c2f01cf9 Mon Sep 17 00:00:00 2001 From: Negue Date: Mon, 24 Nov 2014 20:20:10 +0100 Subject: [PATCH 3/7] more dummy controller, just for iap data --- src/controllers/payments/iap.js | 8 -------- src/routes/payments.js | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index f4c91b663f..2947d44505 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -2,14 +2,6 @@ var iap = require('in-app-purchase'); var async = require('async'); var payments = require('./index'); -/* -For google iap, you need to name your public key file as: -iap-sanbox or iap-live -*/ -iap.config({ - googlePublicKeyPath: "/path/to/google/public/key/dir/" -}); - exports.androidVerify = function(req, res, next) { console.info(req.body); diff --git a/src/routes/payments.js b/src/routes/payments.js index 8cd6b37ef7..5e77417123 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,7 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); -router.post("/iap/android/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapAndroidVerify); -router.post("/iap/ios/verify", auth.authWithUrl, i18n.getUserLanguage, payments.iapIosVerify); +router.post("/iap/android/verify", /*auth.authWithUrl, i18n.getUserLanguage, */payments.iapAndroidVerify); +router.post("/iap/ios/verify", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.iapIosVerify); module.exports = router; \ No newline at end of file From ecf20f423cf31d5a8f79fe5c7db5b627ba6044f4 Mon Sep 17 00:00:00 2001 From: Negue Date: Thu, 27 Nov 2014 21:25:15 +0100 Subject: [PATCH 4/7] IAP Verification for Android --- src/controllers/payments/iap.js | 70 +++++++++++++++++++++++---------- src/routes/payments.js | 2 +- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 2947d44505..daf1627212 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -1,20 +1,37 @@ var iap = require('in-app-purchase'); var async = require('async'); var payments = require('./index'); +var nconf = require('nconf'); + +var inAppPurchase = require('in-app-purchase'); +inAppPurchase.config({ + googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") // this is the path to the directory containing iap-sanbox/iap-live files +}); + +// Validation ERROR Codes +var INVALID_PAYLOAD = 6778001; +var CONNECTION_FAILED = 6778002; +var PURCHASE_EXPIRED = 6778003; exports.androidVerify = function(req, res, next) { - console.info(req.body); - - var token = req.body.id; + var iapBody = req.body; var user = res.locals.user; -iap.setup(function (error) { + iap.setup(function (error) { if (error) { - console.error('something went wrong...'); + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); console.error(error); + res.json(resObj); + return; } + /* google receipt must be provided as an object { @@ -22,29 +39,40 @@ iap.setup(function (error) { "signature": "signature from google" } */ + var testObj = { + data: iapBody.transaction.receipt, + signature: iapBody.transaction.signature + }; + // iap is ready - /*iap.validate(iap.GOOGLE, googleReceipt, function (err, googleRes) { + iap.validate(iap.GOOGLE, testObj, function (err, googleRes) { if (err) { - return console.error(err); + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; } if (iap.isValidated(googleRes)) { + var resObj = { + ok: true, + data: googleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP Android'}); + user.save(); + // yay good! + res.json(resObj); } - });*/ + }); }); - -/* - async.waterfall([ - function(response, cb) { - payments.buyGems(user, {customerId: response.id, paymentMethod: 'IAP'}); - - user.save(cb); - } - ], function(err, saved){ - if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors - res.send(200); - user = token = null; - });*/ }; exports.iosVerify = function(req, res, next) { diff --git a/src/routes/payments.js b/src/routes/payments.js index 5e77417123..adc9a65629 100644 --- a/src/routes/payments.js +++ b/src/routes/payments.js @@ -17,7 +17,7 @@ router.post("/stripe/subscribe/edit", auth.auth, i18n.getUserLanguage, payments. //router.get("/stripe/subscribe", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribe); // checkout route is used (above) with ?plan= instead router.get("/stripe/subscribe/cancel", auth.authWithUrl, i18n.getUserLanguage, payments.stripeSubscribeCancel); -router.post("/iap/android/verify", /*auth.authWithUrl, i18n.getUserLanguage, */payments.iapAndroidVerify); +router.post("/iap/android/verify", auth.authWithUrl, /*i18n.getUserLanguage, */payments.iapAndroidVerify); router.post("/iap/ios/verify", /*auth.authWithUrl, i18n.getUserLanguage, */ payments.iapIosVerify); module.exports = router; \ No newline at end of file From d92832bfb2873b7a768272d459dc0d905929e97f Mon Sep 17 00:00:00 2001 From: Negue Date: Fri, 28 Nov 2014 23:44:42 +0100 Subject: [PATCH 5/7] Hopefully a working IOS Verification --- src/controllers/payments/iap.js | 49 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index daf1627212..05eb3ff8f7 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -65,7 +65,7 @@ exports.androidVerify = function(req, res, next) { data: googleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP Android'}); + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); user.save(); // yay good! @@ -78,6 +78,51 @@ exports.androidVerify = function(req, res, next) { exports.iosVerify = function(req, res, next) { console.info(req.body); - var token = req.body.id; + var iapBody = req.body; var user = res.locals.user; + + iap.setup(function (error) { + if (error) { + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); + console.error(error); + + res.json(resObj); + + return; + } + + // iap is ready + iap.validate(iap.APPLE, iapBody.transaction.receipt, function (err, appleRes) { + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + if (iap.isValidated(appleRes)) { + var resObj = { + ok: true, + data: appleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); + user.save(); + + // yay good! + res.json(resObj); + } + }); +}); }; \ No newline at end of file From bb37c163cf1bca94656d5a962dcc9344f1843cbc Mon Sep 17 00:00:00 2001 From: Negue Date: Mon, 8 Dec 2014 22:21:32 +0100 Subject: [PATCH 6/7] fix spacing --- src/controllers/payments/iap.js | 151 ++++++++++++++++---------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index 05eb3ff8f7..f7d44fcc7b 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -5,7 +5,8 @@ var nconf = require('nconf'); var inAppPurchase = require('in-app-purchase'); inAppPurchase.config({ - googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") // this is the path to the directory containing iap-sanbox/iap-live files + // this is the path to the directory containing iap-sanbox/iap-live files + googlePublicKeyPath: nconf.get("IAP_GOOGLE_KEYDIR") }); // Validation ERROR Codes @@ -19,24 +20,24 @@ exports.androidVerify = function(req, res, next) { iap.setup(function (error) { if (error) { - var resObj = { - ok: false, - data: 'IAP Error' - }; + var resObj = { + ok: false, + data: 'IAP Error' + }; - console.error('IAP Setup ERROR'); - console.error(error); + console.error('IAP Setup ERROR'); + console.error(error); - res.json(resObj); + res.json(resObj); - return; + return; } /* - google receipt must be provided as an object - { - "data": "{stringified data object}", - "signature": "signature from google" + google receipt must be provided as an object + { + "data": "{stringified data object}", + "signature": "signature from google" } */ var testObj = { @@ -46,33 +47,34 @@ exports.androidVerify = function(req, res, next) { // iap is ready iap.validate(iap.GOOGLE, testObj, function (err, googleRes) { - if (err) { - var resObj = { - ok: false, - data: { - code: INVALID_PAYLOAD, - message: err.toString() - } - }; - - res.json(resObj); - console.error(err); - return; - } - if (iap.isValidated(googleRes)) { - var resObj = { - ok: true, - data: googleRes - }; + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + + if (iap.isValidated(googleRes)) { + var resObj = { + ok: true, + data: googleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); + user.save(); - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); - user.save(); - - // yay good! - res.json(resObj); - } + // yay good! + res.json(resObj); + } }); -}); + }); }; exports.iosVerify = function(req, res, next) { @@ -83,46 +85,47 @@ exports.iosVerify = function(req, res, next) { iap.setup(function (error) { if (error) { - var resObj = { - ok: false, - data: 'IAP Error' - }; - - console.error('IAP Setup ERROR'); - console.error(error); - - res.json(resObj); - - return; + var resObj = { + ok: false, + data: 'IAP Error' + }; + + console.error('IAP Setup ERROR'); + console.error(error); + + res.json(resObj); + + return; } // iap is ready iap.validate(iap.APPLE, iapBody.transaction.receipt, function (err, appleRes) { - if (err) { - var resObj = { - ok: false, - data: { - code: INVALID_PAYLOAD, - message: err.toString() - } - }; - - res.json(resObj); - console.error(err); - return; - } - if (iap.isValidated(appleRes)) { - var resObj = { - ok: true, - data: appleRes - }; + if (err) { + var resObj = { + ok: false, + data: { + code: INVALID_PAYLOAD, + message: err.toString() + } + }; + + res.json(resObj); + console.error(err); + return; + } + + if (iap.isValidated(appleRes)) { + var resObj = { + ok: true, + data: appleRes + }; + + payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); + user.save(); - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); - user.save(); - - // yay good! - res.json(resObj); - } + // yay good! + res.json(resObj); + } }); -}); + }); }; \ No newline at end of file From f9cc2adda548a15b296cb9de2d8ced137752e4be Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Wed, 10 Dec 2014 14:25:00 -0700 Subject: [PATCH 7/7] fix(iap): iap verification fixes --- src/controllers/payments/iap.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/controllers/payments/iap.js b/src/controllers/payments/iap.js index f7d44fcc7b..29b03fedfb 100644 --- a/src/controllers/payments/iap.js +++ b/src/controllers/payments/iap.js @@ -67,9 +67,8 @@ exports.androidVerify = function(req, res, next) { data: googleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP GooglePlay'}); - user.save(); - + payments.buyGems({user:user, paymentMethod:'IAP GooglePlay'}); + // yay good! res.json(resObj); } @@ -120,8 +119,7 @@ exports.iosVerify = function(req, res, next) { data: appleRes }; - payments.buyGems(user, {customerId:user.id, paymentMethod:'IAP AppleStore'}); - user.save(); + payments.buyGems({user:user, paymentMethod:'IAP AppleStore'}); // yay good! res.json(resObj);