diff --git a/README.md b/README.md index 13ccfee..9945f7f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# customer +# treasurer -Сервис customer +Сервис платёжки -| Branch | Pipeline | Code coverage | -| ------------- |:-----------------:| --------------:| -| master | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/master/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/master/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | -| staging | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/staging/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/staging/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | -| dev | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/dev/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/dev/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | +| Branch | Pipeline | Code coverage | +| ------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| master | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/master/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/master/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | +| staging | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/staging/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/staging/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | +| dev | [![pipeline status](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/dev/pipeline.svg)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | [![coverage report](https://penahub.gitlab.yandexcloud.net/external/treasurer/badges/dev/coverage.svg?job=test)](https://penahub.gitlab.yandexcloud.net/external/treasurer/-/pipelines) | ## Переменные окружения приложения diff --git a/cmd/mock/main.go b/cmd/mock/main.go index 6223369..bce7e1f 100644 --- a/cmd/mock/main.go +++ b/cmd/mock/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "log" "github.com/walkerus/go-wiremock" @@ -8,33 +9,37 @@ import ( ) func main() { - mocklient := wiremock.NewClient("http://localhost:8000") + mockClient := wiremock.NewClient("http://localhost:8080") - if err := mocklient.StubFor(wiremock. + if err := mockClient.StubFor(wiremock. Post(wiremock.URLPathEqualTo("/payments")). - WithBodyPattern(wiremock.EqualToJson(`{ - "amount": { - "value": "150.00", - "currency": "RUB" - }, - "payment_method_data": { - "type": "bank_card" - }, - "confirmation": { - "type": "redirect", - "enforce": true, - "locale": "ru_RU", - "return_url": "https://www.example.com/return_url" - }, - }`)). WillReturnJSON( - &yandex.Payment{}, + &yandex.Payment{ + ID: "f32d71f4-214b-464b-94bb-f7b4d5299af0", + Status: yandex.PaymentStatusPending, + Amount: yandex.Amount{ + Value: "150.00", + Currency: "RUB", + }, + Confirmation: &yandex.ConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + ConfirmationURL: "https://yandex-redirect-url.com", + }, + }, map[string]string{"Content-Type": "application/json"}, 200, ). AtPriority(1), ); err != nil { - log.Fatal("failed to stub : %w", err) + log.Fatal("failed to stub bankcard payment: %w", err) } + request := wiremock.NewRequest("post", wiremock.URLPathEqualTo("/payments")) + + isVerified, err := mockClient.Verify(request, 0) + if err != nil { + log.Fatal("failed to verify /payments request: %w", err) + } + + fmt.Println(isVerified) } diff --git a/deployments/dev/docker-compose.yaml b/deployments/dev/docker-compose.yaml index c9a4a3d..1be1425 100644 --- a/deployments/dev/docker-compose.yaml +++ b/deployments/dev/docker-compose.yaml @@ -2,69 +2,44 @@ version: "3.3" services: treasurer-app: + container_name: treasurer-service + tty: true build: context: ../../. dockerfile: Dockerfile target: test environment: - HTTP_HOST=0.0.0.0 - - HTTP_PORT=8080 + - HTTP_PORT=8085 - HTTP_DOMEN=http://localhost:8080 - GRPC_HOST=0.0.0.0 - - GRPC_PORT=8081 + - GRPC_PORT=9085 - - YOOMONEY_STORE_ID = id - - YOOMONEY_SECRET_KEY = secret + - YOOMONEY_STORE_ID=id + - YOOMONEY_SECRET_KEY=secret - MONGO_HOST=mongo - MONGO_PORT=27017 - - MONGO_USER=admin - - MONGO_PASSWORD=admin + - MONGO_USER=test + - MONGO_PASSWORD=test - MONGO_DB_NAME=admin - MONGO_AUTH=admin ports: - - 8080:8080 - - 8081:8081 + - 8085:8085 + - 9085:9085 + networks: + - dev depends_on: - - migration - networks: - - dev + - treasurer-mock - migration: - build: - context: ../../. - dockerfile: Dockerfile - target: migration - command: - [ - "sh", - "-c", - 'migrate -source file://migrations -database "mongodb://admin:admin@localhost:27017/admin?authSource=admin" up', - ] - depends_on: - - mongo - networks: - - dev - - mongo: - image: 'mongo:6.0.3' - environment: - MONGO_INITDB_ROOT_USERNAME: admin - MONGO_INITDB_ROOT_PASSWORD: admin - ports: - - '27017:27017' - networks: - - dev - - mock: + treasurer-mock: + container_name: treasurermock-container image: 'wiremock/wiremock:2.35.0' ports: - - 8000:8080 + - 8080:8080 networks: - dev - depends_on: - - app networks: dev: diff --git a/internal/app/app.go b/internal/app/app.go index d855917..605b256 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -7,9 +7,9 @@ import ( "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/initialize" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/swagger" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/server" - "penahub.gitlab.yandexcloud.net/external/treasurer/internal/swagger" "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/closer" "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" ) diff --git a/internal/errors/http.go b/internal/errors/http.go index 43b06ca..a1461e4 100644 --- a/internal/errors/http.go +++ b/internal/errors/http.go @@ -7,6 +7,8 @@ import ( "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" ) +const defaultHTTPCode = http.StatusInternalServerError + var httpStatuses = map[ErrorType]int{ ErrInternalError: http.StatusInternalServerError, ErrInvalidArgs: http.StatusBadRequest, @@ -21,7 +23,7 @@ func HTTP(ctx echo.Context, err Error) error { status, ok := httpStatuses[err.Type()] if !ok { return ctx.JSON(http.StatusInternalServerError, models.ResponseErrorHTTP{ - StatusCode: http.StatusInternalServerError, + StatusCode: defaultHTTPCode, Message: err.Error(), }) } diff --git a/internal/initialize/api.go b/internal/initialize/api.go index 8fabb18..26a1a69 100644 --- a/internal/initialize/api.go +++ b/internal/initialize/api.go @@ -2,7 +2,7 @@ package initialize import ( "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" - "penahub.gitlab.yandexcloud.net/external/treasurer/internal/swagger" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/swagger" ) func NewAPI(controllers *Controllers) (*swagger.API, errors.Error) { diff --git a/internal/interface/client/yandex.go b/internal/interface/client/yandex.go index 24ae729..d6adc7f 100644 --- a/internal/interface/client/yandex.go +++ b/internal/interface/client/yandex.go @@ -14,10 +14,10 @@ import ( ) const ( - yandexPaymentsMockURL = "http://mock:8080/payments" + yandexPaymentsMockURL = "http://treasurer-mock:8080/payments" yandexPaymentsProdURL = "https://api.yookassa.ru/v3/payments" - yandexWebhooksMockURL = "http://mock:8080/webhooks" + yandexWebhooksMockURL = "http://treasurer-mock:8080/webhooks" yandexWebhooksProdURL = "https://api.yookassa.ru/v3/webhooks" ) diff --git a/internal/interface/controller/rest/yandex.go b/internal/interface/controller/rest/yandex.go index bf3b0e1..bb843d6 100644 --- a/internal/interface/controller/rest/yandex.go +++ b/internal/interface/controller/rest/yandex.go @@ -10,7 +10,7 @@ import ( "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" - "penahub.gitlab.yandexcloud.net/external/treasurer/internal/utils/echotools" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/echotools" ) type StatusService interface { @@ -51,8 +51,9 @@ func NewYandexStatusController(deps YandexStatusControllerDeps) (*YandexStatusCo } return &YandexStatusController{ - logger: deps.Logger, - statusService: deps.StatusService, + logger: deps.Logger, + statusService: deps.StatusService, + callbackService: deps.CallbackService, }, nil } diff --git a/internal/interface/swagger/api.gen.go b/internal/interface/swagger/api.gen.go new file mode 100644 index 0000000..2dca525 --- /dev/null +++ b/internal/interface/swagger/api.gen.go @@ -0,0 +1,224 @@ +// Package swagger provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. +package swagger + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // (GET /available) + GetAvailablePayments(ctx echo.Context) error + + // (POST /yandex/payment/status/canceled) + SetYandexPaymentStatusCanceled(ctx echo.Context) error + + // (POST /yandex/payment/status/succeeded) + SetYandexPaymentStatusSucceeded(ctx echo.Context) error + + // (POST /yandex/payment/status/waiting) + SetYandexPaymentStatusWaiting(ctx echo.Context) error + + // (POST /yandex/refund/status/succeeded) + SetYandexRefundStatusSucceeded(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// GetAvailablePayments converts echo context to params. +func (w *ServerInterfaceWrapper) GetAvailablePayments(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetAvailablePayments(ctx) + return err +} + +// SetYandexPaymentStatusCanceled converts echo context to params. +func (w *ServerInterfaceWrapper) SetYandexPaymentStatusCanceled(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SetYandexPaymentStatusCanceled(ctx) + return err +} + +// SetYandexPaymentStatusSucceeded converts echo context to params. +func (w *ServerInterfaceWrapper) SetYandexPaymentStatusSucceeded(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SetYandexPaymentStatusSucceeded(ctx) + return err +} + +// SetYandexPaymentStatusWaiting converts echo context to params. +func (w *ServerInterfaceWrapper) SetYandexPaymentStatusWaiting(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SetYandexPaymentStatusWaiting(ctx) + return err +} + +// SetYandexRefundStatusSucceeded converts echo context to params. +func (w *ServerInterfaceWrapper) SetYandexRefundStatusSucceeded(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SetYandexRefundStatusSucceeded(ctx) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/available", wrapper.GetAvailablePayments) + router.POST(baseURL+"/yandex/payment/status/canceled", wrapper.SetYandexPaymentStatusCanceled) + router.POST(baseURL+"/yandex/payment/status/succeeded", wrapper.SetYandexPaymentStatusSucceeded) + router.POST(baseURL+"/yandex/payment/status/waiting", wrapper.SetYandexPaymentStatusWaiting) + router.POST(baseURL+"/yandex/refund/status/succeeded", wrapper.SetYandexRefundStatusSucceeded) + +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "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", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/interface/swagger/api.go b/internal/interface/swagger/api.go new file mode 100644 index 0000000..060e9fb --- /dev/null +++ b/internal/interface/swagger/api.go @@ -0,0 +1,77 @@ +package swagger + +import ( + "github.com/labstack/echo/v4" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" +) + +//go:generate oapi-codegen --config api.yaml ../../openapi.yaml +//go:generate oapi-codegen --config models.yaml ../../openapi.yaml + +type commonController interface { + // (GET /available) + GetAvailablePayments(ctx echo.Context) error +} + +type yandexStatusController interface { + // (POST /yandex/payment/status/canceled) + SetPaymentStatusCanceled(ctx echo.Context) error + + // (POST /yandex/payment/status/succeeded) + SetPaymentStatusSucceeded(ctx echo.Context) error + + // (POST /yandex/payment/status/waiting) + SetPaymentStatusWaiting(ctx echo.Context) error + + // (POST /yandex/refund/status/succeeded) + SetRefundStatusSucceeded(ctx echo.Context) error +} + +type Deps struct { + CommonController commonController + YandexStatusController yandexStatusController +} + +type API struct { + commonController commonController + yandexStatusController yandexStatusController +} + +func New(deps Deps) (*API, errors.Error) { + if deps.CommonController == nil { + return nil, errors.NewWithMessage("CommonController in nil on ", errors.ErrInvalidArgs) + } + + if deps.YandexStatusController == nil { + return nil, errors.NewWithMessage("YandexStatusController in nil on ", errors.ErrInvalidArgs) + } + + return &API{ + commonController: deps.CommonController, + yandexStatusController: deps.YandexStatusController, + }, nil +} + +// Common + +func (receiver *API) GetAvailablePayments(ctx echo.Context) error { + return receiver.commonController.GetAvailablePayments(ctx) +} + +// Status (Yandex) + +func (receiver *API) SetYandexPaymentStatusCanceled(ctx echo.Context) error { + return receiver.yandexStatusController.SetPaymentStatusCanceled(ctx) +} + +func (receiver *API) SetYandexPaymentStatusSucceeded(ctx echo.Context) error { + return receiver.yandexStatusController.SetPaymentStatusSucceeded(ctx) +} + +func (receiver *API) SetYandexPaymentStatusWaiting(ctx echo.Context) error { + return receiver.yandexStatusController.SetPaymentStatusWaiting(ctx) +} + +func (receiver *API) SetYandexRefundStatusSucceeded(ctx echo.Context) error { + return receiver.yandexStatusController.SetRefundStatusSucceeded(ctx) +} diff --git a/internal/interface/swagger/api.yaml b/internal/interface/swagger/api.yaml new file mode 100644 index 0000000..a2634dd --- /dev/null +++ b/internal/interface/swagger/api.yaml @@ -0,0 +1,6 @@ +output: api.gen.go +package: swagger +generate: + echo-server: true + models: false + embedded-spec: true diff --git a/internal/interface/swagger/models.gen.go b/internal/interface/swagger/models.gen.go new file mode 100644 index 0000000..1c3615b --- /dev/null +++ b/internal/interface/swagger/models.gen.go @@ -0,0 +1,94 @@ +// Package swagger provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. +package swagger + +// Defines values for PaymentStatus. +const ( + Canceled PaymentStatus = "canceled" + Pending PaymentStatus = "pending" + Succeeded PaymentStatus = "succeeded" + WaitingForCapture PaymentStatus = "waiting_for_capture" +) + +// Amount defines model for Amount. +type Amount struct { + // Currency Трехбуквенный код валюты в формате ISO-4217 + Currency string `json:"currency"` + + // Value Сумма в выбранной валюте + Value string `json:"value"` +} + +// PaymentStatus defines model for PaymentStatus. +type PaymentStatus string + +// Recipient defines model for Recipient. +type Recipient struct { + // AccountId Идентификатор магазина в ЮKassa + AccountId string `json:"account_id"` + + // GatewayId Идентификатор субаккаунта + GatewayId string `json:"gateway_id"` +} + +// SetPaymentStatusRequest defines model for SetPaymentStatusRequest. +type SetPaymentStatusRequest struct { + // Event Событие, о котором уведомляет ЮKassa + Event string `json:"event"` + Object YandexPayment `json:"object"` + + // Type Тип объекта. Фиксированное значение — notification (уведомление) + Type string `json:"type"` +} + +// YandexPayment defines model for YandexPayment. +type YandexPayment struct { + Amount Amount `json:"amount"` + + // CapturedAt Время подтверждения платежа + CapturedAt *string `json:"captured_at,omitempty"` + + // Confirmation Выбранный способ подтверждения платежа + Confirmation *map[string]interface{} `json:"confirmation,omitempty"` + + // CreatedAt Время создания заказа + CreatedAt string `json:"created_at"` + + // Description Описание + Description *string `json:"description,omitempty"` + + // ExpiresAt Время, до которого вы можете бесплатно отменить или подтвердить платеж + ExpiresAt *string `json:"expires_at,omitempty"` + + // Id Идентификатор платежа в ЮKassa + Id string `json:"id"` + IncomeAmount *Amount `json:"income_amount,omitempty"` + + // Paid Признак оплаты заказа + Paid bool `json:"paid"` + + // PaymentMethod Структура метода платежа (может отличаться от способа платежа) + PaymentMethod *map[string]interface{} `json:"payment_method,omitempty"` + Recipient Recipient `json:"recipient"` + + // Refundable Возможность провести возврат по API + Refundable bool `json:"refundable"` + RefundedAmount *Amount `json:"refunded_amount,omitempty"` + Status PaymentStatus `json:"status"` + + // Test Признак тестовой операции + Test bool `json:"test"` +} + +// SetYandexPaymentStatusCanceledJSONRequestBody defines body for SetYandexPaymentStatusCanceled for application/json ContentType. +type SetYandexPaymentStatusCanceledJSONRequestBody = SetPaymentStatusRequest + +// SetYandexPaymentStatusSucceededJSONRequestBody defines body for SetYandexPaymentStatusSucceeded for application/json ContentType. +type SetYandexPaymentStatusSucceededJSONRequestBody = SetPaymentStatusRequest + +// SetYandexPaymentStatusWaitingJSONRequestBody defines body for SetYandexPaymentStatusWaiting for application/json ContentType. +type SetYandexPaymentStatusWaitingJSONRequestBody = SetPaymentStatusRequest + +// SetYandexRefundStatusSucceededJSONRequestBody defines body for SetYandexRefundStatusSucceeded for application/json ContentType. +type SetYandexRefundStatusSucceededJSONRequestBody = SetPaymentStatusRequest diff --git a/internal/interface/swagger/models.yaml b/internal/interface/swagger/models.yaml new file mode 100644 index 0000000..9ef06be --- /dev/null +++ b/internal/interface/swagger/models.yaml @@ -0,0 +1,4 @@ +output: models.gen.go +package: swagger +generate: + models: true diff --git a/internal/server/grpc.go b/internal/server/grpc.go index c7c00c0..4eb328b 100644 --- a/internal/server/grpc.go +++ b/internal/server/grpc.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "time" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" @@ -40,7 +41,7 @@ func NewGRPC(deps DepsGRPC) (*GRPC, errors.Error) { )) return &GRPC{ - grpc: grpc.NewServer(grpcStreamInterceptor, grpcUnaryInterceptor), + grpc: grpc.NewServer(grpcStreamInterceptor, grpcUnaryInterceptor, grpc.ConnectionTimeout(5*time.Second)), logger: deps.Logger, }, nil } diff --git a/internal/server/http.go b/internal/server/http.go index 3cf1a75..37c08ef 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -11,8 +11,8 @@ import ( "github.com/labstack/echo/v4/middleware" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/swagger" "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" - "penahub.gitlab.yandexcloud.net/external/treasurer/internal/swagger" ) type DepsHTTP struct { @@ -45,8 +45,9 @@ func NewHTTP(deps DepsHTTP) (*HTTP, errors.Error) { server: &http.Server{ Handler: echo, MaxHeaderBytes: 1 << 20, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, + ReadTimeout: 20 * time.Second, + WriteTimeout: 20 * time.Second, + IdleTimeout: 20 * time.Second, }, }, nil } diff --git a/pkg/echotools/bind.go b/pkg/echotools/bind.go new file mode 100644 index 0000000..50bfef9 --- /dev/null +++ b/pkg/echotools/bind.go @@ -0,0 +1,13 @@ +package echotools + +import "github.com/labstack/echo/v4" + +func Bind[T any](ctx echo.Context) (*T, error) { + item := new(T) + + if err := ctx.Bind(item); err != nil { + return nil, err + } + + return item, nil +}