From a7abe841bbd9881411d00b8d391d2700e940f1b0 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 30 Dec 2025 11:46:58 -0800 Subject: [PATCH] Consolidate RSVP and donation into single endpoint --- main.go | 75 +++++++++++++++++++++++----------------------- static/afac26.html | 34 +++++++++++---------- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/main.go b/main.go index 0dfe89d..d0acdcb 100644 --- a/main.go +++ b/main.go @@ -76,7 +76,6 @@ func main() { http.HandleFunc("POST /auth/google/callback", handleGoogleCallback) http.HandleFunc("GET /api/rsvp/{eventID}", handleRSVPGet) http.HandleFunc("POST /api/rsvp/{eventID}", handleRSVPPost) - http.HandleFunc("POST /api/donate/{eventID}", handleDonate) http.HandleFunc("GET /api/donate/success/{eventID}", handleDonateSuccess) http.HandleFunc("POST /api/stripe/webhook", handleStripeWebhook) @@ -218,46 +217,52 @@ func handleRSVPPost(w http.ResponseWriter, r *http.Request) { } var req struct { - NumPeople int `json:"numPeople"` + NumPeople *int `json:"numPeople"` + DonationCents int64 `json:"donationCents"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid json", http.StatusBadRequest) return } - _, err := db.Exec(` - INSERT INTO rsvps (event_id, google_username, num_people) VALUES ($1, $2, $3) - ON CONFLICT (event_id, google_username) DO UPDATE SET num_people = $3 - `, eventID, email, req.NumPeople) - if err != nil { - log.Println("[ERROR] failed to upsert rsvp:", err) + + if req.NumPeople != nil { + _, err := db.Exec(` + INSERT INTO rsvps (event_id, google_username, num_people) VALUES ($1, $2, $3) + ON CONFLICT (event_id, google_username) DO UPDATE SET num_people = $3 + `, eventID, email, *req.NumPeople) + if err != nil { + log.Println("[ERROR] failed to upsert rsvp:", err) + http.Error(w, "database error", http.StatusInternalServerError) + return + } + } + + var numPeople int + var donation float64 + err := db.QueryRow("SELECT num_people, donation FROM rsvps WHERE event_id = $1 AND google_username = $2", eventID, email).Scan(&numPeople, &donation) + if err != nil && err != sql.ErrNoRows { + log.Println("[ERROR] failed to query rsvp:", err) http.Error(w, "database error", http.StatusInternalServerError) return } + + resp := map[string]any{"numPeople": numPeople, "donation": donation} + + if req.DonationCents > 0 { + stripeURL, err := createCheckoutSession(eventID, email, req.DonationCents) + if err != nil { + log.Println("[ERROR] failed to create checkout session:", err) + http.Error(w, "failed to create checkout session", http.StatusInternalServerError) + return + } + resp["url"] = stripeURL + } + w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]int{"numPeople": req.NumPeople}) + json.NewEncoder(w).Encode(resp) } -func handleDonate(w http.ResponseWriter, r *http.Request) { - eventID := r.PathValue("eventID") - email, ok := authorize(r) - if !ok { - http.Error(w, "unauthorized", http.StatusUnauthorized) - return - } - - var req struct { - Amount int64 `json:"amount"` - } - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "invalid json", http.StatusBadRequest) - return - } - - if req.Amount < 100 { - http.Error(w, "minimum donation is $1", http.StatusBadRequest) - return - } - +func createCheckoutSession(eventID, email string, amountCents int64) (string, error) { baseURL := os.Getenv("BASE_URL") params := &stripe.CheckoutSessionParams{ CustomerEmail: stripe.String(email), @@ -269,7 +274,7 @@ func handleDonate(w http.ResponseWriter, r *http.Request) { ProductData: &stripe.CheckoutSessionLineItemPriceDataProductDataParams{ Name: stripe.String("Donation - Applause for a Cause"), }, - UnitAmount: stripe.Int64(req.Amount), + UnitAmount: stripe.Int64(amountCents), }, Quantity: stripe.Int64(1), }, @@ -284,13 +289,9 @@ func handleDonate(w http.ResponseWriter, r *http.Request) { s, err := session.New(params) if err != nil { - log.Println("[ERROR] failed to create checkout session:", err) - http.Error(w, "failed to create checkout session", http.StatusInternalServerError) - return + return "", err } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{"url": s.URL}) + return s.URL, nil } func processPayment(sess *stripe.CheckoutSession) error { diff --git a/static/afac26.html b/static/afac26.html index f8c55fb..b873987 100644 --- a/static/afac26.html +++ b/static/afac26.html @@ -269,11 +269,15 @@ }, true); setupButtonGroup('additional-donation-group', async val => { + let donationCents = 0; if (val === 'custom') { - const amount = parseInt(document.getElementById('additional-amount').value) || 0; - if (amount > 0) await startDonation(amount); + donationCents = (parseInt(document.getElementById('additional-amount').value) || 0) * 100; } else { - await startDonation(parseInt(val)); + donationCents = parseInt(val) * 100; + } + if (donationCents > 0) { + const data = await api('POST', `/api/rsvp/${eventId}`, { donationCents }); + if (data.url) location.href = data.url; } }, true); @@ -306,21 +310,19 @@ } } - async function startDonation(amount) { - const data = await api('POST', `/api/donate/${eventId}`, { amount: amount * 100 }); - location.href = data.url; - } - document.getElementById('rsvp-btn').addEventListener('click', async () => { - const data = await api('POST', `/api/rsvp/${eventId}`, { numPeople: selectedNumPeople }); - updateUI(data); - - let donationAmount = selectedDonation; - if (donationAmount === 'custom') { - donationAmount = parseInt(document.getElementById('custom-amount').value) || 0; + let donationCents = 0; + if (selectedDonation === 'custom') { + donationCents = (parseInt(document.getElementById('custom-amount').value) || 0) * 100; + } else if (selectedDonation > 0) { + donationCents = selectedDonation * 100; } - if (donationAmount > 0) { - await startDonation(donationAmount); + + const data = await api('POST', `/api/rsvp/${eventId}`, { numPeople: selectedNumPeople, donationCents }); + if (data.url) { + location.href = data.url; + } else { + updateUI(data); } });