fix(client): replace Stripe with PayPal (#41924)
* feat: remove stripe payment option from client * feat: remove stripe completely * fix: remove last Stripe remnants Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@@ -49,6 +49,7 @@ function DonateCompletion({
|
||||
{success && (
|
||||
<div>
|
||||
<p>{t('donate.free-tech')}</p>
|
||||
<p>{t('donate.no-halo')}</p>
|
||||
</div>
|
||||
)}
|
||||
{error && <p>{error}</p>}
|
||||
|
@@ -3,9 +3,7 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Elements } from '@stripe/react-stripe-js';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Row,
|
||||
Tab,
|
||||
@@ -20,23 +18,16 @@ import {
|
||||
durationsConfig,
|
||||
defaultAmount,
|
||||
defaultDonation,
|
||||
donationUrls,
|
||||
modalDefaultDonation
|
||||
} from '../../../../config/donation-settings';
|
||||
import envData from '../../../../config/env.json';
|
||||
import { stripeScriptLoader } from '../../utils/scriptLoaders';
|
||||
import Spacer from '../helpers/Spacer';
|
||||
import PaypalButton from './PaypalButton';
|
||||
import DonateCompletion from './DonateCompletion';
|
||||
import StripeCardForm from './StripeCardForm';
|
||||
import {
|
||||
isSignedInSelector,
|
||||
signInLoadingSelector,
|
||||
donationFormStateSelector,
|
||||
hardGoTo as navigate,
|
||||
addDonation,
|
||||
createStripeSession,
|
||||
postChargeStripe,
|
||||
updateDonationFormState,
|
||||
defaultDonationFormState,
|
||||
userSelector
|
||||
@@ -44,14 +35,11 @@ import {
|
||||
|
||||
import './Donation.css';
|
||||
|
||||
const { stripePublicKey } = envData;
|
||||
|
||||
const numToCommas = num =>
|
||||
num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||
|
||||
const propTypes = {
|
||||
addDonation: PropTypes.func,
|
||||
createStripeSession: PropTypes.func,
|
||||
defaultTheme: PropTypes.string,
|
||||
donationFormState: PropTypes.object,
|
||||
email: PropTypes.string,
|
||||
@@ -59,8 +47,6 @@ const propTypes = {
|
||||
isDonating: PropTypes.bool,
|
||||
isMinimalForm: PropTypes.bool,
|
||||
isSignedIn: PropTypes.bool,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
postChargeStripe: PropTypes.func.isRequired,
|
||||
showLoading: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string,
|
||||
@@ -83,10 +69,7 @@ const mapStateToProps = createSelector(
|
||||
|
||||
const mapDispatchToProps = {
|
||||
addDonation,
|
||||
navigate,
|
||||
postChargeStripe,
|
||||
updateDonationFormState,
|
||||
createStripeSession
|
||||
updateDonationFormState
|
||||
};
|
||||
|
||||
class DonateForm extends Component {
|
||||
@@ -102,63 +85,26 @@ class DonateForm extends Component {
|
||||
|
||||
this.state = {
|
||||
...initialAmountAndDuration,
|
||||
processing: false,
|
||||
stripe: null
|
||||
processing: false
|
||||
};
|
||||
|
||||
this.handleStripeLoad = this.handleStripeLoad.bind(this);
|
||||
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.handleStripeCheckoutRedirect = this.handleStripeCheckoutRedirect.bind(
|
||||
this
|
||||
);
|
||||
this.hideAmountOptionsCB = this.hideAmountOptionsCB.bind(this);
|
||||
this.resetDonation = this.resetDonation.bind(this);
|
||||
this.postStripeDonation = this.postStripeDonation.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (window.Stripe) {
|
||||
this.handleStripeLoad();
|
||||
} else if (document.querySelector('#stripe-js')) {
|
||||
document
|
||||
.querySelector('#stripe-js')
|
||||
.addEventListener('load', this.handleStripeLoad);
|
||||
} else {
|
||||
stripeScriptLoader(this.handleStripeLoad);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const stripeMountPoint = document.querySelector('#stripe-js');
|
||||
if (stripeMountPoint) {
|
||||
stripeMountPoint.removeEventListener('load', this.handleStripeLoad);
|
||||
}
|
||||
this.resetDonation();
|
||||
}
|
||||
|
||||
handleStripeLoad() {
|
||||
// Create Stripe instance once Stripe.js loads
|
||||
if (stripePublicKey) {
|
||||
this.setState(state => ({
|
||||
...state,
|
||||
stripe: window.Stripe(stripePublicKey)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
onDonationStateChange(donationState) {
|
||||
// scroll to top
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
this.props.updateDonationFormState(donationState);
|
||||
// send donation made on the donate page to related news article
|
||||
if (donationState.success && !this.props.isMinimalForm) {
|
||||
this.props.navigate(donationUrls.successUrl);
|
||||
}
|
||||
}
|
||||
|
||||
getActiveDonationAmount(durationSelected, amountSelected) {
|
||||
@@ -204,41 +150,6 @@ class DonateForm extends Component {
|
||||
this.setState({ donationAmount });
|
||||
}
|
||||
|
||||
postStripeDonation(token) {
|
||||
const { donationAmount: amount, donationDuration: duration } = this.state;
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
// change the donation modal button label to close
|
||||
// or display the close button for the cert donation section
|
||||
if (this.props.handleProcessing) {
|
||||
this.props.handleProcessing(duration, amount);
|
||||
}
|
||||
this.props.postChargeStripe({ token, amount, duration });
|
||||
}
|
||||
|
||||
async handleStripeCheckoutRedirect(e, paymentMethod) {
|
||||
e.preventDefault();
|
||||
const { stripe, donationAmount, donationDuration } = this.state;
|
||||
const { handleProcessing, email } = this.props;
|
||||
|
||||
handleProcessing(
|
||||
donationDuration,
|
||||
donationAmount,
|
||||
`stripe (${paymentMethod}) button click`
|
||||
);
|
||||
|
||||
this.props.createStripeSession({
|
||||
stripe,
|
||||
data: {
|
||||
donationAmount,
|
||||
donationDuration,
|
||||
clickedPaymentMethod: paymentMethod,
|
||||
email,
|
||||
context: 'donate page'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderAmountButtons(duration) {
|
||||
return this.amounts[duration].map(amount => (
|
||||
<ToggleButton
|
||||
@@ -318,7 +229,14 @@ class DonateForm extends Component {
|
||||
}
|
||||
|
||||
renderDonationOptions() {
|
||||
const { handleProcessing, isSignedIn, addDonation, t } = this.props;
|
||||
const {
|
||||
handleProcessing,
|
||||
isSignedIn,
|
||||
addDonation,
|
||||
t,
|
||||
defaultTheme,
|
||||
theme
|
||||
} = this.props;
|
||||
const { donationAmount, donationDuration } = this.state;
|
||||
|
||||
const isOneTime = donationDuration === 'onetime';
|
||||
@@ -334,15 +252,6 @@ class DonateForm extends Component {
|
||||
)}
|
||||
<Spacer />
|
||||
<div className='donate-btn-group'>
|
||||
<Button
|
||||
block={true}
|
||||
bsStyle='primary'
|
||||
id='confirm-donation-btn'
|
||||
onClick={e => this.handleStripeCheckoutRedirect(e, 'credit card')}
|
||||
>
|
||||
{}
|
||||
<b>{t('donate.credit-card')} </b>
|
||||
</Button>
|
||||
<PaypalButton
|
||||
addDonation={addDonation}
|
||||
donationAmount={donationAmount}
|
||||
@@ -351,6 +260,7 @@ class DonateForm extends Component {
|
||||
isSubscription={isOneTime ? false : true}
|
||||
onDonationStateChange={this.onDonationStateChange}
|
||||
skipAddDonation={!isSignedIn}
|
||||
theme={defaultTheme ? defaultTheme : theme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -366,22 +276,13 @@ class DonateForm extends Component {
|
||||
}
|
||||
|
||||
renderModalForm() {
|
||||
const { donationAmount, donationDuration, stripe } = this.state;
|
||||
const {
|
||||
handleProcessing,
|
||||
addDonation,
|
||||
email,
|
||||
theme,
|
||||
t,
|
||||
defaultTheme
|
||||
} = this.props;
|
||||
const { donationAmount, donationDuration } = this.state;
|
||||
const { handleProcessing, addDonation, defaultTheme, theme } = this.props;
|
||||
return (
|
||||
<Row>
|
||||
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
|
||||
<Spacer />
|
||||
<b>
|
||||
{this.getDonationButtonLabel()} {t('donate.paypal')}
|
||||
</b>
|
||||
<b>{this.getDonationButtonLabel()}:</b>
|
||||
<Spacer />
|
||||
<PaypalButton
|
||||
addDonation={addDonation}
|
||||
@@ -389,22 +290,9 @@ class DonateForm extends Component {
|
||||
donationDuration={donationDuration}
|
||||
handleProcessing={handleProcessing}
|
||||
onDonationStateChange={this.onDonationStateChange}
|
||||
theme={defaultTheme ? defaultTheme : theme}
|
||||
/>
|
||||
</Col>
|
||||
<Col lg={8} lgOffset={2} sm={10} smOffset={1} xs={12}>
|
||||
<Spacer />
|
||||
<b>{t('donate.credit-card-2')}</b>
|
||||
<Spacer />
|
||||
<Elements stripe={stripe}>
|
||||
<StripeCardForm
|
||||
getDonationButtonLabel={this.getDonationButtonLabel}
|
||||
onDonationStateChange={this.onDonationStateChange}
|
||||
postStripeDonation={this.postStripeDonation}
|
||||
theme={defaultTheme ? defaultTheme : theme}
|
||||
userEmail={email}
|
||||
/>
|
||||
</Elements>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
@@ -48,6 +48,10 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.paypal-buttons-container {
|
||||
min-height: 142px;
|
||||
}
|
||||
|
||||
.donate-input-element {
|
||||
padding-top: 8px;
|
||||
}
|
||||
@@ -56,8 +60,7 @@
|
||||
color: #707070;
|
||||
}
|
||||
|
||||
.donation-form .form-control:focus,
|
||||
.StripeElement--focus {
|
||||
.donation-form .form-control:focus {
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
||||
@@ -66,7 +69,7 @@
|
||||
|
||||
.donation-form .email--invalid.form-control:focus,
|
||||
.donation-form .email--invalid,
|
||||
.donation-form .StripeElement--invalid {
|
||||
.donation-form {
|
||||
border-color: #eb1c26;
|
||||
color: #eb1c26;
|
||||
}
|
||||
@@ -383,7 +386,8 @@ button#confirm-donation-btn:hover {
|
||||
|
||||
.donate-btn-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.donate-btn-group > * {
|
||||
|
@@ -55,11 +55,7 @@ function DonateModal({
|
||||
}) {
|
||||
const [closeLabel, setCloseLabel] = React.useState(false);
|
||||
const { t } = useTranslation();
|
||||
const handleProcessing = (
|
||||
duration,
|
||||
amount,
|
||||
action = 'stripe form submission'
|
||||
) => {
|
||||
const handleProcessing = (duration, amount, action) => {
|
||||
executeGA({
|
||||
type: 'event',
|
||||
data: {
|
||||
|
@@ -25,7 +25,10 @@ export class PayPalButtonScriptLoader extends Component {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.isSubscription !== this.state.isSubscription) {
|
||||
if (
|
||||
prevProps.isSubscription !== this.state.isSubscription ||
|
||||
prevProps.style !== this.props.style
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({ isSdkLoaded: false });
|
||||
this.loadScript(this.state.isSubscription, true);
|
||||
@@ -34,7 +37,7 @@ export class PayPalButtonScriptLoader extends Component {
|
||||
|
||||
loadScript(subscription, deleteScript) {
|
||||
if (deleteScript) scriptRemover('paypal-sdk');
|
||||
let queries = `?client-id=${this.props.clientId}&disable-funding=credit,card,bancontact,blik,eps,giropay,ideal,mybank,p24,sepa,sofort,venmo`;
|
||||
let queries = `?client-id=${this.props.clientId}&disable-funding=credit,bancontact,blik,eps,giropay,ideal,mybank,p24,sepa,sofort,venmo`;
|
||||
if (subscription) queries += '&vault=true&intent=subscription';
|
||||
|
||||
scriptLoader(
|
||||
|
@@ -58,58 +58,61 @@ export class PaypalButton extends Component {
|
||||
|
||||
render() {
|
||||
const { duration, planId, amount } = this.state;
|
||||
const { t } = this.props;
|
||||
const { t, theme } = this.props;
|
||||
const isSubscription = duration !== 'onetime';
|
||||
|
||||
const buttonColor = theme === 'night' ? 'white' : 'gold';
|
||||
if (!paypalClientId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<PayPalButtonScriptLoader
|
||||
amount={amount}
|
||||
clientId={paypalClientId}
|
||||
createOrder={(data, actions) => {
|
||||
return actions.order.create({
|
||||
purchase_units: [
|
||||
{
|
||||
amount: {
|
||||
currency_code: 'USD',
|
||||
value: (amount / 100).toString()
|
||||
<div className={'paypal-buttons-container'}>
|
||||
<PayPalButtonScriptLoader
|
||||
amount={amount}
|
||||
clientId={paypalClientId}
|
||||
createOrder={(data, actions) => {
|
||||
return actions.order.create({
|
||||
purchase_units: [
|
||||
{
|
||||
amount: {
|
||||
currency_code: 'USD',
|
||||
value: (amount / 100).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}}
|
||||
createSubscription={(data, actions) => {
|
||||
return actions.subscription.create({
|
||||
plan_id: planId
|
||||
});
|
||||
}}
|
||||
isSubscription={isSubscription}
|
||||
onApprove={data => {
|
||||
this.handleApproval(data, isSubscription);
|
||||
}}
|
||||
onCancel={() => {
|
||||
this.props.onDonationStateChange({
|
||||
processing: false,
|
||||
success: false,
|
||||
error: t('donate.failed-pay')
|
||||
});
|
||||
}}
|
||||
onError={() =>
|
||||
this.props.onDonationStateChange({
|
||||
processing: false,
|
||||
success: false,
|
||||
error: t('donate.try-again')
|
||||
})
|
||||
}
|
||||
plantId={planId}
|
||||
style={{
|
||||
tagline: false,
|
||||
height: 43
|
||||
}}
|
||||
/>
|
||||
]
|
||||
});
|
||||
}}
|
||||
createSubscription={(data, actions) => {
|
||||
return actions.subscription.create({
|
||||
plan_id: planId
|
||||
});
|
||||
}}
|
||||
isSubscription={isSubscription}
|
||||
onApprove={data => {
|
||||
this.handleApproval(data, isSubscription);
|
||||
}}
|
||||
onCancel={() => {
|
||||
this.props.onDonationStateChange({
|
||||
processing: false,
|
||||
success: false,
|
||||
error: t('donate.failed-pay')
|
||||
});
|
||||
}}
|
||||
onError={() =>
|
||||
this.props.onDonationStateChange({
|
||||
processing: false,
|
||||
success: false,
|
||||
error: t('donate.try-again')
|
||||
})
|
||||
}
|
||||
plantId={planId}
|
||||
style={{
|
||||
tagline: false,
|
||||
height: 43,
|
||||
color: buttonColor
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -122,7 +125,8 @@ const propTypes = {
|
||||
isDonating: PropTypes.bool,
|
||||
onDonationStateChange: PropTypes.func,
|
||||
skipAddDonation: PropTypes.bool,
|
||||
t: PropTypes.func.isRequired
|
||||
t: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
|
@@ -1,198 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
CardNumberElement,
|
||||
CardExpiryElement,
|
||||
useStripe,
|
||||
useElements
|
||||
} from '@stripe/react-stripe-js';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEmail from 'validator/lib/isEmail';
|
||||
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
ControlLabel,
|
||||
FormGroup,
|
||||
Image,
|
||||
Button,
|
||||
Form,
|
||||
FormControl,
|
||||
Alert
|
||||
} from '@freecodecamp/react-bootstrap';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
|
||||
const initialPaymentInfoValidityState = {
|
||||
cardNumber: {
|
||||
complete: false,
|
||||
error: null
|
||||
},
|
||||
cardExpiry: {
|
||||
complete: false,
|
||||
error: null
|
||||
}
|
||||
};
|
||||
|
||||
const propTypes = {
|
||||
getDonationButtonLabel: PropTypes.func.isRequired,
|
||||
onDonationStateChange: PropTypes.func,
|
||||
postStripeDonation: PropTypes.func,
|
||||
t: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string,
|
||||
userEmail: PropTypes.string
|
||||
};
|
||||
|
||||
const StripeCardForm = ({
|
||||
getDonationButtonLabel,
|
||||
theme,
|
||||
t,
|
||||
onDonationStateChange,
|
||||
postStripeDonation,
|
||||
userEmail
|
||||
}) => {
|
||||
const [isSubmissionValid, setSubmissionValidity] = useState(true);
|
||||
const [email, setEmail] = useState(userEmail);
|
||||
const [isEmailValid, setEmailValidity] = useState(true);
|
||||
const [paymentInfoValidation, setPaymentValidity] = useState(
|
||||
initialPaymentInfoValidityState
|
||||
);
|
||||
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
|
||||
function handleInputChange(event) {
|
||||
const { elementType, error, complete } = event;
|
||||
setPaymentValidity({
|
||||
...paymentInfoValidation,
|
||||
[elementType]: {
|
||||
error,
|
||||
complete
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isPaymentInfoValid() {
|
||||
return Object.keys(paymentInfoValidation)
|
||||
.map(key => paymentInfoValidation[key])
|
||||
.every(({ complete, error }) => complete && !error);
|
||||
}
|
||||
|
||||
const options = {
|
||||
style: {
|
||||
base: {
|
||||
fontSize: '18px',
|
||||
color: `${theme === 'night' ? '#fff' : '#0a0a23'}`
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async event => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!isEmailValid || !isPaymentInfoValid())
|
||||
return setSubmissionValidity(false);
|
||||
else setSubmissionValidity(true);
|
||||
|
||||
if (!isEmail(email)) {
|
||||
return onDonationStateChange({
|
||||
error: t('donate.need-email')
|
||||
});
|
||||
}
|
||||
|
||||
const { error, token } = await stripe.createToken(
|
||||
elements.getElement(CardNumberElement),
|
||||
{ email }
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return onDonationStateChange({
|
||||
error: t('donate.went-wrong')
|
||||
});
|
||||
}
|
||||
return postStripeDonation(token);
|
||||
};
|
||||
|
||||
const handleEmailChange = e => {
|
||||
const newValue = e.target.value;
|
||||
setEmail(newValue);
|
||||
setEmailValidity(true);
|
||||
};
|
||||
|
||||
const handleEmailBlur = () => {
|
||||
const newValidation = isEmail(email);
|
||||
setEmailValidity(newValidation);
|
||||
};
|
||||
|
||||
const renderErrorMessage = () => {
|
||||
let message = '';
|
||||
if (!isEmailValid && !isPaymentInfoValid())
|
||||
message = <p>{t('donate.valid-info')}</p>;
|
||||
else if (!isEmailValid) message = <p>{t('donate.valid-email')}</p>;
|
||||
else message = <p>{t('donate.valid-card')}</p>;
|
||||
return <Alert bsStyle='danger'>{message}</Alert>;
|
||||
};
|
||||
|
||||
return (
|
||||
<Form className='donation-form' onSubmit={handleSubmit}>
|
||||
<div>{!isSubmissionValid ? renderErrorMessage() : ''}</div>
|
||||
<FormGroup className='donation-email-container'>
|
||||
<ControlLabel>{t('donate.email-receipt')}</ControlLabel>
|
||||
<FormControl
|
||||
className={!isEmailValid && email ? 'email--invalid' : ''}
|
||||
key='3'
|
||||
onBlur={handleEmailBlur}
|
||||
onChange={handleEmailChange}
|
||||
placeholder='me@example.com'
|
||||
required={true}
|
||||
type='text'
|
||||
value={email || ''}
|
||||
/>
|
||||
</FormGroup>
|
||||
<div className='donation-elements'>
|
||||
<FormGroup>
|
||||
<ControlLabel>{t('donate.card-number')}</ControlLabel>
|
||||
<CardNumberElement
|
||||
className='form-control donate-input-element'
|
||||
onChange={handleInputChange}
|
||||
options={options}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<ControlLabel>{t('donate.expiration')}</ControlLabel>
|
||||
<Row>
|
||||
<Col md={5} xs={12}>
|
||||
<CardExpiryElement
|
||||
className='form-control donate-input-element'
|
||||
onChange={handleInputChange}
|
||||
options={options}
|
||||
/>
|
||||
</Col>
|
||||
<Col className='form-payments-wrapper' md={7} xs={12}>
|
||||
<Image
|
||||
alt='payment options'
|
||||
className='form-payment-methods'
|
||||
src={
|
||||
'https://cdn.freecodecamp.org' +
|
||||
'/platform/universal/form-payments.png'
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<Button
|
||||
block={true}
|
||||
bsStyle='primary'
|
||||
disabled={!stripe}
|
||||
id='confirm-donation-btn'
|
||||
type='submit'
|
||||
>
|
||||
{getDonationButtonLabel()}
|
||||
</Button>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
StripeCardForm.displayName = 'StripeCardForm';
|
||||
StripeCardForm.propTypes = propTypes;
|
||||
|
||||
export default withTranslation()(StripeCardForm);
|
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import stripeObserver from './stripeIframesFix';
|
||||
import UniversalNav from './components/UniversalNav';
|
||||
|
||||
import './header.css';
|
||||
@@ -26,10 +25,6 @@ export class Header extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('click', this.handleClickOutside);
|
||||
|
||||
// Remove stacking Stripe iframes with each navigation
|
||||
// after visiting /donate
|
||||
stripeObserver();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@@ -1,30 +0,0 @@
|
||||
const stripeObserver = () => {
|
||||
const config = { attributes: false, childList: true, subtree: false };
|
||||
|
||||
const filterNodes = nl =>
|
||||
Array.from(nl)
|
||||
.filter(b => b.nodeName === 'IFRAME')
|
||||
.filter(b => /__privateStripeController/.test(b.name));
|
||||
|
||||
const mutationCallback = a => {
|
||||
const controllerAdded = a.reduce(
|
||||
(acc, curr) =>
|
||||
curr.type === 'childList'
|
||||
? [...acc, ...filterNodes(curr.addedNodes)]
|
||||
: acc,
|
||||
[]
|
||||
)[0];
|
||||
if (controllerAdded) {
|
||||
const allControllers = filterNodes(document.body.childNodes);
|
||||
allControllers.forEach(controller => {
|
||||
if (controller.name !== controllerAdded.name) {
|
||||
controller.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return new MutationObserver(mutationCallback).observe(document.body, config);
|
||||
};
|
||||
|
||||
export default stripeObserver;
|
Reference in New Issue
Block a user