From cdc8b1e8e5ef02f53195a49342d9d6952772b3c6 Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Fri, 20 Mar 2020 04:33:27 +0300 Subject: [PATCH] feat: add reconfigurable paypal button to donate page --- client/src/components/Donation/DonateForm.js | 79 ++++++++++++- client/src/components/Donation/Donation.css | 3 + .../components/Donation/MinimalDonateForm.js | 2 + .../src/components/Donation/PaypalButton.js | 107 ++++++++++-------- config/donation-settings.js | 55 ++++++--- 5 files changed, 181 insertions(+), 65 deletions(-) diff --git a/client/src/components/Donation/DonateForm.js b/client/src/components/Donation/DonateForm.js index 26f30dc196..dcdd8348cd 100644 --- a/client/src/components/Donation/DonateForm.js +++ b/client/src/components/Donation/DonateForm.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; @@ -20,6 +20,8 @@ import { } from '../../../../config/donation-settings'; import Spacer from '../helpers/Spacer'; import DonateFormChildViewForHOC from './DonateFormChildViewForHOC'; +import PaypalButton from './PaypalButton'; +import DonateCompletion from './DonateCompletion'; import { isSignedInSelector, signInLoadingSelector, @@ -50,11 +52,18 @@ const mapStateToProps = createSelector( showLoading }) ); - const mapDispatchToProps = { navigate }; +const initialState = { + donationState: { + processing: false, + success: false, + error: '' + } +}; + class DonateForm extends Component { constructor(...args) { super(...args); @@ -63,15 +72,30 @@ class DonateForm extends Component { this.amounts = amountsConfig; this.state = { + ...initialState, ...defaultStateConfig, processing: false }; + this.onDonationStateChange = this.onDonationStateChange.bind(this); this.getActiveDonationAmount = this.getActiveDonationAmount.bind(this); this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this); this.handleSelectAmount = this.handleSelectAmount.bind(this); this.handleSelectDuration = this.handleSelectDuration.bind(this); this.hideAmountOptionsCB = this.hideAmountOptionsCB.bind(this); + this.resetDonation = this.resetDonation.bind(this); + } + + onDonationStateChange(success, processing, error) { + this.setState(state => ({ + ...state, + donationState: { + ...state.donationState, + processing: processing, + success: success, + error: error + } + })); } getActiveDonationAmount(durationSelected, amountSelected) { @@ -199,13 +223,64 @@ class DonateForm extends Component { ); } + resetDonation() { + return this.setState({ ...initialState }); + } + + renderCompletion(props) { + return ; + } + render() { + const { donationAmount, donationDuration } = this.state; + const { handleProcessing } = this.props; + const { + donationState: { processing, success, error } + } = this.state; + const subscriptionPayment = donationDuration !== 'onetime'; + if (processing || success || error) { + return this.renderCompletion({ + processing, + success, + error, + reset: this.resetDonation + }); + } return ( {this.renderDurationAmountOptions()} + {subscriptionPayment ? ( + + + Confirm your donation of ${donationAmount / 100} / year with + PayPal: + + + + ) : ( + '' + )} + + + + + {subscriptionPayment ? ( + + + Or donate with a credit card: + + + ) : ( + '' + )} {this.renderDonationOptions()} diff --git a/client/src/components/Donation/Donation.css b/client/src/components/Donation/Donation.css index 3ca7e9908a..65c4ffddd4 100644 --- a/client/src/components/Donation/Donation.css +++ b/client/src/components/Donation/Donation.css @@ -169,6 +169,9 @@ li.disabled > a { color: var(--gray-15) !important; } +.donate-page-wrapper, +.donate-page-wrapper b, +.donate-page-wrapper h3, .donate-text p, .donation-description, [name='payment-method'] { diff --git a/client/src/components/Donation/MinimalDonateForm.js b/client/src/components/Donation/MinimalDonateForm.js index 88bbb27f7d..d2e10b8670 100644 --- a/client/src/components/Donation/MinimalDonateForm.js +++ b/client/src/components/Donation/MinimalDonateForm.js @@ -134,6 +134,8 @@ class MinimalDonateForm extends Component { Confirm your donation of $60 / year with PayPal: diff --git a/client/src/components/Donation/PaypalButton.js b/client/src/components/Donation/PaypalButton.js index fb566e0385..fd39f35db9 100644 --- a/client/src/components/Donation/PaypalButton.js +++ b/client/src/components/Donation/PaypalButton.js @@ -7,22 +7,33 @@ import { createSelector } from 'reselect'; import { PayPalButton } from 'react-paypal-button-v2'; import { paypalClientId } from '../../../config/env.json'; import { verifySubscriptionPaypal } from '../../utils/ajax'; -import { paypalConfig } from '../../../../config/donation-settings'; -import { signInLoadingSelector, userSelector, executeGA } from '../../redux'; - -const { durationPlans } = paypalConfig; +import { paypalConfigurator } from '../../../../config/donation-settings'; +import { signInLoadingSelector, userSelector } from '../../redux'; export class PaypalButton extends Component { - constructor(...props) { - super(...props); - this.state = { - planId: durationPlans.year['6000'].planId - }; + constructor(props) { + super(props); this.handleApproval = this.handleApproval.bind(this); } + state = {}; + + static getDerivedStateFromProps(props, state) { + const { donationAmount, donationDuration } = props; + + const configurationObj = paypalConfigurator( + donationAmount, + donationDuration + ); + if (state === configurationObj) { + return null; + } + return { ...configurationObj }; + } + handleApproval = data => { - this.props.handleProcessing('year', 6000, 'Paypal payment submission'); + const { amount, duration } = this.state; + this.props.handleProcessing(duration, amount, 'Paypal payment submission'); this.props.onDonationStateChange(false, true, ''); verifySubscriptionPaypal(data) .then(response => { @@ -46,48 +57,48 @@ export class PaypalButton extends Component { }; render() { - return ( - { - executeGA({ - type: 'event', - data: { - category: 'Donation', - action: `Modal Paypal clicked` - } - }); - return actions.subscription.create({ - plan_id: this.state.planId - }); - }} - onApprove={data => { - this.handleApproval(data); - }} - onCancel={() => { - this.props.onDonationStateChange( - false, - false, - 'Payment has been canceled.' - ); - }} - onError={() => - this.props.onDonationStateChange(false, false, 'Please try again.') - } - options={{ - vault: true, - disableFunding: 'card', - clientId: paypalClientId - }} - style={{ - tagline: false, - height: 43 - }} - /> - ); + const { duration, planId } = this.state; + const isOneTimePayment = duration === 'onetime'; + + if (!isOneTimePayment) { + return ( + { + return actions.subscription.create({ + plan_id: planId + }); + }} + onApprove={data => { + this.handleApproval(data); + }} + onCancel={() => { + this.props.onDonationStateChange( + false, + false, + `Uh - oh. It looks like your transaction didn't go through. Could you please try again?` + ); + }} + onError={() => + this.props.onDonationStateChange(false, false, 'Please try again.') + } + options={{ + vault: true, + disableFunding: 'card', + clientId: paypalClientId + }} + style={{ + tagline: false, + height: 43 + }} + /> + ); + } else return ''; } } const propTypes = { + donationAmount: PropTypes.number, + donationDuration: PropTypes.string, handleProcessing: PropTypes.func, isDonating: PropTypes.bool, onDonationStateChange: PropTypes.func diff --git a/config/donation-settings.js b/config/donation-settings.js index 018a0adad3..62d0721e4e 100644 --- a/config/donation-settings.js +++ b/config/donation-settings.js @@ -43,25 +43,38 @@ const donationSubscriptionConfig = { // Shared paypal configuration const paypalConfigTypes = { live: { - durationPlans: { - month: { - '500': { - planId: 'P-1L11422374370240ULZKX3PA' - } + month: { + '500': { + planId: 'P-1L11422374370240ULZKX3PA' + } + }, + year: { + '6000': { + planId: 'P-9Y661558DW462253NLZZ2IMQ' + }, + '25000': { + planId: 'P-3NN39392MK1889318LZZ2KQY' + }, + '100000': { + planId: 'P-7YN43286C4599382LLZZ2JUI' } } }, staging: { - durationPlans: { - month: { - '500': { - planId: 'P-37N14480BW163382FLZYPVMA' - } + month: { + '500': { + planId: 'P-37N14480BW163382FLZYPVMA' + } + }, + year: { + '6000': { + planId: 'P-0UY77185EM3077131LZYP6VY' }, - year: { - '6000': { - planId: 'P-0UY77185EM3077131LZYP6VY' - } + '25000': { + planId: 'P-7K1585908S634694XLZZTHUQ' + }, + '100000': { + planId: 'P-0J5231134H608574XLZZTDLQ' } } } @@ -72,6 +85,17 @@ const paypalConfig = ? paypalConfigTypes['live'] : paypalConfigTypes['staging']; +const paypalConfigurator = (donationAmount, donationDuration) => { + if (donationDuration === 'onetime') { + return { amount: donationAmount, duration: donationDuration }; + } + return { + amount: donationAmount, + duration: donationDuration, + planId: paypalConfig[donationDuration]['' + donationAmount].planId + }; +}; + module.exports = { durationsConfig, amountsConfig, @@ -81,5 +105,6 @@ module.exports = { donationOneTimeConfig, donationSubscriptionConfig, modalDefaultStateConfig, - paypalConfig + paypalConfig, + paypalConfigurator };