124 lines
3.6 KiB
Go
124 lines
3.6 KiB
Go
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
|
|
Notification 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: 2500, // 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,
|
|
Notification: meta.Notification,
|
|
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
|
|
}
|