diff --git a/test/api/v3/unit/libs/amazonPayments.test.js b/test/api/v3/unit/libs/amazonPayments.test.js index c40c0e1f35..005b445e4d 100644 --- a/test/api/v3/unit/libs/amazonPayments.test.js +++ b/test/api/v3/unit/libs/amazonPayments.test.js @@ -113,6 +113,25 @@ describe('Amazon Payments', () => { expectAmazonStubs(); }); + it('should error if gem amount is too low', async () => { + let receivingUser = new User(); + receivingUser.save(); + let gift = { + type: 'gems', + gems: { + amount: 0, + uuid: receivingUser._id, + }, + }; + + await expect(amzLib.checkout({gift, user, orderReferenceId, headers})) + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); + }); + it('should gift gems', async () => { let receivingUser = new User(); receivingUser.save(); diff --git a/test/api/v3/unit/libs/paypalPayments.test.js b/test/api/v3/unit/libs/paypalPayments.test.js index d8a3e096c0..bcad82996a 100644 --- a/test/api/v3/unit/libs/paypalPayments.test.js +++ b/test/api/v3/unit/libs/paypalPayments.test.js @@ -68,6 +68,25 @@ describe('Paypal Payments', () => { expect(link).to.eql(approvalHerf); }); + it('should error if gem amount is too low', async () => { + let receivingUser = new User(); + receivingUser.save(); + let gift = { + type: 'gems', + gems: { + amount: 0, + uuid: receivingUser._id, + }, + }; + + await expect(paypalPayments.checkout({gift})) + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); + }); + it('creates a link for gifting gems', async () => { let receivingUser = new User(); let gift = { diff --git a/test/api/v3/unit/libs/stripePayments.test.js b/test/api/v3/unit/libs/stripePayments.test.js index 00338f9e4c..98988e37b2 100644 --- a/test/api/v3/unit/libs/stripePayments.test.js +++ b/test/api/v3/unit/libs/stripePayments.test.js @@ -48,7 +48,36 @@ describe('Stripe Payments', () => { payments.createSubscription.restore(); }); + it('should error if gem amount is too low', async () => { + let receivingUser = new User(); + receivingUser.save(); + gift = { + type: 'gems', + gems: { + amount: 0, + uuid: receivingUser._id, + }, + }; + + await expect(stripePayments.checkout({ + token, + user, + gift, + groupId, + email, + headers, + coupon, + }, stripe)) + .to.eventually.be.rejected.and.to.eql({ + httpCode: 400, + message: 'Amount must be at least 1.', + name: 'BadRequest', + }); + }); + it('should purchase gems', async () => { + gift = undefined; + await stripePayments.checkout({ token, user, diff --git a/website/client-old/js/services/paymentServices.js b/website/client-old/js/services/paymentServices.js index d45d82250f..5375339511 100644 --- a/website/client-old/js/services/paymentServices.js +++ b/website/client-old/js/services/paymentServices.js @@ -1,8 +1,8 @@ 'use strict'; angular.module('habitrpg').factory('Payments', -['$rootScope', 'User', '$http', 'Content', -function($rootScope, User, $http, Content) { +['$rootScope', 'User', '$http', 'Content', 'Notification', +function($rootScope, User, $http, Content, Notification) { var Payments = {}; var isAmazonReady = false; Payments.amazonButtonEnabled = true; @@ -21,7 +21,18 @@ function($rootScope, User, $http, Content) { amazon.Login.setClientId(window.env.AMAZON_PAYMENTS.CLIENT_ID); }; + Payments.checkGemAmount = function(data) { + if(data.gift.type === "gems" && !data.gift.gems.amount || data.gift.gems.amount === 0) { + Notification.error(window.env.t('badAmountOfGemsToPurchase'), true); + return false; + } + return true; + } + Payments.showStripe = function(data) { + + if(!Payments.checkGemAmount(data)) return; + var sub = false; if (data.subscription) { @@ -121,6 +132,7 @@ function($rootScope, User, $http, Content) { // Needs to be called everytime the modal/router is accessed Payments.amazonPayments.init = function(data) { if(!isAmazonReady) return; + if(!Payments.checkGemAmount(data)) return; if(data.type !== 'single' && data.type !== 'subscription') return; if (data.gift) { @@ -345,6 +357,14 @@ function($rootScope, User, $http, Content) { }); } + Payments.payPalPayment = function(data){ + if(!Payments.checkGemAmount(data)) return; + + var gift = Payments.encodeGift(data.giftedTo, data.gift); + var url = '/paypal/checkout?_id=' + User.user._id + '&apiToken=' + User.settings.auth.apiToken + '&gift=' + gift; + $http.get(url); + } + Payments.encodeGift = function(uuid, gift) { gift.uuid = uuid; var encodedString = JSON.stringify(gift); diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json index 41d378e4ae..e9f349ac7f 100644 --- a/website/common/locales/en/groups.json +++ b/website/common/locales/en/groups.json @@ -286,5 +286,6 @@ "leaderMarker": " - Leader", "managerMarker": " - Manager", "joinedGuild": "Joined a Guild", - "joinedGuildText": "Ventured into the social side of Habitica by joining a Guild!" + "joinedGuildText": "Ventured into the social side of Habitica by joining a Guild!", + "badAmountOfGemsToPurchase": "Amount must be at least 1." } diff --git a/website/server/libs/amazonPayments.js b/website/server/libs/amazonPayments.js index 4351209b30..d723cd976b 100644 --- a/website/server/libs/amazonPayments.js +++ b/website/server/libs/amazonPayments.js @@ -98,6 +98,9 @@ api.checkout = async function checkout (options = {}) { if (gift) { if (gift.type === this.constants.GIFT_TYPE_GEMS) { + if (gift.gems.amount <= 0) { + throw new BadRequest(i18n.t('badAmountOfGemsToPurchase')); + } amount = gift.gems.amount / 4; } else if (gift.type === this.constants.GIFT_TYPE_SUBSCRIPTION) { amount = common.content.subscriptionBlocks[gift.subscription.key].price; diff --git a/website/server/libs/paypalPayments.js b/website/server/libs/paypalPayments.js index 044983a7f0..7182476e3a 100644 --- a/website/server/libs/paypalPayments.js +++ b/website/server/libs/paypalPayments.js @@ -74,6 +74,9 @@ api.checkout = async function checkout (options = {}) { let description = 'Habitica Gems'; if (gift) { if (gift.type === 'gems') { + if (gift.gems.amount <= 0) { + throw new BadRequest(i18n.t('badAmountOfGemsToPurchase')); + } amount = Number(gift.gems.amount / 4).toFixed(2); description = `${description} (Gift)`; } else { diff --git a/website/server/libs/stripePayments.js b/website/server/libs/stripePayments.js index 3175d839d8..f6cfae820b 100644 --- a/website/server/libs/stripePayments.js +++ b/website/server/libs/stripePayments.js @@ -107,6 +107,9 @@ api.checkout = async function checkout (options, stripeInc) { if (gift.type === 'subscription') { amount = `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`; } else { + if (gift.gems.amount <= 0) { + throw new BadRequest(shared.i18n.t('badAmountOfGemsToPurchase')); + } amount = `${gift.gems.amount / 4 * 100}`; } } diff --git a/website/views/shared/modals/members.jade b/website/views/shared/modals/members.jade index 2ce0915626..b45dad77be 100644 --- a/website/views/shared/modals/members.jade +++ b/website/views/shared/modals/members.jade @@ -99,7 +99,7 @@ script(type='text/ng-template', id='modals/send-gift.html') - var fromBal = "gift.type=='gems' && gift.gems.fromBalance" button.btn.btn-primary(ng-show=fromBal, ng-click='sendGift(profile._id)')=env.t("send") a.btn.btn-primary(ng-hide=fromBal, ng-click='Payments.showStripe({gift:gift, uuid:profile._id})')=env.t('card') - a.btn.btn-warning(ng-hide=fromBal, href='/paypal/checkout?_id={{::user._id}}&apiToken={{::User.settings.auth.apiToken}}&gift={{Payments.encodeGift(profile._id, gift)}}') PayPal + a.btn.btn-warning(ng-hide=fromBal, ng-click='Payments.payPalPayment({gift: gift, giftedTo: profile._id})') PayPal .btn.btn-success(ng-hide=fromBal, ng-click="Payments.amazonPayments.init({type: 'single', gift: gift, giftedTo: profile._id})") Amazon Payments button.btn.btn-default(ng-click='$close()')=env.t('cancel')