cart and checkout
Build and Publish / BuildAndDeployAmd64 (push) Failing after 4s
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s

This commit is contained in:
2026-06-27 19:49:00 +02:00
parent 492f54ff45
commit 528c59bfd3
67 changed files with 3618 additions and 1031 deletions
+30 -11
View File
@@ -41,6 +41,7 @@ type server struct {
freg *flow.Registry
flows *flowStore
provider order.PaymentProvider
taxProvider order.TaxProvider
defaultFlow string
dataDir string
idem *idempotency.Store
@@ -134,9 +135,12 @@ func main() {
log.Fatalf("open idempotency store: %v", err)
}
taxProvider := selectTaxProvider()
s := &server{
pool: pool, applier: applier, storage: storage, reg: reg,
engine: engine, freg: freg, flows: flows, provider: provider,
taxProvider: taxProvider,
defaultFlow: def.Name, dataDir: dataDir, idem: idem, logger: logger,
}
@@ -150,12 +154,14 @@ func main() {
mux.HandleFunc("GET /api/orders/{id}", s.handleGet)
mux.HandleFunc("GET /api/orders/{id}/events", s.handleEvents)
// Post-purchase lifecycle (the grain already supports these transitions).
// Post-purchase lifecycle (the grain already supports these transitions).
mux.HandleFunc("POST /api/orders/{id}/fulfillments", s.handleFulfill)
mux.HandleFunc("POST /api/orders/{id}/complete", s.handleComplete)
mux.HandleFunc("POST /api/orders/{id}/cancel", s.handleCancel)
mux.HandleFunc("POST /api/orders/{id}/returns", s.handleReturn)
mux.HandleFunc("POST /api/orders/{id}/refunds", s.handleRefund)
mux.HandleFunc("POST /api/orders/{id}/exchanges", s.handleExchange)
mux.HandleFunc("POST /api/orders/{id}/edit", s.handleEditDetails)
// UCP REST adapter — Universal Commerce Protocol order lifecycle.
orderUCP := ucp.OrderHandler(s.applier)
@@ -183,10 +189,9 @@ func main() {
mux.HandleFunc("GET /sagas/flows/{name}", s.handleGetFlow)
mux.HandleFunc("PUT /sagas/flows/{name}", s.handleSaveFlow)
// Ingest legacy checkout orders (Klarna/Adyen → order-queue) so this service
// is the single source of orders, superseding go-order-manager.
// Ingest checkout orders from order-queue (Klarna/Adyen fallback).
if amqpURL != "" {
startLegacyIngest(context.Background(), amqpURL, applier, logger)
startOrderIngest(context.Background(), amqpURL, s)
}
logger.Info("order service listening", "addr", addr, "data", dataDir, "flows", flowsDir)
@@ -292,7 +297,7 @@ func (s *server) handleCheckout(w http.ResponseWriter, r *http.Request) {
return
}
po := buildPlaceOrder(id, &req)
po := s.buildPlaceOrder(id, &req)
st := flow.NewState(uint64(id), s.logger)
st.Vars[order.PlaceOrderVar] = po
@@ -320,7 +325,8 @@ func (s *server) handleCheckout(w http.ResponseWriter, r *http.Request) {
// buildPlaceOrder converts a checkout request into the PlaceOrder event,
// computing per-line and order totals (inc-vat) from the lines.
func buildPlaceOrder(id order.OrderId, req *checkoutReq) *messages.PlaceOrder {
// Tax amounts are computed using the configured TaxProvider.
func (s *server) buildPlaceOrder(id order.OrderId, req *checkoutReq) *messages.PlaceOrder {
po := &messages.PlaceOrder{
OrderReference: req.OrderReference,
CartId: req.CartId,
@@ -336,11 +342,7 @@ func buildPlaceOrder(id order.OrderId, req *checkoutReq) *messages.PlaceOrder {
var total, totalTax int64
for _, l := range req.Lines {
lineTotal := l.UnitPrice * int64(l.Quantity)
var lineTax int64
if l.TaxRate > 0 {
// inc-vat -> tax portion = total * rate / (100 + rate)
lineTax = lineTotal * int64(l.TaxRate) / int64(100+l.TaxRate)
}
lineTax := s.taxProvider.ComputeTax(lineTotal, int(l.TaxRate))
total += lineTotal
totalTax += lineTax
po.Lines = append(po.Lines, &messages.OrderLine{
@@ -450,10 +452,27 @@ func (s *server) handleList(w http.ResponseWriter, r *http.Request) {
// --- helpers --------------------------------------------------------------
// selectTaxProvider picks the tax provider from the environment. Default is
// NordicTaxProvider with SE as the default country (matching the current
// Nordic-only footprint). Set TAX_PROVIDER=static for dev and tests
// (no country awareness).
func selectTaxProvider() order.TaxProvider {
provider := os.Getenv("TAX_PROVIDER")
switch provider {
case "static":
return order.NewStaticTaxProvider()
case "nordic":
return order.NewNordicTaxProvider(envOr("TAX_DEFAULT_COUNTRY", "SE"))
default:
return order.NewNordicTaxProvider(envOr("TAX_DEFAULT_COUNTRY", "SE"))
}
}
// selectProvider picks the payment provider from the environment. Default is
// the dependency-free mock; set PAYMENT_PROVIDER=stripe + STRIPE_SECRET_KEY for
// real Stripe (STRIPE_API_BASE / STRIPE_PAYMENT_METHOD optionally override).
func selectProvider(logger *slog.Logger) order.PaymentProvider {
if os.Getenv("PAYMENT_PROVIDER") == "stripe" {
key := os.Getenv("STRIPE_SECRET_KEY")
if key == "" {