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
+29 -69
View File
@@ -193,6 +193,18 @@ func (s *CheckoutPoolServer) KlarnaPushHandler(w http.ResponseWriter, r *http.Re
logger.WarnContext(r.Context(), "klarna push: inventory reservation failed; creating order anyway",
"err", err, "checkoutId", grain.Id.String())
invStatus = "failed"
} else {
// Commit step: successfully decremented stock permanently, now release the temporary cart reservations
if s.reservationService != nil {
for _, item := range grain.CartState.Items {
if item == nil || !shouldTrackInventory(item) {
continue
}
if err := s.reservationService.ReleaseForCart(r.Context(), inventory.SKU(item.Sku), getLocationId(item), inventory.CartID(grain.CartId.String())); err != nil {
logger.WarnContext(r.Context(), "klarna push: failed to release cart reservation", "sku", item.Sku, "err", err)
}
}
}
}
s.Apply(r.Context(), uint64(grain.Id), &messages.InventoryReserved{
Id: grain.Id.String(),
@@ -209,22 +221,12 @@ func (s *CheckoutPoolServer) KlarnaPushHandler(w http.ResponseWriter, r *http.Re
CompletedAt: timestamppb.Now(),
})
// ── Create the event-sourced order grain (dual-write with AMQP) ────
if s.orderClient != nil {
if _, orderErr := createOrderFromCheckout(r.Context(), s, grain, order, "klarna"); orderErr != nil {
// Non-fatal: the checkout grain is updated, the order can be
// created on retry (idempotency key guards against duplicates).
logger.WarnContext(r.Context(), "from-checkout failed; will retry on next push",
"err", orderErr, "checkoutId", grain.Id.String())
}
}
// ── Dual-write: AMQP order-queue for legacy consumers ───────────────
if s.orderHandler != nil {
legacyBody, _ := buildLegacyOrderJSON(grain, order, "klarna", orderId)
if pubErr := s.orderHandler.OrderCompleted(legacyBody); pubErr != nil {
logger.WarnContext(r.Context(), "order-queue publish failed", "err", pubErr)
}
// ── Create the event-sourced order grain (HTTP with AMQP fallback) ────
if _, orderErr := createOrderFromCheckout(r.Context(), s, grain, order, "klarna"); orderErr != nil {
// Non-fatal: the checkout grain is updated, the order can be
// created on retry (idempotency key guards against duplicates).
logger.WarnContext(r.Context(), "from-checkout failed; will retry on next push",
"err", orderErr, "checkoutId", grain.Id.String())
}
err = s.klarnaClient.AcknowledgeOrder(r.Context(), orderId)
@@ -264,59 +266,7 @@ func createOrderFromCheckout(ctx context.Context, s *CheckoutPoolServer, grain *
})
}
// buildLegacyOrderJSON builds the go-order-manager compatible JSON for AMQP
// dual-write, so existing downstream consumers on the order-queue still receive
// order events during the migration period.
func buildLegacyOrderJSON(grain *checkout.CheckoutGrain, klarnaOrder *CheckoutOrder, provider string, orderId string) ([]byte, error) {
if grain.CartState == nil {
return nil, fmt.Errorf("buildLegacyOrderJSON: checkout %s has no cart state", grain.Id)
}
type legacyLine struct {
Reference string `json:"reference"`
Name string `json:"name"`
Quantity int `json:"quantity"`
UnitPrice int `json:"unit_price"`
TaxRate int `json:"tax_rate"`
TotalAmount int `json:"total_amount"`
TotalTaxAmount int `json:"total_tax_amount"`
}
type legacyOrder struct {
ID string `json:"order_id"`
PurchaseCountry string `json:"purchase_country"`
PurchaseCurrency string `json:"purchase_currency"`
Locale string `json:"locale"`
OrderAmount int `json:"order_amount"`
OrderTaxAmount int `json:"order_tax_amount"`
OrderLines []legacyLine `json:"order_lines"`
}
lines := make([]legacyLine, 0, len(grain.CartState.Items)+len(grain.Deliveries))
for _, it := range grain.CartState.Items {
if it == nil {
continue
}
lines = append(lines, legacyLine{
Reference: it.Sku,
Name: it.Meta.Name,
Quantity: int(it.Quantity),
UnitPrice: int(it.Price.IncVat),
TaxRate: it.Tax,
TotalAmount: int(it.TotalPrice.IncVat),
TotalTaxAmount: int(it.TotalPrice.TotalVat()),
})
}
lo := legacyOrder{
ID: orderId,
PurchaseCountry: klarnaOrder.PurchaseCountry,
PurchaseCurrency: klarnaOrder.PurchaseCurrency,
Locale: klarnaOrder.Locale,
OrderAmount: klarnaOrder.OrderAmount,
OrderTaxAmount: klarnaOrder.OrderTaxAmount,
OrderLines: lines,
}
return json.Marshal(lo)
}
func getLocationId(item *cart.CartItem) inventory.LocationID {
if item.StoreId == nil || *item.StoreId == "" {
@@ -325,10 +275,20 @@ func getLocationId(item *cart.CartItem) inventory.LocationID {
return inventory.LocationID(*item.StoreId)
}
func shouldTrackInventory(item *cart.CartItem) bool {
if item.DropShip {
return false
}
if item.InventoryTracked {
return true
}
return item.Cgm == "55010"
}
func getInventoryRequests(items []*cart.CartItem) []inventory.ReserveRequest {
var requests []inventory.ReserveRequest
for _, item := range items {
if item == nil {
if item == nil || !shouldTrackInventory(item) {
continue
}
requests = append(requests, inventory.ReserveRequest{