From ea3eb2f0f3ffbd3454213b9541a9a71bf6185a42 Mon Sep 17 00:00:00 2001 From: matst80 Date: Fri, 5 Dec 2025 08:30:59 +0100 Subject: [PATCH] add cancel --- cmd/checkout/pool-server.go | 14 +++ pkg/checkout/mutation-context.go | 1 + pkg/checkout/mutation_cancel_payment.go | 30 +++++ pkg/checkout/mutation_delivery.go | 20 ++- pkg/checkout/mutation_set_pickup_point.go | 9 +- proto/checkout.proto | 6 + proto/checkout/checkout.pb.go | 144 ++++++++++++++++------ 7 files changed, 172 insertions(+), 52 deletions(-) create mode 100644 pkg/checkout/mutation_cancel_payment.go diff --git a/cmd/checkout/pool-server.go b/cmd/checkout/pool-server.go index c27fd2a..7fb2b9a 100644 --- a/cmd/checkout/pool-server.go +++ b/cmd/checkout/pool-server.go @@ -195,6 +195,19 @@ func (s *CheckoutPoolServer) ContactDetailsUpdatedHandler(w http.ResponseWriter, return s.WriteResult(w, result) } +func (s *CheckoutPoolServer) CancelPaymentHandler(w http.ResponseWriter, r *http.Request, checkoutId checkout.CheckoutId) error { + paymentId := r.PathValue("id") + result, err := s.ApplyLocal(r.Context(), checkoutId, &messages.CancelPayment{ + PaymentId: paymentId, + CancelledAt: timestamppb.New(time.Now()), + }) + if err != nil { + return err + } + + return s.WriteResult(w, result) +} + func (s *CheckoutPoolServer) StartCheckoutHandler(w http.ResponseWriter, r *http.Request) { cartIdStr := r.PathValue("cartid") if cartIdStr == "" { @@ -530,6 +543,7 @@ func (s *CheckoutPoolServer) Serve(mux *http.ServeMux) { handleFunc("POST /api/checkout/contact-details", CookieCheckoutIdHandler(s.ProxyHandler(s.ContactDetailsUpdatedHandler))) handleFunc("POST /payment", CookieCheckoutIdHandler(s.ProxyHandler(s.StartPaymentHandler))) handleFunc("POST /payment/{id}/session", CookieCheckoutIdHandler(s.ProxyHandler(s.GetPaymentSessionHandler))) + handleFunc("DELETE /payment/{id}", CookieCheckoutIdHandler(s.ProxyHandler(s.CancelPaymentHandler))) // handleFunc("POST /api/checkout/initialize", CookieCheckoutIdHandler(s.ProxyHandler(s.InitializeCheckoutHandler))) // handleFunc("POST /api/checkout/inventory-reserved", CookieCheckoutIdHandler(s.ProxyHandler(s.InventoryReservedHandler))) // handleFunc("POST /api/checkout/order-created", CookieCheckoutIdHandler(s.ProxyHandler(s.OrderCreatedHandler))) diff --git a/pkg/checkout/mutation-context.go b/pkg/checkout/mutation-context.go index d966d85..2581509 100644 --- a/pkg/checkout/mutation-context.go +++ b/pkg/checkout/mutation-context.go @@ -27,6 +27,7 @@ func NewCheckoutMutationRegistry(ctx *CheckoutMutationContext) actor.MutationReg actor.NewMutation(HandleSetPickupPoint), actor.NewMutation(HandleRemoveDelivery), actor.NewMutation(HandleContactDetailsUpdated), + actor.NewMutation(HandlePaymentCancelled), ) return reg } diff --git a/pkg/checkout/mutation_cancel_payment.go b/pkg/checkout/mutation_cancel_payment.go new file mode 100644 index 0000000..44a6b22 --- /dev/null +++ b/pkg/checkout/mutation_cancel_payment.go @@ -0,0 +1,30 @@ +package checkout + +import ( + "errors" + "slices" + + messages "git.k6n.net/go-cart-actor/proto/checkout" +) + +func HandlePaymentCancelled(g *CheckoutGrain, m *messages.PaymentDeclined) error { + + payment, found := g.FindPayment(m.PaymentId) + if !found { + return ErrPaymentNotFound + } + if payment.CompletedAt != nil { + return errors.New("payment already completed") + } + g.PaymentInProgress-- + g.AmountInCentsStarted -= payment.Amount + g.Payments = removePayment(g.Payments, payment.PaymentId) + + return nil +} + +func removePayment(payment []*Payment, s string) []*Payment { + return slices.DeleteFunc(payment, func(p *Payment) bool { + return p.PaymentId == s + }) +} diff --git a/pkg/checkout/mutation_delivery.go b/pkg/checkout/mutation_delivery.go index d9c0492..0eb4e84 100644 --- a/pkg/checkout/mutation_delivery.go +++ b/pkg/checkout/mutation_delivery.go @@ -11,13 +11,21 @@ func asPickupPoint(p *messages.PickupPoint, deliveryId uint32) *PickupPoint { if p == nil { return nil } + if p.Address == nil { + return &PickupPoint{ + Id: p.Id, + Name: p.Name, + DeliveryId: deliveryId, + } + } return &PickupPoint{ - Id: p.Id, - Name: p.Name, - Address: p.Address, - City: p.City, - Country: p.Country, - Zip: p.Zip, + DeliveryId: deliveryId, + Id: p.Id, + Name: p.Name, + Address: &p.Address.AddressLine1, + City: &p.Address.City, + Zip: &p.Address.Zip, + Country: &p.Address.Country, } } diff --git a/pkg/checkout/mutation_set_pickup_point.go b/pkg/checkout/mutation_set_pickup_point.go index cc7c630..8e1cc08 100644 --- a/pkg/checkout/mutation_set_pickup_point.go +++ b/pkg/checkout/mutation_set_pickup_point.go @@ -36,14 +36,7 @@ func HandleSetPickupPoint(g *CheckoutGrain, m *messages.SetPickupPoint) error { for _, d := range g.Deliveries { if d.Id == uint32(m.DeliveryId) { - d.PickupPoint = &PickupPoint{ - Id: m.PickupPoint.Id, - Name: m.PickupPoint.Name, - Address: m.PickupPoint.Address, - City: m.PickupPoint.City, - Zip: m.PickupPoint.Zip, - Country: m.PickupPoint.Country, - } + d.PickupPoint = asPickupPoint(m.PickupPoint, d.Id) return nil } } diff --git a/proto/checkout.proto b/proto/checkout.proto index acff551..38bcb34 100644 --- a/proto/checkout.proto +++ b/proto/checkout.proto @@ -95,6 +95,12 @@ message ContactDetailsUpdated { optional string name = 3; } +message CancelPayment { + string paymentId = 1; + optional string reason = 2; + google.protobuf.Timestamp cancelledAt = 3; +} + message InventoryReserved { string id = 1; string status = 2; diff --git a/proto/checkout/checkout.pb.go b/proto/checkout/checkout.pb.go index aa47f95..c5dfd58 100644 --- a/proto/checkout/checkout.pb.go +++ b/proto/checkout/checkout.pb.go @@ -915,6 +915,66 @@ func (x *ContactDetailsUpdated) GetName() string { return "" } +type CancelPayment struct { + state protoimpl.MessageState `protogen:"open.v1"` + PaymentId string `protobuf:"bytes,1,opt,name=paymentId,proto3" json:"paymentId,omitempty"` + Reason *string `protobuf:"bytes,2,opt,name=reason,proto3,oneof" json:"reason,omitempty"` + CancelledAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=cancelledAt,proto3" json:"cancelledAt,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelPayment) Reset() { + *x = CancelPayment{} + mi := &file_checkout_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelPayment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelPayment) ProtoMessage() {} + +func (x *CancelPayment) ProtoReflect() protoreflect.Message { + mi := &file_checkout_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelPayment.ProtoReflect.Descriptor instead. +func (*CancelPayment) Descriptor() ([]byte, []int) { + return file_checkout_proto_rawDescGZIP(), []int{13} +} + +func (x *CancelPayment) GetPaymentId() string { + if x != nil { + return x.PaymentId + } + return "" +} + +func (x *CancelPayment) GetReason() string { + if x != nil && x.Reason != nil { + return *x.Reason + } + return "" +} + +func (x *CancelPayment) GetCancelledAt() *timestamppb.Timestamp { + if x != nil { + return x.CancelledAt + } + return nil +} + type InventoryReserved struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -926,7 +986,7 @@ type InventoryReserved struct { func (x *InventoryReserved) Reset() { *x = InventoryReserved{} - mi := &file_checkout_proto_msgTypes[13] + mi := &file_checkout_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -938,7 +998,7 @@ func (x *InventoryReserved) String() string { func (*InventoryReserved) ProtoMessage() {} func (x *InventoryReserved) ProtoReflect() protoreflect.Message { - mi := &file_checkout_proto_msgTypes[13] + mi := &file_checkout_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -951,7 +1011,7 @@ func (x *InventoryReserved) ProtoReflect() protoreflect.Message { // Deprecated: Use InventoryReserved.ProtoReflect.Descriptor instead. func (*InventoryReserved) Descriptor() ([]byte, []int) { - return file_checkout_proto_rawDescGZIP(), []int{13} + return file_checkout_proto_rawDescGZIP(), []int{14} } func (x *InventoryReserved) GetId() string { @@ -998,7 +1058,7 @@ type Mutation struct { func (x *Mutation) Reset() { *x = Mutation{} - mi := &file_checkout_proto_msgTypes[14] + mi := &file_checkout_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1010,7 +1070,7 @@ func (x *Mutation) String() string { func (*Mutation) ProtoMessage() {} func (x *Mutation) ProtoReflect() protoreflect.Message { - mi := &file_checkout_proto_msgTypes[14] + mi := &file_checkout_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1023,7 +1083,7 @@ func (x *Mutation) ProtoReflect() protoreflect.Message { // Deprecated: Use Mutation.ProtoReflect.Descriptor instead. func (*Mutation) Descriptor() ([]byte, []int) { - return file_checkout_proto_rawDescGZIP(), []int{14} + return file_checkout_proto_rawDescGZIP(), []int{15} } func (x *Mutation) GetType() isMutation_Type { @@ -1311,7 +1371,12 @@ const file_checkout_proto_rawDesc = "" + "\x06_emailB\r\n" + "\v_postalCodeB\b\n" + "\x06_phoneB\a\n" + - "\x05_name\"f\n" + + "\x05_name\"\x93\x01\n" + + "\rCancelPayment\x12\x1c\n" + + "\tpaymentId\x18\x01 \x01(\tR\tpaymentId\x12\x1b\n" + + "\x06reason\x18\x02 \x01(\tH\x00R\x06reason\x88\x01\x01\x12<\n" + + "\vcancelledAt\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\vcancelledAtB\t\n" + + "\a_reason\"f\n" + "\x11InventoryReserved\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + "\x06status\x18\x02 \x01(\tR\x06status\x12\x1d\n" + @@ -1346,7 +1411,7 @@ func file_checkout_proto_rawDescGZIP() []byte { return file_checkout_proto_rawDescData } -var file_checkout_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_checkout_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_checkout_proto_goTypes = []any{ (*SetDelivery)(nil), // 0: checkout_messages.SetDelivery (*Address)(nil), // 1: checkout_messages.Address @@ -1361,40 +1426,42 @@ var file_checkout_proto_goTypes = []any{ (*OrderCreated)(nil), // 10: checkout_messages.OrderCreated (*InitializeCheckout)(nil), // 11: checkout_messages.InitializeCheckout (*ContactDetailsUpdated)(nil), // 12: checkout_messages.ContactDetailsUpdated - (*InventoryReserved)(nil), // 13: checkout_messages.InventoryReserved - (*Mutation)(nil), // 14: checkout_messages.Mutation - (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp - (*anypb.Any)(nil), // 16: google.protobuf.Any + (*CancelPayment)(nil), // 13: checkout_messages.CancelPayment + (*InventoryReserved)(nil), // 14: checkout_messages.InventoryReserved + (*Mutation)(nil), // 15: checkout_messages.Mutation + (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp + (*anypb.Any)(nil), // 17: google.protobuf.Any } var file_checkout_proto_depIdxs = []int32{ 3, // 0: checkout_messages.SetDelivery.pickupPoint:type_name -> checkout_messages.PickupPoint 3, // 1: checkout_messages.SetPickupPoint.pickupPoint:type_name -> checkout_messages.PickupPoint 1, // 2: checkout_messages.PickupPoint.address:type_name -> checkout_messages.Address 1, // 3: checkout_messages.PaymentStarted.billingAddress:type_name -> checkout_messages.Address - 15, // 4: checkout_messages.PaymentStarted.startedAt:type_name -> google.protobuf.Timestamp - 16, // 5: checkout_messages.PaymentStarted.sessionData:type_name -> google.protobuf.Any - 15, // 6: checkout_messages.PaymentCompleted.completedAt:type_name -> google.protobuf.Timestamp - 16, // 7: checkout_messages.PaymentEvent.data:type_name -> google.protobuf.Any - 15, // 8: checkout_messages.ConfirmationViewed.viewedAt:type_name -> google.protobuf.Timestamp - 15, // 9: checkout_messages.OrderCreated.createdAt:type_name -> google.protobuf.Timestamp - 16, // 10: checkout_messages.InitializeCheckout.cartState:type_name -> google.protobuf.Any - 0, // 11: checkout_messages.Mutation.set_delivery:type_name -> checkout_messages.SetDelivery - 2, // 12: checkout_messages.Mutation.set_pickup_point:type_name -> checkout_messages.SetPickupPoint - 4, // 13: checkout_messages.Mutation.remove_delivery:type_name -> checkout_messages.RemoveDelivery - 7, // 14: checkout_messages.Mutation.payment_declined:type_name -> checkout_messages.PaymentDeclined - 9, // 15: checkout_messages.Mutation.confirmation_viewed:type_name -> checkout_messages.ConfirmationViewed - 12, // 16: checkout_messages.Mutation.contact_details_updated:type_name -> checkout_messages.ContactDetailsUpdated - 10, // 17: checkout_messages.Mutation.order_created:type_name -> checkout_messages.OrderCreated - 11, // 18: checkout_messages.Mutation.initialize_checkout:type_name -> checkout_messages.InitializeCheckout - 13, // 19: checkout_messages.Mutation.inventory_reserved:type_name -> checkout_messages.InventoryReserved - 5, // 20: checkout_messages.Mutation.payment_started:type_name -> checkout_messages.PaymentStarted - 6, // 21: checkout_messages.Mutation.payment_completed:type_name -> checkout_messages.PaymentCompleted - 8, // 22: checkout_messages.Mutation.payment_event:type_name -> checkout_messages.PaymentEvent - 23, // [23:23] is the sub-list for method output_type - 23, // [23:23] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name + 16, // 4: checkout_messages.PaymentStarted.startedAt:type_name -> google.protobuf.Timestamp + 17, // 5: checkout_messages.PaymentStarted.sessionData:type_name -> google.protobuf.Any + 16, // 6: checkout_messages.PaymentCompleted.completedAt:type_name -> google.protobuf.Timestamp + 17, // 7: checkout_messages.PaymentEvent.data:type_name -> google.protobuf.Any + 16, // 8: checkout_messages.ConfirmationViewed.viewedAt:type_name -> google.protobuf.Timestamp + 16, // 9: checkout_messages.OrderCreated.createdAt:type_name -> google.protobuf.Timestamp + 17, // 10: checkout_messages.InitializeCheckout.cartState:type_name -> google.protobuf.Any + 16, // 11: checkout_messages.CancelPayment.cancelledAt:type_name -> google.protobuf.Timestamp + 0, // 12: checkout_messages.Mutation.set_delivery:type_name -> checkout_messages.SetDelivery + 2, // 13: checkout_messages.Mutation.set_pickup_point:type_name -> checkout_messages.SetPickupPoint + 4, // 14: checkout_messages.Mutation.remove_delivery:type_name -> checkout_messages.RemoveDelivery + 7, // 15: checkout_messages.Mutation.payment_declined:type_name -> checkout_messages.PaymentDeclined + 9, // 16: checkout_messages.Mutation.confirmation_viewed:type_name -> checkout_messages.ConfirmationViewed + 12, // 17: checkout_messages.Mutation.contact_details_updated:type_name -> checkout_messages.ContactDetailsUpdated + 10, // 18: checkout_messages.Mutation.order_created:type_name -> checkout_messages.OrderCreated + 11, // 19: checkout_messages.Mutation.initialize_checkout:type_name -> checkout_messages.InitializeCheckout + 14, // 20: checkout_messages.Mutation.inventory_reserved:type_name -> checkout_messages.InventoryReserved + 5, // 21: checkout_messages.Mutation.payment_started:type_name -> checkout_messages.PaymentStarted + 6, // 22: checkout_messages.Mutation.payment_completed:type_name -> checkout_messages.PaymentCompleted + 8, // 23: checkout_messages.Mutation.payment_event:type_name -> checkout_messages.PaymentEvent + 24, // [24:24] is the sub-list for method output_type + 24, // [24:24] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_checkout_proto_init() } @@ -1410,7 +1477,8 @@ func file_checkout_proto_init() { file_checkout_proto_msgTypes[7].OneofWrappers = []any{} file_checkout_proto_msgTypes[12].OneofWrappers = []any{} file_checkout_proto_msgTypes[13].OneofWrappers = []any{} - file_checkout_proto_msgTypes[14].OneofWrappers = []any{ + file_checkout_proto_msgTypes[14].OneofWrappers = []any{} + file_checkout_proto_msgTypes[15].OneofWrappers = []any{ (*Mutation_SetDelivery)(nil), (*Mutation_SetPickupPoint)(nil), (*Mutation_RemoveDelivery)(nil), @@ -1430,7 +1498,7 @@ func file_checkout_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_checkout_proto_rawDesc), len(file_checkout_proto_rawDesc)), NumEnums: 0, - NumMessages: 15, + NumMessages: 16, NumExtensions: 0, NumServices: 0, },