@@ -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
167126router . post ( '/process-transaction' , async ( req , res ) => {
168127 try {
0 commit comments