fix(donate): allow unauthenticated donations for year-end

This commit is contained in:
Mrugesh Mohapatra
2019-12-18 04:15:55 +05:30
committed by mrugesh
parent fc25c281e4
commit 663f726c4e
4 changed files with 112 additions and 7 deletions

View File

@ -5,7 +5,7 @@ import { isEmail, isNumeric } from 'validator';
import { import {
durationKeysConfig, durationKeysConfig,
// donationOneTimeConfig, donationOneTimeConfig,
donationSubscriptionConfig donationSubscriptionConfig
} from '../../../config/donation-settings'; } from '../../../config/donation-settings';
import keys from '../../../config/secrets'; import keys from '../../../config/secrets';
@ -14,6 +14,7 @@ const log = debug('fcc:boot:donate');
export default function donateBoot(app, done) { export default function donateBoot(app, done) {
let stripe = false; let stripe = false;
const { User } = app.models;
const api = app.loopback.Router(); const api = app.loopback.Router();
const donateRouter = app.loopback.Router(); const donateRouter = app.loopback.Router();
@ -56,8 +57,7 @@ export default function donateBoot(app, done) {
isNumeric('' + amount) && isNumeric('' + amount) &&
durationKeysConfig.includes(duration) && durationKeysConfig.includes(duration) &&
duration === 'onetime' duration === 'onetime'
? // eslint-disable-next-line no-inline-comments ? donationOneTimeConfig.includes(amount)
amount > 1 // donationOneTimeConfig.includes(amount)
: donationSubscriptionConfig.plans[duration]; : donationSubscriptionConfig.plans[duration];
} }
@ -206,6 +206,104 @@ export default function donateBoot(app, done) {
}); });
} }
function createStripeDonationYearEnd(req, res) {
const { user, body } = req;
const {
amount,
duration,
token: { email, id }
} = body;
if (amount < 1 || duration !== 'onetime' || !isEmail(email)) {
return res.status(500).send({
error: 'The donation form had invalid values for this submission.'
});
}
const fccUser = user
? Promise.resolve(user)
: new Promise((resolve, reject) =>
User.findOrCreate(
{ where: { email } },
{ email },
(err, instance, isNew) => {
log('is new user instance: ', isNew);
if (err) {
return reject(err);
}
return resolve(instance);
}
)
);
let donatingUser = {};
let donation = {
email,
amount,
duration,
provider: 'stripe',
startDate: new Date(Date.now()).toISOString()
};
const createCustomer = user => {
donatingUser = user;
return stripe.customers.create({
email,
card: id
});
};
const createOneTimeCharge = customer => {
donation.customerId = customer.id;
return stripe.charges.create({
amount: amount,
currency: 'usd',
customer: customer.id
});
};
const createAsyncUserDonation = () => {
donatingUser
.createDonation(donation)
.toPromise()
.catch(err => {
throw new Error(err);
});
};
return Promise.resolve(fccUser)
.then(nonDonatingUser => {
const { isDonating } = nonDonatingUser;
if (isDonating) {
throw {
message: `User already has active donation(s).`,
type: 'AlreadyDonatingError'
};
}
return nonDonatingUser;
})
.then(createCustomer)
.then(customer => {
return createOneTimeCharge(customer).then(charge => {
donation.subscriptionId = 'one-time-charge-prefix-' + charge.id;
return res.send(charge);
});
})
.then(createAsyncUserDonation)
.catch(err => {
if (
err.type === 'StripeCardError' ||
err.type === 'AlreadyDonatingError'
) {
return res.status(402).send({ error: err.message });
}
return res
.status(500)
.send({ error: 'Donation failed due to a server error.' });
});
}
function createHmacHash(req, res) { function createHmacHash(req, res) {
const { user, body } = req; const { user, body } = req;
@ -261,10 +359,12 @@ export default function donateBoot(app, done) {
done(); done();
} else { } else {
api.post('/charge-stripe', createStripeDonation); api.post('/charge-stripe', createStripeDonation);
api.post('/charge-stripe-year-end', createStripeDonationYearEnd);
api.post('/create-hmac-hash', createHmacHash); api.post('/create-hmac-hash', createHmacHash);
donateRouter.use('/donate', api); donateRouter.use('/donate', api);
app.use(donateRouter); app.use(donateRouter);
app.use('/internal', donateRouter); app.use('/internal', donateRouter);
app.use('/unauthenticated', donateRouter);
connectToStripe().then(done); connectToStripe().then(done);
} }
} }

View File

@ -29,7 +29,8 @@ const propTypes = {
stripe: PropTypes.shape({ stripe: PropTypes.shape({
createToken: PropTypes.func.isRequired createToken: PropTypes.func.isRequired
}), }),
theme: PropTypes.string theme: PropTypes.string,
yearEndGift: PropTypes.bool
}; };
const initialState = { const initialState = {
donationState: { donationState: {
@ -117,6 +118,7 @@ class DonateFormChildViewForHOC extends Component {
postDonation(token) { postDonation(token) {
const { donationAmount: amount, donationDuration: duration } = this.state; const { donationAmount: amount, donationDuration: duration } = this.state;
const { yearEndGift } = this.props;
this.setState(state => ({ this.setState(state => ({
...state, ...state,
donationState: { donationState: {
@ -134,7 +136,7 @@ class DonateFormChildViewForHOC extends Component {
this.props.showCloseBtn(); this.props.showCloseBtn();
} }
return postChargeStripe({ return postChargeStripe(yearEndGift, {
token, token,
amount, amount,
duration duration

View File

@ -141,6 +141,7 @@ class YearEndDonationForm extends Component {
donationAmount={donationAmount} donationAmount={donationAmount}
donationDuration={donationDuration} donationDuration={donationDuration}
getDonationButtonLabel={this.getDonationButtonLabel} getDonationButtonLabel={this.getDonationButtonLabel}
yearEndGift={true}
/> />
</Elements> </Elements>
</StripeProvider> </StripeProvider>

View File

@ -50,8 +50,10 @@ export function getArticleById(shortId) {
} }
/** POST **/ /** POST **/
export function postChargeStripe(body) { export function postChargeStripe(yearEndGift, body) {
return post(`/donate/charge-stripe`, body); return yearEndGift
? postUnauthenticated('/donate/charge-stripe-year-end', body)
: post('/donate/charge-stripe', body);
} }
export function postCreateHmacHash(body) { export function postCreateHmacHash(body) {