feat: add reconfigurable paypal button to donate page

This commit is contained in:
Ahmad Abdolsaheb
2020-03-20 04:33:27 +03:00
committed by Mrugesh Mohapatra
parent a2c7659f2d
commit cdc8b1e8e5
5 changed files with 181 additions and 65 deletions

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react'; import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
@ -20,6 +20,8 @@ import {
} from '../../../../config/donation-settings'; } from '../../../../config/donation-settings';
import Spacer from '../helpers/Spacer'; import Spacer from '../helpers/Spacer';
import DonateFormChildViewForHOC from './DonateFormChildViewForHOC'; import DonateFormChildViewForHOC from './DonateFormChildViewForHOC';
import PaypalButton from './PaypalButton';
import DonateCompletion from './DonateCompletion';
import { import {
isSignedInSelector, isSignedInSelector,
signInLoadingSelector, signInLoadingSelector,
@ -50,11 +52,18 @@ const mapStateToProps = createSelector(
showLoading showLoading
}) })
); );
const mapDispatchToProps = { const mapDispatchToProps = {
navigate navigate
}; };
const initialState = {
donationState: {
processing: false,
success: false,
error: ''
}
};
class DonateForm extends Component { class DonateForm extends Component {
constructor(...args) { constructor(...args) {
super(...args); super(...args);
@ -63,15 +72,30 @@ class DonateForm extends Component {
this.amounts = amountsConfig; this.amounts = amountsConfig;
this.state = { this.state = {
...initialState,
...defaultStateConfig, ...defaultStateConfig,
processing: false processing: false
}; };
this.onDonationStateChange = this.onDonationStateChange.bind(this);
this.getActiveDonationAmount = this.getActiveDonationAmount.bind(this); this.getActiveDonationAmount = this.getActiveDonationAmount.bind(this);
this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this); this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this);
this.handleSelectAmount = this.handleSelectAmount.bind(this); this.handleSelectAmount = this.handleSelectAmount.bind(this);
this.handleSelectDuration = this.handleSelectDuration.bind(this); this.handleSelectDuration = this.handleSelectDuration.bind(this);
this.hideAmountOptionsCB = this.hideAmountOptionsCB.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) { getActiveDonationAmount(durationSelected, amountSelected) {
@ -199,13 +223,64 @@ class DonateForm extends Component {
); );
} }
resetDonation() {
return this.setState({ ...initialState });
}
renderCompletion(props) {
return <DonateCompletion {...props} />;
}
render() { 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 ( return (
<Row> <Row>
<Col sm={10} smOffset={1} xs={12}> <Col sm={10} smOffset={1} xs={12}>
{this.renderDurationAmountOptions()} {this.renderDurationAmountOptions()}
</Col> </Col>
<Col sm={10} smOffset={1} xs={12}> <Col sm={10} smOffset={1} xs={12}>
{subscriptionPayment ? (
<Fragment>
<b>
Confirm your donation of ${donationAmount / 100} / year with
PayPal:
</b>
<Spacer />
</Fragment>
) : (
''
)}
<PaypalButton
donationAmount={donationAmount}
donationDuration={donationDuration}
handleProcessing={handleProcessing}
onDonationStateChange={this.onDonationStateChange}
/>
</Col>
<Col sm={10} smOffset={1} xs={12}>
{subscriptionPayment ? (
<Fragment>
<Spacer />
<b>Or donate with a credit card:</b>
<Spacer />
</Fragment>
) : (
''
)}
{this.renderDonationOptions()} {this.renderDonationOptions()}
</Col> </Col>
</Row> </Row>

View File

@ -169,6 +169,9 @@ li.disabled > a {
color: var(--gray-15) !important; color: var(--gray-15) !important;
} }
.donate-page-wrapper,
.donate-page-wrapper b,
.donate-page-wrapper h3,
.donate-text p, .donate-text p,
.donation-description, .donation-description,
[name='payment-method'] { [name='payment-method'] {

View File

@ -134,6 +134,8 @@ class MinimalDonateForm extends Component {
<b>Confirm your donation of $60 / year with PayPal:</b> <b>Confirm your donation of $60 / year with PayPal:</b>
<Spacer /> <Spacer />
<PaypalButton <PaypalButton
donationAmount={donationAmount}
donationDuration={donationDuration}
handleProcessing={handleProcessing} handleProcessing={handleProcessing}
onDonationStateChange={this.onDonationStateChange} onDonationStateChange={this.onDonationStateChange}
/> />

View File

@ -7,22 +7,33 @@ import { createSelector } from 'reselect';
import { PayPalButton } from 'react-paypal-button-v2'; import { PayPalButton } from 'react-paypal-button-v2';
import { paypalClientId } from '../../../config/env.json'; import { paypalClientId } from '../../../config/env.json';
import { verifySubscriptionPaypal } from '../../utils/ajax'; import { verifySubscriptionPaypal } from '../../utils/ajax';
import { paypalConfig } from '../../../../config/donation-settings'; import { paypalConfigurator } from '../../../../config/donation-settings';
import { signInLoadingSelector, userSelector, executeGA } from '../../redux'; import { signInLoadingSelector, userSelector } from '../../redux';
const { durationPlans } = paypalConfig;
export class PaypalButton extends Component { export class PaypalButton extends Component {
constructor(...props) { constructor(props) {
super(...props); super(props);
this.state = {
planId: durationPlans.year['6000'].planId
};
this.handleApproval = this.handleApproval.bind(this); 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 => { 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, ''); this.props.onDonationStateChange(false, true, '');
verifySubscriptionPaypal(data) verifySubscriptionPaypal(data)
.then(response => { .then(response => {
@ -46,48 +57,48 @@ export class PaypalButton extends Component {
}; };
render() { render() {
return ( const { duration, planId } = this.state;
<PayPalButton const isOneTimePayment = duration === 'onetime';
createSubscription={(data, actions) => {
executeGA({ if (!isOneTimePayment) {
type: 'event', return (
data: { <PayPalButton
category: 'Donation', createSubscription={(data, actions) => {
action: `Modal Paypal clicked` return actions.subscription.create({
} plan_id: planId
}); });
return actions.subscription.create({ }}
plan_id: this.state.planId onApprove={data => {
}); this.handleApproval(data);
}} }}
onApprove={data => { onCancel={() => {
this.handleApproval(data); this.props.onDonationStateChange(
}} false,
onCancel={() => { false,
this.props.onDonationStateChange( `Uh - oh. It looks like your transaction didn't go through. Could you please try again?`
false, );
false, }}
'Payment has been canceled.' onError={() =>
); this.props.onDonationStateChange(false, false, 'Please try again.')
}} }
onError={() => options={{
this.props.onDonationStateChange(false, false, 'Please try again.') vault: true,
} disableFunding: 'card',
options={{ clientId: paypalClientId
vault: true, }}
disableFunding: 'card', style={{
clientId: paypalClientId tagline: false,
}} height: 43
style={{ }}
tagline: false, />
height: 43 );
}} } else return '';
/>
);
} }
} }
const propTypes = { const propTypes = {
donationAmount: PropTypes.number,
donationDuration: PropTypes.string,
handleProcessing: PropTypes.func, handleProcessing: PropTypes.func,
isDonating: PropTypes.bool, isDonating: PropTypes.bool,
onDonationStateChange: PropTypes.func onDonationStateChange: PropTypes.func

View File

@ -43,25 +43,38 @@ const donationSubscriptionConfig = {
// Shared paypal configuration // Shared paypal configuration
const paypalConfigTypes = { const paypalConfigTypes = {
live: { live: {
durationPlans: { month: {
month: { '500': {
'500': { planId: 'P-1L11422374370240ULZKX3PA'
planId: 'P-1L11422374370240ULZKX3PA' }
} },
year: {
'6000': {
planId: 'P-9Y661558DW462253NLZZ2IMQ'
},
'25000': {
planId: 'P-3NN39392MK1889318LZZ2KQY'
},
'100000': {
planId: 'P-7YN43286C4599382LLZZ2JUI'
} }
} }
}, },
staging: { staging: {
durationPlans: { month: {
month: { '500': {
'500': { planId: 'P-37N14480BW163382FLZYPVMA'
planId: 'P-37N14480BW163382FLZYPVMA' }
} },
year: {
'6000': {
planId: 'P-0UY77185EM3077131LZYP6VY'
}, },
year: { '25000': {
'6000': { planId: 'P-7K1585908S634694XLZZTHUQ'
planId: 'P-0UY77185EM3077131LZYP6VY' },
} '100000': {
planId: 'P-0J5231134H608574XLZZTDLQ'
} }
} }
} }
@ -72,6 +85,17 @@ const paypalConfig =
? paypalConfigTypes['live'] ? paypalConfigTypes['live']
: paypalConfigTypes['staging']; : 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 = { module.exports = {
durationsConfig, durationsConfig,
amountsConfig, amountsConfig,
@ -81,5 +105,6 @@ module.exports = {
donationOneTimeConfig, donationOneTimeConfig,
donationSubscriptionConfig, donationSubscriptionConfig,
modalDefaultStateConfig, modalDefaultStateConfig,
paypalConfig paypalConfig,
paypalConfigurator
}; };