diff --git a/deployment/deployment.yaml b/deployment/deployment.yaml index 40bd0e2..bd232c2 100644 --- a/deployment/deployment.yaml +++ b/deployment/deployment.yaml @@ -113,7 +113,7 @@ metadata: arch: arm64 name: cart-actor-arm64 spec: - replicas: 0 + replicas: 3 selector: matchLabels: app: cart-actor diff --git a/frames.go b/frames.go deleted file mode 100644 index 9bc4242..0000000 --- a/frames.go +++ /dev/null @@ -1,102 +0,0 @@ -package main - -// Minimal frame abstractions retained after removal of the legacy TCP/frame -// networking layer. These types remain only to avoid a wide cascading refactor -// across existing grain / pool logic that still constructs and passes -// FrameWithPayload objects internally. -// -// The original responsibilities this replaces: -// - Binary framing, checksums, network IO -// - Distinction between request / reply frame types -// -// What remains: -// - A light weight container (FrameWithPayload) used as an in‑process -// envelope for status code + typed marker + payload bytes (JSON or proto). -// - Message / status constants referenced in existing code paths. -// -// Recommended future cleanup (post‑migration): -// - Remove FrameType entirely and replace with enumerated semantic results -// or error values. -// - Replace FrameWithPayload with a struct { Status int; Data []byte }. -// - Remove remote_* reply type branching once all callers rely on gRPC -// status + strongly typed responses. -// -// For now we keep this minimal surface to keep the gRPC migration focused. - -type ( - // FrameType is a symbolic identifier carried through existing code paths. - // No ordering or bit semantics are required anymore. - FrameType uint32 - StatusCode uint32 -) - -type Frame struct { - Type FrameType - StatusCode StatusCode - Length uint32 - // Checksum retained for compatibility; no longer validated. - Checksum uint32 -} - -// FrameWithPayload wraps a Frame with an opaque payload. -// Payload usually contains JSON encoded cart state or an error message. -type FrameWithPayload struct { - Frame - Payload []byte -} - -// ----------------------------------------------------------------------------- -// Legacy Frame Type Constants (minimal subset still referenced) -// ----------------------------------------------------------------------------- -const ( - RemoteGetState = FrameType(0x01) - RemoteHandleMutation = FrameType(0x02) - ResponseBody = FrameType(0x03) // (rarely used; kept for completeness) - RemoteGetStateReply = FrameType(0x04) - RemoteHandleMutationReply = FrameType(0x05) - RemoteCreateOrderReply = FrameType(0x06) -) - -// MakeFrameWithPayload constructs an in‑process frame wrapper. -// Length & Checksum are filled for backward compatibility (no validation logic -// depends on the checksum anymore). -func MakeFrameWithPayload(msg FrameType, statusCode StatusCode, payload []byte) FrameWithPayload { - length := uint32(len(payload)) - return FrameWithPayload{ - Frame: Frame{ - Type: msg, - StatusCode: statusCode, - Length: length, - Checksum: (uint32(msg) + uint32(statusCode) + length) / 8, // simple legacy formula - }, - Payload: payload, - } -} - -// Clone creates a shallow copy of the frame, duplicating the payload slice. -func (f *FrameWithPayload) Clone() *FrameWithPayload { - if f == nil { - return nil - } - cp := make([]byte, len(f.Payload)) - copy(cp, f.Payload) - return &FrameWithPayload{ - Frame: f.Frame, - Payload: cp, - } -} - -// NewErrorFrame helper for creating an error frame with a textual payload. -func NewErrorFrame(msg FrameType, code StatusCode, err error) FrameWithPayload { - var b []byte - if err != nil { - b = []byte(err.Error()) - } - return MakeFrameWithPayload(msg, code, b) -} - -// IsSuccess returns true if the status code indicates success in the -// conventional HTTP style range (200–299). This mirrors previous usage patterns. -func (f *FrameWithPayload) IsSuccess() bool { - return f != nil && f.StatusCode >= 200 && f.StatusCode < 300 -} diff --git a/main.go b/main.go index b9fbcae..f7b6a36 100644 --- a/main.go +++ b/main.go @@ -249,20 +249,20 @@ func main() { return } cartId := ToCartId(cookie.Value) - _, err = syncedServer.pool.Apply(cartId, getCheckoutOrder(r.Host, cartId)) + order, err = syncedServer.CreateOrUpdateCheckout(r.Host, cartId) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) } // v2: Apply now returns *CartGrain; order creation handled inside grain (no payload to unmarshal) } else { - prevOrder, err := KlarnaInstance.GetOrder(orderId) + order, err = KlarnaInstance.GetOrder(orderId) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - order = prevOrder + } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Permissions-Policy", "payment=(self \"https://js.stripe.com\" \"https://m.stripe.network\" \"https://js.playground.kustom.co\")") diff --git a/pool-server.go b/pool-server.go index 2818279..c23521b 100644 --- a/pool-server.go +++ b/pool-server.go @@ -216,48 +216,51 @@ func (s *PoolServer) HandleConfirmation(w http.ResponseWriter, r *http.Request, return json.NewEncoder(w).Encode(order) } -func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request, id CartId) error { - // Build checkout meta (URLs derived from host) +func (s *PoolServer) CreateOrUpdateCheckout(host string, id CartId) (*CheckoutOrder, error) { meta := &CheckoutMeta{ - Terms: fmt.Sprintf("https://%s/terms", r.Host), - Checkout: fmt.Sprintf("https://%s/checkout?order_id={checkout.order.id}", r.Host), - Confirmation: fmt.Sprintf("https://%s/confirmation/{checkout.order.id}", r.Host), - Validation: fmt.Sprintf("https://%s/validate", r.Host), - Push: fmt.Sprintf("https://%s/push?order_id={checkout.order.id}", r.Host), - Country: getCountryFromHost(r.Host), + Terms: fmt.Sprintf("https://%s/terms", host), + Checkout: fmt.Sprintf("https://%s/checkout?order_id={checkout.order.id}", host), + Confirmation: fmt.Sprintf("https://%s/confirmation/{checkout.order.id}", host), + Validation: fmt.Sprintf("https://%s/validate", host), + Push: fmt.Sprintf("https://%s/push?order_id={checkout.order.id}", host), + Country: getCountryFromHost(host), } // Get current grain state (may be local or remote) grain, err := s.pool.Get(id) if err != nil { - return err + return nil, err } // Build pure checkout payload payload, _, err := BuildCheckoutOrderPayload(grain, meta) if err != nil { - return err + return nil, err } - // Call Klarna (create or update) - var klarnaOrder *CheckoutOrder if grain.OrderReference != "" { - klarnaOrder, err = KlarnaInstance.UpdateOrder(grain.OrderReference, bytes.NewReader(payload)) + return KlarnaInstance.UpdateOrder(grain.OrderReference, bytes.NewReader(payload)) } else { - klarnaOrder, err = KlarnaInstance.CreateOrder(bytes.NewReader(payload)) + return KlarnaInstance.CreateOrder(bytes.NewReader(payload)) } +} + +func (s *PoolServer) ApplyCheckoutStarted(klarnaOrder *CheckoutOrder, id CartId) (*CartGrain, error) { + // Persist initialization state via mutation (best-effort) + return s.pool.Apply(id, &messages.InitializeCheckout{ + OrderId: klarnaOrder.ID, + Status: klarnaOrder.Status, + PaymentInProgress: true, + }) +} + +func (s *PoolServer) HandleCheckout(w http.ResponseWriter, r *http.Request, id CartId) error { + klarnaOrder, err := s.CreateOrUpdateCheckout(r.Host, id) if err != nil { return err } - // Persist initialization state via mutation (best-effort) - if _, applyErr := s.pool.Apply(id, &messages.InitializeCheckout{ - OrderId: klarnaOrder.ID, - Status: klarnaOrder.Status, - PaymentInProgress: true, - }); applyErr != nil { - log.Printf("InitializeCheckout apply error: %v", applyErr) - } + s.ApplyCheckoutStarted(klarnaOrder, id) w.Header().Set("Content-Type", "application/json") return json.NewEncoder(w).Encode(klarnaOrder)