Skip to content

Commit 8bd3d09

Browse files
0xRibeiroexh-AusterVitorH19
committed
Refactor /create-checkout-session route
Co-authored-by: Felipe Ribeiro <[email protected]> Co-authored-by: Vitor Hugo <[email protected]>
1 parent f96b98f commit 8bd3d09

File tree

1 file changed

+82
-123
lines changed

1 file changed

+82
-123
lines changed

server/routes/api/checkout.js

Lines changed: 82 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -21,148 +21,107 @@ class CheckoutError extends Error {
2121
}
2222
}
2323

24-
router.post('/create-checkout-session', async (req, res) => {
25-
let { donation, user, letter, deliveryMethods } = req.body
26-
console.dir(user)
27-
console.dir(letter)
28-
const origin = req.get('origin')
24+
// Helper function to find or create a constituent
25+
async function findOrCreateConstituent(user) {
26+
let constituent = await Constituent.query().where('email', user.email).first();
27+
if (!constituent) {
28+
constituent = await Constituent.query().insert(user);
29+
}
30+
return constituent;
31+
}
2932

30-
try {
31-
const presenter = new PaymentPresenter()
33+
// Helper function to create a transaction
34+
async function createTransaction(stripeTransactionId, constituentId, donation, status = 'pending') {
35+
return await Transaction.query().insert({
36+
stripeTransactionId,
37+
constituentId,
38+
amount: donation,
39+
currency: 'usd',
40+
paymentMethod: 'credit_card',
41+
status,
42+
});
43+
}
3244

33-
// Will throw error if invalid amount is given.
34-
presenter.validatePaymentAmount(donation)
45+
// Helper function to render letter HTML
46+
async function renderLetterHtml(letter, user) {
47+
letter.merge_variables = {
48+
...letter.merge_variables,
49+
firstName: user.firstName,
50+
lastName: user.lastName,
51+
};
52+
const template = await LetterTemplate.query().findById(letter.letter_template_id);
53+
return Handlebars.render(letter.merge_variables, template.html);
54+
}
3555

36-
if (donation === 0 && process.env.VUE_APP_EMPTY_TRANSACTIONS === 'on') {
37-
const CHECKOUT_SESSION_ID = uuidv4()
38-
const redirectUrl = `${origin}/complete?session_id=${CHECKOUT_SESSION_ID}`
56+
// Helper function to save letters for each delivery method
57+
async function saveLetters(transactionId, constituentId, letter, html, deliveryMethods) {
58+
for (const method of deliveryMethods) {
59+
letter.trackingNumber = uuidv4(); // Generate unique tracking number
60+
console.log(letter.trackingNumber);
61+
62+
await Letter.query().insert({
63+
transactionId,
64+
constituentId,
65+
letterTemplate: html,
66+
deliveryMethod: method,
67+
...letter,
68+
});
69+
}
70+
}
3971

40-
let constituent
41-
;[constituent] = await Constituent.query().where('email', user.email)
42-
if (!constituent) {
43-
constituent = await Constituent.query().insert(user)
44-
}
72+
// Refactored /create-checkout-session route
73+
router.post('/create-checkout-session', async (req, res) => {
74+
const { donation, user, letter, deliveryMethods } = req.body;
75+
const origin = req.get('origin');
4576

46-
console.log(constituent.id)
47-
48-
const transaction = await Transaction.query().insert({
49-
stripeTransactionId: 'no-stripe-' + uuidv4(),
50-
constituentId: constituent.id,
51-
amount: donation,
52-
currency: 'usd',
53-
paymentMethod: 'credit_card',
54-
status: 'succeeded'
55-
})
56-
57-
// Using a temporary mapping here also
58-
// Re-render the letter html, merging user data to be saved in case that's in the template.
59-
letter.merge_variables = {
60-
...letter.merge_variables,
61-
firstName: user.firstName,
62-
lastName: user.lastName
63-
}
64-
const template = await LetterTemplate.query().findById(
65-
letter.letter_template_id
66-
)
67-
const html = Handlebars.render(letter.merge_variables, template.html)
68-
69-
// Using a temporary mapping here also
70-
for (const method of deliveryMethods) {
71-
// Generate a uuid so letters are idempotent
72-
letter.trackingNumber = uuidv4()
73-
console.log(letter.trackingNumber)
74-
75-
await Letter.query().insert({
76-
transactionId: transaction.id,
77-
constituentId: constituent.id,
78-
letterTemplate: html,
79-
deliveryMethod: method,
80-
...letter
81-
})
82-
}
77+
try {
78+
const presenter = new PaymentPresenter();
79+
presenter.validatePaymentAmount(donation); // Validate donation amount
8380

84-
return res
85-
.status(200)
86-
.json({ url: redirectUrl, sessionId: CHECKOUT_SESSION_ID })
87-
.end()
88-
}
81+
// Handle free transactions
82+
if (donation === 0 && process.env.VUE_APP_EMPTY_TRANSACTIONS === 'on') {
83+
const CHECKOUT_SESSION_ID = uuidv4();
84+
const redirectUrl = `${origin}/complete?session_id=${CHECKOUT_SESSION_ID}`;
8985

90-
// TODO: Should be strict https but we need to do some deployment fixes first.
91-
const redirectUrl = `${origin}/complete?session_id={CHECKOUT_SESSION_ID}`
92-
const cancelUrl = origin
86+
const constituent = await findOrCreateConstituent(user);
87+
console.log(constituent.id);
9388

94-
const stripe = new Stripe()
95-
const session = await stripe.createCheckoutSession(
96-
donation,
97-
redirectUrl,
98-
cancelUrl
99-
)
89+
const transaction = await createTransaction(
90+
`no-stripe-${uuidv4()}`,
91+
constituent.id,
92+
donation,
93+
'succeeded'
94+
);
10095

101-
// These objects must be recorded in a specific order:
102-
// constituent, then transaction, then letter
103-
// This is because letter needs id from constituent and transaction!
96+
const html = await renderLetterHtml(letter, user);
97+
await saveLetters(transaction.id, constituent.id, letter, html, deliveryMethods);
10498

105-
// TODO: Move Constituent insert to earlier in the cycle.
106-
let constituent
107-
;[constituent] = await Constituent.query().where('email', user.email)
108-
if (!constituent) {
109-
constituent = await Constituent.query().insert(user)
99+
return res.status(200).json({ url: redirectUrl, sessionId: CHECKOUT_SESSION_ID }).end();
110100
}
111101

112-
console.log(constituent.id)
102+
// Handle paid transactions
103+
const redirectUrl = `${origin}/complete?session_id={CHECKOUT_SESSION_ID}`;
104+
const cancelUrl = origin;
113105

114-
const transaction = await Transaction.query().insert({
115-
stripeTransactionId: session.paymentIntent,
116-
constituentId: constituent.id,
117-
amount: donation,
118-
currency: 'usd',
119-
paymentMethod: 'credit_card'
120-
})
106+
const stripe = new Stripe();
107+
const session = await stripe.createCheckoutSession(donation, redirectUrl, cancelUrl);
121108

122-
// Re-render the letter html, merging user data to be saved in case that's in the template.
123-
letter.merge_variables = {
124-
...letter.merge_variables,
125-
firstName: user.firstName,
126-
lastName: user.lastName
127-
}
128-
const template = await LetterTemplate.query().findById(
129-
letter.letter_template_id
130-
)
131-
const html = Handlebars.render(letter.merge_variables, template.html)
132-
133-
// Using a temporary mapping here also
134-
for (const method of deliveryMethods) {
135-
// Generate a uuid so letters are idempotent
136-
letter.trackingNumber = uuidv4()
137-
console.log(letter.trackingNumber)
138-
139-
await Letter.query().insert({
140-
transactionId: transaction.id,
141-
constituentId: constituent.id,
142-
letterTemplate: html,
143-
deliveryMethod: method,
144-
...letter
145-
})
146-
}
109+
const constituent = await findOrCreateConstituent(user);
110+
console.log(constituent.id);
147111

148-
return res
149-
.status(200)
150-
.json({ url: session.url, sessionId: session.id })
151-
.end()
152-
} catch (error) {
153-
console.error(error)
154-
let statusCode = 500
112+
const transaction = await createTransaction(session.paymentIntent, constituent.id, donation);
155113

156-
if (error instanceof PaymentPresenterError) {
157-
statusCode = 400
158-
}
114+
const html = await renderLetterHtml(letter, user);
115+
await saveLetters(transaction.id, constituent.id, letter, html, deliveryMethods);
159116

160-
console.error(error)
117+
return res.status(200).json({ url: session.url, sessionId: session.id }).end();
118+
} catch (error) {
119+
console.error(error);
161120

162-
// TODO: error logging
163-
return res.status(statusCode).json({ error: error.message }).end()
121+
const statusCode = error instanceof PaymentPresenterError ? 400 : 500;
122+
return res.status(statusCode).json({ error: error.message }).end();
164123
}
165-
})
124+
});
166125

167126
router.post('/process-transaction', async (req, res) => {
168127
try {

0 commit comments

Comments
 (0)