checkout backoffice
All checks were successful
Build and Publish / BuildAndDeployAmd64 (push) Successful in 43s
Build and Publish / BuildAndDeployArm64 (push) Successful in 4m44s

This commit is contained in:
matst80
2025-12-04 21:06:56 +01:00
parent f67eeb3c49
commit d78685cd8f
6 changed files with 166 additions and 13 deletions

View File

@@ -18,19 +18,24 @@ import (
"git.k6n.net/go-cart-actor/pkg/actor"
"git.k6n.net/go-cart-actor/pkg/cart"
"git.k6n.net/go-cart-actor/pkg/checkout"
"google.golang.org/protobuf/proto"
)
type FileServer struct {
// Define fields here
dataDir string
checkoutDataDir string
storage actor.LogStorage[cart.CartGrain]
checkoutStorage actor.LogStorage[checkout.CheckoutGrain]
}
func NewFileServer(dataDir string, storage actor.LogStorage[cart.CartGrain]) *FileServer {
func NewFileServer(dataDir string, checkoutDataDir string, storage actor.LogStorage[cart.CartGrain], checkoutStorage actor.LogStorage[checkout.CheckoutGrain]) *FileServer {
return &FileServer{
dataDir: dataDir,
checkoutDataDir: checkoutDataDir,
storage: storage,
checkoutStorage: checkoutStorage,
}
}
@@ -79,6 +84,12 @@ func appendFileInfo(info fs.FileInfo, out *CartFileInfo) *CartFileInfo {
return out
}
func appendCheckoutFileInfo(info fs.FileInfo, out *CheckoutFileInfo) *CheckoutFileInfo {
out.Size = info.Size()
out.Modified = info.ModTime()
return out
}
// var cartFileRe = regexp.MustCompile(`^(\d+)\.events\.log$`)
func listCartFiles(dir string) ([]*CartFileInfo, error) {
@@ -112,6 +123,36 @@ func listCartFiles(dir string) ([]*CartFileInfo, error) {
return out, nil
}
func listCheckoutFiles(dir string) ([]*CheckoutFileInfo, error) {
entries, err := os.ReadDir(dir)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return []*CheckoutFileInfo{}, nil
}
return nil, err
}
out := make([]*CheckoutFileInfo, 0)
for _, e := range entries {
if e.IsDir() {
continue
}
id, valid := isValidFileId(e.Name())
if !valid {
continue
}
info, err := e.Info()
if err != nil {
continue
}
out = append(out, appendCheckoutFileInfo(info, &CheckoutFileInfo{
ID: fmt.Sprintf("%d", id),
CheckoutId: checkout.CheckoutId(id),
}))
}
return out, nil
}
func readRawLogLines(path string) ([]json.RawMessage, error) {
fh, err := os.Open(path)
if err != nil {
@@ -158,6 +199,21 @@ func (fs *FileServer) CartsHandler(w http.ResponseWriter, r *http.Request) {
})
}
func (fs *FileServer) CheckoutsHandler(w http.ResponseWriter, r *http.Request) {
list, err := listCheckoutFiles(fs.checkoutDataDir)
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
// sort by modified desc
sort.Slice(list, func(i, j int) bool { return list[i].Modified.After(list[j].Modified) })
writeJSON(w, http.StatusOK, map[string]any{
"count": len(list),
"checkouts": list,
})
}
func (fs *FileServer) PromotionsHandler(w http.ResponseWriter, r *http.Request) {
fileName := filepath.Join(fs.dataDir, "promotions.json")
if r.Method == http.MethodGet {
@@ -315,3 +371,69 @@ func (fs *FileServer) CartHandler(w http.ResponseWriter, r *http.Request) {
},
})
}
func (fs *FileServer) CheckoutHandler(w http.ResponseWriter, r *http.Request) {
idStr := r.PathValue("id")
if idStr == "" {
writeJSON(w, http.StatusBadRequest, JsonError{Error: "missing id"})
return
}
id, ok := isValidId(idStr)
if !ok {
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid id"})
return
}
// parse query parameters for filtering
query := r.URL.Query()
filterFunction := acceptAll
if maxIndexStr := query.Get("maxIndex"); maxIndexStr != "" {
log.Printf("filter maxIndex: %s", maxIndexStr)
maxIndex, err := strconv.Atoi(maxIndexStr)
if err != nil {
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid maxIndex"})
return
}
filterFunction = acceptUntilIndex(maxIndex)
} else if untilStr := query.Get("until"); untilStr != "" {
log.Printf("filter until: %s", untilStr)
until, err := time.Parse(time.RFC3339, untilStr)
if err != nil {
writeJSON(w, http.StatusBadRequest, JsonError{Error: "invalid until timestamp"})
return
}
filterFunction = acceptUntilTimestamp(until)
}
// reconstruct state from event log if present
grain := checkout.NewCheckoutGrain(id, cart.CartId(id), 0, time.Now(), nil)
err := fs.checkoutStorage.LoadEventsFunc(r.Context(), id, grain, filterFunction)
if err != nil {
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
return
}
path := filepath.Join(fs.checkoutDataDir, fmt.Sprintf("%d.events.log", id))
info, err := os.Stat(path)
if err != nil && errors.Is(err, os.ErrNotExist) {
writeJSON(w, http.StatusNotFound, JsonError{Error: "checkout not found"})
return
} else if err != nil {
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
return
}
lines, err := readRawLogLines(path)
if err != nil {
writeJSON(w, http.StatusInternalServerError, JsonError{Error: err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]any{
"id": id,
"checkoutId": checkout.CheckoutId(id).String(),
"state": grain,
"mutations": lines,
"meta": map[string]any{
"size": info.Size(),
"modified": info.ModTime(),
"path": path,
},
})
}

View File

@@ -11,6 +11,7 @@ import (
actor "git.k6n.net/go-cart-actor/pkg/actor"
"git.k6n.net/go-cart-actor/pkg/cart"
"git.k6n.net/go-cart-actor/pkg/checkout"
"github.com/matst80/go-redis-inventory/pkg/inventory"
"github.com/matst80/slask-finder/pkg/messaging"
amqp "github.com/rabbitmq/amqp091-go"
@@ -24,6 +25,13 @@ type CartFileInfo struct {
Modified time.Time `json:"modified"`
}
type CheckoutFileInfo struct {
ID string `json:"id"`
CheckoutId checkout.CheckoutId `json:"checkoutId"`
Size int64 `json:"size"`
Modified time.Time `json:"modified"`
}
func envOrDefault(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
@@ -97,7 +105,13 @@ func main() {
reg := cart.NewCartMultationRegistry(cart.NewCartMutationContext(nil))
diskStorage := actor.NewDiskStorage[cart.CartGrain](dataDir, reg)
fs := NewFileServer(dataDir, diskStorage)
checkoutDataDir := envOrDefault("CHECKOUT_DATA_DIR", "checkout-data")
_ = os.MkdirAll(checkoutDataDir, 0755)
regCheckout := checkout.NewCheckoutMutationRegistry(checkout.NewCheckoutMutationContext())
diskStorageCheckout := actor.NewDiskStorage[checkout.CheckoutGrain](checkoutDataDir, regCheckout)
fs := NewFileServer(dataDir, checkoutDataDir, diskStorage, diskStorageCheckout)
hub := NewHub()
go hub.Run()
@@ -105,6 +119,8 @@ func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /carts", fs.CartsHandler)
mux.HandleFunc("GET /cart/{id}", fs.CartHandler)
mux.HandleFunc("GET /checkouts", fs.CheckoutsHandler)
mux.HandleFunc("GET /checkout/{id}", fs.CheckoutHandler)
mux.HandleFunc("PUT /inventory/{locationId}/{sku}", func(w http.ResponseWriter, r *http.Request) {
inventoryLocationId := inventory.LocationID(r.PathValue("locationId"))
inventorySku := inventory.SKU(r.PathValue("sku"))

View File

@@ -147,6 +147,7 @@ func (s *CheckoutPoolServer) AdyenHookHandler(w http.ResponseWriter, r *http.Req
PaymentId: item.PspReference,
Status: item.Success,
Amount: item.Amount.Value,
CompletedAt: timestamppb.Now(),
})
} else {
msgs = append(msgs, &messages.PaymentDeclined{

View File

@@ -12,6 +12,7 @@ import (
"git.k6n.net/go-cart-actor/pkg/checkout"
messages "git.k6n.net/go-cart-actor/proto/checkout"
"github.com/matst80/go-redis-inventory/pkg/inventory"
"google.golang.org/protobuf/types/known/timestamppb"
)
/*
@@ -201,6 +202,7 @@ func (s *CheckoutPoolServer) KlarnaPushHandler(w http.ResponseWriter, r *http.Re
ProcessorReference: &order.ID,
Amount: int64(order.OrderAmount),
Currency: order.PurchaseCurrency,
CompletedAt: timestamppb.Now(),
})
// err = confirmOrder(r.Context(), order, orderHandler)

View File

@@ -38,7 +38,7 @@ spec:
volumes:
- name: data
nfs:
path: /i-data/7a8af061/nfs/cart-actor
path: /i-data/7a8af061/nfs/
server: 10.10.1.10
serviceAccountName: default
containers:
@@ -76,6 +76,10 @@ spec:
memory: "70Mi"
cpu: "1200m"
env:
- name: DATA_DIR
value: "/data/cart-actor"
- name: CHECKOUT_DATA_DIR
value: "/data/checkout-actor"
- name: TZ
value: "Europe/Stockholm"
- name: REDIS_ADDRESS
@@ -180,6 +184,10 @@ spec:
memory: "70Mi"
cpu: "1200m"
env:
- name: DATA_DIR
value: "/data/cart-actor"
- name: CHECKOUT_DATA_DIR
value: "/data/checkout-actor"
- name: TZ
value: "Europe/Stockholm"
- name: KLARNA_API_USERNAME

View File

@@ -21,9 +21,13 @@ func HandlePaymentCompleted(g *CheckoutGrain, m *messages.PaymentCompleted) erro
payment.ProcessorReference = m.ProcessorReference
payment.Status = PaymentStatusSuccess
if m.Amount > 0 {
payment.Amount = m.Amount
}
if m.Currency != "" {
payment.Currency = m.Currency
payment.CompletedAt = &time.Time{}
}
if m.CompletedAt != nil {
*payment.CompletedAt = m.CompletedAt.AsTime()
} else {