From ad24d503ba6b36797bc9a51527c7e581a48ac0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Wed, 3 Dec 2025 17:45:38 +0100 Subject: [PATCH] update --- cmd/checkout/klarna-handlers.go | 23 +++++ cmd/checkout/pool-server.go | 174 +++++++++++++++++++++++++++++++- deployment/deployment.yaml | 14 +++ 3 files changed, 209 insertions(+), 2 deletions(-) diff --git a/cmd/checkout/klarna-handlers.go b/cmd/checkout/klarna-handlers.go index a3eaea9..42697d5 100644 --- a/cmd/checkout/klarna-handlers.go +++ b/cmd/checkout/klarna-handlers.go @@ -52,6 +52,29 @@ func (s *CheckoutPoolServer) KlarnaHtmlCheckoutHandler(w http.ResponseWriter, r } +func (s *CheckoutPoolServer) KlarnaSessionHandler(w http.ResponseWriter, r *http.Request, checkoutId checkout.CheckoutId) error { + + orderId := r.URL.Query().Get("order_id") + var order *CheckoutOrder + var err error + if orderId == "" { + order, err = s.CreateOrUpdateCheckout(r, checkoutId) + if err != nil { + logger.Error("unable to create klarna session", "error", err) + return err + } + // s.ApplyKlarnaPaymentStarted(r.Context(), order, checkoutId) + + } + order, err = s.klarnaClient.GetOrder(r.Context(), orderId) + if err != nil { + return err + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + return json.NewEncoder(w).Encode(order) + +} + func (s *CheckoutPoolServer) KlarnaConfirmationHandler(w http.ResponseWriter, r *http.Request) { orderId := r.PathValue("order_id") diff --git a/cmd/checkout/pool-server.go b/cmd/checkout/pool-server.go index 0bc608b..2edf443 100644 --- a/cmd/checkout/pool-server.go +++ b/cmd/checkout/pool-server.go @@ -4,12 +4,14 @@ import ( "bytes" "context" "encoding/json" + "fmt" "log" "net/http" "os" "time" "git.k6n.net/go-cart-actor/pkg/actor" + "git.k6n.net/go-cart-actor/pkg/cart" "git.k6n.net/go-cart-actor/pkg/checkout" messages "git.k6n.net/go-cart-actor/proto/checkout" @@ -22,6 +24,7 @@ import ( "go.opentelemetry.io/contrib/bridges/otelslog" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/timestamppb" "go.opentelemetry.io/otel" @@ -75,6 +78,162 @@ func (s *CheckoutPoolServer) GetCheckoutHandler(w http.ResponseWriter, r *http.R return s.WriteResult(w, grain) } +func (s *CheckoutPoolServer) SetDeliveryHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.SetDelivery + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) RemoveDeliveryHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.RemoveDelivery + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) SetPickupPointHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.SetPickupPoint + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) InitializeCheckoutHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.InitializeCheckout + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) InventoryReservedHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.InventoryReserved + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) OrderCreatedHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.OrderCreated + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) ConfirmationViewedHandler(w http.ResponseWriter, r *http.Request, id checkout.CheckoutId) error { + var msg messages.ConfirmationViewed + if err := json.NewDecoder(r.Body).Decode(&msg); err != nil { + return err + } + + result, err := s.ApplyLocal(r.Context(), id, &msg) + if err != nil { + return err + } + + return s.WriteResult(w, result.Result) +} + +func (s *CheckoutPoolServer) StartCheckoutHandler(w http.ResponseWriter, r *http.Request) { + cartIdStr := r.PathValue("cartid") + if cartIdStr == "" { + http.Error(w, "cart id required", http.StatusBadRequest) + return + } + + cartId, ok := cart.ParseCartId(cartIdStr) + if !ok { + http.Error(w, "invalid cart id", http.StatusBadRequest) + return + } + + // Fetch cart state from cart service + cartGrain, err := s.cartClient.getCartGrain(r.Context(), cartId) + if err != nil { + logger.Error("failed to fetch cart", "error", err, "cartId", cartId) + http.Error(w, "failed to fetch cart", http.StatusInternalServerError) + return + } + + // Serialize cart state to Any + cartStateBytes, err := json.Marshal(cartGrain) + if err != nil { + logger.Error("failed to marshal cart state", "error", err) + http.Error(w, "failed to process cart state", http.StatusInternalServerError) + return + } + + // Create checkout with same ID as cart + checkoutId := checkout.CheckoutId(cartId) + + // Initialize checkout with cart state wrapped in Any + cartStateAny := &messages.InitializeCheckout{ + OrderId: "", + CartId: uint64(cartId), + Version: uint32(cartGrain.Version), + CartState: &anypb.Any{ + TypeUrl: "type.googleapis.com/cart.CartGrain", + Value: cartStateBytes, + }, + } + + result, err := s.ApplyLocal(r.Context(), checkoutId, cartStateAny) + if err != nil { + logger.Error("failed to initialize checkout", "error", err) + http.Error(w, "failed to initialize checkout", http.StatusInternalServerError) + return + } + + // Set checkout cookie + w.Header().Set("Set-Cookie", fmt.Sprintf("checkout_id=%s; Path=/; HttpOnly; SameSite=Lax", checkoutId.String())) + + if err := s.WriteResult(w, result.Result); err != nil { + logger.Error("failed to write result", "error", err) + } +} + func (s *CheckoutPoolServer) WriteResult(w http.ResponseWriter, result any) error { w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-cache") @@ -223,8 +382,19 @@ func (s *CheckoutPoolServer) Serve(mux *http.ServeMux) { orderHandler := NewAmqpOrderHandler(conn) orderHandler.DefineQueue() - handleFunc("GET /checkout", CheckoutIdHandler(s.ProxyHandler(s.KlarnaHtmlCheckoutHandler))) + handleFunc("POST /api/checkout/start/{cartid}", s.StartCheckoutHandler) + handleFunc("GET /api/checkout", CheckoutIdHandler(s.ProxyHandler(s.GetCheckoutHandler))) + handleFunc("POST /api/checkout/delivery", CheckoutIdHandler(s.ProxyHandler(s.SetDeliveryHandler))) + handleFunc("DELETE /api/checkout/delivery", CheckoutIdHandler(s.ProxyHandler(s.RemoveDeliveryHandler))) + handleFunc("POST /api/checkout/pickup-point", CheckoutIdHandler(s.ProxyHandler(s.SetPickupPointHandler))) + handleFunc("POST /api/checkout/initialize", CheckoutIdHandler(s.ProxyHandler(s.InitializeCheckoutHandler))) + handleFunc("POST /api/checkout/inventory-reserved", CheckoutIdHandler(s.ProxyHandler(s.InventoryReservedHandler))) + handleFunc("POST /api/checkout/order-created", CheckoutIdHandler(s.ProxyHandler(s.OrderCreatedHandler))) + handleFunc("POST /api/checkout/confirmation-viewed", CheckoutIdHandler(s.ProxyHandler(s.ConfirmationViewedHandler))) - handleFunc("GET /confirmation/{order_id}", s.KlarnaConfirmationHandler) + handleFunc("GET /payment/klarna/session", CheckoutIdHandler(s.ProxyHandler(s.KlarnaSessionHandler))) + handleFunc("GET /payment/klarna/checkout", CheckoutIdHandler(s.ProxyHandler(s.KlarnaHtmlCheckoutHandler))) + + handleFunc("GET /payment/klarna/confirmation/{order_id}", s.KlarnaConfirmationHandler) } diff --git a/deployment/deployment.yaml b/deployment/deployment.yaml index 22d53e8..a770ff2 100644 --- a/deployment/deployment.yaml +++ b/deployment/deployment.yaml @@ -531,6 +531,20 @@ spec: name: cart-actor port: number: 8080 + - path: /api/checkout + pathType: Prefix + backend: + service: + name: checkout-actor + port: + number: 8080 + - path: /payment + pathType: Prefix + backend: + service: + name: checkout-actor + port: + number: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress