diff --git a/pkg/cart/cart-grain.go b/pkg/cart/cart-grain.go index 37edbe1..90a66ca 100644 --- a/pkg/cart/cart-grain.go +++ b/pkg/cart/cart-grain.go @@ -89,17 +89,29 @@ type Marking struct { Text string `json:"text"` } +type GiftcardItem struct { + Id uint32 `json:"id"` + Value Price `json:"value"` + DeliveryDate string `json:"deliveryDate"` + Recipient string `json:"recipient"` + RecipientType string `json:"recipientType"` + Message string `json:"message"` + DesignConfig json.RawMessage `json:"designConfig,omitempty"` +} + type CartGrain struct { mu sync.RWMutex lastItemId uint32 lastDeliveryId uint32 lastVoucherId uint32 + lastGiftcardId uint32 lastAccess time.Time lastChange time.Time // unix seconds of last successful mutation (replay sets from event ts) userId string InventoryReserved bool `json:"inventoryReserved"` Id CartId `json:"id"` Items []*CartItem `json:"items"` + Giftcards []*GiftcardItem `json:"giftcards,omitempty"` TotalPrice *Price `json:"totalPrice"` TotalDiscount *Price `json:"totalDiscount"` Deliveries []*CartDelivery `json:"deliveries,omitempty"` @@ -185,11 +197,13 @@ func NewCartGrain(id uint64, ts time.Time) *CartGrain { lastItemId: 0, lastDeliveryId: 0, lastVoucherId: 0, + lastGiftcardId: 0, lastAccess: ts, lastChange: ts, TotalDiscount: NewPrice(), Vouchers: []*Voucher{}, Deliveries: []*CartDelivery{}, + Giftcards: []*GiftcardItem{}, Id: CartId(id), Items: []*CartItem{}, TotalPrice: NewPrice(), @@ -337,6 +351,9 @@ func (c *CartGrain) UpdateTotals() { for _, delivery := range c.Deliveries { c.TotalPrice.Add(delivery.Price) } + for _, giftcard := range c.Giftcards { + c.TotalPrice.Add(giftcard.Value) + } for _, voucher := range c.Vouchers { _, ok := voucher.AppliesTo(c) voucher.Applied = false diff --git a/pkg/cart/cart-mutation-helper.go b/pkg/cart/cart-mutation-helper.go index 5fa51af..431293f 100644 --- a/pkg/cart/cart-mutation-helper.go +++ b/pkg/cart/cart-mutation-helper.go @@ -72,6 +72,12 @@ func NewCartMultationRegistry() actor.MutationRegistry { actor.NewMutation(CreateCheckoutOrder, func() *messages.CreateCheckoutOrder { return &messages.CreateCheckoutOrder{} }), + actor.NewMutation(AddGiftcard, func() *messages.AddGiftcard { + return &messages.AddGiftcard{} + }), + actor.NewMutation(RemoveGiftcard, func() *messages.RemoveGiftcard { + return &messages.RemoveGiftcard{} + }), ) return reg diff --git a/pkg/cart/mutation_add_giftcard.go b/pkg/cart/mutation_add_giftcard.go new file mode 100644 index 0000000..548c4c5 --- /dev/null +++ b/pkg/cart/mutation_add_giftcard.go @@ -0,0 +1,41 @@ +package cart + +import ( + "encoding/json" + "fmt" + + messages "git.tornberg.me/go-cart-actor/pkg/messages" + "google.golang.org/protobuf/proto" +) + +func AddGiftcard(grain *CartGrain, req *messages.AddGiftcard) error { + if req.Giftcard == nil { + return fmt.Errorf("giftcard cannot be nil") + } + if req.Giftcard.Value <= 0 { + return fmt.Errorf("giftcard value must be positive") + } + grain.lastGiftcardId++ + designConfig := json.RawMessage{} + if req.Giftcard.DesignConfig != nil { + // Convert Any to RawMessage + data, err := proto.Marshal(req.Giftcard.DesignConfig) + if err != nil { + return fmt.Errorf("failed to marshal designConfig: %w", err) + } + designConfig = data + } + value := NewPriceFromIncVat(req.Giftcard.Value, 25) // Assuming 25% tax; adjust as needed + item := &GiftcardItem{ + Id: grain.lastGiftcardId, + Value: *value, + DeliveryDate: req.Giftcard.DeliveryDate, + Recipient: req.Giftcard.Recipient, + RecipientType: req.Giftcard.RecipientType, + Message: req.Giftcard.Message, + DesignConfig: designConfig, + } + grain.Giftcards = append(grain.Giftcards, item) + grain.UpdateTotals() + return nil +} diff --git a/pkg/cart/mutation_remove_giftcard.go b/pkg/cart/mutation_remove_giftcard.go new file mode 100644 index 0000000..7475a4f --- /dev/null +++ b/pkg/cart/mutation_remove_giftcard.go @@ -0,0 +1,18 @@ +package cart + +import ( + "fmt" + + messages "git.tornberg.me/go-cart-actor/pkg/messages" +) + +func RemoveGiftcard(grain *CartGrain, req *messages.RemoveGiftcard) error { + for i, item := range grain.Giftcards { + if item.Id == req.Id { + grain.Giftcards = append(grain.Giftcards[:i], grain.Giftcards[i+1:]...) + grain.UpdateTotals() + return nil + } + } + return fmt.Errorf("giftcard with ID %d not found", req.Id) +} diff --git a/pkg/cart/mutation_test.go b/pkg/cart/mutation_test.go index 2a93df0..eeb96b5 100644 --- a/pkg/cart/mutation_test.go +++ b/pkg/cart/mutation_test.go @@ -113,6 +113,23 @@ func msgCreateCheckoutOrder(terms, country string) *messages.CreateCheckoutOrder return &messages.CreateCheckoutOrder{Terms: terms, Country: country} } +func msgAddGiftcard(value int64, deliveryDate, recipient, recipientType, message string, designConfig *anypb.Any) *messages.AddGiftcard { + return &messages.AddGiftcard{ + Giftcard: &messages.GiftcardItem{ + Value: value, + DeliveryDate: deliveryDate, + Recipient: recipient, + RecipientType: recipientType, + Message: message, + DesignConfig: designConfig, + }, + } +} + +func msgRemoveGiftcard(id uint32) *messages.RemoveGiftcard { + return &messages.RemoveGiftcard{Id: id} +} + func ptr[T any](v T) *T { return &v } // ---------------------- @@ -181,6 +198,8 @@ func TestMutationRegistryCoverage(t *testing.T) { "PaymentDeclined", "ConfirmationViewed", "CreateCheckoutOrder", + "AddGiftcard", + "RemoveGiftcard", } names := reg.(*actor.ProtoMutationRegistry).RegisteredMutations() @@ -675,7 +694,7 @@ func TestConfirmationViewed(t *testing.T) { // Initial state if g.Confirmation != nil { - t.Fatalf("confirmation should be nil, got %d", g.Confirmation) + t.Fatalf("confirmation should be nil, got %v", g.Confirmation) } // First view @@ -717,3 +736,43 @@ func TestCreateCheckoutOrder(t *testing.T) { // Terms not accepted applyErrorContains(t, reg, g, msgCreateCheckoutOrder("no", ""), "terms must be accepted") } + +func TestAddGiftcard(t *testing.T) { + reg := newRegistry() + g := newTestGrain() + + designConfig, _ := anypb.New(&messages.AddItem{}) // example + applyOK(t, reg, g, msgAddGiftcard(5000, "2023-12-25", "John", "email", "Happy Birthday!", designConfig)) + + if len(g.Giftcards) != 1 { + t.Fatalf("expected 1 giftcard, got %d", len(g.Giftcards)) + } + gc := g.Giftcards[0] + if gc.Value.IncVat != 5000 || gc.DeliveryDate != "2023-12-25" || gc.Recipient != "John" || gc.RecipientType != "email" || gc.Message != "Happy Birthday!" { + t.Fatalf("giftcard not set correctly: %+v", gc) + } + if g.TotalPrice.IncVat != 5000 { + t.Fatalf("total price not updated, got %d", g.TotalPrice.IncVat) + } + + // Test invalid value + applyErrorContains(t, reg, g, msgAddGiftcard(0, "", "", "", "", nil), "must be positive") +} + +func TestRemoveGiftcard(t *testing.T) { + reg := newRegistry() + g := newTestGrain() + + applyOK(t, reg, g, msgAddGiftcard(1000, "2023-01-01", "Jane", "sms", "Cheers!", nil)) + id := g.Giftcards[0].Id + + applyOK(t, reg, g, msgRemoveGiftcard(id)) + if len(g.Giftcards) != 0 { + t.Fatalf("giftcard not removed") + } + if g.TotalPrice.IncVat != 0 { + t.Fatalf("total price not updated after removal, got %d", g.TotalPrice.IncVat) + } + + applyErrorContains(t, reg, g, msgRemoveGiftcard(id), "not found") +} diff --git a/pkg/messages/messages.pb.go b/pkg/messages/messages.pb.go index 88231e6..7faebdb 100644 --- a/pkg/messages/messages.pb.go +++ b/pkg/messages/messages.pb.go @@ -1530,6 +1530,178 @@ func (x *PreConditionFailed) GetInput() *anypb.Any { return nil } +type GiftcardItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` + DeliveryDate string `protobuf:"bytes,2,opt,name=deliveryDate,proto3" json:"deliveryDate,omitempty"` + Recipient string `protobuf:"bytes,3,opt,name=recipient,proto3" json:"recipient,omitempty"` + RecipientType string `protobuf:"bytes,4,opt,name=recipientType,proto3" json:"recipientType,omitempty"` + Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` + DesignConfig *anypb.Any `protobuf:"bytes,6,opt,name=designConfig,proto3,oneof" json:"designConfig,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GiftcardItem) Reset() { + *x = GiftcardItem{} + mi := &file_messages_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GiftcardItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GiftcardItem) ProtoMessage() {} + +func (x *GiftcardItem) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[23] + 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 GiftcardItem.ProtoReflect.Descriptor instead. +func (*GiftcardItem) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{23} +} + +func (x *GiftcardItem) GetValue() int64 { + if x != nil { + return x.Value + } + return 0 +} + +func (x *GiftcardItem) GetDeliveryDate() string { + if x != nil { + return x.DeliveryDate + } + return "" +} + +func (x *GiftcardItem) GetRecipient() string { + if x != nil { + return x.Recipient + } + return "" +} + +func (x *GiftcardItem) GetRecipientType() string { + if x != nil { + return x.RecipientType + } + return "" +} + +func (x *GiftcardItem) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *GiftcardItem) GetDesignConfig() *anypb.Any { + if x != nil { + return x.DesignConfig + } + return nil +} + +type AddGiftcard struct { + state protoimpl.MessageState `protogen:"open.v1"` + Giftcard *GiftcardItem `protobuf:"bytes,1,opt,name=giftcard,proto3" json:"giftcard,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AddGiftcard) Reset() { + *x = AddGiftcard{} + mi := &file_messages_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddGiftcard) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddGiftcard) ProtoMessage() {} + +func (x *AddGiftcard) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[24] + 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 AddGiftcard.ProtoReflect.Descriptor instead. +func (*AddGiftcard) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{24} +} + +func (x *AddGiftcard) GetGiftcard() *GiftcardItem { + if x != nil { + return x.Giftcard + } + return nil +} + +type RemoveGiftcard struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveGiftcard) Reset() { + *x = RemoveGiftcard{} + mi := &file_messages_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveGiftcard) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveGiftcard) ProtoMessage() {} + +func (x *RemoveGiftcard) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[25] + 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 RemoveGiftcard.ProtoReflect.Descriptor instead. +func (*RemoveGiftcard) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{25} +} + +func (x *RemoveGiftcard) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + var File_messages_proto protoreflect.FileDescriptor var file_messages_proto_rawDesc = string([]byte{ @@ -1717,11 +1889,33 @@ var file_messages_proto_rawDesc = string([]byte{ 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x2e, 0x5a, 0x2c, 0x67, - 0x69, 0x74, 0x2e, 0x74, 0x6f, 0x72, 0x6e, 0x62, 0x65, 0x72, 0x67, 0x2e, 0x6d, 0x65, 0x2f, 0x67, - 0x6f, 0x2d, 0x63, 0x61, 0x72, 0x74, 0x2d, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x3b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0xf6, 0x01, 0x0a, 0x0c, + 0x47, 0x69, 0x66, 0x74, 0x63, 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x44, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, + 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, + 0x69, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x63, + 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, + 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x22, 0x41, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x47, 0x69, 0x66, 0x74, 0x63, + 0x61, 0x72, 0x64, 0x12, 0x32, 0x0a, 0x08, 0x67, 0x69, 0x66, 0x74, 0x63, 0x61, 0x72, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x2e, 0x47, 0x69, 0x66, 0x74, 0x63, 0x61, 0x72, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x67, + 0x69, 0x66, 0x74, 0x63, 0x61, 0x72, 0x64, 0x22, 0x20, 0x0a, 0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x47, 0x69, 0x66, 0x74, 0x63, 0x61, 0x72, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, + 0x2e, 0x74, 0x6f, 0x72, 0x6e, 0x62, 0x65, 0x72, 0x67, 0x2e, 0x6d, 0x65, 0x2f, 0x67, 0x6f, 0x2d, + 0x63, 0x61, 0x72, 0x74, 0x2d, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x3b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, }) var ( @@ -1736,7 +1930,7 @@ func file_messages_proto_rawDescGZIP() []byte { return file_messages_proto_rawDescData } -var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_messages_proto_goTypes = []any{ (*ClearCartRequest)(nil), // 0: messages.ClearCartRequest (*AddItem)(nil), // 1: messages.AddItem @@ -1761,17 +1955,22 @@ var file_messages_proto_goTypes = []any{ (*RemoveVoucher)(nil), // 20: messages.RemoveVoucher (*UpsertSubscriptionDetails)(nil), // 21: messages.UpsertSubscriptionDetails (*PreConditionFailed)(nil), // 22: messages.PreConditionFailed - (*anypb.Any)(nil), // 23: google.protobuf.Any + (*GiftcardItem)(nil), // 23: messages.GiftcardItem + (*AddGiftcard)(nil), // 24: messages.AddGiftcard + (*RemoveGiftcard)(nil), // 25: messages.RemoveGiftcard + (*anypb.Any)(nil), // 26: google.protobuf.Any } var file_messages_proto_depIdxs = []int32{ 6, // 0: messages.SetDelivery.pickupPoint:type_name -> messages.PickupPoint - 23, // 1: messages.UpsertSubscriptionDetails.data:type_name -> google.protobuf.Any - 23, // 2: messages.PreConditionFailed.input:type_name -> google.protobuf.Any - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 26, // 1: messages.UpsertSubscriptionDetails.data:type_name -> google.protobuf.Any + 26, // 2: messages.PreConditionFailed.input:type_name -> google.protobuf.Any + 26, // 3: messages.GiftcardItem.designConfig:type_name -> google.protobuf.Any + 23, // 4: messages.AddGiftcard.giftcard:type_name -> messages.GiftcardItem + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_messages_proto_init() } @@ -1786,13 +1985,14 @@ func file_messages_proto_init() { file_messages_proto_msgTypes[12].OneofWrappers = []any{} file_messages_proto_msgTypes[18].OneofWrappers = []any{} file_messages_proto_msgTypes[21].OneofWrappers = []any{} + file_messages_proto_msgTypes[23].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)), NumEnums: 0, - NumMessages: 23, + NumMessages: 26, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/messages.proto b/proto/messages.proto index f20c187..bd6b343 100644 --- a/proto/messages.proto +++ b/proto/messages.proto @@ -152,3 +152,20 @@ message PreConditionFailed { string error = 2; google.protobuf.Any input = 3; } + +message GiftcardItem { + int64 value = 1; + string deliveryDate = 2; + string recipient = 3; + string recipientType = 4; + string message = 5; + optional google.protobuf.Any designConfig = 6; +} + +message AddGiftcard { + GiftcardItem giftcard = 1; +} + +message RemoveGiftcard { + uint32 id = 1; +}