feat: add Stripe card form (#43433)

* eat: add Stripe card form

* Apply suggestions from code review

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* feat: adjust payload and error handling

* feat:  readjust error handling

* Apply suggestions from code review

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>

* feat: refactors from comments

* feat: prevent submition during processing

* feat: redefine isSubmitting

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* fix: show the proper paypal button on donate page

* fix: handle errors from stripe

Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
Co-authored-by: moT01 <20648924+moT01@users.noreply.github.com>
This commit is contained in:
Ahmad Abdolsaheb
2021-09-17 22:15:56 +03:00
committed by GitHub
parent 2dd106eb2f
commit e5523bf16e
14 changed files with 476 additions and 51 deletions

View File

@ -1,12 +1,14 @@
import debug from 'debug';
import Stripe from 'stripe';
import { donationSubscriptionConfig } from '../../../../config/donation-settings';
import keys from '../../../../config/secrets';
import {
getAsyncPaypalToken,
verifyWebHook,
updateUser,
verifyWebHookType
verifyWebHookType,
createStripeCardDonation
} from '../utils/donation';
import { validStripeForm } from '../utils/stripeHelpers';
@ -26,6 +28,18 @@ export default function donateBoot(app, done) {
});
}
async function handleStripeCardDonation(req, res) {
return createStripeCardDonation(req, res, stripe, app).catch(err => {
if (err.type === 'AlreadyDonatingError')
return res.status(402).send({ error: err });
if (err.type === 'InvalidRequest')
return res.status(400).send({ error: err });
return res.status(500).send({
error: 'Donation failed due to a server error.'
});
});
}
function createStripeDonation(req, res) {
const { user, body } = req;
@ -184,7 +198,6 @@ export default function donateBoot(app, done) {
const stripeSecretInvalid = !secKey || secKey === 'sk_from_stripe_dashboard';
const stripPublicInvalid =
!stripeKey || stripeKey === 'pk_from_stripe_dashboard';
const paypalSecretInvalid =
!paypalKey || paypalKey === 'id_from_paypal_dashboard';
const paypalPublicInvalid =
@ -201,6 +214,7 @@ export default function donateBoot(app, done) {
done();
} else {
api.post('/charge-stripe', createStripeDonation);
api.post('/charge-stripe-card', handleStripeCardDonation);
api.post('/add-donation', addDonation);
hooks.post('/update-paypal', updatePaypal);
donateRouter.use('/donate', api);

View File

@ -1,6 +1,7 @@
/* eslint-disable camelcase */
import axios from 'axios';
import debug from 'debug';
import { donationSubscriptionConfig } from '../../../../config/donation-settings';
import keys from '../../../../config/secrets';
const log = debug('fcc:boot:donate');
@ -171,3 +172,79 @@ export async function updateUser(body, app) {
type: 'UnsupportedWebhookType'
};
}
export async function createStripeCardDonation(req, res, stripe) {
const {
body: {
token: { id: tokenId },
amount,
duration
},
user: { name, id: userId, email },
user
} = req;
if (!tokenId || !amount || !duration || !name || !userId || !email) {
throw {
message: 'Request is not valid',
type: 'InvalidRequest'
};
}
if (user.isDonating && duration !== 'onetime') {
throw {
message: `User already has active recurring donation(s).`,
type: 'AlreadyDonatingError'
};
}
let customerId;
try {
const customer = await stripe.customers.create({
email,
card: tokenId,
name
});
customerId = customer?.id;
} catch {
throw {
type: 'customerCreationFailed',
message: 'Failed to create stripe customer'
};
}
log(`Stripe customer with id ${customerId} created`);
let subscriptionId;
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [
{
plan: `${donationSubscriptionConfig.duration[
duration
].toLowerCase()}-donation-${amount}`
}
]
});
subscriptionId = subscription?.id;
} catch {
throw {
type: 'subscriptionCreationFailed',
message: 'Failed to create stripe subscription'
};
}
log(`Stripe subscription with id ${subscriptionId} created`);
// save Donation
let donation = {
email,
amount,
duration,
provider: 'stripe',
subscriptionId,
customerId,
startDate: new Date().toISOString()
};
await createAsyncUserDonation(user, donation);
return res.status(200).json({ isDonating: true });
}