continuation of PR #8161 Display error notification when attempting to purchase invalid amount of gems - fixes #8145 (#8688)

* Translation string for error notification

* Use function instead of a link for paypal

* Inject notification service, function to check the amount of gems to purchase, function to handle payments with paypal

* Throw error if amount of gems is zero or negative

* Add condition to raise error if amount is negative

* Added gem errors for gifts 0 or less

* Fixed linting and broken test

* Fixed test syntax

* Added back needed strings

* Fixed group locales
This commit is contained in:
Keith Holliday
2017-07-06 14:43:43 -06:00
committed by Sabe Jones
parent 48bbc22fb4
commit e901850a6f
9 changed files with 101 additions and 4 deletions

View File

@@ -113,6 +113,25 @@ describe('Amazon Payments', () => {
expectAmazonStubs(); 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 () => { it('should gift gems', async () => {
let receivingUser = new User(); let receivingUser = new User();
receivingUser.save(); receivingUser.save();

View File

@@ -68,6 +68,25 @@ describe('Paypal Payments', () => {
expect(link).to.eql(approvalHerf); 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 () => { it('creates a link for gifting gems', async () => {
let receivingUser = new User(); let receivingUser = new User();
let gift = { let gift = {

View File

@@ -48,7 +48,36 @@ describe('Stripe Payments', () => {
payments.createSubscription.restore(); 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 () => { it('should purchase gems', async () => {
gift = undefined;
await stripePayments.checkout({ await stripePayments.checkout({
token, token,
user, user,

View File

@@ -1,8 +1,8 @@
'use strict'; 'use strict';
angular.module('habitrpg').factory('Payments', angular.module('habitrpg').factory('Payments',
['$rootScope', 'User', '$http', 'Content', ['$rootScope', 'User', '$http', 'Content', 'Notification',
function($rootScope, User, $http, Content) { function($rootScope, User, $http, Content, Notification) {
var Payments = {}; var Payments = {};
var isAmazonReady = false; var isAmazonReady = false;
Payments.amazonButtonEnabled = true; Payments.amazonButtonEnabled = true;
@@ -21,7 +21,18 @@ function($rootScope, User, $http, Content) {
amazon.Login.setClientId(window.env.AMAZON_PAYMENTS.CLIENT_ID); 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) { Payments.showStripe = function(data) {
if(!Payments.checkGemAmount(data)) return;
var sub = false; var sub = false;
if (data.subscription) { if (data.subscription) {
@@ -121,6 +132,7 @@ function($rootScope, User, $http, Content) {
// Needs to be called everytime the modal/router is accessed // Needs to be called everytime the modal/router is accessed
Payments.amazonPayments.init = function(data) { Payments.amazonPayments.init = function(data) {
if(!isAmazonReady) return; if(!isAmazonReady) return;
if(!Payments.checkGemAmount(data)) return;
if(data.type !== 'single' && data.type !== 'subscription') return; if(data.type !== 'single' && data.type !== 'subscription') return;
if (data.gift) { 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) { Payments.encodeGift = function(uuid, gift) {
gift.uuid = uuid; gift.uuid = uuid;
var encodedString = JSON.stringify(gift); var encodedString = JSON.stringify(gift);

View File

@@ -286,5 +286,6 @@
"leaderMarker": " - Leader", "leaderMarker": " - Leader",
"managerMarker": " - Manager", "managerMarker": " - Manager",
"joinedGuild": "Joined a Guild", "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."
} }

View File

@@ -98,6 +98,9 @@ api.checkout = async function checkout (options = {}) {
if (gift) { if (gift) {
if (gift.type === this.constants.GIFT_TYPE_GEMS) { if (gift.type === this.constants.GIFT_TYPE_GEMS) {
if (gift.gems.amount <= 0) {
throw new BadRequest(i18n.t('badAmountOfGemsToPurchase'));
}
amount = gift.gems.amount / 4; amount = gift.gems.amount / 4;
} else if (gift.type === this.constants.GIFT_TYPE_SUBSCRIPTION) { } else if (gift.type === this.constants.GIFT_TYPE_SUBSCRIPTION) {
amount = common.content.subscriptionBlocks[gift.subscription.key].price; amount = common.content.subscriptionBlocks[gift.subscription.key].price;

View File

@@ -74,6 +74,9 @@ api.checkout = async function checkout (options = {}) {
let description = 'Habitica Gems'; let description = 'Habitica Gems';
if (gift) { if (gift) {
if (gift.type === 'gems') { if (gift.type === 'gems') {
if (gift.gems.amount <= 0) {
throw new BadRequest(i18n.t('badAmountOfGemsToPurchase'));
}
amount = Number(gift.gems.amount / 4).toFixed(2); amount = Number(gift.gems.amount / 4).toFixed(2);
description = `${description} (Gift)`; description = `${description} (Gift)`;
} else { } else {

View File

@@ -107,6 +107,9 @@ api.checkout = async function checkout (options, stripeInc) {
if (gift.type === 'subscription') { if (gift.type === 'subscription') {
amount = `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`; amount = `${shared.content.subscriptionBlocks[gift.subscription.key].price * 100}`;
} else { } else {
if (gift.gems.amount <= 0) {
throw new BadRequest(shared.i18n.t('badAmountOfGemsToPurchase'));
}
amount = `${gift.gems.amount / 4 * 100}`; amount = `${gift.gems.amount / 4 * 100}`;
} }
} }

View File

@@ -99,7 +99,7 @@ script(type='text/ng-template', id='modals/send-gift.html')
- var fromBal = "gift.type=='gems' && gift.gems.fromBalance" - var fromBal = "gift.type=='gems' && gift.gems.fromBalance"
button.btn.btn-primary(ng-show=fromBal, ng-click='sendGift(profile._id)')=env.t("send") 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-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 .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') button.btn.btn-default(ng-click='$close()')=env.t('cancel')