Payments: Success Messages (#10903)

* fix v-once

* gems purchase success dialog

* amazon and stripe support for gems message, translations

* subscriptions success message

* subscriptions success message

* group plans success message and many fixes

* finish success messages for payments

* add success modal when gifting from balance
This commit is contained in:
Matteo Pagliazzi
2018-12-09 20:08:01 +01:00
committed by GitHub
parent 4d322c1bf6
commit 0667695390
15 changed files with 230 additions and 38 deletions

View File

@@ -443,7 +443,7 @@ export default {
appState = JSON.parse(appState);
if (appState.paymentCompleted) {
removeLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE);
this.$root.$emit('bv::show::modal', 'payments-success-modal');
this.$root.$emit('habitica:payment-success', appState);
}
}
this.$nextTick(() => {

View File

@@ -6,7 +6,7 @@
.quest(:class='`quest_${user.party.quest.completed}`')
p(v-if='questData.completion && typeof questData.completion === "function"', v-html='questData.completion()')
.quest-rewards.text-center
h3 {{ $t('youReceived') }}
h3(v-once) {{ $t('paymentYouReceived') }}
questDialogDrops(:item="questData")
.modal-footer
button.btn.btn-primary(@click='setQuestCompleted()') {{ $t('ok') }}

View File

@@ -141,7 +141,7 @@ export default {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
const subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
const subscriptionKey = 'group_monthly';
let paymentData = {
subscription: subscriptionKey,
coupon: null,
@@ -149,6 +149,7 @@ export default {
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}

View File

@@ -395,14 +395,15 @@ export default {
this.changePage(this.PAGES.PAY);
},
pay (paymentMethod) {
let subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
let paymentData = {
const subscriptionKey = 'group_monthly'; // @TODO: Get from content API?
const paymentData = {
subscription: subscriptionKey,
coupon: null,
};
if (this.upgradingGroup && this.upgradingGroup._id) {
paymentData.groupId = this.upgradingGroup._id;
paymentData.group = this.upgradingGroup;
} else {
paymentData.groupToCreate = this.newGroup;
}

View File

@@ -34,6 +34,7 @@ import * as Analytics from 'client/libs/analytics';
import axios from 'axios';
import { mapState } from 'client/libs/store';
import { CONSTANTS, setLocalSetting } from 'client/libs/userlocalManager';
import pick from 'lodash/pick';
const AMAZON_PAYMENTS = process.env.AMAZON_PAYMENTS; // eslint-disable-line
const habiticaUrl = `${location.protocol}//${location.host}`;
@@ -56,6 +57,8 @@ export default {
OffAmazonPayments: {},
isAmazonSetup: false,
amazonButtonEnabled: false,
groupToCreate: null, // creating new group
group: null, // upgrading existing group
};
},
computed: {
@@ -189,10 +192,37 @@ export default {
new this.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
},
storePaymentStatusAndReload (url) {
let paymentType;
if (this.amazonPayments.type === 'single' && !this.amazonPayments.gift) paymentType = 'gems';
if (this.amazonPayments.type === 'subscription') paymentType = 'subscription';
if (this.amazonPayments.groupId || this.amazonPayments.groupToCreate) paymentType = 'groupPlan';
if (this.amazonPayments.type === 'single' && this.amazonPayments.gift && this.amazonPayments.giftReceiver) {
paymentType = this.amazonPayments.gift.type === 'gems' ? 'gift-gems' : 'gift-subscription';
}
const appState = {
paymentMethod: 'amazon',
paymentCompleted: true,
paymentType,
};
if (paymentType === 'subscription') {
appState.subscriptionKey = this.amazonPayments.subscription;
} else if (paymentType === 'groupPlan') {
appState.subscriptionKey = this.amazonPayments.subscription;
if (this.amazonPayments.groupToCreate) {
appState.newGroup = true;
appState.group = pick(this.amazonPayments.groupToCreate, ['_id', 'memberCount', 'name']);
} else {
appState.newGroup = false;
appState.group = pick(this.amazonPayments.group, ['_id', 'memberCount', 'name']);
}
} else if (paymentType.indexOf('gift-') === 0) {
appState.gift = this.amazonPayments.gift;
appState.giftReceiver = this.amazonPayments.giftReceiver;
}
setLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE, JSON.stringify(appState));
if (url) {
window.location.assign(url);
@@ -215,11 +245,11 @@ export default {
});
this.$set(this, 'amazonButtonEnabled', true);
this.reset();
this.storePaymentStatusAndReload();
} catch (e) {
console.error(e); // eslint-disable-line no-console
this.$set(this, 'amazonButtonEnabled', true);
this.amazonPaymentsreset();
this.reset();
}
} else if (this.amazonPayments.type === 'subscription') {
let url = '/amazon/subscribe';
@@ -265,7 +295,6 @@ export default {
return;
}
this.reset();
this.storePaymentStatusAndReload();
} catch (e) {
this.$set(this, 'amazonButtonEnabled', true);
@@ -286,13 +315,19 @@ export default {
this.amazonPayments.modal = null;
this.amazonPayments.type = null;
this.amazonPayments.loggedIn = false;
// Gift
this.amazonPayments.gift = null;
this.amazonPayments.giftReceiver = null;
this.amazonPayments.billingAgreementId = null;
this.amazonPayments.orderReferenceId = null;
this.amazonPayments.paymentSelected = false;
this.amazonPayments.recurringConsent = false;
this.amazonPayments.subscription = null;
this.amazonPayments.coupon = null;
this.amazonPayments.groupToCreate = null;
this.amazonPayments.group = null;
},
},
};

View File

@@ -111,7 +111,7 @@
span.superscript $
span 5
span.superscript.muted .00
.small {{ $t('everyMonth') }}
.small(v-once) {{ $t('everyMonth') }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:25})')
.spacer
@@ -122,7 +122,7 @@
span.superscript $
span 15
span.superscript.muted .00
.small {{ $t('everyXMonths', {interval: 3}) }}
.small(v-once) {{ $t('everyXMonths', {interval: 3}) }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:30})')
p.benefits(v-markdown='$t("receiveMysticHourglass")')
@@ -133,7 +133,7 @@
span.superscript $
span 30
span.superscript.muted .00
.small {{ $t('everyXMonths', {interval: 6}) }}
.small(v-once) {{ $t('everyXMonths', {interval: 6}) }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:35})')
p.benefits(v-markdown='$t("receiveMysticHourglasses", {amount:2})')
@@ -144,15 +144,15 @@
span.superscript $
span 48
span.superscript.muted .00
.small {{ $t('everyYear') }}
.small(v-once) {{ $t('everyYear') }}
.divider
p.benefits(v-markdown='$t("earnGemsMonthly", {cap:45})')
p.benefits(v-markdown='$t("receiveMysticHourglasses", {amount:4})')
button.btn.btn-primary(@click='subscriptionPlan = "basic_12mo"') {{ subscriptionPlan === "basic_12mo" ? $t('selected') : $t('select') }}
.row.text-center(v-if='subscriptionPlan')
h2.mx-auto.text-payment {{ $t('choosePaymentMethod') }}
h2.mx-auto.text-payment(v-once) {{ $t('choosePaymentMethod') }}
.row.text-center
a.mx-auto {{ $t('haveCouponCode') }}
a.mx-auto(v-once) {{ $t('haveCouponCode') }}
.card-deck(v-if='subscriptionPlan')
.card.text-center.payment-method
.card-body(@click='showStripe({subscription: subscriptionPlan})')

