feat: add update-stripe-card route (#52389)

Co-authored-by: Naomi Carrigan <nhcarrigan@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2023-12-12 22:49:52 +03:00
committed by GitHub
parent d3ac96c204
commit 575aa172ad
17 changed files with 465 additions and 12 deletions
+16 -1
View File
@@ -8,7 +8,8 @@ import {
verifyWebHook,
updateUser,
verifyWebHookType,
createStripeCardDonation
createStripeCardDonation,
handleStripeCardUpdateSession
} from '../utils/donation';
import { validStripeForm } from '../utils/stripeHelpers';
@@ -181,6 +182,19 @@ export default function donateBoot(app, done) {
});
}
async function handleStripeCardUpdate(req, res, next) {
try {
const sessionIdObj = await handleStripeCardUpdateSession(
req,
app,
stripe
);
return res.status(200).json(sessionIdObj);
} catch (err) {
return next(err);
}
}
function updatePaypal(req, res) {
const { headers, body } = req;
return Promise.resolve(req)
@@ -220,6 +234,7 @@ export default function donateBoot(app, done) {
} else {
api.post('/charge-stripe', createStripeDonation);
api.post('/charge-stripe-card', handleStripeCardDonation);
api.put('/update-stripe-card', handleStripeCardUpdate);
api.post('/add-donation', addDonation);
hooks.post('/update-paypal', updatePaypal);
donateRouter.use('/donate', api);
+37
View File
@@ -314,3 +314,40 @@ export async function createStripeCardDonation(req, res, stripe) {
await createAsyncUserDonation(user, donation);
return res.status(200).json({ isDonating: true });
}
export async function handleStripeCardUpdateSession(req, app, stripe) {
const {
user: { id }
} = req;
const { Donation } = app.models;
log('Updating stripe card for user: ', id);
// multiple donations support should be added
const donation = await Donation.findOne({
where: { userId: id, provider: 'stripe' }
});
if (!donation) throw Error('Stripe donation record not found');
const { customerId, subscriptionId } = donation;
log(subscriptionId);
// Create a Stripe checkout session
// updating customer payment method is handled by webhook handler
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
mode: 'setup',
customer: customerId,
setup_intent_data: {
metadata: {
customer_id: customerId,
subscription_id: subscriptionId
}
},
success_url: `${process.env.HOME_LOCATION}/update-stripe-card?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.HOME_LOCATION}/update-stripe-card`
});
return { sessionId: session.id };
}
+56 -1
View File
@@ -1,5 +1,7 @@
/* eslint-disable camelcase */
import axios from 'axios';
import stripe from 'stripe';
import { ObjectId } from 'mongodb';
import keys from '../../../config/secrets';
import {
mockApp,
@@ -14,10 +16,18 @@ import {
verifyWebHook,
updateUser,
capitalizeKeys,
createDonationObj
createDonationObj,
handleStripeCardUpdateSession
} from './donation';
jest.mock('axios');
jest.mock('stripe', () => ({
checkout: {
sessions: {
create: jest.fn()
}
}
}));
const verificationUrl = `https://api.sandbox.paypal.com/v1/notifications/verify-webhook-signature`;
const tokenUrl = `https://api.sandbox.paypal.com/v1/oauth2/token`;
@@ -155,4 +165,49 @@ describe('donation', () => {
expect(updateUserAttr).not.toHaveBeenCalled();
});
});
describe('handleStripeCardUpdateSession', () => {
const mockUserId = ObjectId('507f1f77bcf86cd799439011');
const mockDonation = {
customerId: 'customer_123',
subscriptionId: 'sub_123'
};
const req = { user: { id: mockUserId } };
const app = {
models: {
Donation: { findOne: jest.fn().mockResolvedValue(mockDonation) }
}
};
stripe.checkout.sessions.create.mockResolvedValue({ id: 'session_123' });
it('creates a session successfully', async () => {
const result = await handleStripeCardUpdateSession(req, app, stripe);
expect(app.models.Donation.findOne).toHaveBeenCalledWith({
where: { userId: mockUserId, provider: 'stripe' }
});
expect(stripe.checkout.sessions.create).toHaveBeenCalled();
expect(result).toEqual({ sessionId: 'session_123' });
});
it('throws an error when donation not found', async () => {
const app = {
models: { Donation: { findOne: jest.fn().mockResolvedValue(null) } }
};
await expect(
handleStripeCardUpdateSession(req, app, stripe)
).rejects.toThrow('Stripe donation record not found');
});
it('handles stripe session creation failure', async () => {
stripe.checkout.sessions.create.mockRejectedValue(
new Error('Stripe error')
);
await expect(
handleStripeCardUpdateSession(req, app, stripe)
).rejects.toThrow('Stripe error');
});
});
});