fix: update stripe wallets to use payment intent (#54668)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2024-06-15 09:15:10 +03:00
committed by GitHub
parent c132ef80f4
commit 7e23b0d69c
11 changed files with 234 additions and 165 deletions

View File

@@ -1,11 +1,15 @@
import debug from 'debug';
import Stripe from 'stripe';
import { donationSubscriptionConfig } from '../../../../shared/config/donation-settings';
import {
donationSubscriptionConfig,
allStripeProductIdsArray
} from '../../../../shared/config/donation-settings';
import keys from '../../../config/secrets';
import {
createStripeCardDonation,
handleStripeCardUpdateSession
handleStripeCardUpdateSession,
inLastFiveMinutes
} from '../utils/donation';
import { validStripeForm } from '../utils/stripeHelpers';
@@ -42,117 +46,82 @@ export default function donateBoot(app, done) {
});
}
function createStripeDonation(req, res) {
const { user, body } = req;
async function createStripeDonation(req, res) {
const { body } = req;
const { amount, duration, email, subscriptionId } = body;
try {
const subscription = await stripe.subscriptions.retrieve(subscriptionId);
const isSubscriptionActive = subscription.status === 'active';
const productId = subscription.items.data[0].plan.product;
const isStartedRecently = inLastFiveMinutes(
subscription.current_period_start
);
const isProductIdValid = allStripeProductIdsArray.includes(productId);
const {
amount,
duration,
token: { id },
email,
name
} = body;
if (isSubscriptionActive && isProductIdValid && isStartedRecently) {
const [donatingUser] = await User.findOrCreate(
{ where: { email } },
{ email }
);
const donation = {
email,
amount,
duration,
provider: 'stripe',
subscriptionId,
customerId: subscription.customer,
startDate: new Date().toISOString()
};
await donatingUser.createDonation(donation);
return res.status(200).send({ isDonating: true });
} else {
throw new Error('Donation failed due to a server error.');
}
} catch (err) {
return res
.status(500)
.send({ error: 'Donation failed due to a server error.' });
}
}
async function createStripePaymentIntent(req, res) {
const { body } = req;
const { amount, duration, email, name } = body;
if (!validStripeForm(amount, duration, email)) {
return res.status(500).send({
return res.status(400).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) => {
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 = async user => {
let customer;
donatingUser = user;
try {
customer = await stripe.customers.create({
email,
card: id,
name
});
} catch (err) {
throw new Error('Error creating stripe customer');
}
log(`Stripe customer with id ${customer.id} created`);
return customer;
};
const createSubscription = async customer => {
donation.customerId = customer.id;
let sub;
try {
sub = await stripe.subscriptions.create({
customer: customer.id,
items: [
{
plan: `${donationSubscriptionConfig.duration[
duration
].toLowerCase()}-donation-${amount}`
}
]
});
} catch (err) {
throw new Error('Error creating stripe subscription');
}
return sub;
};
const createAsyncUserDonation = () => {
donatingUser
.createDonation(donation)
.toPromise()
.catch(err => {
throw new Error(err);
});
};
return Promise.resolve(fccUser)
.then(nonDonatingUser => {
// the logic is removed since users can donate without an account
return nonDonatingUser;
})
.then(createCustomer)
.then(customer => {
return createSubscription(customer).then(subscription => {
log(`Stripe subscription with id ${subscription.id} created`);
donation.subscriptionId = subscription.id;
return res.send(subscription);
});
})
.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.' });
try {
const stripeCustomer = await stripe.customers.create({
email,
name
});
const stripeSubscription = await stripe.subscriptions.create({
customer: stripeCustomer.id,
items: [
{
plan: `${donationSubscriptionConfig.duration[duration]}-donation-${amount}`
}
],
payment_behavior: 'default_incomplete',
payment_settings: { save_default_payment_method: 'on_subscription' },
expand: ['latest_invoice.payment_intent']
});
res.status(200).send({
subscriptionId: stripeSubscription.id,
clientSecret:
stripeSubscription.latest_invoice.payment_intent.client_secret
});
} catch (err) {
return res
.status(500)
.send({ error: 'Donation failed due to a server error.' });
}
}
function addDonation(req, res) {
@@ -208,6 +177,7 @@ export default function donateBoot(app, done) {
} else {
api.post('/charge-stripe', createStripeDonation);
api.post('/charge-stripe-card', handleStripeCardDonation);
api.post('/create-stripe-payment-intent', createStripePaymentIntent);
api.put('/update-stripe-card', handleStripeCardUpdate);
api.post('/add-donation', addDonation);
donateRouter.use('/donate', api);