package main import ( "encoding/json" "fmt" "git.tornberg.me/go-cart-actor/pkg/cart" ) // CheckoutMeta carries the external / URL metadata required to build a // Klarna CheckoutOrder from a CartGrain snapshot. It deliberately excludes // any Klarna-specific response fields (HTML snippet, client token, etc.). type CheckoutMeta struct { Terms string Checkout string Confirmation string Validation string Push string Country string Currency string // optional override (defaults to "SEK" if empty) Locale string // optional override (defaults to "sv-se" if empty) } // BuildCheckoutOrderPayload converts the current cart grain + meta information // into a CheckoutOrder domain struct and returns its JSON-serialized payload // (to send to Klarna) alongside the structured CheckoutOrder object. // // This function is PURE: it does not perform any network I/O or mutate the // grain. The caller is responsible for: // // 1. Choosing whether to create or update the Klarna order. // 2. Invoking KlarnaClient.CreateOrder / UpdateOrder with the returned payload. // 3. Applying an InitializeCheckout mutation (or equivalent) with the // resulting Klarna order id + status. // // If you later need to support different tax rates per line, you can extend // CartItem / Delivery to expose that data and propagate it here. func BuildCheckoutOrderPayload(grain *cart.CartGrain, meta *CheckoutMeta) ([]byte, *CheckoutOrder, error) { if grain == nil { return nil, nil, fmt.Errorf("nil grain") } if meta == nil { return nil, nil, fmt.Errorf("nil checkout meta") } currency := meta.Currency if currency == "" { currency = "SEK" } locale := meta.Locale if locale == "" { locale = "sv-se" } country := meta.Country if country == "" { country = "SE" // sensible default; adjust if multi-country support changes } lines := make([]*Line, 0, len(grain.Items)+len(grain.Deliveries)) // Item lines for _, it := range grain.Items { if it == nil { continue } lines = append(lines, &Line{ Type: "physical", Reference: it.Sku, Name: it.Meta.Name, Quantity: it.Quantity, UnitPrice: int(it.Price.IncVat), TaxRate: it.Tax, // TODO: derive if variable tax rates are introduced QuantityUnit: "st", TotalAmount: int(it.TotalPrice.IncVat), TotalTaxAmount: int(it.TotalPrice.TotalVat()), ImageURL: fmt.Sprintf("https://www.elgiganten.se%s", it.Meta.Image), }) } // Delivery lines for _, d := range grain.Deliveries { if d == nil || d.Price.IncVat <= 0 { continue } lines = append(lines, &Line{ Type: "shipping_fee", Reference: d.Provider, Name: "Delivery", Quantity: 1, UnitPrice: int(d.Price.IncVat), TaxRate: 2500, QuantityUnit: "st", TotalAmount: int(d.Price.IncVat), TotalTaxAmount: int(d.Price.TotalVat()), }) } order := &CheckoutOrder{ PurchaseCountry: country, PurchaseCurrency: currency, Locale: locale, OrderAmount: int(grain.TotalPrice.IncVat), OrderTaxAmount: int(grain.TotalPrice.TotalVat()), OrderLines: lines, MerchantReference1: grain.Id.String(), MerchantURLS: &CheckoutMerchantURLS{ Terms: meta.Terms, Checkout: meta.Checkout, Confirmation: meta.Confirmation, Validation: meta.Validation, Push: meta.Push, }, } payload, err := json.Marshal(order) if err != nil { return nil, nil, fmt.Errorf("marshal checkout order: %w", err) } return payload, order, nil }