Files
go-cart-actor/cmd/checkout/klarna-handlers.go
Mats Törnberg f828f4809b
All checks were successful
Build and Publish / BuildAndDeployAmd64 (push) Successful in 42s
Build and Publish / BuildAndDeployArm64 (push) Successful in 4m15s
update stuff
2025-12-03 20:54:32 +01:00

266 lines
7.6 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"git.k6n.net/go-cart-actor/pkg/cart"
"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"
)
/*
*
*
* s.CheckoutHandler(func(order *CheckoutOrder, w http.ResponseWriter) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Permissions-Policy", "payment=(self \"https://js.stripe.com\" \"https://m.stripe.network\" \"https://js.playground.kustom.co\")")
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprintf(w, tpl, order.HTMLSnippet)
return err
})
*/
// func (s *CheckoutPoolServer) KlarnaHtmlCheckoutHandler(w http.ResponseWriter, r *http.Request, checkoutId checkout.CheckoutId) error {
// orderId := r.URL.Query().Get("order_id")
// var order *CheckoutOrder
// var err error
// if orderId == "" {
// order, err = s.CreateOrUpdateCheckout(r, checkoutId)
// if err != nil {
// logger.Error("unable to create klarna session", "error", err)
// return err
// }
// // s.ApplyKlarnaPaymentStarted(r.Context(), order, checkoutId)
// }
// order, err = s.klarnaClient.GetOrder(r.Context(), orderId)
// if err != nil {
// return err
// }
// w.Header().Set("Content-Type", "text/html; charset=utf-8")
// w.Header().Set("Permissions-Policy", "payment=(self \"https://js.stripe.com\" \"https://m.stripe.network\" \"https://js.playground.kustom.co\")")
// w.WriteHeader(http.StatusOK)
// _, err = fmt.Fprintf(w, tpl, order.HTMLSnippet)
// return err
// }
// func (s *CheckoutPoolServer) KlarnaSessionHandler(w http.ResponseWriter, r *http.Request, checkoutId checkout.CheckoutId) error {
// orderId := r.URL.Query().Get("order_id")
// var order *CheckoutOrder
// var err error
// if orderId == "" {
// order, err = s.CreateOrUpdateCheckout(r, checkoutId)
// if err != nil {
// logger.Error("unable to create klarna session", "error", err)
// return err
// }
// // s.ApplyKlarnaPaymentStarted(r.Context(), order, checkoutId)
// }
// order, err = s.klarnaClient.GetOrder(r.Context(), orderId)
// if err != nil {
// return err
// }
// w.Header().Set("Content-Type", "application/json; charset=utf-8")
// return json.NewEncoder(w).Encode(order)
// }
func (s *CheckoutPoolServer) KlarnaConfirmationHandler(w http.ResponseWriter, r *http.Request) {
orderId := r.PathValue("order_id")
order, err := s.klarnaClient.GetOrder(r.Context(), orderId)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
// Apply ConfirmationViewed mutation
cartId, ok := cart.ParseCartId(order.MerchantReference1)
if ok {
s.Apply(r.Context(), uint64(cartId), &messages.ConfirmationViewed{})
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if order.Status == "checkout_complete" {
http.SetCookie(w, &http.Cookie{
Name: "cartid",
Value: "",
Path: "/",
Secure: true,
HttpOnly: true,
Expires: time.Unix(0, 0),
SameSite: http.SameSiteLaxMode,
})
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, tpl, order.HTMLSnippet)
}
func (s *CheckoutPoolServer) KlarnaValidationHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Klarna order validation, method: %s", r.Method)
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
order := &CheckoutOrder{}
err := json.NewDecoder(r.Body).Decode(order)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
}
logger.InfoContext(r.Context(), "Klarna order validation received", "order_id", order.ID, "cart_id", order.MerchantReference1)
grain, err := s.getGrainFromKlarnaOrder(r.Context(), order)
if err != nil {
logger.ErrorContext(r.Context(), "Unable to get grain from klarna order", "error", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
s.reserveInventory(r.Context(), grain)
w.WriteHeader(http.StatusOK)
}
func (s *CheckoutPoolServer) KlarnaNotificationHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Klarna order notification, method: %s", r.Method)
logger.InfoContext(r.Context(), "Klarna order notification received", "method", r.Method)
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
order := &CheckoutOrder{}
err := json.NewDecoder(r.Body).Decode(order)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
}
log.Printf("Klarna order notification: %s", order.ID)
logger.InfoContext(r.Context(), "Klarna order notification received", "order_id", order.ID)
w.WriteHeader(http.StatusOK)
}
func (s *CheckoutPoolServer) KlarnaPushHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("Klarna order confirmation push, method: %s", r.Method)
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
orderId := r.URL.Query().Get("order_id")
log.Printf("Order confirmation push: %s", orderId)
order, err := s.klarnaClient.GetOrder(r.Context(), orderId)
if err != nil {
log.Printf("Error creating request: %v\n", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
grain, err := s.getGrainFromKlarnaOrder(r.Context(), order)
if err != nil {
logger.ErrorContext(r.Context(), "Unable to get grain from klarna order", "error", err.Error())
w.WriteHeader(http.StatusInternalServerError)
return
}
if s.inventoryService != nil {
inventoryRequests := getInventoryRequests(grain.CartState.Items)
err = s.inventoryService.ReserveInventory(r.Context(), inventoryRequests...)
if err != nil {
logger.WarnContext(r.Context(), "placeorder inventory reservation failed")
w.WriteHeader(http.StatusNotAcceptable)
return
}
s.Apply(r.Context(), uint64(grain.Id), &messages.InventoryReserved{
Id: grain.Id.String(),
Status: "success",
})
}
// err = confirmOrder(r.Context(), order, orderHandler)
// if err != nil {
// log.Printf("Error confirming order: %v\n", err)
// w.WriteHeader(http.StatusInternalServerError)
// return
// }
// err = triggerOrderCompleted(r.Context(), a.server, order)
// if err != nil {
// log.Printf("Error processing cart message: %v\n", err)
// w.WriteHeader(http.StatusInternalServerError)
// return
// }
err = s.klarnaClient.AcknowledgeOrder(r.Context(), orderId)
if err != nil {
log.Printf("Error acknowledging order: %v\n", err)
}
w.WriteHeader(http.StatusOK)
}
var tpl = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>s10r testing - checkout</title>
</head>
<body>
%s
</body>
</html>
`
func getLocationId(item *cart.CartItem) inventory.LocationID {
if item.StoreId == nil || *item.StoreId == "" {
return "se"
}
return inventory.LocationID(*item.StoreId)
}
func getInventoryRequests(items []*cart.CartItem) []inventory.ReserveRequest {
var requests []inventory.ReserveRequest
for _, item := range items {
if item == nil {
continue
}
requests = append(requests, inventory.ReserveRequest{
InventoryReference: &inventory.InventoryReference{
SKU: inventory.SKU(item.Sku),
LocationID: getLocationId(item),
},
Quantity: uint32(item.Quantity),
})
}
return requests
}
func (a *CheckoutPoolServer) getGrainFromKlarnaOrder(ctx context.Context, order *CheckoutOrder) (*checkout.CheckoutGrain, error) {
cartId, ok := cart.ParseCartId(order.MerchantReference1)
if !ok {
return nil, fmt.Errorf("invalid cart id in order reference: %s", order.MerchantReference1)
}
grain, err := a.Get(ctx, uint64(cartId))
if err != nil {
return nil, fmt.Errorf("failed to get cart grain: %w", err)
}
return grain, nil
}