Files
terminalShop/api/handlers/products.go
marcusDenslow 18c7a93e0b feat: implement server-side cart persistance with Stripe PaymentIntents
feat: implement server-side cart persistence with Stripe PaymentIntents

Replaced the client-only in-mem cart with a database-backed cart system

- Add Cart and CartItem GORM models (two-table design)
- Add 6 cart API endpoints (get, set item, set address, set card, clear,
conver)
- Switch from Stripe Charge API to PaymentIntents with confirm:true
- Add 9 API client methods for cart and card operations
- Refactor TUI cart from map[int] (slice index) to map[uint] (CoffeeID)
- Replace single-shot checkout with saveCardAndConvert multi-step flow
- Add CheckingOut state with "submitting order..." processing indicator
- Fix Stripe tokenization to use secret key for server-side SSH apps
- Remove admin product CRUD endpoints and old checkout handler
2026-02-28 15:01:06 +01:00

61 lines
1.5 KiB
Go

package handlers
import (
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"terminalShop/pkg/database"
"terminalShop/pkg/models"
"terminalShop/pkg/utils"
)
// ProductHandler handles product-related requests
type ProductHandler struct{}
// NewProductHandler creates a new product handler
func NewProductHandler() *ProductHandler {
return &ProductHandler{}
}
// GetProducts returns all products from the database
func (h *ProductHandler) GetProducts(w http.ResponseWriter, r *http.Request) {
db := database.GetDB()
var products []models.Coffee
// Query all products from database
if err := db.Find(&products).Error; err != nil {
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to fetch products", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"products": products,
})
}
// GetProduct returns a single product by ID
func (h *ProductHandler) GetProduct(w http.ResponseWriter, r *http.Request) {
db := database.GetDB()
// Get ID from URL parameter
idStr := chi.URLParam(r, "id")
id, err := strconv.ParseUint(idStr, 10, 32)
if err != nil {
utils.RespondError(w, http.StatusBadRequest, "INVALID_ID", "invalid product ID", nil)
return
}
var product models.Coffee
if err := db.First(&product, id).Error; err != nil {
utils.RespondError(w, http.StatusNotFound, "NOT_FOUND", "product not found", nil)
return
}
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
"product": product,
})
}