New payments buttons and new Amazon checkout flow (#10940)

* start implementing separate amazon button component

* wio

* switch amazon to new flow
This commit is contained in:
Matteo Pagliazzi 2019-03-04 21:51:17 +01:00 committed by GitHub
parent 17d8a7b706
commit 77f3bb53de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 195 additions and 118 deletions

View file

@ -25,7 +25,8 @@
.payment-providers
.box.payment-button(@click='pay(PAYMENTS.STRIPE)')
.svg-icon.credit-card-icon(v-html="icons.creditCard")
.box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)')
amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)")
//.box.payment-button.amazon(@click='pay(PAYMENTS.AMAZON)')
.svg-icon.amazon-pay-icon(v-html="icons.amazonpay")
</template>
@ -87,17 +88,21 @@
import * as Analytics from 'client/libs/analytics';
import { mapState } from 'client/libs/store';
import paymentsMixin from '../../mixins/payments';
import amazonButton from 'client/components/payments/amazonButton';
import amazonpay from 'assets/svg/amazonpay.svg';
// import amazonpay from 'assets/svg/amazonpay.svg';
import creditCard from 'assets/svg/credit-card.svg';
export default {
mixins: [paymentsMixin],
components: {
amazonButton,
},
data () {
return {
amazonPayments: {},
icons: Object.freeze({
amazonpay,
// amazonpay,
creditCard,
}),
PAGES: {
@ -159,7 +164,7 @@ export default {
this.showStripe(paymentData);
} else if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
this.amazonPaymentsInit(paymentData);
return paymentData;
}
},
},

View file

@ -53,7 +53,8 @@ div
.svg-icon.credit-card-icon(v-html="icons.group")
p.credit-card Credit Card
p Powered by Stripe
.box.payment-button(@click='pay(PAYMENTS.AMAZON)')
amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)")
//.box.payment-button(@click='pay(PAYMENTS.AMAZON)')
.svg-icon.amazon-pay-icon(v-html="icons.amazonpay")
.container.col-6.offset-3.create-option(v-if='!upgradingGroup._id')
@ -104,7 +105,8 @@ div
.box.payment-button(@click='pay(PAYMENTS.STRIPE)')
p Credit Card
p Powered by Stripe
.box.payment-button(@click='pay(PAYMENTS.AMAZON)')
amazon-button(:amazon-data="pay(PAYMENTS.AMAZON)")
//.box.payment-button(@click='pay(PAYMENTS.AMAZON)')
| Amazon Pay
</template>
@ -334,17 +336,21 @@ div
import paymentsMixin from '../../mixins/payments';
import { mapState } from 'client/libs/store';
import group from 'assets/svg/group.svg';
import amazonpay from 'assets/svg/amazonpay.svg';
// import amazonpay from 'assets/svg/amazonpay.svg';
import positiveIcon from 'assets/svg/positive.svg';
import amazonButton from 'client/components/payments/amazonButton';
export default {
mixins: [paymentsMixin],
components: {
amazonButton,
},
data () {
return {
amazonPayments: {},
icons: Object.freeze({
group,
amazonpay,
// amazonpay,
positiveIcon,
}),
PAGES: {
@ -413,7 +419,7 @@ export default {
this.showStripe(paymentData);
} else if (this.paymentMethod === this.PAYMENTS.AMAZON) {
paymentData.type = 'subscription';
this.amazonPaymentsInit(paymentData);
return paymentData;
}
},
},

View file

@ -0,0 +1,110 @@
<template lang="pug">
.amazon-pay-button(:id="buttonId")
</template>
<style scoped>
.amazon-pay-button {
width: 150px;
margin-bottom: 12px;
margin: 0 auto;
}
</style>
<script>
import axios from 'axios';
import { mapState } from 'client/libs/store';
import uuid from 'uuid';
import paymentsMixin from 'client/mixins/payments';
const AMAZON_PAYMENTS = process.env.AMAZON_PAYMENTS; // eslint-disable-line
export default {
mixins: [paymentsMixin],
data () {
return { // @TODO what needed here? can be moved to mixin?
amazonPayments: {
modal: null,
type: null,
gift: null,
loggedIn: false,
paymentSelected: false,
billingAgreementId: '',
recurringConsent: false,
orderReferenceId: null,
subscription: null,
coupon: null,
},
isAmazonSetup: false,
amazonButtonEnabled: false,
groupToCreate: null, // creating new group
group: null, // upgrading existing group
buttonId: null,
};
},
props: {
amazonData: Object,
},
computed: {
...mapState({user: 'user.data'}),
...mapState(['isAmazonReady']),
amazonPaymentsCanCheckout () {
if (this.amazonPayments.type === 'single') {
return this.amazonPayments.paymentSelected === true;
} else if (this.amazonPayments.type === 'subscription') {
return this.amazonPayments.paymentSelected && this.amazonPayments.recurringConsent;
}
return false;
},
},
beforeMount () {
this.buttonId = `AmazonPayButton-${uuid.v4()}`;
},
mounted () {
this.amazonPaymentsInit(this.amazonData); // TOOD clone
if (this.isAmazonReady) return this.setupAmazon();
this.$store.watch(state => state.isAmazonReady, (isAmazonReady) => {
if (isAmazonReady) return this.setupAmazon();
});
},
methods: {
setupAmazon () {
if (this.isAmazonSetup) return false;
this.isAmazonSetup = true;
this.showButton();
},
showButton () {
window.OffAmazonPayments.Button( // eslint-disable-line new-cap
this.buttonId, // ID of the button
AMAZON_PAYMENTS.SELLER_ID,
{
type: 'PwA',
color: 'Gold',
size: 'small',
agreementType: 'BillingAgreement',
onSignIn: async (contract) => { // @TODO send to modal
this.amazonPayments.billingAgreementId = contract.getAmazonBillingAgreementId();
this.$set(this.amazonPayments, 'loggedIn', true);
this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments);
},
authorization: () => {
window.amazon.Login.authorize({
scope: 'payments:widget',
popup: true,
}, function amazonSuccess (response) {
if (response.error) return alert(response.error);
const url = '/amazon/verifyAccessToken';
axios.post(url, response).catch((e) => {
alert(e.message);
});
});
},
onError: this.amazonOnError, // @TODO port here
});
},
},
};
</script>

View file

@ -1,7 +1,6 @@
<template lang="pug">
b-modal#amazon-payment(title="Amazon", :hide-footer="true", size='md')
h2.text-center Continue with Amazon
#AmazonPayButton
#AmazonPayWallet(v-if="amazonPayments.loggedIn", style="width: 400px; height: 228px;")
template(v-if="amazonPayments.loggedIn && amazonPayments.type === 'subscription'")
br
@ -19,7 +18,7 @@
margin-bottom: 12px;
}
#AmazonPayButton, #AmazonPayWallet, #AmazonPayRecurring {
#AmazonPayWallet, #AmazonPayRecurring {
margin: 0 auto;
}
@ -54,7 +53,6 @@ export default {
subscription: null,
coupon: null,
},
OffAmazonPayments: {},
isAmazonSetup: false,
amazonButtonEnabled: false,
groupToCreate: null, // creating new group
@ -74,12 +72,6 @@ export default {
},
},
mounted () {
if (this.isAmazonReady) return this.setupAmazon();
this.$store.watch(state => state.isAmazonReady, (isAmazonReady) => {
if (isAmazonReady) return this.setupAmazon();
});
this.$root.$on('habitica::pay-with-amazon', (amazonPaymentsData) => {
if (!amazonPaymentsData) return;
@ -90,67 +82,30 @@ export default {
this.amazonPayments = Object.assign({}, amazonPayments, amazonPaymentsData);
this.$root.$emit('bv::show::modal', 'amazon-payment');
this.$nextTick(async () => {
if (this.amazonPayments.type === 'subscription') {
this.amazonInitWidgets();
} else {
let url = '/amazon/createOrderReferenceId';
let response = await axios.post(url, {
billingAgreementId: this.amazonPayments.billingAgreementId,
});
if (response.status <= 400) {
this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
this.amazonInitWidgets();
} else {
alert(response.message);
}
}
});
});
},
destroyed () {
this.$root.$off('habitica::pay-with-amazon');
},
methods: {
setupAmazon () {
if (this.isAmazonSetup) return false;
this.isAmazonSetup = true;
this.OffAmazonPayments = window.OffAmazonPayments;
this.showButton();
},
showButton () {
// @TODO: prevent modal close form clicking outside
let amazonButton = this.OffAmazonPayments.Button( // eslint-disable-line
'AmazonPayButton',
AMAZON_PAYMENTS.SELLER_ID,
{
type: 'PwA',
color: 'Gold',
size: 'small',
agreementType: 'BillingAgreement',
onSignIn: async (contract) => {
this.amazonPayments.billingAgreementId = contract.getAmazonBillingAgreementId();
this.$set(this.amazonPayments, 'loggedIn', true);
if (this.amazonPayments.type === 'subscription') {
this.amazonInitWidgets();
} else {
let url = '/amazon/createOrderReferenceId';
let response = await axios.post(url, {
billingAgreementId: this.amazonPayments.billingAgreementId,
});
if (response.status <= 400) {
this.amazonPayments.orderReferenceId = response.data.data.orderReferenceId;
this.amazonInitWidgets();
return;
}
alert(response.message);
}
},
authorization: () => {
window.amazon.Login.authorize({
scope: 'payments:widget',
popup: true,
}, function amazonSuccess (response) {
if (response.error) return alert(response.error);
let url = '/amazon/verifyAccessToken';
axios.post(url, response)
.catch((e) => {
alert(e.message);
});
});
},
onError: this.amazonOnError,
});
},
amazonInitWidgets () {
let walletParams = {
sellerId: AMAZON_PAYMENTS.SELLER_ID, // @TODO: Import
@ -167,15 +122,14 @@ export default {
walletParams.onReady = (billingAgreement) => {
this.amazonPayments.billingAgreementId = billingAgreement.getAmazonBillingAgreementId();
new this.OffAmazonPayments.Widgets.Consent({
new window.OffAmazonPayments.Widgets.Consent({
sellerId: AMAZON_PAYMENTS.SELLER_ID,
amazonBillingAgreementId: this.amazonPayments.billingAgreementId,
design: {
designMode: 'responsive',
},
onReady: (consent) => {
let getConsent = consent.getConsentStatus;
this.$set(this.amazonPayments, 'recurringConsent', getConsent ? Boolean(getConsent()) : false);
this.$set(this.amazonPayments, 'recurringConsent', consent.getConsentStatus ? Boolean(consent.getConsentStatus()) : false);
this.$set(this, 'amazonButtonEnabled', true);
},
onConsent: (consent) => {
@ -189,7 +143,7 @@ export default {
walletParams.amazonOrderReferenceId = this.amazonPayments.orderReferenceId;
}
new this.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
new window.OffAmazonPayments.Widgets.Wallet(walletParams).bind('AmazonPayWallet');
},
storePaymentStatusAndReload (url) {
let paymentType;
@ -305,30 +259,6 @@ export default {
amazonOnPaymentSelect () {
this.$set(this.amazonPayments, 'paymentSelected', true);
},
amazonOnError (error) {
alert(error.getErrorMessage());
this.reset();
},
reset () {
// @TODO: Ensure we are using all of these
// some vars are set in the payments mixin. We should try to edit in one place
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;
},
},
};
</script>

View file

@ -51,7 +51,8 @@
.card.text-center.payment-method
a.card-body.paypal(@click="openPaypal(paypalCheckoutLink, 'gems')")
img(src='~assets/images/paypal.png')
.card.text-center.payment-method(@click="amazonPaymentsInit({type: 'single'})")
amazon-button(:amazon-data="{type: 'single'}")
// .card.text-center.payment-method(@click="amazonPaymentsInit({type: 'single'})")
.card-body.amazon
img(src='~assets/images/amazon-payments.png')
.row.text-center
@ -135,7 +136,8 @@
a.card-body.paypal(@click="openPaypal(paypalSubscriptionLink, 'subscription')")
img(src='~assets/images/paypal.png')
.card.text-center.payment-method
.card-body.amazon(@click="amazonPaymentsInit({type: 'subscription', subscription: subscriptionPlan})")
amazon-button(:amazon-data="{type: 'subscription', subscription: subscriptionPlan}")
// .card-body.amazon(@click="amazonPaymentsInit({type: 'subscription', subscription: subscriptionPlan})")
img(src='~assets/images/amazon-payments.png')
.row.text-center
.svg-icon.mx-auto(v-html='icons.heart', style='"height: 24px; width: 24px;"')
@ -174,10 +176,6 @@
font-size: 16px;
}
.amazon {
padding-top: 1.8em;
}
.benefits {
font-size: 14px;
}
@ -348,10 +346,13 @@
import fortyTwoGems from 'assets/svg/42-gems.svg';
import eightyFourGems from 'assets/svg/84-gems.svg';
import amazonButton from 'client/components/payments/amazonButton';
export default {
mixins: [paymentsMixin],
components: {
planGemLimits,
amazonButton,
},
computed: {
...mapState({user: 'user.data'}),
@ -383,7 +384,6 @@
gemAmount: 0,
subscriptionPlan: '',
selectedPage: 'subscribe',
amazonPayments: {},
planGemLimits,
};
},

View file

@ -51,7 +51,8 @@ b-modal#send-gems(:title="title", :hide-footer="true", size='lg', @hide='onHide(
template(v-else)
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
amazon-button(:amazon-data="{type: 'single', gift, giftedTo: userReceivingGems._id, receiverName}")
//button.btn.btn-success(@click="amazonPaymentsInit({type: 'single', gift, giftedTo: userReceivingGems._id, receiverName})") Amazon Payments
button.btn.btn-secondary(@click='close()') {{$t('cancel')}}
</template>
@ -86,12 +87,16 @@ import { mapState } from 'client/libs/store';
import planGemLimits from '../../../common/script/libs/planGemLimits';
import paymentsMixin from 'client/mixins/payments';
import notificationsMixin from 'client/mixins/notifications';
import amazonButton from 'client/components/payments/amazonButton';
// @TODO: EMAILS.TECH_ASSISTANCE_EMAIL, load from config
const TECH_ASSISTANCE_EMAIL = 'admin@habitica.com';
export default {
mixins: [paymentsMixin, notificationsMixin],
components: {
amazonButton,
},
data () {
return {
planGemLimits,

View file

@ -80,7 +80,8 @@
a.purchase(@click="openPaypal(paypalPurchaseLink, 'subscription')", :disabled='!subscription.key')
img(src='https://www.paypalobjects.com/webstatic/en_US/i/buttons/pp-acceptance-small.png', :alt="$t('paypal')")
.col-md-4
a.btn.btn-secondary.purchase(@click="payWithAmazon()")
amazon-button(:amazon-data="{type: 'subscription', subscription: this.subscription.key, coupon: this.subscription.coupon}")
// a.btn.btn-secondary.purchase(@click="payWithAmazon()")
img(src='https://payments.amazon.com/gp/cba/button', :alt="$t('amazonPayments')")
.row
.col-6
@ -115,8 +116,13 @@ import planGemLimits from '../../../common/script/libs/planGemLimits';
import paymentsMixin from '../../mixins/payments';
import notificationsMixin from '../../mixins/notifications';
import amazonButton from 'client/components/payments/amazonButton';
export default {
mixins: [paymentsMixin, notificationsMixin],
components: {
amazonButton,
},
data () {
return {
loading: false,
@ -240,13 +246,6 @@ export default {
},
},
methods: {
payWithAmazon () {
this.amazonPaymentsInit({
type: 'subscription',
subscription: this.subscription.key,
coupon: this.subscription.coupon,
});
},
async applyCoupon (coupon) {
const response = await axios.post(`/api/v4/coupons/validate/${coupon}`);

View file

@ -251,8 +251,30 @@ export default {
this.amazonPayments.gift = data.gift;
this.amazonPayments.type = data.type;
},
amazonOnError (error) {
alert(error.getErrorMessage());
this.reset();
},
reset () {
// @TODO: Ensure we are using all of these
// some vars are set in the payments mixin. We should try to edit in one place
this.amazonPayments.modal = null;
this.amazonPayments.type = null;
this.amazonPayments.loggedIn = false;
this.$root.$emit('habitica::pay-with-amazon', this.amazonPayments);
// 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;
},
async cancelSubscription (config) {
if (config && config.group && !confirm(this.$t('confirmCancelGroupPlan'))) return;