From 2c19b9c29b380d972a3c09e9e569ded98c4fb9d1 Mon Sep 17 00:00:00 2001 From: alxeg Date: Tue, 17 Mar 2026 16:37:29 +0300 Subject: [PATCH] Try to use generated yookassa client (unsuccessful) --- internal/api/payout/payout_handler.go | 16 +- internal/api/user/user_handler.go | 4 +- internal/models/payout.go | 5 +- internal/service/database/db_service.go | 27 +- internal/service/database/module.go | 7 +- internal/service/database/orm/payout.go | 73 +- internal/service/yookassa/gen/oas_json_gen.go | 762 +++++++++--------- .../yookassa/gen/oas_request_encoders_gen.go | 2 + .../service/yookassa/gen/oas_schemas_gen.go | 293 +++---- .../yookassa/gen/oas_validators_gen.go | 310 +++---- internal/service/yookassa/module.go | 2 +- internal/service/yookassa/ogen.yml | 11 +- .../yookassa-openapi-specification.yaml | 1 - internal/service/yookassa/yookassa_service.go | 52 +- 14 files changed, 867 insertions(+), 698 deletions(-) diff --git a/internal/api/payout/payout_handler.go b/internal/api/payout/payout_handler.go index c60e1fb..476822e 100644 --- a/internal/api/payout/payout_handler.go +++ b/internal/api/payout/payout_handler.go @@ -9,6 +9,7 @@ import ( "net/http" "regexp" + "github.com/google/uuid" "go.uber.org/fx" "payouts/internal/config" @@ -124,13 +125,26 @@ func (p *payoutHandler) PayoutCreate(w http.ResponseWriter, r *http.Request) { return } + idempotenceKey := uuid.New().String() + payoutModel := &orm.Payout{ + User: *userSession, + IdempotenceKey: idempotenceKey, + Type: payoutReq.PayoutType.String(), + Amount: payoutReq.Amount, + Status: orm.StatusCreated, + } + err = p.dbService.CreatePayout(payoutModel, database.WithContext(r.Context())) + slog.Debug(fmt.Sprintf("Received create payload request: %v from user %v", payoutReq, userSession)) - err = p.yooKassa.CreatePayout(payoutReq, userSession) + payoutResp, err := p.yooKassa.CreatePayout(payoutReq, userSession, idempotenceKey, yookassa.WithContext(r.Context())) if err != nil { slog.Error("Failed to create payout request", slog.String("error", err.Error())) errResponse("failed to create payout request", err, http.StatusBadRequest) return } + + encoder := json.NewEncoder(w) + encoder.Encode(payoutResp) } // PaymentCallback implements [Handler]. diff --git a/internal/api/user/user_handler.go b/internal/api/user/user_handler.go index ee503b8..4f2815e 100644 --- a/internal/api/user/user_handler.go +++ b/internal/api/user/user_handler.go @@ -82,7 +82,7 @@ func (u *userHandler) UserRegister(w http.ResponseWriter, r *http.Request) { copier.Copy(&ormUser, user) // todo: add data validation - err = u.dbService.CreateUser(ormUser, database.WithContext(r.Context())) + err = u.dbService.CreateUser(&ormUser, database.WithContext(r.Context())) if err != nil { slog.Error("Failed to create user", slog.String("error", err.Error())) errResponse("failed to create user", err, http.StatusBadRequest) @@ -114,7 +114,7 @@ func (u *userHandler) UserLogin(w http.ResponseWriter, r *http.Request) { return } - ormUser, err := u.dbService.GetUser(orm.User{Phone: user.Phone}, database.WithContext(r.Context())) + ormUser, err := u.dbService.GetUser(&orm.User{Phone: user.Phone}, database.WithContext(r.Context())) if err != nil { errResponse("invalid credentials", nil, http.StatusUnauthorized) return diff --git a/internal/models/payout.go b/internal/models/payout.go index ee99c50..677d469 100644 --- a/internal/models/payout.go +++ b/internal/models/payout.go @@ -46,6 +46,7 @@ type PayoutReq struct { } type PayoutResp struct { - Success bool `json:"success"` - Reason string `json:"reason,omitempty"` + Result string `json:"result"` + PayoutID string `json:"payout_id,omitempty"` + ErrorReason string `json:"error_reason,omitempty"` } diff --git a/internal/service/database/db_service.go b/internal/service/database/db_service.go index 11c3272..305f1de 100644 --- a/internal/service/database/db_service.go +++ b/internal/service/database/db_service.go @@ -73,16 +73,33 @@ func (d *dbService) getParams(options ...Optional) *params { } // AddUser implements [Service]. -func (d *dbService) CreateUser(userModel orm.User, opts ...Optional) error { +func (d *dbService) CreateUser(userModel *orm.User, opts ...Optional) error { p := d.getParams(opts...) - return gorm.G[orm.User](d.db).Create(p.ctx, &userModel) + return gorm.G[orm.User](d.db).Create(p.ctx, userModel) } // GetUser implements [Service]. -func (d *dbService) GetUser(userModel orm.User, opts ...Optional) (orm.User, error) { +func (d *dbService) GetUser(userModel *orm.User, opts ...Optional) (orm.User, error) { + p := d.getParams(opts...) + return gorm.G[orm.User](d.db).Where(userModel).First(p.ctx) +} + +// GetPayout implements [Service]. +func (d *dbService) GetPayout(payoutModel *orm.Payout, opts ...Optional) (orm.Payout, error) { + p := d.getParams(opts...) + return gorm.G[orm.Payout](d.db).Where(payoutModel).First(p.ctx) +} + +// CreatePayout implements [Service]. +func (d *dbService) CreatePayout(payoutModel *orm.Payout, opts ...Optional) error { p := d.getParams(opts...) - userResp, err := gorm.G[orm.User](d.db).Where(&userModel).First(p.ctx) - return userResp, err + return gorm.G[orm.Payout](d.db).Create(p.ctx, payoutModel) +} + +// UpdatePayout implements [Service]. +func (d *dbService) UpdatePayout(payoutModel *orm.Payout, opts ...Optional) error { + // p := d.getParams(opts...) + panic("unimplemented") } diff --git a/internal/service/database/module.go b/internal/service/database/module.go index 89cc5e4..03bfaeb 100644 --- a/internal/service/database/module.go +++ b/internal/service/database/module.go @@ -25,8 +25,11 @@ func WithContext(ctx context.Context) Optional { } type Service interface { - CreateUser(user orm.User, opts ...Optional) error - GetUser(user orm.User, opts ...Optional) (orm.User, error) + CreateUser(user *orm.User, opts ...Optional) error + GetUser(user *orm.User, opts ...Optional) (orm.User, error) + GetPayout(payoutModel *orm.Payout, opts ...Optional) (orm.Payout, error) + CreatePayout(payoutModel *orm.Payout, opts ...Optional) error + UpdatePayout(payoutModel *orm.Payout, opts ...Optional) error } // Params represents the module input params diff --git a/internal/service/database/orm/payout.go b/internal/service/database/orm/payout.go index 1a0d359..a35a34b 100644 --- a/internal/service/database/orm/payout.go +++ b/internal/service/database/orm/payout.go @@ -1,6 +1,60 @@ package orm -import "gorm.io/gorm" +import ( + "fmt" + "strings" + + "gorm.io/gorm" +) + +type PayoutStatus int64 + +const ( + StatusCreated PayoutStatus = iota + StatusCanceled + StatusPending + StatusSucceeded + StatusFailed +) + +func (r PayoutStatus) String() string { + switch r { + case StatusCreated: + return "created" + case StatusCanceled: + return "canceled" + case StatusPending: + return "pending" + case StatusSucceeded: + return "succeeded" + case StatusFailed: + return "failed" + } + return "unknown" +} + +func (r PayoutStatus) MarshalText() (text []byte, err error) { + return []byte(r.String()), nil +} + +func (r *PayoutStatus) UnmarshalText(text []byte) (err error) { + s := strings.ToLower(string(text)) + switch s { + case "canceled": + *r = StatusCanceled + case "created": + *r = StatusCreated + case "pending": + *r = StatusPending + case "succeeded": + *r = StatusSucceeded + case "failed": + *r = StatusFailed + default: + err = fmt.Errorf("invalid payment type: %s", s) + } + return err +} type Payout struct { gorm.Model @@ -8,12 +62,13 @@ type Payout struct { UserID uint User User - Description string - PayoutID string - Type string - AccountNumber string - Amount float32 - Currency string - Status string - Test bool + Description string + IdempotenceKey string + PayoutID string + Type string + AccountNumber string + Amount float32 + Currency string + Status PayoutStatus + Test bool } diff --git a/internal/service/yookassa/gen/oas_json_gen.go b/internal/service/yookassa/gen/oas_json_gen.go index d725baa..c6934ea 100644 --- a/internal/service/yookassa/gen/oas_json_gen.go +++ b/internal/service/yookassa/gen/oas_json_gen.go @@ -1535,6 +1535,115 @@ func (s *BadRequestType) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s *BankCard) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *BankCard) encodeFields(e *jx.Encoder) { + { + e.FieldStart("type") + s.Type.Encode(e) + } + { + e.FieldStart("card") + s.Card.Encode(e) + } +} + +var jsonFieldsNameOfBankCard = [2]string{ + 0: "type", + 1: "card", +} + +// Decode decodes BankCard from json. +func (s *BankCard) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode BankCard to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "type": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.Type.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"type\"") + } + case "card": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.Card.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"card\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode BankCard") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfBankCard) { + name = jsonFieldsNameOfBankCard[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *BankCard) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *BankCard) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s *BankCardData) Encode(e *jx.Encoder) { e.ObjStart() @@ -28019,9 +28128,15 @@ func (s *PayoutRequest) encodeFields(e *jx.Encoder) { s.Metadata.Encode(e) } } + { + if s.Test.Set { + e.FieldStart("test") + s.Test.Encode(e) + } + } } -var jsonFieldsNameOfPayoutRequest = [8]string{ +var jsonFieldsNameOfPayoutRequest = [9]string{ 0: "amount", 1: "payout_destination_data", 2: "payout_token", @@ -28030,6 +28145,7 @@ var jsonFieldsNameOfPayoutRequest = [8]string{ 5: "deal", 6: "personal_data", 7: "metadata", + 8: "test", } // Decode decodes PayoutRequest from json. @@ -28037,7 +28153,7 @@ func (s *PayoutRequest) Decode(d *jx.Decoder) error { if s == nil { return errors.New("invalid: unable to decode PayoutRequest to nil") } - var requiredBitSet [1]uint8 + var requiredBitSet [2]uint8 if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { switch string(k) { @@ -28128,6 +28244,16 @@ func (s *PayoutRequest) Decode(d *jx.Decoder) error { }(); err != nil { return errors.Wrap(err, "decode field \"metadata\"") } + case "test": + if err := func() error { + s.Test.Reset() + if err := s.Test.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"test\"") + } default: return d.Skip() } @@ -28137,8 +28263,9 @@ func (s *PayoutRequest) Decode(d *jx.Decoder) error { } // Validate required fields. var failures []validate.FieldError - for i, mask := range [1]uint8{ + for i, mask := range [2]uint8{ 0b00000001, + 0b00000000, } { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { // Mask only required fields and check equality to mask using XOR. @@ -28304,31 +28431,31 @@ func (s PayoutRequestPayoutDestinationData) Encode(e *jx.Encoder) { func (s PayoutRequestPayoutDestinationData) encodeFields(e *jx.Encoder) { switch s.Type { - case PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData: + case YooMoneyPayoutRequestPayoutDestinationData: e.FieldStart("type") - e.Str("PayoutToYooMoneyDestinationData") + e.Str("yoo_money") { - s := s.PayoutToYooMoneyDestinationData + s := s.YooMoney { e.FieldStart("account_number") e.Str(s.AccountNumber) } } - case PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData: + case BankCardPayoutRequestPayoutDestinationData: e.FieldStart("type") - e.Str("PayoutToBankCardDestinationData") + e.Str("bank_card") { - s := s.PayoutToBankCardDestinationData + s := s.BankCard { e.FieldStart("card") s.Card.Encode(e) } } - case PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData: + case SbpPayoutRequestPayoutDestinationData: e.FieldStart("type") - e.Str("PayoutToSbpDestinationData") + e.Str("sbp") { - s := s.PayoutToSbpDestinationData + s := s.Sbp { e.FieldStart("phone") e.Str(s.Phone) @@ -28364,14 +28491,14 @@ func (s *PayoutRequestPayoutDestinationData) Decode(d *jx.Decoder) error { return err } switch typ { - case "PayoutToYooMoneyDestinationData": - s.Type = PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData + case "yoo_money": + s.Type = YooMoneyPayoutRequestPayoutDestinationData found = true - case "PayoutToBankCardDestinationData": - s.Type = PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData + case "bank_card": + s.Type = BankCardPayoutRequestPayoutDestinationData found = true - case "PayoutToSbpDestinationData": - s.Type = PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData + case "sbp": + s.Type = SbpPayoutRequestPayoutDestinationData found = true default: return errors.Errorf("unknown type %s", typ) @@ -28387,16 +28514,16 @@ func (s *PayoutRequestPayoutDestinationData) Decode(d *jx.Decoder) error { return errors.New("unable to detect sum type variant") } switch s.Type { - case PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToYooMoneyDestinationData.Decode(d); err != nil { + case YooMoneyPayoutRequestPayoutDestinationData: + if err := s.YooMoney.Decode(d); err != nil { return err } - case PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToBankCardDestinationData.Decode(d); err != nil { + case BankCardPayoutRequestPayoutDestinationData: + if err := s.BankCard.Decode(d); err != nil { return err } - case PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToSbpDestinationData.Decode(d); err != nil { + case SbpPayoutRequestPayoutDestinationData: + if err := s.Sbp.Decode(d); err != nil { return err } default: @@ -28729,115 +28856,6 @@ func (s *PayoutStatus) UnmarshalJSON(data []byte) error { return s.Decode(d) } -// Encode implements json.Marshaler. -func (s *PayoutToBankCardDestinationData) Encode(e *jx.Encoder) { - e.ObjStart() - s.encodeFields(e) - e.ObjEnd() -} - -// encodeFields encodes fields. -func (s *PayoutToBankCardDestinationData) encodeFields(e *jx.Encoder) { - { - e.FieldStart("type") - s.Type.Encode(e) - } - { - e.FieldStart("card") - s.Card.Encode(e) - } -} - -var jsonFieldsNameOfPayoutToBankCardDestinationData = [2]string{ - 0: "type", - 1: "card", -} - -// Decode decodes PayoutToBankCardDestinationData from json. -func (s *PayoutToBankCardDestinationData) Decode(d *jx.Decoder) error { - if s == nil { - return errors.New("invalid: unable to decode PayoutToBankCardDestinationData to nil") - } - var requiredBitSet [1]uint8 - - if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { - switch string(k) { - case "type": - requiredBitSet[0] |= 1 << 0 - if err := func() error { - if err := s.Type.Decode(d); err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"type\"") - } - case "card": - requiredBitSet[0] |= 1 << 1 - if err := func() error { - if err := s.Card.Decode(d); err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"card\"") - } - default: - return d.Skip() - } - return nil - }); err != nil { - return errors.Wrap(err, "decode PayoutToBankCardDestinationData") - } - // Validate required fields. - var failures []validate.FieldError - for i, mask := range [1]uint8{ - 0b00000011, - } { - if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { - // Mask only required fields and check equality to mask using XOR. - // - // If XOR result is not zero, result is not equal to expected, so some fields are missed. - // Bits of fields which would be set are actually bits of missed fields. - missed := bits.OnesCount8(result) - for bitN := 0; bitN < missed; bitN++ { - bitIdx := bits.TrailingZeros8(result) - fieldIdx := i*8 + bitIdx - var name string - if fieldIdx < len(jsonFieldsNameOfPayoutToBankCardDestinationData) { - name = jsonFieldsNameOfPayoutToBankCardDestinationData[fieldIdx] - } else { - name = strconv.Itoa(fieldIdx) - } - failures = append(failures, validate.FieldError{ - Name: name, - Error: validate.ErrFieldRequired, - }) - // Reset bit. - result &^= 1 << bitIdx - } - } - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - - return nil -} - -// MarshalJSON implements stdjson.Marshaler. -func (s *PayoutToBankCardDestinationData) MarshalJSON() ([]byte, error) { - e := jx.Encoder{} - s.Encode(&e) - return e.Bytes(), nil -} - -// UnmarshalJSON implements stdjson.Unmarshaler. -func (s *PayoutToBankCardDestinationData) UnmarshalJSON(data []byte) error { - d := jx.DecodeBytes(data) - return s.Decode(d) -} - // Encode implements json.Marshaler. func (s *PayoutToCardDestination) Encode(e *jx.Encoder) { e.ObjStart() @@ -29094,134 +29112,6 @@ func (s *PayoutToSbpDestination) UnmarshalJSON(data []byte) error { return s.Decode(d) } -// Encode implements json.Marshaler. -func (s *PayoutToSbpDestinationData) Encode(e *jx.Encoder) { - e.ObjStart() - s.encodeFields(e) - e.ObjEnd() -} - -// encodeFields encodes fields. -func (s *PayoutToSbpDestinationData) encodeFields(e *jx.Encoder) { - { - e.FieldStart("type") - s.Type.Encode(e) - } - { - e.FieldStart("phone") - e.Str(s.Phone) - } - { - e.FieldStart("bank_id") - e.Str(s.BankID) - } -} - -var jsonFieldsNameOfPayoutToSbpDestinationData = [3]string{ - 0: "type", - 1: "phone", - 2: "bank_id", -} - -// Decode decodes PayoutToSbpDestinationData from json. -func (s *PayoutToSbpDestinationData) Decode(d *jx.Decoder) error { - if s == nil { - return errors.New("invalid: unable to decode PayoutToSbpDestinationData to nil") - } - var requiredBitSet [1]uint8 - - if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { - switch string(k) { - case "type": - requiredBitSet[0] |= 1 << 0 - if err := func() error { - if err := s.Type.Decode(d); err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"type\"") - } - case "phone": - requiredBitSet[0] |= 1 << 1 - if err := func() error { - v, err := d.Str() - s.Phone = string(v) - if err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"phone\"") - } - case "bank_id": - requiredBitSet[0] |= 1 << 2 - if err := func() error { - v, err := d.Str() - s.BankID = string(v) - if err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"bank_id\"") - } - default: - return d.Skip() - } - return nil - }); err != nil { - return errors.Wrap(err, "decode PayoutToSbpDestinationData") - } - // Validate required fields. - var failures []validate.FieldError - for i, mask := range [1]uint8{ - 0b00000111, - } { - if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { - // Mask only required fields and check equality to mask using XOR. - // - // If XOR result is not zero, result is not equal to expected, so some fields are missed. - // Bits of fields which would be set are actually bits of missed fields. - missed := bits.OnesCount8(result) - for bitN := 0; bitN < missed; bitN++ { - bitIdx := bits.TrailingZeros8(result) - fieldIdx := i*8 + bitIdx - var name string - if fieldIdx < len(jsonFieldsNameOfPayoutToSbpDestinationData) { - name = jsonFieldsNameOfPayoutToSbpDestinationData[fieldIdx] - } else { - name = strconv.Itoa(fieldIdx) - } - failures = append(failures, validate.FieldError{ - Name: name, - Error: validate.ErrFieldRequired, - }) - // Reset bit. - result &^= 1 << bitIdx - } - } - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - - return nil -} - -// MarshalJSON implements stdjson.Marshaler. -func (s *PayoutToSbpDestinationData) MarshalJSON() ([]byte, error) { - e := jx.Encoder{} - s.Encode(&e) - return e.Bytes(), nil -} - -// UnmarshalJSON implements stdjson.Unmarshaler. -func (s *PayoutToSbpDestinationData) UnmarshalJSON(data []byte) error { - d := jx.DecodeBytes(data) - return s.Decode(d) -} - // Encode implements json.Marshaler. func (s *PayoutToYooMoneyDestination) Encode(e *jx.Encoder) { e.ObjStart() @@ -29331,117 +29221,6 @@ func (s *PayoutToYooMoneyDestination) UnmarshalJSON(data []byte) error { return s.Decode(d) } -// Encode implements json.Marshaler. -func (s *PayoutToYooMoneyDestinationData) Encode(e *jx.Encoder) { - e.ObjStart() - s.encodeFields(e) - e.ObjEnd() -} - -// encodeFields encodes fields. -func (s *PayoutToYooMoneyDestinationData) encodeFields(e *jx.Encoder) { - { - e.FieldStart("type") - s.Type.Encode(e) - } - { - e.FieldStart("account_number") - e.Str(s.AccountNumber) - } -} - -var jsonFieldsNameOfPayoutToYooMoneyDestinationData = [2]string{ - 0: "type", - 1: "account_number", -} - -// Decode decodes PayoutToYooMoneyDestinationData from json. -func (s *PayoutToYooMoneyDestinationData) Decode(d *jx.Decoder) error { - if s == nil { - return errors.New("invalid: unable to decode PayoutToYooMoneyDestinationData to nil") - } - var requiredBitSet [1]uint8 - - if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { - switch string(k) { - case "type": - requiredBitSet[0] |= 1 << 0 - if err := func() error { - if err := s.Type.Decode(d); err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"type\"") - } - case "account_number": - requiredBitSet[0] |= 1 << 1 - if err := func() error { - v, err := d.Str() - s.AccountNumber = string(v) - if err != nil { - return err - } - return nil - }(); err != nil { - return errors.Wrap(err, "decode field \"account_number\"") - } - default: - return d.Skip() - } - return nil - }); err != nil { - return errors.Wrap(err, "decode PayoutToYooMoneyDestinationData") - } - // Validate required fields. - var failures []validate.FieldError - for i, mask := range [1]uint8{ - 0b00000011, - } { - if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { - // Mask only required fields and check equality to mask using XOR. - // - // If XOR result is not zero, result is not equal to expected, so some fields are missed. - // Bits of fields which would be set are actually bits of missed fields. - missed := bits.OnesCount8(result) - for bitN := 0; bitN < missed; bitN++ { - bitIdx := bits.TrailingZeros8(result) - fieldIdx := i*8 + bitIdx - var name string - if fieldIdx < len(jsonFieldsNameOfPayoutToYooMoneyDestinationData) { - name = jsonFieldsNameOfPayoutToYooMoneyDestinationData[fieldIdx] - } else { - name = strconv.Itoa(fieldIdx) - } - failures = append(failures, validate.FieldError{ - Name: name, - Error: validate.ErrFieldRequired, - }) - // Reset bit. - result &^= 1 << bitIdx - } - } - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - - return nil -} - -// MarshalJSON implements stdjson.Marshaler. -func (s *PayoutToYooMoneyDestinationData) MarshalJSON() ([]byte, error) { - e := jx.Encoder{} - s.Encode(&e) - return e.Bytes(), nil -} - -// UnmarshalJSON implements stdjson.Unmarshaler. -func (s *PayoutToYooMoneyDestinationData) UnmarshalJSON(data []byte) error { - d := jx.DecodeBytes(data) - return s.Decode(d) -} - // Encode encodes PayoutsGetInternalServerError as json. func (s *PayoutsGetInternalServerError) Encode(e *jx.Encoder) { unwrapped := (*TooManyRequests)(s) @@ -37969,6 +37748,134 @@ func (s *SavePaymentMethodType) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s *Sbp) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *Sbp) encodeFields(e *jx.Encoder) { + { + e.FieldStart("type") + s.Type.Encode(e) + } + { + e.FieldStart("phone") + e.Str(s.Phone) + } + { + e.FieldStart("bank_id") + e.Str(s.BankID) + } +} + +var jsonFieldsNameOfSbp = [3]string{ + 0: "type", + 1: "phone", + 2: "bank_id", +} + +// Decode decodes Sbp from json. +func (s *Sbp) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode Sbp to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "type": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.Type.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"type\"") + } + case "phone": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Phone = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"phone\"") + } + case "bank_id": + requiredBitSet[0] |= 1 << 2 + if err := func() error { + v, err := d.Str() + s.BankID = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"bank_id\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode Sbp") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000111, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfSbp) { + name = jsonFieldsNameOfSbp[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *Sbp) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *Sbp) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes SbpBankBic as json. func (s SbpBankBic) Encode(e *jx.Encoder) { unwrapped := string(s) @@ -41949,6 +41856,117 @@ func (s *WebhooksWebhookIDDeleteOK) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s *YooMoney) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *YooMoney) encodeFields(e *jx.Encoder) { + { + e.FieldStart("type") + s.Type.Encode(e) + } + { + e.FieldStart("account_number") + e.Str(s.AccountNumber) + } +} + +var jsonFieldsNameOfYooMoney = [2]string{ + 0: "type", + 1: "account_number", +} + +// Decode decodes YooMoney from json. +func (s *YooMoney) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode YooMoney to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "type": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.Type.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"type\"") + } + case "account_number": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.AccountNumber = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"account_number\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode YooMoney") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfYooMoney) { + name = jsonFieldsNameOfYooMoney[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *YooMoney) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *YooMoney) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode encodes YooMoneyAccountNumber as json. func (s YooMoneyAccountNumber) Encode(e *jx.Encoder) { unwrapped := string(s) diff --git a/internal/service/yookassa/gen/oas_request_encoders_gen.go b/internal/service/yookassa/gen/oas_request_encoders_gen.go index 13ad936..7cea407 100644 --- a/internal/service/yookassa/gen/oas_request_encoders_gen.go +++ b/internal/service/yookassa/gen/oas_request_encoders_gen.go @@ -4,6 +4,7 @@ package gen import ( "bytes" + "log/slog" "net/http" "github.com/go-faster/jx" @@ -76,6 +77,7 @@ func encodePayoutsPostRequest( req.Encode(e) } encoded := e.Bytes() + slog.Info(string(encoded)) ht.SetBody(r, bytes.NewReader(encoded), contentType) return nil } diff --git a/internal/service/yookassa/gen/oas_schemas_gen.go b/internal/service/yookassa/gen/oas_schemas_gen.go index 1fdd7d1..d71a780 100644 --- a/internal/service/yookassa/gen/oas_schemas_gen.go +++ b/internal/service/yookassa/gen/oas_schemas_gen.go @@ -684,6 +684,33 @@ func (s *BadRequestType) UnmarshalText(data []byte) error { } } +// Merged schema. +// Ref: #/components/schemas/bank_card +type BankCard struct { + Type PayoutDestinationDataType `json:"type"` + Card CardDataForPayoutDestination `json:"card"` +} + +// GetType returns the value of Type. +func (s *BankCard) GetType() PayoutDestinationDataType { + return s.Type +} + +// GetCard returns the value of Card. +func (s *BankCard) GetCard() CardDataForPayoutDestination { + return s.Card +} + +// SetType sets the value of Type. +func (s *BankCard) SetType(val PayoutDestinationDataType) { + s.Type = val +} + +// SetCard sets the value of Card. +func (s *BankCard) SetCard(val CardDataForPayoutDestination) { + s.Card = val +} + // Данные банковской карты. // Ref: #/components/schemas/BankCardData type BankCardData struct { @@ -18581,6 +18608,7 @@ type PayoutRequest struct { // только для разных типов данных. PersonalData []PayoutsPersonalData `json:"personal_data"` Metadata OptMetadata `json:"metadata"` + Test OptTest `json:"test"` } // GetAmount returns the value of Amount. @@ -18623,6 +18651,11 @@ func (s *PayoutRequest) GetMetadata() OptMetadata { return s.Metadata } +// GetTest returns the value of Test. +func (s *PayoutRequest) GetTest() OptTest { + return s.Test +} + // SetAmount sets the value of Amount. func (s *PayoutRequest) SetAmount(val PayoutRequestAmount) { s.Amount = val @@ -18663,6 +18696,11 @@ func (s *PayoutRequest) SetMetadata(val OptMetadata) { s.Metadata = val } +// SetTest sets the value of Test. +func (s *PayoutRequest) SetTest(val OptTest) { + s.Test = val +} + // Сумма в выбранной валюте. type PayoutRequestAmount struct { // Сумма в выбранной валюте. Всегда дробное значение. @@ -18695,10 +18733,10 @@ func (s *PayoutRequestAmount) SetCurrency(val CurrencyCode) { // PayoutRequestPayoutDestinationData represents sum type. type PayoutRequestPayoutDestinationData struct { - Type PayoutRequestPayoutDestinationDataType // switch on this field - PayoutToYooMoneyDestinationData PayoutToYooMoneyDestinationData - PayoutToBankCardDestinationData PayoutToBankCardDestinationData - PayoutToSbpDestinationData PayoutToSbpDestinationData + Type PayoutRequestPayoutDestinationDataType // switch on this field + YooMoney YooMoney + BankCard BankCard + Sbp Sbp } // PayoutRequestPayoutDestinationDataType is oneOf type of PayoutRequestPayoutDestinationData. @@ -18706,86 +18744,86 @@ type PayoutRequestPayoutDestinationDataType string // Possible values for PayoutRequestPayoutDestinationDataType. const ( - PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "PayoutToYooMoneyDestinationData" - PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "PayoutToBankCardDestinationData" - PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "PayoutToSbpDestinationData" + YooMoneyPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "yoo_money" + BankCardPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "bank_card" + SbpPayoutRequestPayoutDestinationData PayoutRequestPayoutDestinationDataType = "sbp" ) -// IsPayoutToYooMoneyDestinationData reports whether PayoutRequestPayoutDestinationData is PayoutToYooMoneyDestinationData. -func (s PayoutRequestPayoutDestinationData) IsPayoutToYooMoneyDestinationData() bool { - return s.Type == PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData +// IsYooMoney reports whether PayoutRequestPayoutDestinationData is YooMoney. +func (s PayoutRequestPayoutDestinationData) IsYooMoney() bool { + return s.Type == YooMoneyPayoutRequestPayoutDestinationData } -// IsPayoutToBankCardDestinationData reports whether PayoutRequestPayoutDestinationData is PayoutToBankCardDestinationData. -func (s PayoutRequestPayoutDestinationData) IsPayoutToBankCardDestinationData() bool { - return s.Type == PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData +// IsBankCard reports whether PayoutRequestPayoutDestinationData is BankCard. +func (s PayoutRequestPayoutDestinationData) IsBankCard() bool { + return s.Type == BankCardPayoutRequestPayoutDestinationData } -// IsPayoutToSbpDestinationData reports whether PayoutRequestPayoutDestinationData is PayoutToSbpDestinationData. -func (s PayoutRequestPayoutDestinationData) IsPayoutToSbpDestinationData() bool { - return s.Type == PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData +// IsSbp reports whether PayoutRequestPayoutDestinationData is Sbp. +func (s PayoutRequestPayoutDestinationData) IsSbp() bool { + return s.Type == SbpPayoutRequestPayoutDestinationData } -// SetPayoutToYooMoneyDestinationData sets PayoutRequestPayoutDestinationData to PayoutToYooMoneyDestinationData. -func (s *PayoutRequestPayoutDestinationData) SetPayoutToYooMoneyDestinationData(v PayoutToYooMoneyDestinationData) { - s.Type = PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData - s.PayoutToYooMoneyDestinationData = v +// SetYooMoney sets PayoutRequestPayoutDestinationData to YooMoney. +func (s *PayoutRequestPayoutDestinationData) SetYooMoney(v YooMoney) { + s.Type = YooMoneyPayoutRequestPayoutDestinationData + s.YooMoney = v } -// GetPayoutToYooMoneyDestinationData returns PayoutToYooMoneyDestinationData and true boolean if PayoutRequestPayoutDestinationData is PayoutToYooMoneyDestinationData. -func (s PayoutRequestPayoutDestinationData) GetPayoutToYooMoneyDestinationData() (v PayoutToYooMoneyDestinationData, ok bool) { - if !s.IsPayoutToYooMoneyDestinationData() { +// GetYooMoney returns YooMoney and true boolean if PayoutRequestPayoutDestinationData is YooMoney. +func (s PayoutRequestPayoutDestinationData) GetYooMoney() (v YooMoney, ok bool) { + if !s.IsYooMoney() { return v, false } - return s.PayoutToYooMoneyDestinationData, true + return s.YooMoney, true } -// NewPayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from PayoutToYooMoneyDestinationData. -func NewPayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData(v PayoutToYooMoneyDestinationData) PayoutRequestPayoutDestinationData { +// NewYooMoneyPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from YooMoney. +func NewYooMoneyPayoutRequestPayoutDestinationData(v YooMoney) PayoutRequestPayoutDestinationData { var s PayoutRequestPayoutDestinationData - s.SetPayoutToYooMoneyDestinationData(v) + s.SetYooMoney(v) return s } -// SetPayoutToBankCardDestinationData sets PayoutRequestPayoutDestinationData to PayoutToBankCardDestinationData. -func (s *PayoutRequestPayoutDestinationData) SetPayoutToBankCardDestinationData(v PayoutToBankCardDestinationData) { - s.Type = PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData - s.PayoutToBankCardDestinationData = v +// SetBankCard sets PayoutRequestPayoutDestinationData to BankCard. +func (s *PayoutRequestPayoutDestinationData) SetBankCard(v BankCard) { + s.Type = BankCardPayoutRequestPayoutDestinationData + s.BankCard = v } -// GetPayoutToBankCardDestinationData returns PayoutToBankCardDestinationData and true boolean if PayoutRequestPayoutDestinationData is PayoutToBankCardDestinationData. -func (s PayoutRequestPayoutDestinationData) GetPayoutToBankCardDestinationData() (v PayoutToBankCardDestinationData, ok bool) { - if !s.IsPayoutToBankCardDestinationData() { +// GetBankCard returns BankCard and true boolean if PayoutRequestPayoutDestinationData is BankCard. +func (s PayoutRequestPayoutDestinationData) GetBankCard() (v BankCard, ok bool) { + if !s.IsBankCard() { return v, false } - return s.PayoutToBankCardDestinationData, true + return s.BankCard, true } -// NewPayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from PayoutToBankCardDestinationData. -func NewPayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData(v PayoutToBankCardDestinationData) PayoutRequestPayoutDestinationData { +// NewBankCardPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from BankCard. +func NewBankCardPayoutRequestPayoutDestinationData(v BankCard) PayoutRequestPayoutDestinationData { var s PayoutRequestPayoutDestinationData - s.SetPayoutToBankCardDestinationData(v) + s.SetBankCard(v) return s } -// SetPayoutToSbpDestinationData sets PayoutRequestPayoutDestinationData to PayoutToSbpDestinationData. -func (s *PayoutRequestPayoutDestinationData) SetPayoutToSbpDestinationData(v PayoutToSbpDestinationData) { - s.Type = PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData - s.PayoutToSbpDestinationData = v +// SetSbp sets PayoutRequestPayoutDestinationData to Sbp. +func (s *PayoutRequestPayoutDestinationData) SetSbp(v Sbp) { + s.Type = SbpPayoutRequestPayoutDestinationData + s.Sbp = v } -// GetPayoutToSbpDestinationData returns PayoutToSbpDestinationData and true boolean if PayoutRequestPayoutDestinationData is PayoutToSbpDestinationData. -func (s PayoutRequestPayoutDestinationData) GetPayoutToSbpDestinationData() (v PayoutToSbpDestinationData, ok bool) { - if !s.IsPayoutToSbpDestinationData() { +// GetSbp returns Sbp and true boolean if PayoutRequestPayoutDestinationData is Sbp. +func (s PayoutRequestPayoutDestinationData) GetSbp() (v Sbp, ok bool) { + if !s.IsSbp() { return v, false } - return s.PayoutToSbpDestinationData, true + return s.Sbp, true } -// NewPayoutToSbpDestinationDataPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from PayoutToSbpDestinationData. -func NewPayoutToSbpDestinationDataPayoutRequestPayoutDestinationData(v PayoutToSbpDestinationData) PayoutRequestPayoutDestinationData { +// NewSbpPayoutRequestPayoutDestinationData returns new PayoutRequestPayoutDestinationData from Sbp. +func NewSbpPayoutRequestPayoutDestinationData(v Sbp) PayoutRequestPayoutDestinationData { var s PayoutRequestPayoutDestinationData - s.SetPayoutToSbpDestinationData(v) + s.SetSbp(v) return s } @@ -18935,33 +18973,6 @@ func (s *PayoutStatus) UnmarshalText(data []byte) error { } } -// Merged schema. -// Ref: #/components/schemas/PayoutToBankCardDestinationData -type PayoutToBankCardDestinationData struct { - Type PayoutDestinationDataType `json:"type"` - Card CardDataForPayoutDestination `json:"card"` -} - -// GetType returns the value of Type. -func (s *PayoutToBankCardDestinationData) GetType() PayoutDestinationDataType { - return s.Type -} - -// GetCard returns the value of Card. -func (s *PayoutToBankCardDestinationData) GetCard() CardDataForPayoutDestination { - return s.Card -} - -// SetType sets the value of Type. -func (s *PayoutToBankCardDestinationData) SetType(val PayoutDestinationDataType) { - s.Type = val -} - -// SetCard sets the value of Card. -func (s *PayoutToBankCardDestinationData) SetCard(val CardDataForPayoutDestination) { - s.Card = val -} - // Merged schema. // Ref: #/components/schemas/PayoutToCardDestination type PayoutToCardDestination struct { @@ -19044,44 +19055,6 @@ func (s *PayoutToSbpDestination) SetRecipientChecked(val bool) { s.RecipientChecked = val } -// Merged schema. -// Ref: #/components/schemas/PayoutToSbpDestinationData -type PayoutToSbpDestinationData struct { - Type PayoutDestinationDataType `json:"type"` - Phone string `json:"phone"` - BankID string `json:"bank_id"` -} - -// GetType returns the value of Type. -func (s *PayoutToSbpDestinationData) GetType() PayoutDestinationDataType { - return s.Type -} - -// GetPhone returns the value of Phone. -func (s *PayoutToSbpDestinationData) GetPhone() string { - return s.Phone -} - -// GetBankID returns the value of BankID. -func (s *PayoutToSbpDestinationData) GetBankID() string { - return s.BankID -} - -// SetType sets the value of Type. -func (s *PayoutToSbpDestinationData) SetType(val PayoutDestinationDataType) { - s.Type = val -} - -// SetPhone sets the value of Phone. -func (s *PayoutToSbpDestinationData) SetPhone(val string) { - s.Phone = val -} - -// SetBankID sets the value of BankID. -func (s *PayoutToSbpDestinationData) SetBankID(val string) { - s.BankID = val -} - // Merged schema. // Ref: #/components/schemas/PayoutToYooMoneyDestination type PayoutToYooMoneyDestination struct { @@ -19109,33 +19082,6 @@ func (s *PayoutToYooMoneyDestination) SetAccountNumber(val YooMoneyAccountNumber s.AccountNumber = val } -// Merged schema. -// Ref: #/components/schemas/PayoutToYooMoneyDestinationData -type PayoutToYooMoneyDestinationData struct { - Type PayoutDestinationDataType `json:"type"` - AccountNumber string `json:"account_number"` -} - -// GetType returns the value of Type. -func (s *PayoutToYooMoneyDestinationData) GetType() PayoutDestinationDataType { - return s.Type -} - -// GetAccountNumber returns the value of AccountNumber. -func (s *PayoutToYooMoneyDestinationData) GetAccountNumber() string { - return s.AccountNumber -} - -// SetType sets the value of Type. -func (s *PayoutToYooMoneyDestinationData) SetType(val PayoutDestinationDataType) { - s.Type = val -} - -// SetAccountNumber sets the value of AccountNumber. -func (s *PayoutToYooMoneyDestinationData) SetAccountNumber(val string) { - s.AccountNumber = val -} - type PayoutsGetInternalServerError TooManyRequests func (*PayoutsGetInternalServerError) payoutsGetRes() {} @@ -23333,6 +23279,44 @@ func (s *SavePaymentMethodType) UnmarshalText(data []byte) error { } } +// Merged schema. +// Ref: #/components/schemas/sbp +type Sbp struct { + Type PayoutDestinationDataType `json:"type"` + Phone string `json:"phone"` + BankID string `json:"bank_id"` +} + +// GetType returns the value of Type. +func (s *Sbp) GetType() PayoutDestinationDataType { + return s.Type +} + +// GetPhone returns the value of Phone. +func (s *Sbp) GetPhone() string { + return s.Phone +} + +// GetBankID returns the value of BankID. +func (s *Sbp) GetBankID() string { + return s.BankID +} + +// SetType sets the value of Type. +func (s *Sbp) SetType(val PayoutDestinationDataType) { + s.Type = val +} + +// SetPhone sets the value of Phone. +func (s *Sbp) SetPhone(val string) { + s.Phone = val +} + +// SetBankID sets the value of BankID. +func (s *Sbp) SetBankID(val string) { + s.BankID = val +} + type SbpBankBic string type SbpBankId string @@ -24787,4 +24771,31 @@ type WebhooksWebhookIDDeleteOK struct{} func (*WebhooksWebhookIDDeleteOK) webhooksWebhookIDDeleteRes() {} +// Merged schema. +// Ref: #/components/schemas/yoo_money +type YooMoney struct { + Type PayoutDestinationDataType `json:"type"` + AccountNumber string `json:"account_number"` +} + +// GetType returns the value of Type. +func (s *YooMoney) GetType() PayoutDestinationDataType { + return s.Type +} + +// GetAccountNumber returns the value of AccountNumber. +func (s *YooMoney) GetAccountNumber() string { + return s.AccountNumber +} + +// SetType sets the value of Type. +func (s *YooMoney) SetType(val PayoutDestinationDataType) { + s.Type = val +} + +// SetAccountNumber sets the value of AccountNumber. +func (s *YooMoney) SetAccountNumber(val string) { + s.AccountNumber = val +} + type YooMoneyAccountNumber string diff --git a/internal/service/yookassa/gen/oas_validators_gen.go b/internal/service/yookassa/gen/oas_validators_gen.go index 35da1a1..0417a8c 100644 --- a/internal/service/yookassa/gen/oas_validators_gen.go +++ b/internal/service/yookassa/gen/oas_validators_gen.go @@ -625,6 +625,40 @@ func (s BadRequestType) Validate() error { } } +func (s *BankCard) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if err := s.Type.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "type", + Error: err, + }) + } + if err := func() error { + if err := s.Card.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "card", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + func (s *BankCardData) Validate() error { if s == nil { return validate.ErrNilPointer @@ -8892,18 +8926,18 @@ func (s *PayoutRequestAmount) Validate() error { func (s PayoutRequestPayoutDestinationData) Validate() error { switch s.Type { - case PayoutToYooMoneyDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToYooMoneyDestinationData.Validate(); err != nil { + case YooMoneyPayoutRequestPayoutDestinationData: + if err := s.YooMoney.Validate(); err != nil { return err } return nil - case PayoutToBankCardDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToBankCardDestinationData.Validate(); err != nil { + case BankCardPayoutRequestPayoutDestinationData: + if err := s.BankCard.Validate(); err != nil { return err } return nil - case PayoutToSbpDestinationDataPayoutRequestPayoutDestinationData: - if err := s.PayoutToSbpDestinationData.Validate(); err != nil { + case SbpPayoutRequestPayoutDestinationData: + if err := s.Sbp.Validate(); err != nil { return err } return nil @@ -9029,40 +9063,6 @@ func (s PayoutStatus) Validate() error { } } -func (s *PayoutToBankCardDestinationData) Validate() error { - if s == nil { - return validate.ErrNilPointer - } - - var failures []validate.FieldError - if err := func() error { - if err := s.Type.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "type", - Error: err, - }) - } - if err := func() error { - if err := s.Card.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "card", - Error: err, - }) - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - return nil -} - func (s *PayoutToCardDestination) Validate() error { if s == nil { return validate.ErrNilPointer @@ -9173,75 +9173,6 @@ func (s *PayoutToSbpDestination) Validate() error { return nil } -func (s *PayoutToSbpDestinationData) Validate() error { - if s == nil { - return validate.ErrNilPointer - } - - var failures []validate.FieldError - if err := func() error { - if err := s.Type.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "type", - Error: err, - }) - } - if err := func() error { - if err := (validate.String{ - MinLength: 0, - MinLengthSet: false, - MaxLength: 0, - MaxLengthSet: false, - Email: false, - Hostname: false, - Regex: regexMap["[0-9]{4,15}"], - MinNumeric: 0, - MinNumericSet: false, - MaxNumeric: 0, - MaxNumericSet: false, - }).Validate(string(s.Phone)); err != nil { - return errors.Wrap(err, "string") - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "phone", - Error: err, - }) - } - if err := func() error { - if err := (validate.String{ - MinLength: 0, - MinLengthSet: false, - MaxLength: 12, - MaxLengthSet: true, - Email: false, - Hostname: false, - Regex: regexMap["[a-zA-Z0-9]{12}"], - MinNumeric: 0, - MinNumericSet: false, - MaxNumeric: 0, - MaxNumericSet: false, - }).Validate(string(s.BankID)); err != nil { - return errors.Wrap(err, "string") - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "bank_id", - Error: err, - }) - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - return nil -} - func (s *PayoutToYooMoneyDestination) Validate() error { if s == nil { return validate.ErrNilPointer @@ -9276,52 +9207,6 @@ func (s *PayoutToYooMoneyDestination) Validate() error { return nil } -func (s *PayoutToYooMoneyDestinationData) Validate() error { - if s == nil { - return validate.ErrNilPointer - } - - var failures []validate.FieldError - if err := func() error { - if err := s.Type.Validate(); err != nil { - return err - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "type", - Error: err, - }) - } - if err := func() error { - if err := (validate.String{ - MinLength: 11, - MinLengthSet: true, - MaxLength: 33, - MaxLengthSet: true, - Email: false, - Hostname: false, - Regex: nil, - MinNumeric: 0, - MinNumericSet: false, - MaxNumeric: 0, - MaxNumericSet: false, - }).Validate(string(s.AccountNumber)); err != nil { - return errors.Wrap(err, "string") - } - return nil - }(); err != nil { - failures = append(failures, validate.FieldError{ - Name: "account_number", - Error: err, - }) - } - if len(failures) > 0 { - return &validate.Error{Fields: failures} - } - return nil -} - func (s *PayoutsGetInternalServerError) Validate() error { alias := (*TooManyRequests)(s) if err := alias.Validate(); err != nil { @@ -12616,6 +12501,75 @@ func (s SavePaymentMethodType) Validate() error { } } +func (s *Sbp) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if err := s.Type.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "type", + Error: err, + }) + } + if err := func() error { + if err := (validate.String{ + MinLength: 0, + MinLengthSet: false, + MaxLength: 0, + MaxLengthSet: false, + Email: false, + Hostname: false, + Regex: regexMap["[0-9]{4,15}"], + MinNumeric: 0, + MinNumericSet: false, + MaxNumeric: 0, + MaxNumericSet: false, + }).Validate(string(s.Phone)); err != nil { + return errors.Wrap(err, "string") + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "phone", + Error: err, + }) + } + if err := func() error { + if err := (validate.String{ + MinLength: 0, + MinLengthSet: false, + MaxLength: 12, + MaxLengthSet: true, + Email: false, + Hostname: false, + Regex: regexMap["[a-zA-Z0-9]{12}"], + MinNumeric: 0, + MinNumericSet: false, + MaxNumeric: 0, + MaxNumericSet: false, + }).Validate(string(s.BankID)); err != nil { + return errors.Wrap(err, "string") + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "bank_id", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + func (s SbpBankBic) Validate() error { alias := (string)(s) if err := (validate.String{ @@ -13831,6 +13785,52 @@ func (s WebhooksPostReqEvent) Validate() error { } } +func (s *YooMoney) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if err := s.Type.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "type", + Error: err, + }) + } + if err := func() error { + if err := (validate.String{ + MinLength: 11, + MinLengthSet: true, + MaxLength: 33, + MaxLengthSet: true, + Email: false, + Hostname: false, + Regex: nil, + MinNumeric: 0, + MinNumericSet: false, + MaxNumeric: 0, + MaxNumericSet: false, + }).Validate(string(s.AccountNumber)); err != nil { + return errors.Wrap(err, "string") + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "account_number", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + func (s YooMoneyAccountNumber) Validate() error { alias := (string)(s) if err := (validate.String{ diff --git a/internal/service/yookassa/module.go b/internal/service/yookassa/module.go index 2e98261..1b4713c 100644 --- a/internal/service/yookassa/module.go +++ b/internal/service/yookassa/module.go @@ -27,7 +27,7 @@ func WithContext(ctx context.Context) Optional { } type Service interface { - CreatePayout(models.PayoutReq, *orm.User, ...Optional) error + CreatePayout(models.PayoutReq, *orm.User, string, ...Optional) (models.PayoutResp, error) GetConfig() yookassaConf.YooKassa } diff --git a/internal/service/yookassa/ogen.yml b/internal/service/yookassa/ogen.yml index 8727e17..f4f3aaf 100644 --- a/internal/service/yookassa/ogen.yml +++ b/internal/service/yookassa/ogen.yml @@ -1,2 +1,11 @@ generator: - ignore_not_implemented: ["all"] \ No newline at end of file + ignore_not_implemented: ["all"] + # features: + # enable: + # # Enables paths client generation + # - "paths/client" + # # Enables validation of client requests + # - "client/request/validation" + # # Enables validation of server responses + # - "server/response/validation" + # disable_all: true \ No newline at end of file diff --git a/internal/service/yookassa/yookassa-openapi-specification.yaml b/internal/service/yookassa/yookassa-openapi-specification.yaml index e850627..37b47d8 100644 --- a/internal/service/yookassa/yookassa-openapi-specification.yaml +++ b/internal/service/yookassa/yookassa-openapi-specification.yaml @@ -5040,7 +5040,6 @@ components: discriminator: propertyName: "type" mapping: - bank_card: "#/components/schemas/PayoutToCardDestination" yoo_money: "#/components/schemas/PayoutToYooMoneyDestination" sbp: "#/components/schemas/PayoutToSbpDestination" properties: diff --git a/internal/service/yookassa/yookassa_service.go b/internal/service/yookassa/yookassa_service.go index 5c7ac06..d0e6f6e 100644 --- a/internal/service/yookassa/yookassa_service.go +++ b/internal/service/yookassa/yookassa_service.go @@ -2,6 +2,9 @@ package yookassa import ( "context" + "errors" + "fmt" + "log/slog" "net/http" "payouts/internal/models" "payouts/internal/service/database/orm" @@ -31,8 +34,6 @@ func NewYookassaService(conf config.YooKassa) (Service, error) { return nil, err } svc.payClient = payClient - - // payClient.PaymentsPost() return svc, nil } @@ -50,7 +51,7 @@ func (y *yookassaService) getParams(options ...Optional) *params { func (y *yookassaService) BasicAuth(ctx context.Context, operationName gen.OperationName) (gen.BasicAuth, error) { return gen.BasicAuth{ Username: y.conf.ApiPaymentKey, - Password: y.conf.ApiBaseSecret, + Password: y.conf.ApiPaymentSecret, }, nil } @@ -60,12 +61,51 @@ func (y *yookassaService) OAuth2(ctx context.Context, operationName gen.Operatio } // CreatePayout implements [Service]. -func (y *yookassaService) CreatePayout(req models.PayoutReq, userSession *orm.User, opts ...Optional) error { +func (y *yookassaService) CreatePayout(req models.PayoutReq, userSession *orm.User, idempotenceKey string, opts ...Optional) (models.PayoutResp, error) { params := y.getParams(opts...) - y.payClient.PayoutsPost(params.ctx, &gen.PayoutRequest{}, gen.PayoutsPostParams{}) + var payoutDestination gen.PayoutRequestPayoutDestinationData - return nil + switch req.PayoutType { + case models.TypeSBP: + payoutDestination = gen.NewSbpPayoutRequestPayoutDestinationData(gen.Sbp{ + Type: gen.PayoutDestinationDataTypeSbp, + Phone: userSession.Phone, + }) + case models.TypeYooMoney: + payoutDestination = gen.NewYooMoneyPayoutRequestPayoutDestinationData(gen.YooMoney{ + Type: gen.PayoutDestinationDataTypeYooMoney, + AccountNumber: req.AccountNumber, + }) + default: + return models.PayoutResp{Result: "failed", ErrorReason: "unsupported payout type"}, errors.New("unsupported payout type") + } + + payoutOpt := gen.NewOptPayoutRequestPayoutDestinationData(payoutDestination) + + postResp, err := y.payClient.PayoutsPost(params.ctx, &gen.PayoutRequest{ + Amount: gen.PayoutRequestAmount{ + Value: fmt.Sprintf("%.2f", req.Amount), + Currency: gen.CurrencyCodeRUB, + }, + PayoutDestinationData: payoutOpt, + Test: gen.NewOptTest(gen.Test(y.conf.Test)), + }, gen.PayoutsPostParams{ + IdempotenceKey: idempotenceKey, + }) + + slog.Debug(fmt.Sprintf("Received from yookassa: error %v; response %v", err, postResp)) + if err == nil { + switch payoutResp := postResp.(type) { + case *gen.Payout: + slog.Info(fmt.Sprintf("Succeeded payout. It's id = %s", payoutResp.ID)) + return models.PayoutResp{Result: string(payoutResp.Status), PayoutID: string(payoutResp.ID)}, nil + default: + return models.PayoutResp{Result: "failed", ErrorReason: fmt.Sprintf("%T", postResp)}, nil + } + } + + return models.PayoutResp{Result: "failed", ErrorReason: errors.Join(errors.New("failed to call yookassa api"), err).Error()}, err } // GetConfig implements [Service].