View File

@@ -47,9 +47,9 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
:disabled="sendingInProgress"
) {{ $t("send") }}
template(v-else)
button.btn.btn-primary(@click='showStripe({gift, uuid: userReceivingGems._id})') {{ $t('card') }}
button.btn.btn-warning(@click='openPaypalGift({gift: gift, giftedTo: userReceivingGems._id})') PayPal
button.btn.btn-success(@click="amazonPaymentsInit({type: 'single', gift, giftedTo: userReceivingGems._id})") Amazon Payments
button.btn.btn-primary(@click='showStripe({gift, uuid: userReceivingGems._id, receiverName})') {{ $t('card') }}
button.btn.btn-warning(@click='openPaypalGift({gift: gift, giftedTo: userReceivingGems._id, receiverName})') PayPal
button.btn.btn-success(@click="amazonPaymentsInit({type: 'single', gift, giftedTo: userReceivingGems._id, receiverName})") Amazon Payments
button.btn.btn-secondary(@click='close()') {{$t('cancel')}}
</template>
@@ -131,6 +131,13 @@ export default {
if (!this.userReceivingGems) return '';
return this.$t('sendGiftHeading', {name: this.userReceivingGems.profile.name});
},
receiverName () {
if (this.userReceivingGems.auth && this.userReceivingGems.auth.local && this.userReceivingGems.auth.local.username) {
return this.userReceivingGems.auth.local.username;
} else {
return this.userReceivingGems.profile.name;
}
},
},
methods: {
// @TODO move to payments mixin or action (problem is that we need notifications)
@@ -141,11 +148,21 @@ export default {
toUserId: this.userReceivingGems._id,
gemAmount: this.gift.gems.amount,
});
this.text(this.$t('sentGems'));
this.close();
this.$root.$emit('habitica:payment-success', {
paymentMethod: 'balance',
paymentCompleted: true,
paymentType: 'gift-gems-balance',
gift: {
gems: {
amount: this.gift.gems.amount,
},
},
giftReceiver: this.receiverName,
});
},
onHide () {
// TODO this breaks amazon purchases because when the amazon modal
// @TODO this breaks amazon purchases because when the amazon modal
// is opened this one is closed and the amount reset
// this.gift.gems.amount = 0;
this.gift.message = '';

View File

@@ -2,16 +2,38 @@
b-modal#payments-success-modal(
:title="$t('accountSuspendedTitle')",
size='sm',
:hideFooter="isFromBalance",
:modalClass="isFromBalance ? ['modal-hidden-footer'] : []"
)
div(slot="modal-header")
.check-container.d-flex.align-items-center.justify-content-center
.svg-icon.check(v-html="icons.check")
h2(v-ocne) {{ $t('paymentSuccessful') }}
.svg-icon.check(v-html="icons.check", v-once)
h2 {{ $t(isFromBalance ? 'success' : 'paymentSuccessful') }}
div(slot="modal-footer")
.small-text(v-once) {{ $t('giftSubscriptionText4') }}
.row
.col-12.text-center
button.btn.btn-primary(@click='close()') {{$t('onwards')}}
.col-12.modal-body-col
template(v-if="paymentData.paymentType === 'gems'")
span(v-once) {{ $t('paymentYouReceived') }}
.details-block.gems
.svg-icon(v-html="icons.gem", v-once)
span 20
template(v-if="paymentData.paymentType === 'gift-gems' || paymentData.paymentType === 'gift-gems-balance'")
span(v-html="$t('paymentYouSentGems', {name: paymentData.giftReceiver})")
.details-block.gems
.svg-icon(v-html="icons.gem", v-once)
span {{ paymentData.gift.gems.amount }}
template(v-if="paymentData.paymentType === 'gift-subscription'")
span(v-html="$t('paymentYouSentSubscription', {name: paymentData.giftReceiver, months: paymentData.subscription.months})")
template(v-if="paymentData.paymentType === 'subscription'")
strong(v-once) {{ $t('nowSubscribed') }}
.details-block
span(v-html="$t('paymentSubBilling', {amount: paymentData.subscription.price, months: paymentData.subscription.months})")
template(v-if="paymentData.paymentType === 'groupPlan'")
span(v-html="$t(paymentData.newGroup ? 'groupPlanCreated' : 'groupPlanUpgraded', {groupName: paymentData.group.name})")
.details-block
span(v-html="$t('paymentSubBilling', {amount: groupPlanCost, months: paymentData.subscription.months})")
button.btn.btn-primary(@click='close()', v-once) {{$t('onwards')}}
</template>
<style lang="scss">
@@ -21,6 +43,11 @@
background: transparent;
}
#payments-success-modal.modal-hidden-footer .modal-body {
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
}
#payments-success-modal .modal-header {
justify-content: center;
padding-top: 24px;
@@ -52,6 +79,41 @@
padding-top: 16px;
padding-bottom: 24px;
background: white;
.modal-body-col {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.btn.btn-primary {
margin-top: 24px;
}
}
.details-block {
background: $gray-700;
border-radius: 4px;
padding: 8px 24px;
margin-top: 16px;
display: flex;
flex-direction: row;
text-align: center;
&.gems {
padding: 12px 16px 12px 20px;
color: $green-10;
font-size: 24px;
font-weight: bold;
line-height: 1.33;
.svg-icon {
margin-right: 8px;
width: 32px;
height: 32px;
}
}
}
}
#payments-success-modal .modal-footer {
@@ -68,17 +130,45 @@
<script>
import checkIcon from 'assets/svg/check.svg';
import gemIcon from 'assets/svg/gem.svg';
import subscriptionBlocks from '../../../common/script/content/subscriptionBlocks';
export default {
data () {
return {
icons: Object.freeze({
check: checkIcon,
gem: gemIcon,
}),
paymentData: {},
};
},
mounted () {
this.$root.$on('habitica:payment-success', (data) => {
if (['subscription', 'groupPlan', 'gift-subscription'].indexOf(data.paymentType) !== -1) {
data.subscription = subscriptionBlocks[data.subscriptionKey || data.gift.subscription.key];
}
this.paymentData = data;
this.$root.$emit('bv::show::modal', 'payments-success-modal');
});
},
destroyed () {
this.paymentData = {};
this.$root.$off('habitica:payments-success');
},
computed: {
groupPlanCost () {
const sub = this.paymentData.subscription;
const memberCount = this.paymentData.group.memberCount || 1;
return sub.price + 3 * (memberCount - 1);
},
isFromBalance () {
return this.paymentData.paymentType === 'gift-gems-balance';
},
},
methods: {
close () {
this.paymentData = {};
this.$root.$emit('bv::hide::modal', 'payments-success-modal');
},
},

View File

@@ -84,12 +84,12 @@
img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')")
.row
.col-6
h2 {{ $t('giftSubscription') }}
h2(v-once) {{ $t('giftSubscription') }}
ol
li {{ $t('giftSubscriptionText1') }}
li {{ $t('giftSubscriptionText2') }}
li {{ $t('giftSubscriptionText3') }}
h4 {{ $t('giftSubscriptionText4') }}
li(v-once) {{ $t('giftSubscriptionText1') }}
li(v-once) {{ $t('giftSubscriptionText2') }}
li(v-once) {{ $t('giftSubscriptionText3') }}
h4(v-once) {{ $t('giftSubscriptionText4') }}
</template>
<style scoped>

View File

@@ -1,12 +1,13 @@
import axios from 'axios';
const STRIPE_PUB_KEY = process.env.STRIPE_PUB_KEY; // eslint-disable-line
const STRIPE_PUB_KEY = process.env.STRIPE_PUB_KEY; // eslint-disable-line no-process-env
import subscriptionBlocks from '../../common/script/content/subscriptionBlocks';
import { mapState } from 'client/libs/store';
import encodeParams from 'client/libs/encodeParams';
import notificationsMixin from 'client/mixins/notifications';
import * as Analytics from 'client/libs/analytics';
import { CONSTANTS, setLocalSetting } from 'client/libs/userlocalManager';
import pick from 'lodash/pick';
const habiticaUrl = `${location.protocol}//${location.host}`;
@@ -43,13 +44,24 @@ export default {
let gift = this.encodeGift(data.giftedTo, data.gift);
const url = `/paypal/checkout?gift=${gift}`;
this.openPaypal(url, 'gift');
this.openPaypal(url, `gift-${data.gift.type === 'gems' ? 'gems' : 'subscription'}`, data);
},
openPaypal (url/* , type*/) {
openPaypal (url, type, giftData) {
const appState = {
paymentMethod: 'paypal',
paymentCompleted: false,
paymentType: type,
};
if (type === 'subscription') {
appState.subscriptionKey = this.subscriptionPlan || this.subscription.key;
}
if (type.indexOf('gift-') === 0) {
appState.gift = giftData.gift;
appState.giftReceiver = giftData.receiverName;
}
setLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE, JSON.stringify(appState));
window.open(url, '_blank');
@@ -82,6 +94,13 @@ export default {
if (data.gift && data.gift.type === 'gems') amount = data.gift.gems.amount / 4 * 100;
if (data.group) amount = (sub.price + 3 * (data.group.memberCount - 1)) * 100;
let paymentType;
if (sub === false && !data.gift) paymentType = 'gems';
if (sub !== false && !data.gift) paymentType = 'subscription';
if (data.group || data.groupToCreate) paymentType = 'groupPlan';
if (data.gift && data.gift.type === 'gems') paymentType = 'gift-gems';
if (data.gift && data.gift.type === 'subscription') paymentType = 'gift-subscription';
window.StripeCheckout.open({
key: STRIPE_PUB_KEY,
address: false,
@@ -116,7 +135,26 @@ export default {
const appState = {
paymentMethod: 'stripe',
paymentCompleted: true,
paymentType,
};
if (paymentType === 'subscription') {
appState.subscriptionKey = sub.key;
} else if (paymentType === 'groupPlan') {
appState.subscriptionKey = sub.key;
if (data.groupToCreate) {
appState.newGroup = true;
appState.group = pick(data.groupToCreate, ['_id', 'memberCount', 'name']);
} else {
appState.newGroup = false;
appState.group = pick(data.group, ['_id', 'memberCount', 'name']);
}
} else if (paymentType.indexOf('gift-') === 0) {
appState.gift = data.gift;
appState.giftReceiver = data.receiverName;
}
setLocalSetting(CONSTANTS.savedAppStateValues.SAVED_APP_STATE, JSON.stringify(appState));
let newGroup = response.data.data;
@@ -191,6 +229,7 @@ export default {
if (data.gift) {
if (data.gift.gems && data.gift.gems.amount && data.gift.gems.amount <= 0) return;
data.gift.uuid = data.giftedTo;
this.amazonPayments.giftReceiver = data.receiverName;
}
if (data.subscription) {
@@ -202,7 +241,11 @@ export default {
this.amazonPayments.groupId = data.groupId;
}
if (data.groupToCreate) {
if (data.group) { // upgrading a group
this.amazonPayments.group = data.group;
}
if (data.groupToCreate) { // creating a group
this.amazonPayments.groupToCreate = data.groupToCreate;
}

View File

@@ -1,7 +1,6 @@
import axios from 'axios';
const LOCALSTORAGE_AUTH_KEY = 'habit-mobile-settings';
const LOCALSTORAGE_SOCIAL_AUTH_KEY = 'hello'; // Used by hello.js for social auth
export async function register (store, params) {
let url = '/api/v4/user/auth/local/register';
@@ -84,7 +83,6 @@ export async function socialAuth (store, params) {
}
export function logout () {
localStorage.removeItem(LOCALSTORAGE_AUTH_KEY);
localStorage.removeItem(LOCALSTORAGE_SOCIAL_AUTH_KEY);
localStorage.clear();
window.location.href = '/logout-server';
}

View File

@@ -336,6 +336,8 @@
"claimedBy": "\n\nClaimed by: <%= claimingUsers %>",
"cantDeleteAssignedGroupTasks": "Can't delete group tasks that are assigned to you.",
"confirmGuildPlanCreation": "Create this group?",
"groupPlanUpgraded": "<strong><%= groupName %></strong> was upgraded to a Group Plan!",
"groupPlanCreated": "<strong><%= groupName %></strong> was created!",
"onlyGroupLeaderCanInviteToGroupPlan": "Only the group leader can invite users to a group with a subscription.",
"paymentDetails": "Payment Details",
"aboutToJoinCancelledGroupPlan": "You are about to join a group with a canceled plan. You will NOT receive a free subscription.",

View File

@@ -116,6 +116,11 @@
"amazonInstructions": "Click the button to pay using Amazon Payments",
"paymentMethods": "Purchase using",
"paymentSuccessful": "Your payment was successful!",
"paymentYouReceived": "You received:",
"paymentYouSentGems": "You sent <strong><%= name %></strong>:",
"paymentYouSentSubscription": "You sent <strong><%= name %></strong> a <%= months %>-months Habitica subscription.",
"paymentSubBilling": "Your subscription will be billed <strong><%= amount %></strong>$ every <strong><%= months %></strong> months.",
"success": "Success!",
"classGear": "Class Gear",
"classGearText": "Congratulations on choosing a class! I've added your new basic weapon to your inventory. Take a look below to equip it!",

View File

@@ -112,7 +112,6 @@
"wonChallenge": "You won a Challenge!",
"newPM": "Received Private Message",
"newPMInfo": "New Message from <%= name %>: <%= message %>",
"sentGems": "Sent gems!",
"giftedGems": "Gifted Gems",
"giftedGemsInfo": "<%= name %> gifted you <%= amount %> Gems",
"giftedGemsFull": "Hello <%= username %>, <%= sender %> has sent you <%= gemAmount %> gems!",

View File

@@ -37,6 +37,7 @@
"individualSub": "Individual Subscription",
"subscribe": "Subscribe",
"subscribed": "Subscribed",
"nowSubscribed": "You are now subscribed to Habitica!",
"manageSub": "Click to manage subscription",
"cancelSub": "Cancel Subscription",
"cancelSubInfoGoogle": "Please go to the \"Account\" > \"Subscriptions\" section of the Google Play Store app to cancel your subscription or to see your subscription's termination date if you have already cancelled it. This screen is not able to show you whether your subscription has been cancelled.",