diff --git a/api/handlers/addresses.go b/api/handlers/addresses.go index 31e0ae6..3c6454c 100644 --- a/api/handlers/addresses.go +++ b/api/handlers/addresses.go @@ -16,7 +16,7 @@ import ( "terminalShop/pkg/utils" ) -var addressLog = slog.With("component", "address") +func addressLog() *slog.Logger { return slog.With("component", "address") } type AddressHandler struct { shippoKey string @@ -93,7 +93,7 @@ func (h *AddressHandler) CreateAddress(w http.ResponseWriter, r *http.Request) { Phone: req.Phone, }) if err != nil { - addressLog.Warn("shippo address validation failed", "error", err, "country", "US") + addressLog().Warn("shippo address validation failed", "error", err, "country", "US") utils.RespondError(w, http.StatusBadRequest, "INVALID_ADDRESS", "address is invalid", nil) return } @@ -124,7 +124,7 @@ func (h *AddressHandler) CreateAddress(w http.ResponseWriter, r *http.Request) { Phone: req.Phone, }) if err != nil { - addressLog.Warn("bring address validation failed", "error", err, "country", "NO") + addressLog().Warn("bring address validation failed", "error", err, "country", "NO") utils.RespondError(w, http.StatusBadRequest, "INVALID_ADDRESS", "address is invalid", nil) return } diff --git a/api/handlers/auth.go b/api/handlers/auth.go index a5c4f77..84b9d16 100644 --- a/api/handlers/auth.go +++ b/api/handlers/auth.go @@ -14,7 +14,7 @@ import ( "gorm.io/gorm" ) -var authLog = slog.With("component", "auth") +func authLog() *slog.Logger { return slog.With("component", "auth") } // AuthHandler handles authentication-related requests type AuthHandler struct { @@ -58,7 +58,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { // validate shared secret if req.ClientSecret != h.authFingerprintKey { middleware.RecordAuthAttempt("invalid_secret") - authLog.Warn("invalid client secret", "remote", r.RemoteAddr) + authLog().Warn("invalid client secret", "remote", r.RemoteAddr) utils.RespondError(w, http.StatusUnauthorized, "INVALID_SECRET", "invalid client secret", nil) return } @@ -76,7 +76,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { if err == gorm.ErrRecordNotFound { if req.SSHPublicKey == "" { middleware.RecordAuthAttempt("user_not_found") - authLog.Warn("user not found and no public key for registration", "fingerprint", req.Fingerprint) + authLog().Warn("user not found and no public key for registration", "fingerprint", req.Fingerprint) utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "no user found for this fingerprint", nil) return } @@ -86,15 +86,15 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { } if err := db.Create(&user).Error; err != nil { middleware.RecordAuthAttempt("error") - authLog.Error("failed to create user", "error", err) + authLog().Error("failed to create user", "error", err) utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to create user", nil) return } middleware.RecordAuthAttempt("registered") - authLog.Info("user registered", "user_id", user.ID) + authLog().Info("user registered", "user_id", user.ID) } else if err != nil { middleware.RecordAuthAttempt("error") - authLog.Error("failed to look up user", "error", err) + authLog().Error("failed to look up user", "error", err) utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to look up user", nil) return } @@ -102,7 +102,7 @@ func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) { token, err := h.jwtManager.GenerateToken(user.ID, user.Email, user.Name) if err != nil { middleware.RecordAuthAttempt("error") - authLog.Error("failed to generate token", "user_id", user.ID, "error", err) + authLog().Error("failed to generate token", "user_id", user.ID, "error", err) utils.RespondError(w, http.StatusInternalServerError, "TOKEN_ERROR", "failed to generate token", nil) return } diff --git a/api/handlers/reconcile.go b/api/handlers/reconcile.go index 0b49a16..7708842 100644 --- a/api/handlers/reconcile.go +++ b/api/handlers/reconcile.go @@ -13,7 +13,7 @@ import ( "gorm.io/gorm" ) -var reconcileLog = slog.With("component", "reconcile") +func reconcileLog() *slog.Logger { return slog.With("component", "reconcile") } // ReconcileOrders finds pending orders with no Stripe ID older than 10 minutes // and checks whether Stripe has a succeeded PaymentIntent for them. This fixes @@ -32,7 +32,7 @@ func ReconcileOrders(stripeKey string) { return } - reconcileLog.Info("checking pending orders", "count", len(orders)) + reconcileLog().Info("checking pending orders", "count", len(orders)) for _, order := range orders { params := &stripe.PaymentIntentSearchParams{ @@ -44,7 +44,7 @@ func ReconcileOrders(stripeKey string) { iter := paymentintent.Search(params) for iter.Next() { pi := iter.PaymentIntent() - reconcileLog.Warn("found succeeded payment intent for unrecorded order — patching", + reconcileLog().Warn("found succeeded payment intent for unrecorded order — patching", "order_id", order.ID, "pi", pi.ID) txErr := db.Transaction(func(tx *gorm.DB) error { @@ -67,11 +67,11 @@ func ReconcileOrders(stripeKey string) { }).Error }) if txErr != nil { - reconcileLog.Error("failed to patch order", "order_id", order.ID, "error", txErr) + reconcileLog().Error("failed to patch order", "order_id", order.ID, "error", txErr) } } if err := iter.Err(); err != nil { - reconcileLog.Error("stripe search error", "order_id", order.ID, "error", err) + reconcileLog().Error("stripe search error", "order_id", order.ID, "error", err) } } } diff --git a/api/handlers/webhook.go b/api/handlers/webhook.go index e206d3e..f7323a2 100644 --- a/api/handlers/webhook.go +++ b/api/handlers/webhook.go @@ -19,7 +19,7 @@ import ( "terminalShop/pkg/utils" ) -var webhookLog = slog.With("component", "webhook") +func webhookLog() *slog.Logger { return slog.With("component", "webhook") } // WebhookHandler handles inbound Stripe webhook events. type WebhookHandler struct { @@ -48,13 +48,13 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) { // Verify the Stripe signature. Reject anything that doesn't match. if h.webhookSecret == "" { // No secret configured — log a loud warning but still process in dev. - webhookLog.Warn("stripe webhook secret not set; skipping signature verification") + webhookLog().Warn("stripe webhook secret not set; skipping signature verification") } else { sig := r.Header.Get("Stripe-Signature") if _, err := webhook.ConstructEventWithOptions(body, sig, h.webhookSecret, webhook.ConstructEventOptions{ IgnoreAPIVersionMismatch: true, }); err != nil { - webhookLog.Warn("signature verification failed", "error", err) + webhookLog().Warn("signature verification failed", "error", err) utils.RespondError(w, http.StatusUnauthorized, "INVALID_SIGNATURE", "webhook signature verification failed", nil) return } @@ -87,7 +87,7 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) { "payment_method.detached", "payment_method.updated": // Handled by the reconcile job and direct API calls; log only. - webhookLog.Info("payment method event received", "event_type", string(event.Type)) + webhookLog().Info("payment method event received", "event_type", string(event.Type)) } // Always return 200 so Stripe doesn't retry. @@ -97,7 +97,7 @@ func (h *WebhookHandler) HandleStripe(w http.ResponseWriter, r *http.Request) { func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) { var pi stripe.PaymentIntent if err := json.Unmarshal(event.Data.Raw, &pi); err != nil { - webhookLog.Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.succeeded") + webhookLog().Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.succeeded") return } @@ -110,7 +110,7 @@ func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) { db := database.GetDB() var order models.Order if err := db.Where("id = ?", orderIDStr).First(&order).Error; err != nil { - webhookLog.Warn("order not found for payment intent", "order_id", orderIDStr, "pi", pi.ID) + webhookLog().Warn("order not found for payment intent", "order_id", orderIDStr, "pi", pi.ID) return } @@ -140,19 +140,19 @@ func (h *WebhookHandler) handlePaymentIntentSucceeded(event stripe.Event) { }).Error }) if txErr != nil { - webhookLog.Error("failed to record paid status", + webhookLog().Error("failed to record paid status", "order_id", orderIDStr, "pi", pi.ID, "error", txErr) return } audit.OrderPaid(order.UserID, order.ID, int(pi.Amount), pi.ID) - webhookLog.Info("order marked paid", "order_id", orderIDStr, "pi", pi.ID) + webhookLog().Info("order marked paid", "order_id", orderIDStr, "pi", pi.ID) } func (h *WebhookHandler) handlePaymentIntentFailed(event stripe.Event) { var pi stripe.PaymentIntent if err := json.Unmarshal(event.Data.Raw, &pi); err != nil { - webhookLog.Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.payment_failed") + webhookLog().Error("failed to unmarshal payment_intent", "error", err, "event", "payment_intent.payment_failed") return } @@ -164,20 +164,20 @@ func (h *WebhookHandler) handlePaymentIntentFailed(event stripe.Event) { db := database.GetDB() if err := db.Model(&models.Order{}).Where("id = ? AND status = ?", orderIDStr, models.OrderStatusPending). Update("status", models.OrderStatusFailed).Error; err != nil { - webhookLog.Error("failed to mark order failed", "order_id", orderIDStr, "error", err) + webhookLog().Error("failed to mark order failed", "order_id", orderIDStr, "error", err) } errMsg := "unknown" if pi.LastPaymentError != nil { errMsg = pi.LastPaymentError.Msg } - webhookLog.Warn("payment failed", "order_id", orderIDStr, "pi", pi.ID, "reason", errMsg) + webhookLog().Warn("payment failed", "order_id", orderIDStr, "pi", pi.ID, "reason", errMsg) } func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) { var sess stripe.CheckoutSession if err := json.Unmarshal(event.Data.Raw, &sess); err != nil { - webhookLog.Error("failed to unmarshal checkout session", "error", err) + webhookLog().Error("failed to unmarshal checkout session", "error", err) return } @@ -186,7 +186,7 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) { } if sess.SetupIntent == nil || sess.Customer == nil { - webhookLog.Warn("checkout session missing setup_intent or customer") + webhookLog().Warn("checkout session missing setup_intent or customer") return } @@ -194,25 +194,25 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) { si, err := setupintent.Get(sess.SetupIntent.ID, nil) if err != nil { - webhookLog.Error("failed to get setup intent", "setup_intent", sess.SetupIntent.ID, "error", err) + webhookLog().Error("failed to get setup intent", "setup_intent", sess.SetupIntent.ID, "error", err) return } if si.PaymentMethod == nil { - webhookLog.Warn("setup intent has no payment method", "setup_intent", si.ID) + webhookLog().Warn("setup intent has no payment method", "setup_intent", si.ID) return } pm, err := paymentmethod.Get(si.PaymentMethod.ID, nil) if err != nil { - webhookLog.Error("failed to get payment method", "payment_method", si.PaymentMethod.ID, "error", err) + webhookLog().Error("failed to get payment method", "payment_method", si.PaymentMethod.ID, "error", err) return } db := database.GetDB() var user models.User if err := db.Where("stripe_customer_id = ?", sess.Customer.ID).First(&user).Error; err != nil { - webhookLog.Warn("user not found for customer", "customer", sess.Customer.ID, "error", err) + webhookLog().Warn("user not found for customer", "customer", sess.Customer.ID, "error", err) return } @@ -232,10 +232,10 @@ func (h *WebhookHandler) handleCheckoutSessionCompleted(event stripe.Event) { } if err := db.Create(&card).Error; err != nil { - webhookLog.Error("failed to save card", "user_id", user.ID, "error", err) + webhookLog().Error("failed to save card", "user_id", user.ID, "error", err) return } audit.CardAdded(user.ID, card.ID, card.Brand, card.Last4) - webhookLog.Info("card saved via checkout.session.completed", "user_id", user.ID) + webhookLog().Info("card saved via checkout.session.completed", "user_id", user.ID) }