fix(donate): allow unauthenticated donations for year-end
This commit is contained in:
committed by
mrugesh
parent
fc25c281e4
commit
663f726c4e
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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) {
|
||||||
|
Reference in New Issue
Block a user