diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7a61806 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.mockery.yaml +.golangci.yaml +.gitlab-ci.yaml +.gitingore +.Makefile +.README.md +deployments +tests \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..11eb2a1 --- /dev/null +++ b/.env.test @@ -0,0 +1,16 @@ +HTTP_HOST=0.0.0.0 +HTTP_PORT=8080 +HTTP_DOMEN=http://localhost:8080 + +GRPC_HOST=0.0.0.0 +GRPC_PORT=8081 + +YOOMONEY_STORE_ID=storeid +YOOMONEY_SECRET_KEY=secret + +MONGO_HOST=mongo +MONGO_PORT=27017 +MONGO_USER=admin +MONGO_PASSWORD=admin +MONGO_DB_NAME=admin +MONGO_AUTH=admin \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4b221c3..04c4c8b 100644 --- a/.gitignore +++ b/.gitignore @@ -48,5 +48,4 @@ Thumbs.db *.mov *.wmv profile -treasurer diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..ae77d80 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,164 @@ +run: + timeout: 5m + skip-files: + - \.pb\.go$ + - \.pb\.validate\.go$ + - \.pb\.gw\.go$ + - \.gen\.go$ + skip-dirs: + - mocks + - proto + +linters: + disable-all: true + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - depguard + - dogsled + - dupword + - durationcheck + - errcheck + - errchkjson + - exportloopref + - goconst + - gocritic + - godot + - gofmt + - gci + - goprintffuncname + - gosec + - gosimple + - govet + - importas + - ineffassign + - misspell + - nakedret + - nilerr + - noctx + - nolintlint + - nosprintfhostport + - prealloc + - predeclared + - revive + - rowserrcheck + - staticcheck + - stylecheck + - thelper + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + - whitespace + +linters-settings: + errcheck: + exclude-functions: + - (io.Closer).Close + govet: + check-shadowing: true + gci: + custom-order: false + section-separators: + - newLine + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled. + - dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled. + importas: + no-unaliased: true + alias: + # Foundation libraries + - pkg: git.sbercloud.tech/products/paas/shared/foundation/management-server + alias: mgmtserver + maligned: + suggest-new: true + goconst: + min-len: 2 + min-occurrences: 2 + lll: + line-length: 140 + revive: + rules: + # The following rules are recommended https://github.com/mgechev/revive#recommended-configuration + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + # - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + - name: var-declaration + # - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + # + # Rules in addition to the recommended configuration above. + # + - name: bool-literal-in-expr + - name: constant-logical-expr + gosec: + excludes: + - G307 # Deferring unsafe method "Close" on type "\*os.File" + - G108 # Profiling endpoint is automatically exposed on /debug/pprof + gocritic: + enabled-tags: + - diagnostic + - experimental + - performance + disabled-checks: + - appendAssign + - dupImport # https://github.com/go-critic/go-critic/issues/845 + - evalOrder + - ifElseChain + - octalLiteral + - regexpSimplify + - sloppyReassign + - truncateCmp + - typeDefFirst + - unnamedResult + - unnecessaryDefer + - whyNoLint + - wrapperFunc + - rangeValCopy + - hugeParam + +issues: + exclude-rules: + - text: "at least one file in a package should have a package comment" + linters: + - stylecheck + - text: "should have a package comment, unless it's in another file for this package" + linters: + - golint + - text: "should have comment or be unexported" + linters: + - golint + - path: _test\.go + linters: + - gosec + - dupl + exclude-use-default: false + +output: + # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" + format: colored-line-number + print-linter-name: true diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 0000000..ff824a0 --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,6 @@ +exported: True +inpackage: False +keeptree: True +case: underscore +with-expecter: True +inpackage-suffix: True diff --git a/Dockerfile b/Dockerfile index 2b87fef..92522a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,72 @@ -FROM alpine -ADD treasurer / -COPY prod.crt / -COPY prod.key / -ENV APP_ADDR=:3000 -ENV DATABASE_URI=mongodb://mongodb,mongodb2 -ENV PAYWAY_DB_URI=mongodb://mongodb,mongodb2 -ENV DEVELOPMENT=false -RUN apk add --no-cache ca-certificates -CMD ["/treasurer"] +# BUILD +FROM golang:1.20.3-alpine AS build + +# Update depences +RUN apk update && apk add --no-cache curl +# Create build directory +RUN mkdir /app/bin -p +RUN mkdir /bin/golang-migrate -p +# Download migrate app +RUN GOLANG_MIGRATE_VERSION=v4.15.1 && \ + curl -L https://github.com/golang-migrate/migrate/releases/download/${GOLANG_MIGRATE_VERSION}/migrate.linux-amd64.tar.gz |\ + tar xvz migrate -C /bin/golang-migrate +# Download health check utility +RUN GRPC_HEALTH_PROBE_VERSION=v0.4.6 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe +# Set home directory +WORKDIR /app +# Copy go.mod +ADD go.mod go.sum /app/ +# Download go depences +RUN go mod download +# Copy all local files +ADD . /app +# Build app +RUN GOOS=linux go build -o bin ./... + + + +# TEST +FROM alpine:latest AS test + +# Install packages +RUN apk --no-cache add ca-certificates +ENV GO111MODULE=off +# Create home directory +WORKDIR /app +# Copy build file +COPY --from=build /app/bin/app ./app +# CMD +CMD [ "./app" ] + + +# MIGRATION +FROM alpine:latest AS migration + +# Install packages +RUN apk --no-cache add ca-certificates +# Create home directory +WORKDIR /app +# Copy migration dir +COPY --from=build /app/migrations/test ./migrations +# Install migrate tool +COPY --from=build /bin/golang-migrate /usr/local/bin + + + +# PRODUCTION +FROM alpine:latest AS production + +# Install packages +RUN apk --no-cache add ca-certificates +# Create home directory +WORKDIR /app +# Copy build file +COPY --from=build /app/bin/app ./app +# Copy grpc health probe dir +COPY --from=build /bin/grpc_health_probe /bin/grpc_health_probe +# Install migrate tool +COPY --from=build /bin/golang-migrate /usr/local/bin +# CMD +CMD ["./app"] diff --git a/DockerfileStaging b/DockerfileStaging deleted file mode 100644 index ae632c5..0000000 --- a/DockerfileStaging +++ /dev/null @@ -1,10 +0,0 @@ -FROM alpine -ADD treasurer / -COPY prod.crt / -COPY prod.key / -ENV APP_ADDR=:3000 -ENV DATABASE_URI=mongodb://mongodb -ENV PAYWAY_DB_URI=mongodb://mongodb -ENV DEVELOPMENT=false -RUN apk add --no-cache ca-certificates -CMD ["/treasurer"] diff --git a/Makefile b/Makefile index cf8f7dc..7605a22 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,50 @@ -GOCMD=go -GOBUILD=$(GOCMD) build -GOCLEAN=$(GOCMD) clean -GOTEST=$(GOCMD) test -RELEASE?=$(git tag) -COMMIT?=$(shell git rev-parse --short HEAD) -BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S') -PROJECT?=bitbucket.org/skeris/treasurer -GOOS?=linux -GOARCH?=amd64 -BINARY_NAME=treasurer -PORT?=3000 -all: build run -clean: - $(GOCLEAN) - rm -f $(BINARY_NAME) -build: clean - CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} $(GOBUILD) \ - -ldflags "-s -w -X ${PROJECT}/version.Release=${RELEASE} \ - -X ${PROJECT}/version.Commit=${COMMIT} -X ${PROJECT}/version.BuildTime=${BUILD_TIME}" \ - -o ${BINARY_NAME} -container: build - docker build -t $(BINARY_NAME):$(RELEASE) . -#run: container -# docker stop $(BINARY_NAME):$(RELEASE) || true && docker rm $(BINARY_NAME):$(RELEASE) -test-build: - CGO_ENABLED=0 $(GOTEST) -c -o ./payway/test ./payway - docker compose up --build --force-recreate --abort-on-container-exit --force-recreate --remove-orphans --exit-code-from test - docker compose down +help: ## show this help + @echo 'usage: make [target] ...' + @echo '' + @echo 'targets:' + @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' + +install: ## install all go dependencies + go get \ + github.com/bufbuild/buf/cmd/buf \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc \ + google.golang.org/protobuf/cmd/protoc-gen-go + +generate: ## generate grpc proto for golang + buf generate + +test: ## run all layers tests + @make test.unit + @make test.integration + +test.unit: ## run unit tests + go test ./internal/... + +test.integration: ## run integration tests + @make test.integration.up + @make test.integration.start + @make test.integration.down + +test.integration.up: ## build integration test environment + docker-compose -f deployments/test/docker-compose.yaml up -d + +test.integration.start: ## run integration test + go test ./tests/integration/... + +test.integration.down: ## shutting down integration environment + docker-compose -f deployments/test/docker-compose.yaml down --volumes --rmi local + +run: ## run app + go run ./cmd/app/main.go + +mock: ## run mock + go run ./cmd/mock/main.go + +dev.up: ## run dev environment + docker-compose -f deployments/dev/docker-compose.yaml up -d + +dev.down: ## shutting down dev environment + docker-compose -f deployments/dev/docker-compose.yaml down --volumes --rmi local \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..13ccfee --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# customer + +Сервис 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) | + +## Переменные окружения приложения + +``` +HTTP_HOST - хост приложения (HTTP) +HTTP_PORT - порт приложения (HTTP) +HTTP_DOMEN - домен приложения (HTTP) + +GRPC_HOST - хост приложения (GRPC) +GRPC_PORT - порт приложения (GRPC) + +MONGO_HOST - хост MongoDB +MONGO_PORT - порт MongoDB +MONGO_USER - пользователь MongoDB +MONGO_DB_NAME - название базы данных для подключения +MONGO_PASSWORD - пароль пользователя MongoDB +MONGO_AUTH - имя базы данных Mongo, по которой будет производится авторизация + +YOOMONEY_STORE_ID - id магазина, зарегистрированного на yoomoney +YOOMONEY_SECRET_KEY - секретный ключ yoomoney +``` diff --git a/app/app.go b/app/app.go deleted file mode 100644 index fd13624..0000000 --- a/app/app.go +++ /dev/null @@ -1,293 +0,0 @@ -package app - -import ( - "context" - "errors" - "fmt" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "bitbucket.org/skeris/bbfoundation/middleware" - "bitbucket.org/skeris/bbfoundation/role" - r "bitbucket.org/skeris/bbfoundation/router" - "bitbucket.org/skeris/treasurer/dal" - h "bitbucket.org/skeris/treasurer/handlers" - "bitbucket.org/skeris/treasurer/payway" - v "bitbucket.org/skeris/treasurer/version" - "github.com/danilsolovyov/validator" - "github.com/skeris/appInit" - "github.com/themakers/hlog" - "go.uber.org/zap" - tb "gopkg.in/tucnak/telebot.v2" -) - -//#region ======== Application Structs & Functions ======== - -type Options struct { - IsProduction bool `env:"IS_PRODUCTION" default:"false"` - Development bool `env:"DEVELOPMENT" default:"true"` - AppName string `env:"APP_NAME" default:"treasurer"` - AppAddr string `env:"APP_ADDR" default:"localhost:3001"` //localhost:3000 - AllowedOrigins string `env:"ALLOWED_ORIGINS" default:"*"` - AllowedHeaders string `env:"ALLOWED_HEADERS" default:"*"` - ExposeHeaders string `env:"EXPOSE_HEADERS" default:"*"` - MongoDbUri string `env:"DATABASE_URI" default:"mongodb+srv://user_1:cEDxC8ptLMMeoA5m@cluster0.aomle.mongodb.net/test"` - PayWayDbUri string `env:"PAYWAY_DB_URI" default:"mongodb+srv://user_2:vW3WNWViJaXYSraT@cluster0.aomle.mongodb.net/test"` - MongoDbTable string `env:"DATABASE_TABLE" default:"treasurer"` - MongoCollections string `env:"COLLECTION_NAME" default:"payment"` -} - -type App struct { - logger *zap.Logger - err chan error -} - -func (a App) GetLogger() *zap.Logger { - return a.logger -} - -func (a App) GetErr() chan error { - return a.err -} - -//#endregion - -//#region ======== Hl Info Structs ======== - -type InfoSvcStarted struct{} -type InfoInterruptSignal struct{} -type InfoTerminateSignal struct{} -type InfoSvcShuttingDown struct{} -type InfoSvcDone struct{} - -//#endregion - -//#region ======== Hl ErrorTypeApp Structs ======== - -type ErrorTypeApp struct { - Error error -} -type ErrorCanNotServe ErrorTypeApp -type ErrorSvcShuttingDown ErrorTypeApp -type ErrorZlogSync ErrorTypeApp -type ErrorConnectToMongo ErrorTypeApp -type ErrorDisconnectFromMongo ErrorTypeApp -type ErrorConnectToToRoles ErrorTypeApp - -//#endregion - -func New(ctx context.Context, options interface{}) (appInit.CommonApp, error) { - opts := options.(Options) - var errCh chan error - - // Create logger - zlog, err := zap.NewDevelopment() - if err != nil { - panic(err) - } - - zlog = zlog.WithOptions(zap.AddCallerSkip(2)) - hlogger := hlog.New(zlog) - hlogger = hlogger.With(v.GetVersion()) - - defer func() { - err = zlog.Sync() - if err != nil { - hlogger.Emit(ErrorZlogSync{err}) - } - }() - - // Connect to MongoDb "Treasurer" - connMongo, err := dal.CreateMongo(ctx, hlogger, dal.MongoDbOptions{ - DalName: "MongoDB", - URI: opts.MongoDbUri, - DbTable: opts.MongoDbTable, - Collections: opts.MongoCollections, - }) - - if err != nil { - hlogger.Emit(ErrorConnectToMongo{err}) - return App{zlog, errCh}, err - } - ctxL, cancL := context.WithCancel(ctx) - defer cancL() - - go connMongo.ListenPayment(ctxL) - - // Connect to MongoDb "PayWay" - connPayWay, err := dal.CreateMongo(ctx, hlogger, dal.MongoDbOptions{ - DalName: "MongoPayWay", - URI: opts.PayWayDbUri, - DbTable: "payway", - Collections: "payway,paywayPayment", - }) - - if err != nil { - hlogger.Emit(ErrorConnectToMongo{err}) - return App{zlog, errCh}, err - } - - payWay, err := payway.NewPayWay(ctx, hlogger, connPayWay, connMongo) - if err != nil { - fmt.Println("PAYWAYERR", err) - //return nil, err - } - - go connPayWay.ListenPayWay(ctx, func(data *dal.PayWay) error { - _, ok := payWay.Cache[data.ShortName] - if !ok { - errText := fmt.Sprintf("%v (%v) not found in cache", data.ID, data.ShortName) - return errors.New(errText) - } - - payWay.Cache[data.ShortName].UpdateData(data) - - return nil - }) - - // TODO: remove roles logic from project - // Start Roles - roles, err := role.NewRoles(ctx, hlogger, role.MongoDalOptions{ - DalName: "MongoRoles", - URI: opts.MongoDbUri, - }) - - if err != nil { - hlogger.Emit(ErrorConnectToToRoles{err}) - return App{zlog, errCh}, err - } - - // Set routers - // TODO: remove - validators := h.CreateValidators( - &h.RequestCreatePayment{}, - &h.RequestCreatePayout{}, - ) - - chCommon := h.CommonOpts{ - AllowedOrigins: opts.AllowedOrigins, - Mongo: connMongo, - Hl: hlogger, - } - // TODO: remove unnecessary with all usages - crucsNotifier, err := tb.NewBot(tb.Settings{ - Token: "", - Verbose: false, - ParseMode: tb.ModeHTML, - Poller: &tb.LongPoller{ - Timeout: time.Second, - }, - }) - if err != nil { - return nil, err - } - - // TODO: use echo or gofiber. please remove this f*cking strange shit - // TODO: actualise api for invoice and available methods - // invoice используется для получения ссылки на оплату. по сути он выполняет 2 действия: складывает запись запроса в базу, чтобы хранились actions которые надо выполнить в ответ на события, и запросить у соответствующего платёжного сервиса правильную платёжную ссылку - // TODO: для каждого платёжного аггрегатора надо реализовать хендлер типа /listener/{paywayId}, где paywayId - айди аггрегатора - - router, svcRouter, allowedRoles := r.NewCombineRouter("", r.Handlers{ - "/listener/create/refill": { - Handle: func(w http.ResponseWriter, r *http.Request) { - ch := h.NewHandler(w, r, chCommon, validators["RequestCreatePayment"]) - h.CreatePaymentHandler(ch, payWay, crucsNotifier) - }, - }, - "/listener/create/payout": { - Handle: func(w http.ResponseWriter, r *http.Request) { - ch := h.NewHandler(w, r, chCommon, validators["RequestCreatePayout"]) - h.CreatePayoutHandler(ch, payWay) - }, - }, // pub 0b922668de7e440f263d36bf91c506d3 sek 82afd8dad1db3785d4a94e539e142a39 - "/listener/{payWay}": { - Handle: func(w http.ResponseWriter, r *http.Request) { - ch := h.NewHandler(w, r, chCommon, validator.Validator{}) - h.PaymentListenerHandler(ch, payWay) - }, - }, - "/payoutListener/{payWay}": { //TODO: remove unnecessary - Handle: func(w http.ResponseWriter, r *http.Request) { - ch := h.NewHandler(w, r, chCommon, validator.Validator{}) - h.PayoutListenerHandler(ch, payWay) - }, - }, - "/listener/getPayWays": { - Handle: func(w http.ResponseWriter, r *http.Request) { - ch := h.NewHandler(w, r, chCommon, validator.Validator{}) - h.GetPayWays(ch, payWay) - }, - }, - }) - - - // TODO: remove unnecessary - // Set Middlewares - mw := middleware.NewMiddleware( - opts.IsProduction, - hlogger, - roles, - opts.AllowedOrigins, - opts.AllowedHeaders, - allowedRoles, - opts.ExposeHeaders, - ) - - - //TODO: remove unnecessary - router.Use( - mw.MiddlewareLogger, - mw.MiddlewareOriginAccess, - mw.MiddlewareRecovery, - ) - - // TODO: remove unnecessary - svcRouter.Use( - //mw.MiddlewareJwtCookie, - //mw.MiddlewareJwt, - //mw.MiddlewareGetJwt, - //mw.MiddlewareRoleAccess, - ) - - // Startup server - srv := &http.Server{Addr: opts.AppAddr, Handler: router} - - go func() { - tmplKey := "%s.key" - tmplCrt := "%s.crt" - //if !options.LoggerDevMode { - tmplCrt = fmt.Sprintf(tmplCrt, "prod") - tmplKey = fmt.Sprintf(tmplKey, "prod") - - if err := srv.ListenAndServeTLS(tmplCrt, tmplKey); err != http.ErrServerClosed && err != nil { - hlogger.Emit(ErrorCanNotServe{Error: err}) - } - }() - hlogger.Emit(InfoSvcStarted{}) - - // Graceful Shutdown - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - killSignal := <-interrupt - switch killSignal { - case os.Interrupt: - err = errors.New("interrupted") - hlogger.Emit(InfoInterruptSignal{}) - case syscall.SIGTERM: - err = errors.New("terminated") - hlogger.Emit(InfoTerminateSignal{}) - } - - hlogger.Emit(InfoSvcShuttingDown{}) - err = srv.Shutdown(ctx) - if err != nil { - hlogger.Emit(ErrorSvcShuttingDown{err}) - } - hlogger.Emit(InfoSvcDone{}) - - err = errors.New("finished") // Костыль чтобы приложение нормально закрывалось... - return App{zlog, errCh}, err -} diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..18f30cf --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v1 +plugins: + - name: go + out: internal/proto + - name: go-grpc + out: internal/proto + opt: + - require_unimplemented_servers=false diff --git a/buf.work.yaml b/buf.work.yaml new file mode 100644 index 0000000..dcf56df --- /dev/null +++ b/buf.work.yaml @@ -0,0 +1,3 @@ +version: v1 +directories: + - proto diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..8c4a662 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,4 @@ +version: v1 +lint: + use: + - DEFAULT diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 0000000..f10bafe --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "errors" + "log" + "os/signal" + "syscall" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/app" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/initialize" +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + logger, err := zap.NewProduction(zap.AddStacktrace(zap.DPanicLevel)) + if err != nil { + log.Fatalf("failed to init zap logger: %v", err) + } + + defer cancel() + defer func() { + if syncErr := logger.Sync(); syncErr != nil { + if !errors.Is(syncErr, syscall.EBADF) && !errors.Is(syncErr, syscall.ENOTTY) { + log.Fatalf("failed to sync zap logger: %v", syncErr) + } + } + }() + + config, err := initialize.Configuration(".env.test") + if err != nil { + logger.Fatal("failed to init config: %v", zap.Error(err)) + } + + if err := app.Run(ctx, config, logger); err != nil { + logger.Fatal("failed to run app: %v", zap.Error(err)) + } +} diff --git a/cmd/mock/main.go b/cmd/mock/main.go new file mode 100644 index 0000000..6223369 --- /dev/null +++ b/cmd/mock/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + + "github.com/walkerus/go-wiremock" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" +) + +func main() { + mocklient := wiremock.NewClient("http://localhost:8000") + + if err := mocklient.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{}, + map[string]string{"Content-Type": "application/json"}, + 200, + ). + AtPriority(1), + ); err != nil { + log.Fatal("failed to stub : %w", err) + } + +} diff --git a/cmd/mock/mock_payments.go b/cmd/mock/mock_payments.go new file mode 100644 index 0000000..cfb6a74 --- /dev/null +++ b/cmd/mock/mock_payments.go @@ -0,0 +1,3 @@ +package main + +func MockWebhooks() {} diff --git a/compose.yml b/compose.yml deleted file mode 100644 index 7d82993..0000000 --- a/compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - test: - build: ./payway - depends_on: - - mongo - - mongosetup - mongo: - image: mongo:6 - hostname: mongo - restart: always - entrypoint: ["/usr/bin/mongod", "--logpath", "/dev/null","--bind_ip_all","--replSet","rs0"] - mongosetup: - image: mongo:6 - depends_on: - - mongo - restart: "no" - logging: - driver: "none" - entrypoint: [ "bash", "-c", "sleep 2 && mongosh --host mongo:27017 --eval 'rs.initiate()'"] diff --git a/dal/hlogger.go b/dal/hlogger.go deleted file mode 100644 index 8b3e244..0000000 --- a/dal/hlogger.go +++ /dev/null @@ -1,104 +0,0 @@ -package dal - -// This file contains Hl structs for Data Access Layer - -//#region ======== HLOGGER ERRORS ======== - -//#region ======== Common ======== - -type ErrorType struct { - Err error -} - -type ErrorDalNewClient ErrorType -type ErrorDalUnableToConnect ErrorType -type ErrorDalPingFailed ErrorType -type ErrorDalDisconnectFailure ErrorType -type ErrorDalInit ErrorType - -//#endregion - -//#region ======== Postgre SQL ======== - -//#endregion - -//#region ======== MongoDB ======== - -type ErrorInsertPayment ErrorType -type ErrorUpdatePayment ErrorType -type ErrorGetPayment ErrorType -type ErrorListenPayment ErrorType - -type ErrorInsertPayWay ErrorType -type ErrorUpdatePayWay ErrorType -type ErrorGetPayWay ErrorType -type ErrorListenPayWay ErrorType - -type ErrorInsertPayout ErrorType -type ErrorUpdatePayout ErrorType -type ErrorGetPayout ErrorType -type ErrorListenPayout ErrorType - -type ErrorInsertPayWayPayment ErrorType -type ErrorUpdatePayWayPayment ErrorType -type ErrorDeletePayWayPayment ErrorType -type ErrorGetPayWayPayment ErrorType - -//#endregion -//#endregion - -//#region ======== HLOGGER INFO ======== - -//#region ======== MongoDB ======== - -type InfoInsertPayWay InfoType -type InfoUpdatePayWay InfoType -type InfoGetPayWay InfoType -type InfoListenPayWay InfoType - -type InfoInsertPayment InfoType -type InfoUpdatePayment InfoType -type InfoGetPayment InfoType -type InfoListenPayment InfoType - -type InfoInsertPayout InfoType -type InfoUpdatePayout InfoType -type InfoGetPayout InfoType -type InfoListenPayout InfoType - -type InfoInsertPayWayPayment InfoType -type InfoUpdatePayWayPayment InfoType -type InfoDeletePayWayPayment InfoType -type InfoGetPayWayPayment InfoType - -type InfoPayWay struct { - CtxPayWay string - CtxName string - CtxEvent string -} - -//#endregion - -//#region ======== Common ======== - -type InfoType struct { - Info interface{} -} - -type InfoDalConnected InfoType -type InfoDalDisconnected InfoType -type InfoDalInit struct { - Result string -} - -type InfoDalPing struct { - Delay int64 // Milliseconds -} - -//#endregion - -//#region ======== Postgre SQL ======== - -//#endregion - -//#endregion diff --git a/dal/mongo.go b/dal/mongo.go deleted file mode 100644 index 25aa4ca..0000000 --- a/dal/mongo.go +++ /dev/null @@ -1,139 +0,0 @@ -package dal - -import ( - //"bitbucket.org/skeris/bbfoundation/sse" - "context" - "strings" - "time" - - "github.com/themakers/hlog" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/mongo/readpref" -) - -//#region ======== Data Access Layer structs ======== - -// MongoConnection - Constructor LayerMongoDb -type MongoConnection struct { - conn *mongo.Client // Mongo connected pairCache - hl hlog.Logger // HLogger for Data Access Layer - opts MongoDbOptions // Database options - db *mongo.Database // Database - coll map[string]*mongo.Collection // Collections map -} - -// MongoDbOptions - Connect params -type MongoDbOptions struct { - DalName string // Name of the Data Access Layer - URI string // MongoDB URI - DbTable string // MongoDB name - Collections string // MongoDB list collection names -} - -//#endregion - -//#region ======== Data Access Layer Constructor ======== - -// CreateMongo подключается к MongoDB. -// -// Input: -// ctx - Application context -// hlogger - Application logger -// opts - Application options for MongoDB -// -// Return: MongoConnection, error -// -// Logger: -// ErrorDalNewClient - mongo.NewClient return error -// ErrorDalUnableToConnect - Unable to connect to MongoDB -// InfoDalConnected - Successfully connected to MongoDB -func CreateMongo(ctx context.Context, hlogger hlog.Logger, opts MongoDbOptions) (*MongoConnection, error) { - hlogger = hlogger.With(opts) - - conn, err := mongo.NewClient(options.Client().ApplyURI(opts.URI)) - - if err != nil { - hlogger.Emit(ErrorDalNewClient{err}) - return nil, err - } - - ctxTO, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - - err = conn.Connect(ctxTO) - - if err != nil { - hlogger.Emit(ErrorDalUnableToConnect{err}) - } - - collections := map[string]*mongo.Collection{} - - for _, name := range strings.Split(opts.Collections, ",") { - collections[name] = conn.Database(opts.DbTable).Collection(name) - } - - mongoConn := &MongoConnection{ - conn: conn, - hl: hlogger, - opts: opts, - db: conn.Database(opts.DbTable), - coll: collections, - } - - _, err = mongoConn.Ping(ctx) - - if err == nil { - hlogger.Emit(InfoDalConnected{}) - } - - return mongoConn, err -} - -//#endregion - -//#region ======== Data Access Layer functions ======== - -// DisconnectFromDb разрывает соединение с MongoDB -// -// Return: error -// -// Logger: -// ErrorDalDisconnectFailure - Something is wrong -// InfoDalDisconnected - Successfully disconnected from MongoDB -func (mc *MongoConnection) DisconnectFromDb() error { - err := mc.conn.Disconnect(context.TODO()) - - if err != nil { - mc.hl.Emit(ErrorDalDisconnectFailure{err}) - } - - mc.hl.Emit(InfoDalDisconnected{}) - - return err -} - -// Ping пингует соединение MongoDB. -// -// Return: -// int64 - ping delay in milliseconds -// error -// -// Logger: -// ErrorDalPingFailed - Ping received an error -// InfoDalPing - Ping delay in milliseconds -func (mc *MongoConnection) Ping(ctx context.Context) (int64, error) { - start := time.Now() - err := mc.conn.Ping(ctx, readpref.Primary()) - delay := time.Since(start).Milliseconds() - - if err != nil { - mc.hl.Emit(ErrorDalPingFailed{err}) - } else { - mc.hl.Emit(InfoDalPing{delay}) - } - - return delay, err -} - -//#endregion diff --git a/dal/payment.go b/dal/payment.go deleted file mode 100644 index 8fcc5a1..0000000 --- a/dal/payment.go +++ /dev/null @@ -1,363 +0,0 @@ -package dal - -import ( - "bytes" - "context" - "errors" - "fmt" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "net/http" - "time" -) - -const PaymentTimeout = 15 * time.Minute - -type Payment struct { - ID string `json:"id" bson:"_id"` - RequesterID string `json:"requester_id" bson:"requester_id"` - UserIP string `json:"user_ip" bson:"user_ip"` - Email string `json:"email" bson:"email"` - Phone string `json:"phone" bson:"phone"` - Description string `json:"description" bson:"description"` - IsRefill bool `json:"is_refill" bson:"is_refill"` // Флаг описывает ввод/вывод средств - Status string `json:"status" bson:"status"` // Значения: open, wait, pending, in_progress, timeout, - // accepted, declined - PayWayID string `json:"payway_id" bson:"payway_id"` // - PaymentType string `json:"payment_type" bson:"payment_type"` // - ServiceID string `json:"service_id" bson:"service_id"` // ID платежа в платежном сервисе - Destination string `json:"destination" bson:"destination"` // Назначение платежа. Only for payout/withdrawal - Amount float64 `json:"amount" bson:"amount"` - Currency string `json:"currency" bson:"currency"` - IsFake bool `json:"is_fake" bson:"is_fake"` - OnAccept Action `json:"on_accept" bson:"on_accept"` - OnDecline Action `json:"on_decline" bson:"on_decline"` - OnTimeout Action `json:"on_timeout" bson:"on_timeout"` - PendedAt time.Time `json:"pended_at" bson:"pended_at"` - CreatedAt time.Time `json:"created_at" bson:"created_at"` - UpdatedAt time.Time `json:"updated_at" bson:"updated_at"` -} - -type Action struct { - ActionType string `json:"action_type" bson:"action_type"` // Значения: mail, vk, tg, sms, request - Target string `json:"target" bson:"target"` - Data string `json:"data" bson:"data"` -} - -func (mc *MongoConnection) InsertPayment(ctx context.Context, record *Payment) (string, error) { - err := record.Prepare() - - if err != nil { - mc.hl.Emit(ErrorInsertPayment{err}) - return "", nil - } - - result, err := mc.coll["payment"].InsertOne(ctx, record) - - if err != nil { - mc.hl.Emit(ErrorInsertPayment{err}) - return "", err - } - - mc.hl.Emit(InfoInsertPayment{result}) - - return result.InsertedID.(string), nil -} - -func (mc *MongoConnection) GetPayment(ctx context.Context, id string) (*Payment, error) { - filter := bson.M{"_id": id} - - var result Payment - - err := mc.coll["payment"].FindOne(ctx, filter).Decode(&result) - - if err == mongo.ErrNoDocuments { - return nil, nil - } else { - if err != nil { - mc.hl.Emit(ErrorGetPayment{err}) - return nil, err - } - } - - mc.hl.Emit(InfoGetPayment{id}) - - return &result, err -} - -func (mc *MongoConnection) GetPaymentByServiceID(ctx context.Context, serviceId, isRefill string) (*Payment, - error) { - filter := bson.M{"api_id": serviceId} - - switch isRefill { - case "yes", "true": - filter["is_refill"] = true - case "no", "false": - filter["is_refill"] = false - } - - var result Payment - - err := mc.coll["payment"].FindOne(ctx, filter).Decode(&result) - - if err == mongo.ErrNoDocuments { - return nil, nil - } else { - if err != nil { - mc.hl.Emit(ErrorGetPayment{err}) - } - } - - mc.hl.Emit(InfoGetPayment{fmt.Sprintf("by service_id %v", serviceId)}) - - return &result, err -} - -func (mc *MongoConnection) GetPaymentListByStatus(ctx context.Context, status, isRefill string) ([]Payment, - error) { - err := CheckPaymentStatus(status) - - if err != nil { - mc.hl.Emit(ErrorGetPayment{err}) - return nil, nil - } - - filter := bson.M{"status": status} - - switch isRefill { - case "yes", "true": - filter["is_refill"] = true - case "no", "false": - filter["is_refill"] = false - } - - cursor, err := mc.coll["payment"].Find(ctx, filter) - - if err != nil { - mc.hl.Emit(ErrorGetPayment{err}) - return nil, nil - } - - var results []Payment - err = cursor.All(ctx, &results) - - if err != nil { - mc.hl.Emit(ErrorGetPayment{err}) - return nil, nil - } - - mc.hl.Emit(InfoGetPayment{fmt.Sprintf("by status %v", status)}) - - return results, nil -} - -func (mc *MongoConnection) UpdatePaymentStatus(ctx context.Context, id, status string) error { - err := CheckPaymentStatus(status) - - if err != nil { - mc.hl.Emit(ErrorUpdatePayment{err}) - return err - } - - update := bson.M{ - "updated_at": time.Now(), - "status": status, - } - - result, err := mc.coll["payment"].UpdateByID(ctx, id, bson.D{{"$set", update}}) - if err != nil { - mc.hl.Emit(ErrorUpdatePayment{err}) - return err - } - - mc.hl.Emit(InfoUpdatePayment{result}) - - return nil -} - -func (mc *MongoConnection) UpdatePaymentServiceID(ctx context.Context, id, serviceId string) error { - update := bson.M{ - "updated_at": time.Now(), - "service_id": serviceId, - } - - result, err := mc.coll["payment"].UpdateByID(ctx, id, bson.D{{"$set", update}}) - if err != nil { - mc.hl.Emit(ErrorUpdatePayment{err}) - return err - } - - mc.hl.Emit(InfoUpdatePayment{result}) - return nil -} - -func (mc *MongoConnection) ListenPayment(ctx context.Context) { - operationTypes := []bson.D{{{"operationType", "update"}}} - - matchStage := bson.D{ - {"$match", bson.D{{"$or", operationTypes}}}, - } - - opts := options.ChangeStream().SetFullDocument(options.UpdateLookup) - - changeStream, err := mc.coll["payment"].Watch(ctx, - mongo.Pipeline{matchStage}, opts) - - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - return - } - - // routine: SetTimeout - каждые 3 минуты проверяет БД на наличие записей с истекшим PaymentTimeout - go func() { - for { - paymentList, err := mc.GetPaymentListByStatus(ctx, "open", "") - - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - } else { - for _, payment := range paymentList { - if time.Since(payment.CreatedAt) >= PaymentTimeout { - err = mc.UpdatePaymentStatus(ctx, payment.ID, "timeout") - } - - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - } - } - } - - // Прерывание - select { - case <-ctx.Done(): - return - case <-time.After(3 * time.Minute): // Сладкий сон - continue - } - } - }() - - // routine: UpdateListener - go func() { - // Перехват паники (см. ниже) - defer func() { - if err := recover(); err != nil { - mc.hl.Emit(ErrorListenPayment{err.(error)}) - } - }() - - for changeStream.Next(ctx) { - // При закрытии приложения происходит паника (change_stream.go 561). context не важен...' - current := changeStream.Current - - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - return - } - - var payment Payment - - err = bson.Unmarshal(current.Lookup("fullDocument").Value, &payment) - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - return - } - var action Action - switch payment.Status { - case "accepted": - action = payment.OnAccept - case "timeout": - action = payment.OnTimeout - case "declined": - action = payment.OnDecline - } - - // Some work with action - if payment.Status == "accepted" && payment.IsRefill { - switch action.ActionType { - case "request": - fmt.Println("CYCLE", payment ) - buf := bytes.Buffer{} - buf.Write([]byte(action.Data)) - if _, err := http.Post("https://fynrods.ru/bet/increase", "application/json", &buf); err != nil { - fmt.Println("ERRINC", err ) - } - } - } - - mc.hl.Emit(InfoListenPayment{fmt.Sprintf("%v %v done", payment.ID, payment.Status)}) - } - if err = changeStream.Err(); err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - } - }() - - select { - case <-ctx.Done(): - err = changeStream.Close(context.TODO()) - if err != nil { - mc.hl.Emit(ErrorListenPayment{err}) - } - return - } -} - -func (p *Payment) Prepare() error { - err := CheckPaymentStatus(p.Status) - - if err != nil { - return err - } - - err = CheckActionType(p.OnAccept.ActionType) - - if err != nil { - return err - } - - err = CheckActionType(p.OnDecline.ActionType) - - if err != nil { - return err - } - - err = CheckActionType(p.OnTimeout.ActionType) - - if err != nil { - return err - } - - now := time.Now() - - if p.ID == "" { - p.ID = primitive.NewObjectIDFromTimestamp(now).Hex() - } - - p.CreatedAt = now - p.UpdatedAt = now - - return nil -} - -func CheckPaymentStatus(status string) error { - allowedStatus := map[string]struct{}{"open": {}, "wait": {}, "pending": {}, "timeout": {}, "accepted": {}, - "declined": {}, "in_progress": {}} - - if _, ok := allowedStatus[status]; !ok { - return errors.New("got bad status: " + status) - } - - return nil -} - -func CheckActionType(aType string) error { - allowedType := map[string]struct{}{"mail": {}, "vk": {}, "tg": {}, "sms": {}, "request": {}} - - if _, ok := allowedType[aType]; !ok { - return errors.New("got bad type: " + aType) - } - - return nil -} diff --git a/dal/payway.go b/dal/payway.go deleted file mode 100644 index 19711e3..0000000 --- a/dal/payway.go +++ /dev/null @@ -1,359 +0,0 @@ -package dal - -import ( - "context" - "crypto/aes" - "encoding/hex" - "fmt" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "strings" - "time" -) - -const BlockSize = 16 -const PrivateKey = "SomePrivateKey1!" //16 byte - -type PayWay struct { - ID string `bson:"_id"` - Name string `bson:"name"` - ShortName string `bson:"short_name"` - MerchantID string `bson:"merchant_id"` - WalletID string `bson:"wallet_id"` - Secret1 string `bson:"secret_1"` - Secret2 string `bson:"secret_2"` - Secret3 string `bson:"secret_3"` - PayoutTypeList []PayoutType `bson:"payout_type_list"` // TODO: перенести так же как и payment - CreatedAt time.Time `bson:"created_at"` - UpdatedAt time.Time `bson:"updated_at"` -} - -type PayoutType struct { - Name string `bson:"name"` - IsEnabled bool `bson:"is_enabled"` - Currency string `bson:"currency"` // Валюта вывода: RUB, EUR, USD и т.д. - ApiId string `bson:"api_id"` - Commission float64 `bson:"commission"` - Minimum float64 `bson:"minimum"` - Maximum float64 `bson:"maximum"` -} - -func (mc *MongoConnection) InsertPayWay(ctx context.Context, record PayWay) (string, error) { - now := time.Now() - if record.ID == "" { - record.ID = primitive.NewObjectIDFromTimestamp(now).Hex() - } - record.CreatedAt = now - record.UpdatedAt = now - - if record.Secret1 != "" { - s, err := EncryptAES(PrivateKey, record.Secret1) - if err != nil { - mc.hl.Emit(ErrorInsertPayWay{err}) - return "", err - } - - record.Secret1 = s - } - - if record.Secret2 != "" { - s, err := EncryptAES(PrivateKey, record.Secret2) - if err != nil { - mc.hl.Emit(ErrorInsertPayWay{err}) - return "", err - } - - record.Secret2 = s - } - - if record.Secret3 != "" { - s, err := EncryptAES(PrivateKey, record.Secret3) - if err != nil { - mc.hl.Emit(ErrorInsertPayWay{err}) - return "", err - } - - record.Secret3 = s - } - - result, err := mc.coll["payway"].InsertOne(ctx, record) - - if err != nil { - mc.hl.Emit(ErrorInsertPayWay{err}) - return "", err - } - - mc.hl.Emit(InfoInsertPayWay{result}) - return result.InsertedID.(string), nil -} - -func (mc *MongoConnection) UpdatePayWay(ctx context.Context, record PayWay) error { - update := bson.M{"updated_at": time.Now()} - - if record.Name != "" { - update["name"] = record.Name - } - - if record.ShortName != "" { - update["short_name"] = record.ShortName - } - - if record.MerchantID != "" { - update["merchant_id"] = record.MerchantID - } - - if record.WalletID != "" { - update["wallet_id"] = record.WalletID - } - if record.PayoutTypeList != nil || len(record.PayoutTypeList) != 0 { - update["payout_type_list"] = record.PayoutTypeList - } - - if record.Secret1 != "" { - s, err := EncryptAES(PrivateKey, record.Secret1) - if err != nil { - mc.hl.Emit(ErrorUpdatePayWay{err}) - return err - } - - update["secret_1"] = s - } - - if record.Secret2 != "" { - s, err := EncryptAES(PrivateKey, record.Secret2) - if err != nil { - mc.hl.Emit(ErrorUpdatePayWay{err}) - return err - } - - update["secret_2"] = s - } - - if record.Secret3 != "" { - s, err := EncryptAES(PrivateKey, record.Secret3) - if err != nil { - mc.hl.Emit(ErrorUpdatePayWay{err}) - return err - } - - update["secret_3"] = s - } - - result, err := mc.coll["payway"].UpdateByID(ctx, record.ID, bson.D{{"$set", update}}) - - if err != nil { - mc.hl.Emit(ErrorUpdatePayWay{err}) - return err - } - - mc.hl.Emit(InfoUpdatePayWay{result}) - - return nil -} - -func (mc *MongoConnection) GetPayWay(ctx context.Context, id string) (*PayWay, error) { - filter := bson.M{"_id": id} - - var result PayWay - - err := mc.coll["payway"].FindOne(ctx, filter).Decode(&result) - - if err == mongo.ErrNoDocuments { - return nil, nil - } else { - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - } - - if result.Secret1 != "" { - s, err := DecryptAES(PrivateKey, result.Secret1) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret1 = s - } - - if result.Secret2 != "" { - s, err := DecryptAES(PrivateKey, result.Secret2) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret2 = s - } - - if result.Secret3 != "" { - s, err := DecryptAES(PrivateKey, result.Secret3) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret3 = s - } - - return &result, err -} - -func (mc *MongoConnection) GetPayWayByName(ctx context.Context, name string) (*PayWay, error) { - filter := bson.M{"name": name} - - var result PayWay - - err := mc.coll["payway"].FindOne(ctx, filter).Decode(&result) - - if err == mongo.ErrNoDocuments { - return nil, err - } else { - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - } - - if result.Secret1 != "" { - s, err := DecryptAES(PrivateKey, result.Secret1) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret1 = s - } - - if result.Secret2 != "" { - s, err := DecryptAES(PrivateKey, result.Secret2) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret2 = s - } - - if result.Secret3 != "" { - s, err := DecryptAES(PrivateKey, result.Secret3) - if err != nil { - mc.hl.Emit(ErrorGetPayWay{err}) - return nil, err - } - - result.Secret3 = s - } - - return &result, err -} - -//func (mc *MongoConnection) GetPayWayListByPayoutType(ctx context.Context, name string) ([]PayWay, error) { -// -//} - -func (mc *MongoConnection) ListenPayWay(ctx context.Context, updateAction func(data *PayWay) error) { - operationTypes := []bson.D{{{"operationType", "update"}}} - - matchStage := bson.D{ - {"$match", bson.D{{"$or", operationTypes}}}, - } - - opts := options.ChangeStream().SetFullDocument(options.UpdateLookup) - - changeStream, err := mc.coll["payway"].Watch(ctx, - mongo.Pipeline{matchStage}, opts) - - if err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - return - } - - go func() { - // Перехват паники (см. ниже) - defer func() { - if err := recover(); err != nil { - mc.hl.Emit(ErrorListenPayWay{err.(error)}) - } - }() - - for changeStream.Next(ctx) { - // При закрытии приложения происходит паника (change_stream.go 561). context не важен...' - current := changeStream.Current - - if err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - return - } - - var payWay PayWay - - err = bson.Unmarshal(current.Lookup("fullDocument").Value, &payWay) - if err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - return - } - - err = updateAction(&payWay) - - if err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - continue - } - - mc.hl.Emit(InfoListenPayWay{fmt.Sprintf("%v (%v) updated", payWay.ID, payWay.ShortName)}) - } - if err = changeStream.Err(); err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - } - }() - - select { - case <-ctx.Done(): - err = changeStream.Close(context.TODO()) - if err != nil { - mc.hl.Emit(ErrorListenPayWay{err}) - } - return - } -} - -func EncryptAES(key, data string) (string, error) { - c, err := aes.NewCipher([]byte(key)) - - if err != nil { - return "", err - } - - // @TODO: Выглядит как костыль, но что поделать? - // Для большей безопасности можно генерировать случайную строку с указателем конца информации. example = "...`randS" - if len(data) < BlockSize { - x := BlockSize - len(data) - data += strings.Repeat("`", x) - } - - out := make([]byte, len(data)) - c.Encrypt(out, []byte(data)) - - return hex.EncodeToString(out), nil -} - -func DecryptAES(key, data string) (string, error) { - ct, _ := hex.DecodeString(data) - - c, err := aes.NewCipher([]byte(key)) - if err != nil { - return "", err - } - - pt := make([]byte, len(ct)) - c.Decrypt(pt, ct) - - // @TODO: Выглядит как костыль, но что поделать? - result := strings.ReplaceAll(string(pt[:]), "`", "") - - return result, nil -} diff --git a/dal/paywayPayment.go b/dal/paywayPayment.go deleted file mode 100644 index 28b6056..0000000 --- a/dal/paywayPayment.go +++ /dev/null @@ -1,180 +0,0 @@ -package dal - -import ( - "context" - "errors" - "fmt" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "time" -) - -// @TODO: Rename to PayWayRefill - -type PayWayPayment struct { - ID string `bson:"_id"` // PayWay-ApiId - ApiId string `bson:"api_id"` - Name string `bson:"name"` - PayWay string `bson:"payWay"` - Currency string `bson:"currency"` - Commission float64 `bson:"commission"` - Minimum float64 `bson:"minimum"` - Maximum float64 `bson:"maximum"` - Status string `bson:"status"` // active, inactive - CreatedAt time.Time `bson:"created_at"` - UpdatedAt time.Time `bson:"updated_at"` -} - -func (mc *MongoConnection) InsertPayWayPayment(ctx context.Context, record PayWayPayment) (string, error) { - now := time.Now() - - if record.ApiId == "" { - err := errors.New("got empty apiId") - mc.hl.Emit(ErrorInsertPayWayPayment{err}) - return "", err - } - - if record.PayWay == "" { - err := errors.New("got empty payWay") - mc.hl.Emit(ErrorInsertPayWayPayment{err}) - return "", err - } - - record.ID = fmt.Sprintf("%v-%v", record.PayWay, record.ApiId) - - record.CreatedAt = now - record.UpdatedAt = now - - result, err := mc.coll["paywayPayment"].InsertOne(ctx, record) - - if err != nil { - mc.hl.Emit(ErrorInsertPayWayPayment{err}) - return "", err - } - - mc.hl.Emit(InfoInsertPayWayPayment{result}) - - mc.hl.Emit(InfoPayWay{ - CtxPayWay: record.PayWay, - CtxName: record.Name, - CtxEvent: "add", - }) - - return result.InsertedID.(string), nil -} - -func (mc *MongoConnection) UpdatePayWayPayment(ctx context.Context, record PayWayPayment) error { - //update := bson.M{"updated_at": time.Now()} - - //if record.ID == "" { - // err := errors.New("got empty id") - // mc.hl.Emit(ErrorUpdatePayWayPayment{err}) - // return err - //} - // - //if record.Name != "" { - // update["name"] = record.Name - //} - // - //if record.PayWay != "" { - // update["payway"] = record.Name - //} - // - //if record.Status != "" { - // update["status"] = record.Name - //} - record.UpdatedAt = time.Now() - - opts := options.FindOneAndUpdate().SetUpsert(true) - - err := mc.coll["paywayPayment"].FindOneAndUpdate(ctx, bson.M{ - "_id": record.ID, - }, bson.D{{"$set", record}}, opts).Decode(&PayWayPayment{}) - - if err != nil { - mc.hl.Emit(ErrorUpdatePayWayPayment{err}) - return err - } - - //mc.hl.Emit(InfoInsertPayWayPayment{result}) - - if record.Status != "" { - //update["status"] = record.Name - - pw, _ := mc.GetPayWayPayment(ctx, record.ID) - - if pw.Status != record.Status { - event := "stop" - if record.Status == "active" { - event = "start" - } - - mc.hl.Emit(InfoPayWay{ - CtxPayWay: pw.PayWay, - CtxName: pw.Name, - CtxEvent: event, - }) - } - } - return nil -} - -func (mc *MongoConnection) GetPayWayPayment(ctx context.Context, id string) (*PayWayPayment, error) { - filter := bson.M{"_id": id} - - var result PayWayPayment - - err := mc.coll["paywayPayment"].FindOne(ctx, filter).Decode(&result) - - if err == mongo.ErrNoDocuments { - return nil, nil - } else { - if err != nil { - mc.hl.Emit(ErrorGetPayWayPayment{err}) - return nil, err - } - } - - mc.hl.Emit(InfoGetPayWayPayment{result.ID}) - - return &result, err -} - -func (mc *MongoConnection) GetPayWayPaymentByPayWay(ctx context.Context, payWay string) ([]PayWayPayment, error) { - filter := bson.M{"payWay": payWay} - - var results []PayWayPayment - cursor, err := mc.coll["paywayPayment"].Find(ctx, filter) - - if err != nil { - mc.hl.Emit(ErrorGetPayWayPayment{err}) - return results, nil - } - - err = cursor.All(ctx, &results) - - if err != nil { - mc.hl.Emit(ErrorGetPayWayPayment{err}) - return nil, nil - } - - mc.hl.Emit(InfoGetPayWayPayment{fmt.Sprintf("by status %v", payWay)}) - - return results, nil -} - -func (mc *MongoConnection) DeletePayWayPayment(ctx context.Context, id string) error { - filter := bson.M{"_id": id} - - result, err := mc.coll["paywayPayment"].DeleteOne(ctx, filter) - - if err != nil { - mc.hl.Emit(ErrorDeletePayWayPayment{err}) - return err - } - - mc.hl.Emit(InfoDeletePayWayPayment{result}) - - return nil -} diff --git a/deployments/dev/docker-compose.yaml b/deployments/dev/docker-compose.yaml new file mode 100644 index 0000000..c9a4a3d --- /dev/null +++ b/deployments/dev/docker-compose.yaml @@ -0,0 +1,70 @@ +version: "3.3" + +services: + treasurer-app: + build: + context: ../../. + dockerfile: Dockerfile + target: test + environment: + - HTTP_HOST=0.0.0.0 + - HTTP_PORT=8080 + - HTTP_DOMEN=http://localhost:8080 + + - GRPC_HOST=0.0.0.0 + - GRPC_PORT=8081 + + - YOOMONEY_STORE_ID = id + - YOOMONEY_SECRET_KEY = secret + + - MONGO_HOST=mongo + - MONGO_PORT=27017 + - MONGO_USER=admin + - MONGO_PASSWORD=admin + - MONGO_DB_NAME=admin + - MONGO_AUTH=admin + ports: + - 8080:8080 + - 8081:8081 + depends_on: + - migration + networks: + - dev + + 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: + image: 'wiremock/wiremock:2.35.0' + ports: + - 8000:8080 + networks: + - dev + depends_on: + - app + +networks: + dev: diff --git a/deployments/test/docker-compose.yaml b/deployments/test/docker-compose.yaml new file mode 100644 index 0000000..204991f --- /dev/null +++ b/deployments/test/docker-compose.yaml @@ -0,0 +1,54 @@ +version: "3.3" + +services: + treasurer-app: + build: + context: ../../. + dockerfile: Dockerfile + target: test + environment: + - HTTP_HOST=0.0.0.0 + - HTTP_PORT=8080 + - HTTP_DOMEN=http://localhost:8080 + + - GRPC_HOST=0.0.0.0 + - GRPC_PORT=8081 + + - YOOMONEY_STORE_ID=storeid + - YOOMONEY_SECRET_KEY=secret + + - MONGO_HOST=mongo + - MONGO_PORT=27017 + - MONGO_USER=admin + - MONGO_PASSWORD=admin + - MONGO_DB_NAME=admin + - MONGO_AUTH=admin + ports: + - 8080:8080 + - 8081:8081 + networks: + - dev + depends_on: + - mongo + + mongo: + image: 'mongo:6.0.3' + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: admin + ports: + - '27017:27017' + networks: + - dev + + mock: + image: 'wiremock/wiremock:2.35.0' + ports: + - 8000:8080 + networks: + - dev + depends_on: + - treasurer-app + +networks: + dev: diff --git a/go.mod b/go.mod index 9f0c499..2955af2 100644 --- a/go.mod +++ b/go.mod @@ -1,55 +1,56 @@ -module bitbucket.org/skeris/treasurer +module penahub.gitlab.yandexcloud.net/external/treasurer -go 1.18 +go 1.20 require ( - bitbucket.org/skeris/bbfoundation v0.0.0-20210610132656-b53719699505 - github.com/danilsolovyov/validator v0.0.9 - github.com/fatih/structs v1.1.0 - github.com/gofiber/fiber/v2 v2.38.1 - github.com/gorilla/mux v1.8.0 - github.com/gorilla/schema v1.2.0 - github.com/rs/xid v1.3.0 - github.com/skeris/appInit v0.1.12 - github.com/stretchr/testify v1.8.1 - github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 - github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf - go.mongodb.org/mongo-driver v1.10.1 - go.uber.org/zap v1.23.0 - gopkg.in/tucnak/telebot.v2 v2.5.0 + github.com/getkin/kin-openapi v0.118.0 + github.com/go-resty/resty/v2 v2.7.0 + github.com/google/uuid v1.3.0 + github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + github.com/joho/godotenv v1.5.1 + github.com/labstack/echo/v4 v4.10.2 + github.com/sethvargo/go-envconfig v0.9.0 + github.com/stretchr/testify v1.8.4 + github.com/walkerus/go-wiremock v1.5.0 + go.mongodb.org/mongo-driver v1.11.7 + go.uber.org/zap v1.24.0 + google.golang.org/grpc v1.55.0 + google.golang.org/protobuf v1.30.0 ) require ( - github.com/MarsherSusanin/pena_hub_packages_common v0.0.0-20220912173602-40f15e2b8a39 // indirect - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/aws/aws-sdk-go v1.34.28 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/fatih/color v1.10.0 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/gofiber/fiber v1.14.6 // indirect - github.com/gofiber/utils v0.0.10 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/klauspost/compress v1.15.0 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect - github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/perimeterx/marshmallow v1.1.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.40.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.1 // indirect - github.com/xdg-go/stringprep v1.0.3 // indirect - github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect - golang.org/x/text v0.3.7 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sync v0.2.0 // indirect + golang.org/x/sys v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e803d7d..3479d09 100644 --- a/go.sum +++ b/go.sum @@ -1,146 +1,110 @@ -bitbucket.org/skeris/bbfoundation v0.0.0-20210610132656-b53719699505 h1:fVCLuKNCoXjNjzfz9/evcNcx2EA+WxoTjf27Ek1ANd8= -bitbucket.org/skeris/bbfoundation v0.0.0-20210610132656-b53719699505/go.mod h1:QVOC6t15n9LpOSIgL2UjKzFdpnOsOptuZJfbrg2v0xk= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/MarsherSusanin/pena_hub_packages_common v0.0.0-20220912173602-40f15e2b8a39 h1:8IlSS2NqXq0/fvviP0tr88LJP8+qOIChikFoSeKLhEo= -github.com/MarsherSusanin/pena_hub_packages_common v0.0.0-20220912173602-40f15e2b8a39/go.mod h1:ojl1vRFZav2wB+L603ewTi6nSJRAVSd+w4d/AednibU= -github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/danilsolovyov/validator v0.0.9 h1:XlbB/lxrfLw2Gk4QTMrrEW8wM0NzWHQmEvSHEZqScGM= -github.com/danilsolovyov/validator v0.0.9/go.mod h1:2jwh4O+5hyKFNRQQTo395fW+l2gyRKPtv+V+uMzAbGM= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= +github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o= -github.com/gofiber/fiber v1.14.6/go.mod h1:Yw2ekF1YDPreO9V6TMYjynu94xRxZBdaa8X5HhHsjCM= -github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU= -github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= -github.com/gofiber/utils v0.0.10 h1:3Mr7X7JdCUo7CWf/i5sajSaDmArEDtti8bM1JUVso2U= -github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= -github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= -github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2 h1:HFB2fbVIlhIfCfOW81bZFbiC/RvnpXSdhbF2/DJr134= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.12.0 h1:p4oGGk2M2UJc0wWN4lHFvIB71lxsh0T/UiKCCgFADY8= -github.com/onsi/gomega v1.12.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= +github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE= +github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skeris/appInit v0.1.12 h1:osGnk4d0vzUVsEV66JOmXcqiHbC2+N9IiOBjOiSCzm0= -github.com/skeris/appInit v0.1.12/go.mod h1:4ElEeXWVGzU3dlYq/eMWJ/U5hd+LKisc1z3+ySh1XmY= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -148,172 +112,165 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA= -github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E= -github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf h1:TJJm6KcBssmbWzplF5lzixXl1RBAi/ViPs1GaSOkhwo= -github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf/go.mod h1:1FsorU3vnXO9xS9SrhUp8fRb/6H/Zfll0rPt1i4GWaA= -github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1 h1:DuzLgIqC0AxW3Gy+Mhwn15R9KvFe8nifO6Pbe3j07Ng= -github.com/themakers/identity v0.0.0-20200703212242-9142bb6b35e1/go.mod h1:lHYuLs7VL+KVZpEzfXnrv8YwlGXcuZgUZjV5pSRb+Fc= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= -github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= -github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/walkerus/go-wiremock v1.5.0 h1:ipaYzaZnnOJRQS4wNFqz4YFphC/sM9GM+EiLEzv3KLc= +github.com/walkerus/go-wiremock v1.5.0/go.mod h1:gMzQpReT5mG5T/PaW8pSFiPhazrcHb1mnf6JHdKwY5w= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.5.2 h1:AsxOLoJTgP6YNM0fXWw4OjdluYmWzQYp+lFJL7xu9fU= -go.mongodb.org/mongo-driver v1.5.2/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= -go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4= -go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= +go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= -gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/handlers/handlers.go b/handlers/handlers.go deleted file mode 100644 index e56f920..0000000 --- a/handlers/handlers.go +++ /dev/null @@ -1,164 +0,0 @@ -package handlers - -import ( - "bitbucket.org/skeris/bbfoundation/jwt_adapter" - "bitbucket.org/skeris/treasurer/dal" - "encoding/json" - "errors" - "fmt" - "github.com/danilsolovyov/validator" - "github.com/gorilla/schema" - "github.com/themakers/hlog" - "net/http" -) - -//#region ======== Handler Struct ======== - -type Handler struct { - w http.ResponseWriter - r *http.Request - origins string - mongo *dal.MongoConnection - hl hlog.Logger - validator validator.Validator -} - -type CommonOpts struct { - AllowedOrigins string - Mongo *dal.MongoConnection - Hl hlog.Logger -} - -func NewHandler( - w http.ResponseWriter, - r *http.Request, - common CommonOpts, - validator validator.Validator) *Handler { - return &Handler{ - w: w, r: r, - origins: common.AllowedOrigins, - mongo: common.Mongo, - hl: common.Hl, - validator: validator, - } -} - -func (h *Handler) Prepare(w http.ResponseWriter, r *http.Request, validator validator.Validator) { - h.w = w - h.r = r - h.validator = validator -} - -func (h *Handler) getJwtUserId() (string, error) { - if jwtAdapter, ok := h.r.Context().Value("JWT").(*jwt_adapter.JwtAdapter); ok { - return jwtAdapter.GetUserID(), nil - } - return "", errors.New("no token in context") -} - -// report Error - Send false Response with string error -func (h *Handler) reportError(status int, errClient string, errLog error) { - h.w.Header().Set("Access-Control-Allow-Origin", h.origins) - h.w.Header().Set("Content-Type", "application/json; charset=utf-8") - - h.w.WriteHeader(status) - errEncode := json.NewEncoder(h.w).Encode(Response{false, errClient}) - - if errEncode != nil { - h.hl.Emit(ErrorReportCanNotSend{errEncode}) - } - - h.hl.Emit(ErrorHandler{h.r.URL.String(), errLog}) -} - -// send Response struct with http status 200 -func (h *Handler) sendResponse(response Response) { - h.w.Header().Set("Access-Control-Allow-Origin", h.origins) - h.w.Header().Set("Content-Type", "application/json; charset=utf-8") - - h.w.WriteHeader(http.StatusOK) - err := json.NewEncoder(h.w).Encode(response) - - if err != nil { - h.reportError(http.StatusInternalServerError, "response failed", err) - } -} - -// send response as sse -func (h *Handler) sendSseData(data interface{}, flusher http.Flusher) { - response, err := json.Marshal(data) - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - _, err = fmt.Fprintf(h.w, "data: %s\n\n", response) - flusher.Flush() - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - } -} - -func (h *Handler) handlePostRequest(request interface{}) error { - decoder := validator.Decoder{Decoder: json.NewDecoder(h.r.Body)} - err := decoder.DecodeAndValidate(request, h.validator) - if err != nil { - h.reportError(http.StatusBadRequest, err.Error(), err) - return err - } - - return nil -} - -func (h *Handler) handleGetRequest(request interface{}) error { - //query := query - err := h.r.ParseForm() - if err != nil { - h.reportError(http.StatusBadRequest, err.Error(), err) - return err - } -fmt.Println("DATAAAA",h.r.Form) - err = schema.NewDecoder().Decode(request, h.r.Form) - - if err != nil { - h.reportError(http.StatusBadRequest, err.Error(), err) - return err - } - - return nil -} - -//#endregion - -// MB Size constants -const ( - MB = 1 << 20 -) - -//#region ======== Response Structs ======== - -type Response struct { - Success bool `json:"success"` // True if OK - Message interface{} `json:"message"` // Message Response body -} - -//#endregion - -//#region ======== Hl Handlers Errors ======== - -type ErrorReportCanNotSend struct { - Err error -} - -type ErrorHandler struct { - URL string - Err error -} - -//#endregion - -//#region ======== Other functions ======== - -//#endregion diff --git a/handlers/payment.go b/handlers/payment.go deleted file mode 100644 index 7bbd84c..0000000 --- a/handlers/payment.go +++ /dev/null @@ -1,196 +0,0 @@ -package handlers - -import ( - "errors" - "fmt" - "net/http" - "strconv" - - "bitbucket.org/skeris/treasurer/dal" - "bitbucket.org/skeris/treasurer/payway" - "github.com/gorilla/mux" - tb "gopkg.in/tucnak/telebot.v2" -) - -type RequestCreatePayment struct { - RequesterID string `json:"requester_id" validate:"required"` - Amount float64 `json:"amount" validate:">0"` - PaymentType string `json:"payment_type" validate:"required"` // Тип пополнения dal.PayWayPayment.ApiId - Currency string `json:"currency"` - PayWayID string `json:"payway_id" validate:"required"` // Присылать именно ID, а не shortName! id: fk1, sn: fk - Email string `json:"email"` - Phone string `json:"phone"` - OnAccept Action `json:"on_accept"` - OnDecline Action `json:"on_decline"` - OnTimeout Action `json:"on_timeout"` -} - -type Action struct { - ActionType string `json:"action_type" validate:"required,format=^(mail|vk|tg|sms|request)$"` - Target string `json:"target" validate:"required"` - Data string `json:"data" validate:"required"` -} - -func CreatePaymentHandler(h *Handler, pwc *payway.PayWay, crush *tb.Bot) { - var request RequestCreatePayment - err := h.handlePostRequest(&request) - if err != nil { - fmt.Println("handlePostRequest", err) - return - } - fmt.Println("Skeris1") - - record := &dal.Payment{ - RequesterID: request.RequesterID, - UserIP: h.r.RemoteAddr, - Email: request.Email, - Phone: request.Phone, - Status: "open", - Amount: request.Amount, - PaymentType: request.PaymentType, - Currency: request.Currency, - PayWayID: request.PayWayID, - IsRefill: true, - OnAccept: dal.Action{ActionType: request.OnAccept.ActionType, Data: request.OnAccept.Data}, - OnDecline: dal.Action{ActionType: request.OnDecline.ActionType, Data: request.OnDecline.Data}, - OnTimeout: dal.Action{ActionType: request.OnTimeout.ActionType, Data: request.OnTimeout.Data}, - } - fmt.Println("Skeris2") - - id, err := h.mongo.InsertPayment(h.r.Context(), record) - - fmt.Println("Skeris3") - if err != nil { - fmt.Println("InsertPayment", err) - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - fmt.Println("Skeris4") - pw := pwc.GetPayWayById(request.PayWayID) - - fmt.Println("Skeris5") - if pw == nil { - errs := "got bad pay_way: " + request.PayWayID - h.reportError(http.StatusBadRequest, errs, errors.New(errs)) - return - } - - if request.Email == "" { - request.Email = "solovyevdanil@yandex.ru" - } - if request.Phone == "" { - request.Phone = "+79885895677" - } - - if request.PaymentType == "2" { - request.PaymentType = "Qiwi" - } - fmt.Println("Skeris6") - url, err := pw.CreatePaymentUrl( - fmt.Sprintf("%.2f", request.Amount), - id, - request.PaymentType, // Использовать значение -1 для тестов - request.Currency, - "ru", - request.Email, - request.Phone, - request.RequesterID, - h.r.RemoteAddr, - ) - - fmt.Println("Skeris7") - if err != nil { - fmt.Println("CreatePaymentUrl", err) - if _, err := crush.Send(tb.ChatID(-1001572699960), - fmt.Sprintf("Платёжка %s не справилась создать ссылку и вот почему: %s", request.PayWayID, err.Error())); err != nil { - fmt.Println("CAN NOT NOTIFY", err) - } - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - fmt.Println("Skeris8") - h.sendResponse(Response{ - Success: true, - Message: map[string]string{"id": id, "url": url}, - }) -} - -func PaymentListenerHandler(h *Handler, pwc *payway.PayWay) { - payWay := mux.Vars(h.r)["payWay"] - var err error - - switch payWay { - case "fk": - var request payway.ReqPaymentListenerFk - err = h.handleGetRequest(&request) - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - err = pwc.Cache[payWay].PaymentListener(h.r.Context(), h.r.RemoteAddr, &request, h.mongo) - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - // уведомляем фрикассу о получении данных - // @TODO: не уверен что надо слать в конце, а не в начале... Может случиться так что фрикасса начнет бесконечно - // долбить сервис даже если имеется ошибка в данных. - _, err = fmt.Fprint(h.w, "YES") - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - case "bt": - var request payway.ReqPaymentListenerBt - - if err := h.r.ParseForm(); err != nil { - return - } - request.Sign = h.r.Form.Get("sign") - request.OrderId = h.r.Form.Get("orderId") - request.UserComment = h.r.Form.Get("user_comment") - amt, err := strconv.ParseFloat(h.r.Form.Get("amount"), 64) - request.Amount = amt - - err = pwc.Cache[payWay].PaymentListener(h.r.Context(), h.r.RemoteAddr, &request, h.mongo) - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - //// уведомляем фрикассу о получении данных - //// @TODO: не уверен что надо слать в конце, а не в начале... Может случиться так что фрикасса начнет бесконечно - //// долбить сервис даже если имеется ошибка в данных. - _, err = fmt.Fprint(h.w, "OK") - //if err != nil { - // h.reportError(http.StatusInternalServerError, "failed", err) - // return - //} - - } -} - -type GetPayWaysResp struct { - Name string `json:"name"` - ID string `json:"ID"` -} - -func GetPayWays(h *Handler, pwc *payway.PayWay) { - paymentList, err := pwc.GetPaymentList() - - if err != nil { - h.reportError(http.StatusInternalServerError, fmt.Sprint(err), err) - return - } - - h.sendResponse(Response{ - Success: true, - Message: paymentList, - }) -} diff --git a/handlers/payout.go b/handlers/payout.go deleted file mode 100644 index ba9c55a..0000000 --- a/handlers/payout.go +++ /dev/null @@ -1,90 +0,0 @@ -package handlers - -import ( - "bitbucket.org/skeris/treasurer/dal" - "bitbucket.org/skeris/treasurer/payway" - "github.com/gorilla/mux" - "net/http" -) - -type RequestCreatePayout struct { - RequesterID string `json:"requester_id" validate:"required"` - Amount float64 `json:"amount" validate:">0"` // Сумма для вывода - Destination string `json:"destination" validate:"required"` // Счет на который выводим - PayWayID string `json:"payway_id" validate:"required"` // Присылать именно ID, а не shortName! id: fk1, sn: fk - PayoutType string `json:"payout_type" validate:"required"` // Тип вывода: payoutType.CommonID - Currency string `json:"currency"` - Description string `json:"description"` // Пока не придумал что сюда заполнять - Email string `json:"email"` - Phone string `json:"phone"` - IsFake bool `json:"is_fake"` - OnAccept Action `json:"on_accept"` - OnDecline Action `json:"on_decline"` - OnTimeout Action `json:"on_timeout"` -} - -func CreatePayoutHandler(h *Handler, pwc *payway.PayWay) { - var request RequestCreatePayout - - err := h.handlePostRequest(&request) - if err != nil { - return - } - - // Добавить в монгу запись - record := &dal.Payment{ - RequesterID: request.RequesterID, - Amount: request.Amount, - Destination: request.Destination, - PayWayID: request.PayWayID, - PaymentType: request.PayoutType, - Currency: request.Currency, - Description: request.Description, // Пока не придумал что сюда заполнять - Email: request.Email, - Phone: request.Phone, - IsFake: request.IsFake, - UserIP: h.r.RemoteAddr, - Status: "open", - OnAccept: dal.Action{ActionType: request.OnAccept.ActionType, Data: request.OnAccept.Data}, - OnDecline: dal.Action{ActionType: request.OnDecline.ActionType, Data: request.OnDecline.Data}, - OnTimeout: dal.Action{ActionType: request.OnTimeout.ActionType, Data: request.OnTimeout.Data}, - IsRefill: false, - } - - id, err := h.mongo.InsertPayment(h.r.Context(), record) - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - // Добавить в очередь запись - pwc.QueuePayout.Enqueue(id) - - h.sendResponse(Response{ - Success: true, - Message: map[string]string{"id": id}, - }) -} - -func PayoutListenerHandler(h *Handler, pwc *payway.PayWay) { - payWay := mux.Vars(h.r)["payWay"] - var err error - - switch payWay { - case "fk": - var request payway.ReqPayoutListenerFk - err = h.handleGetRequest(&request) - if err != nil { - return - } - - err = pwc.Cache[payWay].PayoutListener(h.r.Context(), h.r.RemoteAddr, &request, h.mongo) - - if err != nil { - h.reportError(http.StatusInternalServerError, "failed", err) - return - } - - } -} diff --git a/handlers/validators.go b/handlers/validators.go deleted file mode 100644 index 9481a55..0000000 --- a/handlers/validators.go +++ /dev/null @@ -1,18 +0,0 @@ -package handlers - -import ( - "github.com/danilsolovyov/validator" -) - -type Validators map[string]validator.Validator - -func CreateValidators(s ...interface{}) Validators { - validators := Validators{} - - for _, val := range s { - v := validator.NewValidator(val) - validators[v.GetName()] = v - } - - return validators -} diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..d855917 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,115 @@ +package app + +import ( + "context" + "fmt" + "time" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/initialize" + "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" +) + +const ( + shutdownTimeout = 5 * time.Second +) + +func Run(ctx context.Context, config *models.Config, logger *zap.Logger) error { + mongoDB, connectionErr := mongo.Connect(ctx, &mongo.ConnectDeps{ + Configuration: &config.Database, + Timeout: 10 * time.Second, + }) + if connectionErr != nil { + return fmt.Errorf("failed connection to db: %w", connectionErr) + } + + closer := closer.New() + + openapi, swaggerErr := swagger.GetSwagger() + if swaggerErr != nil { + return fmt.Errorf("failed to loading openapi spec: %w", swaggerErr) + } + + repositories, err := initialize.NewRepositories(initialize.RepositoriesDeps{ + Logger: logger, + Database: mongoDB, + }) + if err != nil { + return err.Wrap("failed to initialize repositories") + } + + clients, err := initialize.NewClients(initialize.ClientsDeps{ + Logger: logger, + YoomoneyConfiguration: &config.Service.YoomomeyConfiguration, + }) + if err != nil { + return err.Wrap("failed to initialize clients") + } + + services, err := initialize.NewServices(initialize.ServicesDeps{ + Logger: logger, + Repositories: *repositories, + Clients: *clients, + ConfigurationHTTP: &config.HTTP, + }) + if err != nil { + return err.Wrap("failed to initialize services") + } + + controllers, err := initialize.NewControllers(initialize.ControllersDeps{ + Logger: logger, + Services: *services, + }) + if err != nil { + return err.Wrap("failed to initialize controllers") + } + + api, err := initialize.NewAPI(controllers) + if err != nil { + return err.Wrap("failed to initialize api") + } + + httpServer, err := server.NewHTTP(server.DepsHTTP{ + Logger: logger, + Swagger: openapi, + }) + if err != nil { + return err.Wrap("failed to initialize http server") + } + + httpServer.Register(api) + + grpcServer, err := server.NewGRPC(server.DepsGRPC{ + Logger: logger, + }) + if err != nil { + return err.Wrap("failed to initialize grpc server") + } + + grpcServer.Register(controllers.PaymentGRPC) + + go httpServer.Run(&config.HTTP) + go grpcServer.Run(&config.GRPC) + + closer.Add(mongoDB.Client().Disconnect) + closer.Add(httpServer.Stop) + closer.Add(grpcServer.Stop) + + <-ctx.Done() + + logger.Info("shutting down app gracefully") + + shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + + defer cancel() + + if err := closer.Close(shutdownCtx); err != nil { + return fmt.Errorf("closer: %w", err) + } + + return nil +} diff --git a/internal/errors/errors.go b/internal/errors/errors.go new file mode 100644 index 0000000..77273e2 --- /dev/null +++ b/internal/errors/errors.go @@ -0,0 +1,64 @@ +package errors + +import ( + "errors" + "fmt" +) + +type ErrorType error + +var ( + ErrInternalError ErrorType = errors.New("internal error") + ErrInvalidArgs ErrorType = errors.New("invalid arguments") + ErrMethodNotImplemented ErrorType = errors.New("method is not implemented") + ErrNotFound ErrorType = errors.New("record not found") + ErrNoAccess ErrorType = errors.New("no access") + ErrConflict ErrorType = errors.New("record already exist") + ErrInsufficientFunds ErrorType = errors.New("insufficient funds") +) + +type Error interface { + Error() string + Type() ErrorType + Wrap(message string) Error + SetType(errorType ErrorType) Error +} + +type customError struct { + errorType ErrorType + err error +} + +func NewWithError(err error, errorType ErrorType) Error { + return &customError{ + errorType: errorType, + err: err, + } +} + +func NewWithMessage(message string, errorType ErrorType) Error { + return &customError{ + errorType: errorType, + err: errors.New(message), + } +} + +func (receiver *customError) Error() string { + return receiver.err.Error() +} + +func (receiver *customError) Type() ErrorType { + return receiver.errorType +} + +func (receiver *customError) Wrap(message string) Error { + receiver.err = fmt.Errorf("%s: %w", message, receiver.err) + + return receiver +} + +func (receiver *customError) SetType(errorType ErrorType) Error { + receiver.errorType = errorType + + return receiver +} diff --git a/internal/errors/grpc.go b/internal/errors/grpc.go new file mode 100644 index 0000000..a89bab0 --- /dev/null +++ b/internal/errors/grpc.go @@ -0,0 +1,27 @@ +package errors + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const defaultGRPCCode = codes.Internal + +var grpcCodes = map[error]codes.Code{ + ErrInvalidArgs: codes.InvalidArgument, + ErrConflict: codes.AlreadyExists, + ErrInsufficientFunds: codes.Unavailable, + ErrInternalError: codes.Internal, + ErrMethodNotImplemented: codes.Unimplemented, + ErrNoAccess: codes.Unavailable, + ErrNotFound: codes.NotFound, +} + +func GRPC(message string, err Error) error { + currentStatus, ok := grpcCodes[err] + if !ok { + return status.Errorf(defaultGRPCCode, message, err) + } + + return status.Errorf(currentStatus, message, err) +} diff --git a/internal/errors/http.go b/internal/errors/http.go new file mode 100644 index 0000000..43b06ca --- /dev/null +++ b/internal/errors/http.go @@ -0,0 +1,33 @@ +package errors + +import ( + "net/http" + + "github.com/labstack/echo/v4" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +var httpStatuses = map[ErrorType]int{ + ErrInternalError: http.StatusInternalServerError, + ErrInvalidArgs: http.StatusBadRequest, + ErrNoAccess: http.StatusForbidden, + ErrNotFound: http.StatusNotFound, + ErrMethodNotImplemented: http.StatusNotImplemented, + ErrConflict: http.StatusConflict, + ErrInsufficientFunds: http.StatusPaymentRequired, +} + +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, + Message: err.Error(), + }) + } + + return ctx.JSON(status, models.ResponseErrorHTTP{ + StatusCode: status, + Message: err.Error(), + }) +} diff --git a/internal/initialize/api.go b/internal/initialize/api.go new file mode 100644 index 0000000..8fabb18 --- /dev/null +++ b/internal/initialize/api.go @@ -0,0 +1,13 @@ +package initialize + +import ( + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/swagger" +) + +func NewAPI(controllers *Controllers) (*swagger.API, errors.Error) { + return swagger.New(swagger.Deps{ + CommonController: controllers.CommonREST, + YandexStatusController: controllers.YandexStatusREST, + }) +} diff --git a/internal/initialize/clients.go b/internal/initialize/clients.go new file mode 100644 index 0000000..9826dc8 --- /dev/null +++ b/internal/initialize/clients.go @@ -0,0 +1,38 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/client" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +type ClientsDeps struct { + Logger *zap.Logger + YoomoneyConfiguration *models.YoomomeyConfiguration +} + +type Clients struct { + Callback *client.CallbackClient + Yandex *client.YandexClient +} + +func NewClients(deps ClientsDeps) (*Clients, errors.Error) { + callbackClient, err := client.NewCallbackClient(client.CallbackClientDeps{Logger: deps.Logger}) + if err != nil { + return nil, err + } + + yandexClient, err := client.NewYandexClient(client.YandexClientDeps{ + Logger: deps.Logger, + Configuration: deps.YoomoneyConfiguration, + }) + if err != nil { + return nil, err + } + + return &Clients{ + Callback: callbackClient, + Yandex: yandexClient, + }, nil +} diff --git a/internal/initialize/config.go b/internal/initialize/config.go new file mode 100644 index 0000000..e461f5b --- /dev/null +++ b/internal/initialize/config.go @@ -0,0 +1,15 @@ +package initialize + +import ( + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/env" +) + +func Configuration(path string) (*models.Config, error) { + config, err := env.Parse[models.Config](path) + if err != nil { + return nil, err + } + + return config, nil +} diff --git a/internal/initialize/controllers.go b/internal/initialize/controllers.go new file mode 100644 index 0000000..5a77110 --- /dev/null +++ b/internal/initialize/controllers.go @@ -0,0 +1,52 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/controller/grpc" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/controller/rest" +) + +type ControllersDeps struct { + Logger *zap.Logger + Services Services +} + +type Controllers struct { + CommonREST *rest.CommonController + YandexStatusREST *rest.YandexStatusController + PaymentGRPC *grpc.PaymentController +} + +func NewControllers(deps ControllersDeps) (*Controllers, errors.Error) { + commonControllerREST, err := rest.NewCommonController(rest.CommonControllerDeps{ + Logger: deps.Logger, + PaymentService: deps.Services.Payment, + }) + if err != nil { + return nil, err + } + + yandexStatusControllerREST, err := rest.NewYandexStatusController(rest.YandexStatusControllerDeps{ + Logger: deps.Logger, + StatusService: deps.Services.Status, + CallbackService: deps.Services.Callback, + }) + if err != nil { + return nil, err + } + + paymentControllerGRPC, err := grpc.NewPaymentController(grpc.PaymentControllerDeps{ + Logger: deps.Logger, + PaymentService: deps.Services.Payment, + }) + if err != nil { + return nil, err + } + + return &Controllers{ + CommonREST: commonControllerREST, + YandexStatusREST: yandexStatusControllerREST, + PaymentGRPC: paymentControllerGRPC, + }, nil +} diff --git a/internal/initialize/repositories.go b/internal/initialize/repositories.go new file mode 100644 index 0000000..205d07e --- /dev/null +++ b/internal/initialize/repositories.go @@ -0,0 +1,29 @@ +package initialize + +import ( + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/interface/repository" +) + +type RepositoriesDeps struct { + Logger *zap.Logger + Database *mongo.Database +} + +type Repositories struct { + Payment *repository.PaymentRepository +} + +func NewRepositories(deps RepositoriesDeps) (*Repositories, errors.Error) { + paymentRepository, err := repository.NewPaymentRepository(repository.PaymentRepositoryDeps{ + Logger: deps.Logger, + Collection: deps.Database.Collection("payments"), + }) + if err != nil { + return nil, err + } + + return &Repositories{Payment: paymentRepository}, nil +} diff --git a/internal/initialize/services.go b/internal/initialize/services.go new file mode 100644 index 0000000..a880a26 --- /dev/null +++ b/internal/initialize/services.go @@ -0,0 +1,79 @@ +package initialize + +import ( + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/service/callback" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/service/payment" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/service/status" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/service/webhook" +) + +type ServicesDeps struct { + Logger *zap.Logger + Repositories Repositories + Clients Clients + ConfigurationHTTP *models.ConfigurationHTTP +} + +type Services struct { + Callback *callback.Service + Payment *payment.Service + YandexPayment *payment.Yandex + Status *status.Service + YandexWebhook *webhook.Yandex +} + +func NewServices(deps ServicesDeps) (*Services, errors.Error) { + callbackService, err := callback.New(callback.Deps{ + Logger: deps.Logger, + CallbackClient: deps.Clients.Callback, + PaymentRepository: deps.Repositories.Payment, + }) + if err != nil { + return nil, err + } + + yandexPaymentService, err := payment.NewYandex(payment.YandexPaymentServiceDeps{ + Logger: deps.Logger, + YandexPaymentClient: deps.Clients.Yandex, + }) + if err != nil { + return nil, err + } + + paymentService, err := payment.New(payment.Deps{ + Logger: deps.Logger, + PaymentRepository: deps.Repositories.Payment, + PaymentStrategyService: yandexPaymentService, + }) + if err != nil { + return nil, err + } + + statusService, err := status.New(status.Deps{ + Logger: deps.Logger, + PaymentRepository: deps.Repositories.Payment, + }) + if err != nil { + return nil, err + } + + yandexWebhookService, err := webhook.NewYandex(webhook.YandexDeps{ + Logger: deps.Logger, + YandexWebhookClient: deps.Clients.Yandex, + Configuration: deps.ConfigurationHTTP, + }) + if err != nil { + return nil, err + } + + return &Services{ + Callback: callbackService, + Payment: paymentService, + YandexPayment: yandexPaymentService, + Status: statusService, + YandexWebhook: yandexWebhookService, + }, nil +} diff --git a/internal/interface/client/callback.go b/internal/interface/client/callback.go new file mode 100644 index 0000000..e7a5aa9 --- /dev/null +++ b/internal/interface/client/callback.go @@ -0,0 +1,109 @@ +package client + +import ( + "context" + "fmt" + + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/payment_callback" +) + +type CallbackClientDeps struct { + Logger *zap.Logger +} + +type CallbackClient struct { + logger *zap.Logger +} + +func NewCallbackClient(deps CallbackClientDeps) (*CallbackClient, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + return &CallbackClient{logger: deps.Logger}, nil +} + +func (receiver *CallbackClient) SendOnSuccess(ctx context.Context, host string, event *models.Event) errors.Error { + connection, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(err), zap.String("host", host)) + return errors.NewWithError(fmt.Errorf("failed connect to callback service: %w", err), errors.ErrInternalError) + } + defer func() { + if closeErr := connection.Close(); closeErr != nil { + receiver.logger.Error("failed to close connection on of ", zap.Error(closeErr)) + } + }() + + client := payment_callback.NewPaymentCallbackServiceClient(connection) + + if _, err := client.OnSuccess(ctx, &payment_callback.Event{ + Key: event.Key, + Message: event.Message, + Payment: &payment_callback.Payment{ + ID: event.Payment.ID, + UserID: event.Payment.UserID, + PaymentID: event.Payment.PaymentID, + IdempotencePaymentID: event.Payment.IdempotencePaymentID, + Amount: event.Payment.Amount, + Currency: event.Payment.Currency, + Type: string(event.Payment.Type), + Status: string(event.Payment.Status), + Completed: event.Payment.Completed, + }, + }); err != nil { + receiver.logger.Error("failed to send success callback on of ", + zap.Error(err), + zap.String("host", host), + zap.Any("event", event), + ) + return errors.NewWithError(fmt.Errorf("failed to send success callback: %w", err), errors.ErrInternalError) + } + + return nil +} + +func (receiver *CallbackClient) SendOnFailure(ctx context.Context, host string, event *models.Event) errors.Error { + connection, err := grpc.Dial(host, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + receiver.logger.Error("failed to connect on of ", zap.Error(err), zap.String("host", host)) + return errors.NewWithError(fmt.Errorf("failed connect to callback service: %w", err), errors.ErrInternalError) + } + defer func() { + if closeErr := connection.Close(); closeErr != nil { + receiver.logger.Error("failed to close connection on of ", zap.Error(closeErr)) + } + }() + + client := payment_callback.NewPaymentCallbackServiceClient(connection) + + if _, err := client.OnFailure(ctx, &payment_callback.Event{ + Key: event.Key, + Message: event.Message, + Payment: &payment_callback.Payment{ + ID: event.Payment.ID, + UserID: event.Payment.UserID, + PaymentID: event.Payment.PaymentID, + IdempotencePaymentID: event.Payment.IdempotencePaymentID, + Amount: event.Payment.Amount, + Currency: event.Payment.Currency, + Type: string(event.Payment.Type), + Status: string(event.Payment.Status), + Completed: event.Payment.Completed, + }, + }); err != nil { + receiver.logger.Error("failed to send failure callback on of ", + zap.Error(err), + zap.String("host", host), + zap.Any("event", event), + ) + return errors.NewWithError(fmt.Errorf("failed to send failure callback: %w", err), errors.ErrInternalError) + } + + return nil +} diff --git a/internal/interface/client/yandex.go b/internal/interface/client/yandex.go new file mode 100644 index 0000000..24ae729 --- /dev/null +++ b/internal/interface/client/yandex.go @@ -0,0 +1,253 @@ +package client + +import ( + "context" + "fmt" + "net/url" + + "go.uber.org/zap" + "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" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/client" +) + +const ( + yandexPaymentsMockURL = "http://mock:8080/payments" + yandexPaymentsProdURL = "https://api.yookassa.ru/v3/payments" + + yandexWebhooksMockURL = "http://mock:8080/webhooks" + yandexWebhooksProdURL = "https://api.yookassa.ru/v3/webhooks" +) + +const ( + yandexPaymentsURL = yandexPaymentsMockURL + yandexWebhooksURL = yandexWebhooksMockURL +) + +type YandexClientDeps struct { + Logger *zap.Logger + Configuration *models.YoomomeyConfiguration +} + +type YandexClient struct { + logger *zap.Logger + configuration *models.YoomomeyConfiguration +} + +func NewYandexClient(deps YandexClientDeps) (*YandexClient, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.Configuration == nil { + return nil, errors.NewWithMessage("Configuration is nil on ", errors.ErrInvalidArgs) + } + + return &YandexClient{ + logger: deps.Logger, + configuration: deps.Configuration, + }, nil +} + +func (receiver *YandexClient) CreatePayment(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodType]) (*yandex.Payment, errors.Error) { + response, err := client.Post[yandex.Payment, any](ctx, &client.RequestSettings{ + URL: yandexPaymentsURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return nil, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to create payment on of ", zap.Any("response", response.Error)) + return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body, nil +} + +func (receiver *YandexClient) CreatePaymentB2B(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodB2B]) (*yandex.Payment, errors.Error) { + response, err := client.Post[yandex.Payment, any](ctx, &client.RequestSettings{ + URL: yandexPaymentsURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return nil, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to create payment on of ", zap.Any("response", response.Error)) + return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body, nil +} + +func (receiver *YandexClient) CreatePaymentBankCard(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodBankCard]) (*yandex.Payment, errors.Error) { + response, err := client.Post[yandex.Payment, any](ctx, &client.RequestSettings{ + URL: yandexPaymentsURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return nil, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to create payment on of ", zap.Any("response", response.Error)) + return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body, nil +} + +func (receiver *YandexClient) CreatePaymentLogin(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodLogin]) (*yandex.Payment, errors.Error) { + response, err := client.Post[yandex.Payment, any](ctx, &client.RequestSettings{ + URL: yandexPaymentsURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return nil, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to create payment on of ", zap.Any("response", response.Error)) + return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body, nil +} + +func (receiver *YandexClient) CreatePaymentPhone(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodPhone]) (*yandex.Payment, errors.Error) { + response, err := client.Post[yandex.Payment, any](ctx, &client.RequestSettings{ + URL: yandexPaymentsURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return nil, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to create payment on of ", zap.Any("response", response.Error)) + return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body, nil +} + +func (receiver *YandexClient) DeleteWebhook(ctx context.Context, webhookID string) errors.Error { + url, err := url.JoinPath(yandexWebhooksURL, webhookID) + if err != nil { + receiver.logger.Error("failed to join path url on of ", + zap.Error(err), + zap.String("yandex webhook url", yandexWebhooksURL), + zap.String("webhookID", webhookID), + ) + + return errors.NewWithError(fmt.Errorf("failed to join url path: %w", err), errors.ErrInternalError) + } + + response, err := client.Delete[any, any](ctx, &client.RequestSettings{ + URL: url, + Headers: map[string]string{ + "Content-Type": "application/json", + + // TODO: узнать о получении access token + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to delete webhook on of ", + zap.Any("response", response.Error), + zap.String("url", url), + ) + + return errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return nil +} + +func (receiver *YandexClient) GetWebhookEvents(ctx context.Context) ([]yandex.Webhook, errors.Error) { + response, err := client.Delete[[]yandex.Webhook, any](ctx, &client.RequestSettings{ + URL: yandexWebhooksURL, + Headers: map[string]string{ + "Content-Type": "application/json", + + // TODO: узнать о получении access token + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return []yandex.Webhook{}, errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to delete webhook on of ", + zap.Any("response", response.Error), + zap.String("url", yandexWebhooksURL), + ) + + return []yandex.Webhook{}, errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return *response.Body, nil +} + +func (receiver *YandexClient) SetWebhookEvent(ctx context.Context, idempotenceKey string, request *yandex.CreateWebhookRequest) (string, errors.Error) { + response, err := client.Post[yandex.Webhook, any](ctx, &client.RequestSettings{ + URL: yandexWebhooksURL, + Body: request, + Headers: map[string]string{ + "Content-Type": "application/json", + "Idempotence-Key": idempotenceKey, + + // TODO: узнать о получении access token + "Authorization": utils.ConvertYoomoneySercetsToAuth("Basic", receiver.configuration.StoreID, receiver.configuration.SecretKey), + }, + }) + if err != nil { + receiver.logger.Error("failed to make request on of ", zap.Error(err)) + return "", errors.NewWithError(fmt.Errorf("failed to make request: %w", err), errors.ErrInternalError) + } + if response.Error != nil { + receiver.logger.Error("failed to delete webhook on of ", + zap.Any("response", response.Error), + zap.String("url", yandexWebhooksURL), + ) + + return "", errors.NewWithMessage("failed to create payment", errors.ErrInternalError) + } + + return response.Body.ID, nil +} diff --git a/internal/interface/controller/grpc/payment.go b/internal/interface/controller/grpc/payment.go new file mode 100644 index 0000000..d77e523 --- /dev/null +++ b/internal/interface/controller/grpc/payment.go @@ -0,0 +1,240 @@ +package grpc + +import ( + "context" + + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" +) + +type PaymentService interface { + CreatePaymentPhone(context.Context, *models.CreatePayment[string]) (string, errors.Error) + CreatePaymentBankCard(context.Context, *models.CreatePayment[*models.BankCard]) (string, errors.Error) + CreatePayment(context.Context, *models.CreatePayment[*any]) (string, errors.Error) + CreatePaymentLogin(context.Context, *models.CreatePayment[string]) (string, errors.Error) +} + +type PaymentControllerDeps struct { + Logger *zap.Logger + PaymentService PaymentService +} + +type PaymentController struct { + logger *zap.Logger + paymentService PaymentService +} + +func NewPaymentController(deps PaymentControllerDeps) (*PaymentController, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger in nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentService == nil { + return nil, errors.NewWithMessage("PaymentService in nil on ", errors.ErrInvalidArgs) + } + + return &PaymentController{ + logger: deps.Logger, + paymentService: deps.PaymentService, + }, nil +} + +func (receiver *PaymentController) GetPaymentLinkBankCard(ctx context.Context, in *treasurer.GetBankCardPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentBankCard(ctx, &models.CreatePayment[*models.BankCard]{ + Type: models.PaymentTypeBankCard, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: &models.BankCard{ + Number: in.BankCard.Number, + ExpiryYear: in.BankCard.ExpiryYear, + ExpiryMonth: in.BankCard.ExpiryMonth, + CSC: in.BankCard.CSC, + CardHolderName: in.BankCard.CardHolderName, + }, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkYooMoney(ctx context.Context, in *treasurer.GetPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePayment(ctx, &models.CreatePayment[*any]{ + Type: models.PaymentTypeYoomoney, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkQIWI(ctx context.Context, in *treasurer.GetPhonePaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentPhone(ctx, &models.CreatePayment[string]{ + Type: models.PaymentTypeQiwi, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: in.Phone, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkSberPay(ctx context.Context, in *treasurer.GetPhonePaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentPhone(ctx, &models.CreatePayment[string]{ + Type: models.PaymentTypeSberPay, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: in.Phone, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkAlfaClick(ctx context.Context, in *treasurer.GetLoginPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentLogin(ctx, &models.CreatePayment[string]{ + Type: models.PaymentTypeAlfabank, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: in.Login, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkTinkoff(ctx context.Context, in *treasurer.GetPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePayment(ctx, &models.CreatePayment[*any]{ + Type: models.PaymentTypeTinkoff, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkSBP(ctx context.Context, in *treasurer.GetPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePayment(ctx, &models.CreatePayment[*any]{ + Type: models.PaymentTypeSBP, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkMobile(ctx context.Context, in *treasurer.GetPhonePaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentPhone(ctx, &models.CreatePayment[string]{ + Type: models.PaymentTypeMobile, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: in.Phone, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkCash(ctx context.Context, in *treasurer.GetPhonePaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePaymentPhone(ctx, &models.CreatePayment[string]{ + Type: models.PaymentTypeCash, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + Requisites: in.Phone, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkInstallments(ctx context.Context, in *treasurer.GetPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + link, err := receiver.paymentService.CreatePayment(ctx, &models.CreatePayment[*any]{ + Type: models.PaymentTypeInstallments, + Currency: in.MainSettings.Currency, + UserID: in.MainSettings.UserID, + ClientIP: in.MainSettings.ClientIP, + Amount: in.MainSettings.Amount, + CallbackHostGRPC: in.MainSettings.CallbackHostGRPC, + ReturnURL: in.MainSettings.ReturnURL, + }) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return nil, errors.GRPC("failed to get payment link", err) + } + + return &treasurer.GetPaymentLinkResponse{RedirectURL: link}, nil +} + +func (receiver *PaymentController) GetPaymentLinkSberbankB2B(_ context.Context, _ *treasurer.GetB2BPaymentLinkRequest) (*treasurer.GetPaymentLinkResponse, error) { + return nil, status.Error(codes.Unimplemented, "method not implemented") +} diff --git a/internal/interface/controller/rest/common.go b/internal/interface/controller/rest/common.go new file mode 100644 index 0000000..9a1458f --- /dev/null +++ b/internal/interface/controller/rest/common.go @@ -0,0 +1,50 @@ +package rest + +import ( + "context" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +type PaymentService interface { + GetAvailablePaymentMethods(context.Context) ([]models.PaymentType, errors.Error) +} + +type CommonControllerDeps struct { + Logger *zap.Logger + PaymentService PaymentService +} + +type CommonController struct { + logger *zap.Logger + paymentService PaymentService +} + +func NewCommonController(deps CommonControllerDeps) (*CommonController, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger in nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentService == nil { + return nil, errors.NewWithMessage("PaymentService in nil on ", errors.ErrInvalidArgs) + } + + return &CommonController{ + logger: deps.Logger, + paymentService: deps.PaymentService, + }, nil +} + +func (receiver *CommonController) GetAvailablePayments(ctx echo.Context) error { + methods, err := receiver.paymentService.GetAvailablePaymentMethods(ctx.Request().Context()) + if err != nil { + receiver.logger.Error("failed to get payment link on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, methods) +} diff --git a/internal/interface/controller/rest/yandex.go b/internal/interface/controller/rest/yandex.go new file mode 100644 index 0000000..bf3b0e1 --- /dev/null +++ b/internal/interface/controller/rest/yandex.go @@ -0,0 +1,137 @@ +package rest + +import ( + "context" + "fmt" + "net/http" + + "github.com/labstack/echo/v4" + "go.uber.org/zap" + "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" +) + +type StatusService interface { + SetStatusCanceled(context.Context, string) (*models.Payment, errors.Error) + SetStatusSuccess(context.Context, string) (*models.Payment, errors.Error) + SetStatusWaiting(context.Context, string) (*models.Payment, errors.Error) + SetStatusRefund(context.Context, string) (*models.Payment, errors.Error) +} + +type CallbackService interface { + OnSuccess(context.Context, *models.Event) errors.Error + OnFailure(context.Context, *models.Event) errors.Error +} + +type YandexStatusControllerDeps struct { + Logger *zap.Logger + StatusService StatusService + CallbackService CallbackService +} + +type YandexStatusController struct { + logger *zap.Logger + statusService StatusService + callbackService CallbackService +} + +func NewYandexStatusController(deps YandexStatusControllerDeps) (*YandexStatusController, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.StatusService == nil { + return nil, errors.NewWithMessage("StatusService is nil on ", errors.ErrInvalidArgs) + } + + if deps.CallbackService == nil { + return nil, errors.NewWithMessage("CallbackService is nil on ", errors.ErrInvalidArgs) + } + + return &YandexStatusController{ + logger: deps.Logger, + statusService: deps.StatusService, + }, nil +} + +func (receiver *YandexStatusController) SetPaymentStatusCanceled(ctx echo.Context) error { + request, err := echotools.Bind[yandex.WebhookNotification[yandex.Payment]](ctx) + if err != nil { + receiver.logger.Error("failed to parse body on of ", zap.Error(err)) + return errors.HTTP(ctx, errors.NewWithError(fmt.Errorf("failed to parse input body: %w", err), errors.ErrInternalError)) + } + + payment, setStatusErr := receiver.statusService.SetStatusCanceled(ctx.Request().Context(), request.Object.ID) + if setStatusErr != nil { + receiver.logger.Error("failed to set canceled payment status on of ", zap.Error(setStatusErr)) + return errors.HTTP(ctx, setStatusErr) + } + + if err := receiver.callbackService.OnFailure(ctx.Request().Context(), &models.Event{ + Key: string(request.Event), + Message: "yandex send event: payment canceled", + Payment: payment, + }); err != nil { + receiver.logger.Error("failed send success callback on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, nil) +} + +func (receiver *YandexStatusController) SetPaymentStatusSucceeded(ctx echo.Context) error { + request, err := echotools.Bind[yandex.WebhookNotification[yandex.Payment]](ctx) + if err != nil { + receiver.logger.Error("failed to parse body on of ", zap.Error(err)) + return errors.HTTP(ctx, errors.NewWithError(fmt.Errorf("failed to parse input body: %w", err), errors.ErrInternalError)) + } + + payment, setStatusErr := receiver.statusService.SetStatusSuccess(ctx.Request().Context(), request.Object.ID) + if setStatusErr != nil { + receiver.logger.Error("failed to set success payment status on of ", zap.Error(setStatusErr)) + return errors.HTTP(ctx, setStatusErr) + } + + if err := receiver.callbackService.OnSuccess(ctx.Request().Context(), &models.Event{ + Key: string(request.Event), + Message: "yandex send event: payment succeeded", + Payment: payment, + }); err != nil { + receiver.logger.Error("failed send success callback on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, nil) +} + +func (receiver *YandexStatusController) SetPaymentStatusWaiting(ctx echo.Context) error { + request, err := echotools.Bind[yandex.WebhookNotification[yandex.Payment]](ctx) + if err != nil { + receiver.logger.Error("failed to parse body on of ", zap.Error(err)) + return errors.HTTP(ctx, errors.NewWithError(fmt.Errorf("failed to parse input body: %w", err), errors.ErrInternalError)) + } + + if _, err := receiver.statusService.SetStatusWaiting(ctx.Request().Context(), request.Object.ID); err != nil { + receiver.logger.Error("failed to set waiting payment status on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, nil) +} + +func (receiver *YandexStatusController) SetRefundStatusSucceeded(ctx echo.Context) error { + request, err := echotools.Bind[yandex.WebhookNotification[yandex.Payment]](ctx) + if err != nil { + receiver.logger.Error("failed to parse body on of ", zap.Error(err)) + return errors.HTTP(ctx, errors.NewWithError(fmt.Errorf("failed to parse input body: %w", err), errors.ErrInternalError)) + } + + if _, err := receiver.statusService.SetStatusRefund(ctx.Request().Context(), request.Object.ID); err != nil { + receiver.logger.Error("failed to set payment status refund on of ", zap.Error(err)) + return errors.HTTP(ctx, err) + } + + return ctx.JSON(http.StatusOK, nil) +} diff --git a/internal/interface/repository/payment.go b/internal/interface/repository/payment.go new file mode 100644 index 0000000..041fab2 --- /dev/null +++ b/internal/interface/repository/payment.go @@ -0,0 +1,158 @@ +package repository + +import ( + "context" + "fmt" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +var PaymentFields = struct { + ID string + UserID string + PaymentID string + IdempotencePaymentID string + ClientIP string + Currency string + Amount string + Type string + Status string + Completed string + IsDeleted string + CreatedAt string + UpdatedAt string + DeletedAt string + RawPaymentBody string + CallbackHostGRPC string +}{ + ID: "_id", + UserID: "userId", + PaymentID: "paymentId", + IdempotencePaymentID: "idempotencePaymentId", + ClientIP: "clientIp", + Currency: "currency", + Amount: "amount", + Type: "type", + Status: "status", + Completed: "completed", + IsDeleted: "isDeleted", + CreatedAt: "createdAt", + UpdatedAt: "updatedAt", + DeletedAt: "deletedAt", + RawPaymentBody: "rawPaymentBody", + CallbackHostGRPC: "callbackHostGrpc", +} + +type PaymentRepositoryDeps struct { + Logger *zap.Logger + Collection *mongo.Collection +} + +type PaymentRepository struct { + logger *zap.Logger + collection *mongo.Collection +} + +func NewPaymentRepository(deps PaymentRepositoryDeps) (*PaymentRepository, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger in nil on ", errors.ErrInvalidArgs) + } + + if deps.Collection == nil { + return nil, errors.NewWithMessage("Collection in nil on ", errors.ErrInvalidArgs) + } + + return &PaymentRepository{ + logger: deps.Logger, + collection: deps.Collection, + }, nil +} + +func (receiver *PaymentRepository) SetPaymentComplete(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { + payment := models.Payment{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + filter := bson.M{PaymentFields.PaymentID: paymentID} + update := bson.M{"$set": bson.M{ + PaymentFields.Completed: true, + PaymentFields.UpdatedAt: time.Now(), + }} + + if err := receiver.collection.FindOneAndUpdate(ctx, filter, update, options).Decode(&payment); err != nil { + receiver.logger.Error("failed to set payment complete on of ", + zap.Error(err), + zap.String("paymentID", paymentID), + ) + + removeErr := errors.NewWithError( + fmt.Errorf("failed to set payment complete with <%s> on of : %w", paymentID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + return nil, removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &payment, nil +} + +func (receiver *PaymentRepository) Insert(ctx context.Context, payment *models.Payment) (*models.Payment, errors.Error) { + sanitizedPayment := payment.Sanitize() + + result, err := receiver.collection.InsertOne(ctx, sanitizedPayment) + if err != nil { + receiver.logger.Error("failed to insert payment on of ", + zap.Any("payment", sanitizedPayment), + zap.Error(err), + ) + + return nil, errors.NewWithError( + fmt.Errorf("failed to insert payment on of : %w", err), + errors.ErrInternalError, + ) + } + + insertedID := result.InsertedID.(primitive.ObjectID).Hex() + sanitizedPayment.ID = insertedID + + return sanitizedPayment, nil +} + +func (receiver *PaymentRepository) SetPaymentStatus(ctx context.Context, paymentID string, status models.PaymentStatus) (*models.Payment, errors.Error) { + payment := models.Payment{} + options := options.FindOneAndUpdate().SetReturnDocument(options.After) + filter := bson.M{PaymentFields.PaymentID: paymentID} + update := bson.M{"$set": bson.M{ + PaymentFields.Status: status, + PaymentFields.UpdatedAt: time.Now(), + }} + + if err := receiver.collection.FindOneAndUpdate(ctx, filter, update, options).Decode(&payment); err != nil { + receiver.logger.Error("failed to set payment status on of ", + zap.Error(err), + zap.String("paymentID", paymentID), + ) + + removeErr := errors.NewWithError( + fmt.Errorf("failed to set payment status with <%s> on of : %w", paymentID, err), + errors.ErrInternalError, + ) + + if err == mongo.ErrNoDocuments { + return nil, removeErr.SetType(errors.ErrNotFound) + } + + return nil, removeErr + } + + return &payment, nil +} diff --git a/internal/models/common.go b/internal/models/common.go new file mode 100644 index 0000000..b94ef86 --- /dev/null +++ b/internal/models/common.go @@ -0,0 +1,6 @@ +package models + +type ResponseErrorHTTP struct { + StatusCode int `json:"statusCode"` + Message string `json:"message"` +} diff --git a/internal/models/config.go b/internal/models/config.go new file mode 100644 index 0000000..3396fe4 --- /dev/null +++ b/internal/models/config.go @@ -0,0 +1,32 @@ +package models + +import ( + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" +) + +type Config struct { + HTTP ConfigurationHTTP + GRPC ConfigurationGRPC + Service ServiceConfiguration + Database mongo.Configuration +} + +type ConfigurationHTTP struct { + Host string `env:"HTTP_HOST,default=localhost"` + Port string `env:"HTTP_PORT,default=8080"` + Domen string `env:"HTTP_DOMEN,required"` +} + +type ConfigurationGRPC struct { + Host string `env:"GRPC_HOST,default=0.0.0.0"` + Port string `env:"GRPC_PORT,default=8081"` +} + +type ServiceConfiguration struct { + YoomomeyConfiguration YoomomeyConfiguration +} + +type YoomomeyConfiguration struct { + StoreID string `env:"YOOMONEY_STORE_ID,required"` + SecretKey string `env:"YOOMONEY_SECRET_KEY,required"` +} diff --git a/internal/models/event.go b/internal/models/event.go new file mode 100644 index 0000000..96349c3 --- /dev/null +++ b/internal/models/event.go @@ -0,0 +1,7 @@ +package models + +type Event struct { + Key string + Message string + Payment *Payment +} diff --git a/internal/models/payment.go b/internal/models/payment.go new file mode 100644 index 0000000..9bb0567 --- /dev/null +++ b/internal/models/payment.go @@ -0,0 +1,101 @@ +package models + +import ( + "time" + + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" +) + +type Payment struct { + ID string `json:"id" bson:"_id,omitempty"` + UserID string `json:"userId" bson:"userId"` + PaymentID string `json:"paymentId" bson:"paymentId"` + IdempotencePaymentID string `json:"idempotencePaymentId" bson:"idempotencePaymentId"` + ClientIP string `json:"clientIp" bson:"clientIp"` + Currency string `json:"currency" bson:"currency"` + Amount int64 `json:"amount" bson:"amount"` + Type PaymentType `json:"type" bson:"type"` + Status PaymentStatus `json:"status" bson:"status"` + Completed bool `json:"completed" bson:"completed"` + IsDeleted bool `json:"isDeleted" bson:"isDeleted"` + CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` + DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"` + RawPaymentBody any `json:"rawPaymentBody,omitempty" bson:"rawPaymentBody,omitempty"` + + /* + Список адресов в формате host:port, куда будет отправляться платёж при успешной операции + + Запрос будет отправляться по протоколу GRPC + */ + CallbackHostGRPC []string `json:"callbackHostGrpc" bson:"callbackHostGrpc"` +} + +func (receiver *Payment) Sanitize() *Payment { + now := time.Now() + + receiver.ID = "" + receiver.CreatedAt = now + receiver.UpdatedAt = now + receiver.DeletedAt = nil + receiver.IsDeleted = false + receiver.Completed = false + + return receiver +} + +type CreatePaymentResult struct { + RedirectURL string + Payment *Payment +} + +type CreatePayment[T any] struct { + Type PaymentType + Currency string + Amount int64 + CallbackHostGRPC []string + ReturnURL string + UserID string + ClientIP string + Requisites T +} + +type BankCard struct { + Number string + ExpiryYear string + ExpiryMonth string + CSC *string + CardHolderName *string +} + +type PaymentType string + +const ( + PaymentTypeBankCard PaymentType = "bankCard" + PaymentTypeTinkoff PaymentType = "tinkoffBank" + PaymentTypeQiwi PaymentType = "qiwi" + PaymentTypeSberPay PaymentType = "sberbank" + PaymentTypeYoomoney PaymentType = "yoomoney" + PaymentTypeMobile PaymentType = "mobile" + PaymentTypeInstallments PaymentType = "installments" + PaymentTypeCash PaymentType = "cash" + PaymentTypeSBP PaymentType = "sbp" + PaymentTypeSberB2B PaymentType = "b2bSberbank" + PaymentTypeAlfabank PaymentType = "alfabank" +) + +var ( + YandexPaymentTypeMap = map[PaymentType]yandex.PaymentType{ + PaymentTypeBankCard: yandex.PaymentTypeBankCard, + PaymentTypeTinkoff: yandex.PaymentTypeTinkoff, + PaymentTypeQiwi: yandex.PaymentTypeQiwi, + PaymentTypeSberPay: yandex.PaymentTypeSberPay, + PaymentTypeYoomoney: yandex.PaymentTypeYoomoney, + PaymentTypeMobile: yandex.PaymentTypeMobile, + PaymentTypeInstallments: yandex.PaymentTypeInstallments, + PaymentTypeCash: yandex.PaymentTypeCash, + PaymentTypeSBP: yandex.PaymentTypeSBP, + PaymentTypeSberB2B: yandex.PaymentTypeSberB2B, + PaymentTypeAlfabank: yandex.PaymentTypeAlfabank, + } +) diff --git a/internal/models/status.go b/internal/models/status.go new file mode 100644 index 0000000..3fdca5e --- /dev/null +++ b/internal/models/status.go @@ -0,0 +1,29 @@ +package models + +import "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + +type PaymentStatus string + +const ( + PaymentStatusPending PaymentStatus = "pending" + PaymentStatusWaiting PaymentStatus = "waiting" + PaymentStatusSuccessfully PaymentStatus = "success" + PaymentStatusCanceled PaymentStatus = "canceled" + PaymentStatusRefund PaymentStatus = "refund" +) + +var ( + YandexPaymentStatusMap = map[PaymentStatus]yandex.PaymentStatus{ + PaymentStatusPending: yandex.PaymentStatusPending, + PaymentStatusWaiting: yandex.PaymentStatusWaiting, + PaymentStatusSuccessfully: yandex.PaymentStatusSuccessfully, + PaymentStatusCanceled: yandex.PaymentStatusCanceled, + } + + PaymentStatusMap = map[string]PaymentStatus{ + string(yandex.PaymentStatusPending): PaymentStatusPending, + string(yandex.PaymentStatusWaiting): PaymentStatusWaiting, + string(yandex.PaymentStatusSuccessfully): PaymentStatusSuccessfully, + string(yandex.PaymentStatusCanceled): PaymentStatusCanceled, + } +) diff --git a/internal/models/yandex/common.go b/internal/models/yandex/common.go new file mode 100644 index 0000000..fdf87c7 --- /dev/null +++ b/internal/models/yandex/common.go @@ -0,0 +1,14 @@ +package yandex + +type Amount struct { + Value string `json:"value" bson:"value"` + Currency string `json:"currency" bson:"currency"` +} + +type BankCardInformation struct { + Number string `json:"number"` + ExpiryYear string `json:"expiry_year"` + ExpiryMonth string `json:"expiry_month"` + CSC string `json:"csc,omitempty"` + CardHolderName string `json:"cardholder,omitempty"` +} diff --git a/internal/models/yandex/payment.go b/internal/models/yandex/payment.go new file mode 100644 index 0000000..36c194f --- /dev/null +++ b/internal/models/yandex/payment.go @@ -0,0 +1,44 @@ +package yandex + +// Payment description https://yookassa.ru/developers/api#payment_object +type Payment struct { + ID string `json:"id" bson:"id"` + Status PaymentStatus `json:"status" bson:"status"` + Amount Amount `json:"amount" bson:"amount"` + Confirmation *ConfirmationRedirect `json:"confirmation" bson:"confirmation"` + IncomeAmount *Amount `json:"income_amount,omitempty" bson:"income_amount,omitempty"` + Description string `json:"description,omitempty" bson:"description,omitempty"` + PaymentMethod any `json:"payment_method,omitempty" bson:"payment_method,omitempty"` + Recipient Recipient `json:"recipient" bson:"recipient"` + CapturedAt string `json:"captured_at,omitempty" bson:"captured_at,omitempty"` + ExpiresAt string `json:"expires_at,omitempty" bson:"expires_at,omitempty"` + CreatedAt string `json:"created_at" bson:"created_at"` + IsTest bool `json:"test" bson:"test"` + RefundedAmount *Amount `json:"refunded_amount,omitempty" bson:"refunded_amount,omitempty"` + IsPaid bool `json:"paid" bson:"paid"` + IsRefundable bool `json:"refundable" bson:"refundable"` + ReceiptRegistration string `json:"receipt_registration,omitempty" bson:"receipt_registration,omitempty"` + Metadata any `json:"metadata,omitempty" bson:"metadata,omitempty"` + CancellationDetails any `json:"cancellation_details,omitempty" bson:"cancellation_details,omitempty"` + AuthorizationDetails any `json:"authorization_details,omitempty" bson:"authorization_details,omitempty"` + Transfers []any `json:"transfers,omitempty" bson:"transfers,omitempty"` + Deal any `json:"deal,omitempty" bson:"deal,omitempty"` + MerchantCustomerID string `json:"merchant_customer_id,omitempty" bson:"merchant_customer_id,omitempty"` +} + +type Recipient struct { + AccountID string `json:"account_id" bson:"account_id"` + GatewayID string `json:"gateway_id" bson:"gateway_id"` +} + +type Confirmation struct { +} + +type PaymentStatus string + +const ( + PaymentStatusPending PaymentStatus = "pending" + PaymentStatusWaiting PaymentStatus = "waiting_for_capture" + PaymentStatusSuccessfully PaymentStatus = "succeeded" + PaymentStatusCanceled PaymentStatus = "canceled" +) diff --git a/internal/models/yandex/payment_confirmation.go b/internal/models/yandex/payment_confirmation.go new file mode 100644 index 0000000..338a861 --- /dev/null +++ b/internal/models/yandex/payment_confirmation.go @@ -0,0 +1,55 @@ +package yandex + +type ConfirmationType string + +const ( + ConfirmationTypeEmbedded ConfirmationType = "embedded" + ConfirmationTypeExternal ConfirmationType = "external" + ConfirmationTypeMobile ConfirmationType = "mobile_application" + ConfirmationTypeQR ConfirmationType = "qr" + ConfirmationTypeRedirect ConfirmationType = "redirect" +) + +type ConfirmationEmbedded struct { + Type ConfirmationType `json:"type"` + ConfirmationToken string `json:"confirmation_token"` +} + +type ConfirmationExternal struct { + Type ConfirmationType `json:"type"` +} + +type ConfirmationMobile struct { + Type ConfirmationType `json:"type"` + ConfirmationURL string `json:"confirmation_url"` +} + +type ConfirmationQR struct { + Type ConfirmationType `json:"type"` + ConfirmationData string `json:"confirmation_data"` +} + +type ConfirmationRedirect struct { + Type ConfirmationType `json:"type"` + ConfirmationURL string `json:"confirmation_url"` + ReturnURL *string `json:"return_url,omitempty"` + Enforce *bool `json:"enforce,omitempty"` +} + +type CreateConfirmationLocale struct { + Type ConfirmationType `json:"type"` + Locale string `json:"locale,omitempty"` +} + +type CreateConfirmationMobile struct { + Type ConfirmationType `json:"type"` + Locale string `json:"locale,omitempty"` + ReturnURL string `json:"return_url"` +} + +type CreateConfirmationRedirect struct { + Type ConfirmationType `json:"type"` + Locale string `json:"locale,omitempty"` + ReturnURL string `json:"return_url"` + Enforce bool `json:"enforce,omitempty"` +} diff --git a/internal/models/yandex/payment_create.go b/internal/models/yandex/payment_create.go new file mode 100644 index 0000000..2f62c08 --- /dev/null +++ b/internal/models/yandex/payment_create.go @@ -0,0 +1,19 @@ +package yandex + +// CreatePaymentRequest description https://yookassa.ru/developers/api#create_payment +type CreatePaymentRequest[T any] struct { + Amount Amount `json:"amount"` + Description string `json:"description,omitempty"` + PaymentMethodID *string `json:"payment_method_id,omitempty"` + PaymentMethodData *T `json:"payment_method_data,omitempty"` + Confirmation *CreateConfirmationRedirect `json:"confirmation,omitempty"` + SavePaymentMethod bool `json:"save_payment_method,omitempty"` + Capture bool `json:"capture,omitempty"` + ClientIP string `json:"client_ip,omitempty"` + Metadata any `json:"metadata,omitempty"` + Airline any `json:"airline,omitempty"` + Transfers []any `json:"transfers,omitempty"` + Deal any `json:"deal,omitempty"` + FraudData any `json:"fraud_data,omitempty"` + MerchantCustomerID any `json:"merchant_customer_id,omitempty"` +} diff --git a/internal/models/yandex/payment_method.go b/internal/models/yandex/payment_method.go new file mode 100644 index 0000000..64a07f5 --- /dev/null +++ b/internal/models/yandex/payment_method.go @@ -0,0 +1,42 @@ +package yandex + +type PaymentType string + +const ( + PaymentTypeBankCard PaymentType = "bank_card" + PaymentTypeTinkoff PaymentType = "tinkoff_bank" + PaymentTypeQiwi PaymentType = "qiwi" + PaymentTypeSberPay PaymentType = "sberbank" + PaymentTypeYoomoney PaymentType = "yoo_money" + PaymentTypeMobile PaymentType = "mobile_balance" + PaymentTypeInstallments PaymentType = "installments" + PaymentTypeCash PaymentType = "cash" + PaymentTypeSBP PaymentType = "sbp" + PaymentTypeSberB2B PaymentType = "b2b_sberbank" + PaymentTypeAlfabank PaymentType = "alfabank" +) + +type PaymentMethodBankCard struct { + Type PaymentType `json:"type"` + Card *BankCardInformation `json:"card,omitempty"` +} + +type PaymentMethodLogin struct { + Type PaymentType `json:"type"` + Login string `json:"login,omitempty"` +} + +type PaymentMethodB2B struct { + Type PaymentType `json:"type"` + PaymentPurpose string `json:"payment_purpose"` + VatData any `json:"vat_data"` +} + +type PaymentMethodPhone struct { + Type PaymentType `json:"type"` + Phone string `json:"phone,omitempty"` +} + +type PaymentMethodType struct { + Type PaymentType `json:"type"` +} diff --git a/internal/models/yandex/webhook.go b/internal/models/yandex/webhook.go new file mode 100644 index 0000000..55bbfc0 --- /dev/null +++ b/internal/models/yandex/webhook.go @@ -0,0 +1,54 @@ +package yandex + +type WebhookEventType string + +const ( + /* Платеж перешел в статус ожидания. */ + WebhookEventPaymentWaiting WebhookEventType = "payment.waiting_for_capture" + + /* Платеж перешел в статус "Успешно". */ + WebhookEventPaymentSucceeded WebhookEventType = "payment.succeeded" + + /* Платеж перешел в статус "Отменён". */ + WebhookEventPaymentCanceled WebhookEventType = "payment.canceled" +) + +const ( + /* Возврат перешел в статус "Успешно". */ + WebhookEventRefundSucceeded WebhookEventType = "refund.succeeded" +) + +const ( + /* Сделка перешла в статус "Закрыта". */ + WebhookEventDealClosed WebhookEventType = "deal.closed" +) + +const ( + /* Выплата перешла в статус "Отменено". */ + WebhookEventPayoutCanceled WebhookEventType = "payout.canceled" + + /* Выплата перешла в статус "Успешно". */ + WebhookEventPayoutSucceeded WebhookEventType = "payout.succeeded" +) + +type CreateWebhookRequest struct { + Event WebhookEventType `json:"event"` + URL string `json:"url"` +} + +type GetWebhooksResponse struct { + Type string `json:"type"` + Webhooks []Webhook `json:"items"` +} + +type Webhook struct { + ID string `json:"id"` + Event WebhookEventType `json:"event"` + URL string `json:"url"` +} + +type WebhookNotification[T any] struct { + Type string `json:"type"` + Event WebhookEventType `json:"event"` + Object T `json:"object"` +} diff --git a/internal/proto/payment_callback/service.pb.go b/internal/proto/payment_callback/service.pb.go new file mode 100644 index 0000000..f477716 --- /dev/null +++ b/internal/proto/payment_callback/service.pb.go @@ -0,0 +1,325 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: callback/service.proto + +package payment_callback + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + _ "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"` + Message string `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` + Payment *Payment `protobuf:"bytes,3,opt,name=Payment,proto3" json:"Payment,omitempty"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_callback_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_callback_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_callback_service_proto_rawDescGZIP(), []int{0} +} + +func (x *Event) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *Event) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Event) GetPayment() *Payment { + if x != nil { + return x.Payment + } + return nil +} + +type Payment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"` + PaymentID string `protobuf:"bytes,3,opt,name=PaymentID,proto3" json:"PaymentID,omitempty"` + IdempotencePaymentID string `protobuf:"bytes,4,opt,name=IdempotencePaymentID,proto3" json:"IdempotencePaymentID,omitempty"` + Amount int64 `protobuf:"varint,5,opt,name=Amount,proto3" json:"Amount,omitempty"` + Currency string `protobuf:"bytes,6,opt,name=Currency,proto3" json:"Currency,omitempty"` + Type string `protobuf:"bytes,7,opt,name=Type,proto3" json:"Type,omitempty"` + Status string `protobuf:"bytes,8,opt,name=Status,proto3" json:"Status,omitempty"` + Completed bool `protobuf:"varint,9,opt,name=Completed,proto3" json:"Completed,omitempty"` +} + +func (x *Payment) Reset() { + *x = Payment{} + if protoimpl.UnsafeEnabled { + mi := &file_callback_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Payment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payment) ProtoMessage() {} + +func (x *Payment) ProtoReflect() protoreflect.Message { + mi := &file_callback_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Payment.ProtoReflect.Descriptor instead. +func (*Payment) Descriptor() ([]byte, []int) { + return file_callback_service_proto_rawDescGZIP(), []int{1} +} + +func (x *Payment) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Payment) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *Payment) GetPaymentID() string { + if x != nil { + return x.PaymentID + } + return "" +} + +func (x *Payment) GetIdempotencePaymentID() string { + if x != nil { + return x.IdempotencePaymentID + } + return "" +} + +func (x *Payment) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *Payment) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *Payment) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Payment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Payment) GetCompleted() bool { + if x != nil { + return x.Completed + } + return false +} + +var File_callback_service_proto protoreflect.FileDescriptor + +var file_callback_service_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x68, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x33, 0x0a, + 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x22, 0x81, 0x02, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, + 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x44, 0x12, 0x32, 0x0a, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, + 0x6e, 0x63, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x32, 0x98, 0x01, 0x0a, 0x16, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x4f, 0x6e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x17, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x12, 0x3e, 0x0a, 0x09, 0x4f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x17, + 0x2e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, + 0x00, 0x42, 0x14, 0x5a, 0x12, 0x2e, 0x2f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x63, + 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_callback_service_proto_rawDescOnce sync.Once + file_callback_service_proto_rawDescData = file_callback_service_proto_rawDesc +) + +func file_callback_service_proto_rawDescGZIP() []byte { + file_callback_service_proto_rawDescOnce.Do(func() { + file_callback_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_callback_service_proto_rawDescData) + }) + return file_callback_service_proto_rawDescData +} + +var file_callback_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_callback_service_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: payment_callback.Event + (*Payment)(nil), // 1: payment_callback.Payment + (*emptypb.Empty)(nil), // 2: google.protobuf.Empty +} +var file_callback_service_proto_depIdxs = []int32{ + 1, // 0: payment_callback.Event.Payment:type_name -> payment_callback.Payment + 0, // 1: payment_callback.PaymentCallbackService.OnSuccess:input_type -> payment_callback.Event + 0, // 2: payment_callback.PaymentCallbackService.OnFailure:input_type -> payment_callback.Event + 2, // 3: payment_callback.PaymentCallbackService.OnSuccess:output_type -> google.protobuf.Empty + 2, // 4: payment_callback.PaymentCallbackService.OnFailure:output_type -> google.protobuf.Empty + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_callback_service_proto_init() } +func file_callback_service_proto_init() { + if File_callback_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_callback_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_callback_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_callback_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_callback_service_proto_goTypes, + DependencyIndexes: file_callback_service_proto_depIdxs, + MessageInfos: file_callback_service_proto_msgTypes, + }.Build() + File_callback_service_proto = out.File + file_callback_service_proto_rawDesc = nil + file_callback_service_proto_goTypes = nil + file_callback_service_proto_depIdxs = nil +} diff --git a/internal/proto/payment_callback/service_grpc.pb.go b/internal/proto/payment_callback/service_grpc.pb.go new file mode 100644 index 0000000..e47ffb6 --- /dev/null +++ b/internal/proto/payment_callback/service_grpc.pb.go @@ -0,0 +1,136 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package payment_callback + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PaymentCallbackServiceClient is the client API for PaymentCallbackService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PaymentCallbackServiceClient interface { + OnSuccess(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) + OnFailure(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type paymentCallbackServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPaymentCallbackServiceClient(cc grpc.ClientConnInterface) PaymentCallbackServiceClient { + return &paymentCallbackServiceClient{cc} +} + +func (c *paymentCallbackServiceClient) OnSuccess(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/payment_callback.PaymentCallbackService/OnSuccess", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *paymentCallbackServiceClient) OnFailure(ctx context.Context, in *Event, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/payment_callback.PaymentCallbackService/OnFailure", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PaymentCallbackServiceServer is the server API for PaymentCallbackService service. +// All implementations should embed UnimplementedPaymentCallbackServiceServer +// for forward compatibility +type PaymentCallbackServiceServer interface { + OnSuccess(context.Context, *Event) (*emptypb.Empty, error) + OnFailure(context.Context, *Event) (*emptypb.Empty, error) +} + +// UnimplementedPaymentCallbackServiceServer should be embedded to have forward compatible implementations. +type UnimplementedPaymentCallbackServiceServer struct { +} + +func (UnimplementedPaymentCallbackServiceServer) OnSuccess(context.Context, *Event) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnSuccess not implemented") +} +func (UnimplementedPaymentCallbackServiceServer) OnFailure(context.Context, *Event) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method OnFailure not implemented") +} + +// UnsafePaymentCallbackServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PaymentCallbackServiceServer will +// result in compilation errors. +type UnsafePaymentCallbackServiceServer interface { + mustEmbedUnimplementedPaymentCallbackServiceServer() +} + +func RegisterPaymentCallbackServiceServer(s grpc.ServiceRegistrar, srv PaymentCallbackServiceServer) { + s.RegisterService(&PaymentCallbackService_ServiceDesc, srv) +} + +func _PaymentCallbackService_OnSuccess_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Event) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentCallbackServiceServer).OnSuccess(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/payment_callback.PaymentCallbackService/OnSuccess", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentCallbackServiceServer).OnSuccess(ctx, req.(*Event)) + } + return interceptor(ctx, in, info, handler) +} + +func _PaymentCallbackService_OnFailure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Event) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PaymentCallbackServiceServer).OnFailure(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/payment_callback.PaymentCallbackService/OnFailure", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PaymentCallbackServiceServer).OnFailure(ctx, req.(*Event)) + } + return interceptor(ctx, in, info, handler) +} + +// PaymentCallbackService_ServiceDesc is the grpc.ServiceDesc for PaymentCallbackService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PaymentCallbackService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "payment_callback.PaymentCallbackService", + HandlerType: (*PaymentCallbackServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "OnSuccess", + Handler: _PaymentCallbackService_OnSuccess_Handler, + }, + { + MethodName: "OnFailure", + Handler: _PaymentCallbackService_OnFailure_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "callback/service.proto", +} diff --git a/internal/proto/treasurer/payment.model.pb.go b/internal/proto/treasurer/payment.model.pb.go new file mode 100644 index 0000000..eae24ef --- /dev/null +++ b/internal/proto/treasurer/payment.model.pb.go @@ -0,0 +1,531 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: treasurer/payment.model.proto + +package treasurer + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type MainPaymentSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Currency string `protobuf:"bytes,1,opt,name=Currency,proto3" json:"Currency,omitempty"` + Amount int64 `protobuf:"varint,2,opt,name=Amount,proto3" json:"Amount,omitempty"` + CallbackHostGRPC []string `protobuf:"bytes,3,rep,name=CallbackHostGRPC,proto3" json:"CallbackHostGRPC,omitempty"` + ReturnURL string `protobuf:"bytes,4,opt,name=ReturnURL,proto3" json:"ReturnURL,omitempty"` + UserID string `protobuf:"bytes,5,opt,name=UserID,proto3" json:"UserID,omitempty"` + ClientIP string `protobuf:"bytes,6,opt,name=ClientIP,proto3" json:"ClientIP,omitempty"` +} + +func (x *MainPaymentSettings) Reset() { + *x = MainPaymentSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_payment_model_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MainPaymentSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MainPaymentSettings) ProtoMessage() {} + +func (x *MainPaymentSettings) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_payment_model_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MainPaymentSettings.ProtoReflect.Descriptor instead. +func (*MainPaymentSettings) Descriptor() ([]byte, []int) { + return file_treasurer_payment_model_proto_rawDescGZIP(), []int{0} +} + +func (x *MainPaymentSettings) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *MainPaymentSettings) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *MainPaymentSettings) GetCallbackHostGRPC() []string { + if x != nil { + return x.CallbackHostGRPC + } + return nil +} + +func (x *MainPaymentSettings) GetReturnURL() string { + if x != nil { + return x.ReturnURL + } + return "" +} + +func (x *MainPaymentSettings) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *MainPaymentSettings) GetClientIP() string { + if x != nil { + return x.ClientIP + } + return "" +} + +type BankCardInformation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Number string `protobuf:"bytes,1,opt,name=Number,proto3" json:"Number,omitempty"` + ExpiryYear string `protobuf:"bytes,2,opt,name=ExpiryYear,proto3" json:"ExpiryYear,omitempty"` + ExpiryMonth string `protobuf:"bytes,3,opt,name=ExpiryMonth,proto3" json:"ExpiryMonth,omitempty"` + CSC *string `protobuf:"bytes,4,opt,name=CSC,proto3,oneof" json:"CSC,omitempty"` + CardHolderName *string `protobuf:"bytes,5,opt,name=CardHolderName,proto3,oneof" json:"CardHolderName,omitempty"` +} + +func (x *BankCardInformation) Reset() { + *x = BankCardInformation{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_payment_model_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BankCardInformation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BankCardInformation) ProtoMessage() {} + +func (x *BankCardInformation) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_payment_model_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BankCardInformation.ProtoReflect.Descriptor instead. +func (*BankCardInformation) Descriptor() ([]byte, []int) { + return file_treasurer_payment_model_proto_rawDescGZIP(), []int{1} +} + +func (x *BankCardInformation) GetNumber() string { + if x != nil { + return x.Number + } + return "" +} + +func (x *BankCardInformation) GetExpiryYear() string { + if x != nil { + return x.ExpiryYear + } + return "" +} + +func (x *BankCardInformation) GetExpiryMonth() string { + if x != nil { + return x.ExpiryMonth + } + return "" +} + +func (x *BankCardInformation) GetCSC() string { + if x != nil && x.CSC != nil { + return *x.CSC + } + return "" +} + +func (x *BankCardInformation) GetCardHolderName() string { + if x != nil && x.CardHolderName != nil { + return *x.CardHolderName + } + return "" +} + +type Payment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"` + PaymentID string `protobuf:"bytes,3,opt,name=PaymentID,proto3" json:"PaymentID,omitempty"` + IdempotencePaymentID string `protobuf:"bytes,4,opt,name=IdempotencePaymentID,proto3" json:"IdempotencePaymentID,omitempty"` + ClientIP string `protobuf:"bytes,5,opt,name=ClientIP,proto3" json:"ClientIP,omitempty"` + Currency string `protobuf:"bytes,6,opt,name=Currency,proto3" json:"Currency,omitempty"` + Amount int64 `protobuf:"varint,7,opt,name=Amount,proto3" json:"Amount,omitempty"` + Type string `protobuf:"bytes,8,opt,name=Type,proto3" json:"Type,omitempty"` + Status string `protobuf:"bytes,9,opt,name=Status,proto3" json:"Status,omitempty"` + RawPaymentBody *anypb.Any `protobuf:"bytes,10,opt,name=RawPaymentBody,proto3" json:"RawPaymentBody,omitempty"` + CallbackHostGRPC []string `protobuf:"bytes,11,rep,name=CallbackHostGRPC,proto3" json:"CallbackHostGRPC,omitempty"` + Completed bool `protobuf:"varint,12,opt,name=Completed,proto3" json:"Completed,omitempty"` + IsDeleted bool `protobuf:"varint,13,opt,name=IsDeleted,proto3" json:"IsDeleted,omitempty"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,14,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` + DeletedAt *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=DeletedAt,proto3,oneof" json:"DeletedAt,omitempty"` +} + +func (x *Payment) Reset() { + *x = Payment{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_payment_model_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Payment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payment) ProtoMessage() {} + +func (x *Payment) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_payment_model_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Payment.ProtoReflect.Descriptor instead. +func (*Payment) Descriptor() ([]byte, []int) { + return file_treasurer_payment_model_proto_rawDescGZIP(), []int{2} +} + +func (x *Payment) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +func (x *Payment) GetUserID() string { + if x != nil { + return x.UserID + } + return "" +} + +func (x *Payment) GetPaymentID() string { + if x != nil { + return x.PaymentID + } + return "" +} + +func (x *Payment) GetIdempotencePaymentID() string { + if x != nil { + return x.IdempotencePaymentID + } + return "" +} + +func (x *Payment) GetClientIP() string { + if x != nil { + return x.ClientIP + } + return "" +} + +func (x *Payment) GetCurrency() string { + if x != nil { + return x.Currency + } + return "" +} + +func (x *Payment) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +func (x *Payment) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Payment) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Payment) GetRawPaymentBody() *anypb.Any { + if x != nil { + return x.RawPaymentBody + } + return nil +} + +func (x *Payment) GetCallbackHostGRPC() []string { + if x != nil { + return x.CallbackHostGRPC + } + return nil +} + +func (x *Payment) GetCompleted() bool { + if x != nil { + return x.Completed + } + return false +} + +func (x *Payment) GetIsDeleted() bool { + if x != nil { + return x.IsDeleted + } + return false +} + +func (x *Payment) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +func (x *Payment) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Payment) GetDeletedAt() *timestamppb.Timestamp { + if x != nil { + return x.DeletedAt + } + return nil +} + +var File_treasurer_payment_model_proto protoreflect.FileDescriptor + +var file_treasurer_payment_model_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, 0x01, 0x0a, 0x13, 0x4d, 0x61, 0x69, 0x6e, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1a, + 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x6f, + 0x73, 0x74, 0x47, 0x52, 0x50, 0x43, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x43, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x6f, 0x73, 0x74, 0x47, 0x52, 0x50, 0x43, 0x12, 0x1c, + 0x0a, 0x09, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x55, 0x52, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, + 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, + 0x22, 0xce, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x59, 0x65, 0x61, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x59, 0x65, 0x61, 0x72, + 0x12, 0x20, 0x0a, 0x0b, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x4d, 0x6f, 0x6e, + 0x74, 0x68, 0x12, 0x15, 0x0a, 0x03, 0x43, 0x53, 0x43, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x03, 0x43, 0x53, 0x43, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0e, 0x43, 0x61, 0x72, + 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x01, 0x52, 0x0e, 0x43, 0x61, 0x72, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x43, 0x53, 0x43, 0x42, 0x11, + 0x0a, 0x0f, 0x5f, 0x43, 0x61, 0x72, 0x64, 0x48, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xe6, 0x04, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, + 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x44, 0x12, 0x32, 0x0a, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, + 0x63, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x49, 0x50, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x49, 0x50, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x52, 0x61, 0x77, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x42, 0x6f, 0x64, 0x79, 0x18, 0x0a, 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, 0x0e, 0x52, 0x61, 0x77, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x64, + 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x6f, 0x73, + 0x74, 0x47, 0x52, 0x50, 0x43, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x43, 0x61, 0x6c, + 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x6f, 0x73, 0x74, 0x47, 0x52, 0x50, 0x43, 0x12, 0x1c, 0x0a, + 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x49, + 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x49, 0x73, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3d, 0x0a, + 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, + 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_treasurer_payment_model_proto_rawDescOnce sync.Once + file_treasurer_payment_model_proto_rawDescData = file_treasurer_payment_model_proto_rawDesc +) + +func file_treasurer_payment_model_proto_rawDescGZIP() []byte { + file_treasurer_payment_model_proto_rawDescOnce.Do(func() { + file_treasurer_payment_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_treasurer_payment_model_proto_rawDescData) + }) + return file_treasurer_payment_model_proto_rawDescData +} + +var file_treasurer_payment_model_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_treasurer_payment_model_proto_goTypes = []interface{}{ + (*MainPaymentSettings)(nil), // 0: treasurer.MainPaymentSettings + (*BankCardInformation)(nil), // 1: treasurer.BankCardInformation + (*Payment)(nil), // 2: treasurer.Payment + (*anypb.Any)(nil), // 3: google.protobuf.Any + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp +} +var file_treasurer_payment_model_proto_depIdxs = []int32{ + 3, // 0: treasurer.Payment.RawPaymentBody:type_name -> google.protobuf.Any + 4, // 1: treasurer.Payment.UpdatedAt:type_name -> google.protobuf.Timestamp + 4, // 2: treasurer.Payment.CreatedAt:type_name -> google.protobuf.Timestamp + 4, // 3: treasurer.Payment.DeletedAt:type_name -> google.protobuf.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_treasurer_payment_model_proto_init() } +func file_treasurer_payment_model_proto_init() { + if File_treasurer_payment_model_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_treasurer_payment_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MainPaymentSettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_payment_model_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BankCardInformation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_payment_model_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_treasurer_payment_model_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_treasurer_payment_model_proto_msgTypes[2].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_treasurer_payment_model_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_treasurer_payment_model_proto_goTypes, + DependencyIndexes: file_treasurer_payment_model_proto_depIdxs, + MessageInfos: file_treasurer_payment_model_proto_msgTypes, + }.Build() + File_treasurer_payment_model_proto = out.File + file_treasurer_payment_model_proto_rawDesc = nil + file_treasurer_payment_model_proto_goTypes = nil + file_treasurer_payment_model_proto_depIdxs = nil +} diff --git a/internal/proto/treasurer/service.pb.go b/internal/proto/treasurer/service.pb.go new file mode 100644 index 0000000..eca2100 --- /dev/null +++ b/internal/proto/treasurer/service.pb.go @@ -0,0 +1,634 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc (unknown) +// source: treasurer/service.proto + +package treasurer + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetBankCardPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + BankCard *BankCardInformation `protobuf:"bytes,2,opt,name=BankCard,proto3" json:"BankCard,omitempty"` +} + +func (x *GetBankCardPaymentLinkRequest) Reset() { + *x = GetBankCardPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBankCardPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBankCardPaymentLinkRequest) ProtoMessage() {} + +func (x *GetBankCardPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetBankCardPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetBankCardPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{0} +} + +func (x *GetBankCardPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetBankCardPaymentLinkRequest) GetBankCard() *BankCardInformation { + if x != nil { + return x.BankCard + } + return nil +} + +type GetPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` +} + +func (x *GetPaymentLinkRequest) Reset() { + *x = GetPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentLinkRequest) ProtoMessage() {} + +func (x *GetPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{1} +} + +func (x *GetPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +type GetPhonePaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + Phone string `protobuf:"bytes,2,opt,name=Phone,proto3" json:"Phone,omitempty"` +} + +func (x *GetPhonePaymentLinkRequest) Reset() { + *x = GetPhonePaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPhonePaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPhonePaymentLinkRequest) ProtoMessage() {} + +func (x *GetPhonePaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPhonePaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetPhonePaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{2} +} + +func (x *GetPhonePaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetPhonePaymentLinkRequest) GetPhone() string { + if x != nil { + return x.Phone + } + return "" +} + +type GetLoginPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + Login string `protobuf:"bytes,2,opt,name=Login,proto3" json:"Login,omitempty"` +} + +func (x *GetLoginPaymentLinkRequest) Reset() { + *x = GetLoginPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLoginPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLoginPaymentLinkRequest) ProtoMessage() {} + +func (x *GetLoginPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLoginPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetLoginPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{3} +} + +func (x *GetLoginPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetLoginPaymentLinkRequest) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +type GetB2BPaymentLinkRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MainSettings *MainPaymentSettings `protobuf:"bytes,1,opt,name=MainSettings,proto3" json:"MainSettings,omitempty"` + PaymentPurpose string `protobuf:"bytes,2,opt,name=PaymentPurpose,proto3" json:"PaymentPurpose,omitempty"` + VatData *emptypb.Empty `protobuf:"bytes,3,opt,name=VatData,proto3" json:"VatData,omitempty"` +} + +func (x *GetB2BPaymentLinkRequest) Reset() { + *x = GetB2BPaymentLinkRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetB2BPaymentLinkRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetB2BPaymentLinkRequest) ProtoMessage() {} + +func (x *GetB2BPaymentLinkRequest) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetB2BPaymentLinkRequest.ProtoReflect.Descriptor instead. +func (*GetB2BPaymentLinkRequest) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{4} +} + +func (x *GetB2BPaymentLinkRequest) GetMainSettings() *MainPaymentSettings { + if x != nil { + return x.MainSettings + } + return nil +} + +func (x *GetB2BPaymentLinkRequest) GetPaymentPurpose() string { + if x != nil { + return x.PaymentPurpose + } + return "" +} + +func (x *GetB2BPaymentLinkRequest) GetVatData() *emptypb.Empty { + if x != nil { + return x.VatData + } + return nil +} + +type GetPaymentLinkResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RedirectURL string `protobuf:"bytes,1,opt,name=RedirectURL,proto3" json:"RedirectURL,omitempty"` +} + +func (x *GetPaymentLinkResponse) Reset() { + *x = GetPaymentLinkResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_treasurer_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPaymentLinkResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPaymentLinkResponse) ProtoMessage() {} + +func (x *GetPaymentLinkResponse) ProtoReflect() protoreflect.Message { + mi := &file_treasurer_service_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPaymentLinkResponse.ProtoReflect.Descriptor instead. +func (*GetPaymentLinkResponse) Descriptor() ([]byte, []int) { + return file_treasurer_service_proto_rawDescGZIP(), []int{5} +} + +func (x *GetPaymentLinkResponse) GetRedirectURL() string { + if x != nil { + return x.RedirectURL + } + return "" +} + +var File_treasurer_service_proto protoreflect.FileDescriptor + +var file_treasurer_service_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1d, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x22, 0x9f, 0x01, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, + 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, + 0x72, 0x64, 0x22, 0x5b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, + 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, + 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, + 0x76, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, + 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, + 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, + 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x22, 0x76, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, + 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x6f, 0x67, + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x22, + 0xb8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x42, 0x32, 0x42, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0c, + 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x4d, + 0x61, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x52, 0x0c, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x26, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x72, 0x70, 0x6f, + 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x56, 0x61, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x52, 0x07, 0x56, 0x61, 0x74, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3a, 0x0a, 0x16, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x32, 0xd8, 0x08, 0x0a, 0x10, 0x54, 0x72, 0x65, 0x61, 0x73, + 0x75, 0x72, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x16, 0x47, + 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x42, 0x61, 0x6e, + 0x6b, 0x43, 0x61, 0x72, 0x64, 0x12, 0x28, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6e, 0x6b, 0x43, 0x61, 0x72, 0x64, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x59, 0x6f, 0x6f, 0x4d, 0x6f, 0x6e, 0x65, 0x79, 0x12, 0x20, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x51, 0x49, 0x57, 0x49, 0x12, 0x25, 0x2e, 0x74, 0x72, + 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x68, 0x6f, 0x6e, 0x65, + 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x62, 0x65, 0x72, 0x50, 0x61, 0x79, + 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x17, + 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x41, 0x6c, + 0x66, 0x61, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x69, 0x6e, 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x20, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x62, 0x65, 0x72, 0x62, 0x61, 0x6e, 0x6b, 0x42, 0x32, 0x42, + 0x12, 0x23, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x42, 0x32, 0x42, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x11, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x42, 0x50, 0x12, + 0x20, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x12, 0x25, + 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x68, + 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x60, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x43, 0x61, 0x73, 0x68, + 0x12, 0x25, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x68, 0x6f, 0x6e, 0x65, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, + 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x1a, + 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x74, 0x72, 0x65, + 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x74, + 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x72, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_treasurer_service_proto_rawDescOnce sync.Once + file_treasurer_service_proto_rawDescData = file_treasurer_service_proto_rawDesc +) + +func file_treasurer_service_proto_rawDescGZIP() []byte { + file_treasurer_service_proto_rawDescOnce.Do(func() { + file_treasurer_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_treasurer_service_proto_rawDescData) + }) + return file_treasurer_service_proto_rawDescData +} + +var file_treasurer_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_treasurer_service_proto_goTypes = []interface{}{ + (*GetBankCardPaymentLinkRequest)(nil), // 0: treasurer.GetBankCardPaymentLinkRequest + (*GetPaymentLinkRequest)(nil), // 1: treasurer.GetPaymentLinkRequest + (*GetPhonePaymentLinkRequest)(nil), // 2: treasurer.GetPhonePaymentLinkRequest + (*GetLoginPaymentLinkRequest)(nil), // 3: treasurer.GetLoginPaymentLinkRequest + (*GetB2BPaymentLinkRequest)(nil), // 4: treasurer.GetB2BPaymentLinkRequest + (*GetPaymentLinkResponse)(nil), // 5: treasurer.GetPaymentLinkResponse + (*MainPaymentSettings)(nil), // 6: treasurer.MainPaymentSettings + (*BankCardInformation)(nil), // 7: treasurer.BankCardInformation + (*emptypb.Empty)(nil), // 8: google.protobuf.Empty +} +var file_treasurer_service_proto_depIdxs = []int32{ + 6, // 0: treasurer.GetBankCardPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 7, // 1: treasurer.GetBankCardPaymentLinkRequest.BankCard:type_name -> treasurer.BankCardInformation + 6, // 2: treasurer.GetPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 3: treasurer.GetPhonePaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 4: treasurer.GetLoginPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 6, // 5: treasurer.GetB2BPaymentLinkRequest.MainSettings:type_name -> treasurer.MainPaymentSettings + 8, // 6: treasurer.GetB2BPaymentLinkRequest.VatData:type_name -> google.protobuf.Empty + 0, // 7: treasurer.TreasurerService.GetPaymentLinkBankCard:input_type -> treasurer.GetBankCardPaymentLinkRequest + 1, // 8: treasurer.TreasurerService.GetPaymentLinkYooMoney:input_type -> treasurer.GetPaymentLinkRequest + 2, // 9: treasurer.TreasurerService.GetPaymentLinkQIWI:input_type -> treasurer.GetPhonePaymentLinkRequest + 2, // 10: treasurer.TreasurerService.GetPaymentLinkSberPay:input_type -> treasurer.GetPhonePaymentLinkRequest + 3, // 11: treasurer.TreasurerService.GetPaymentLinkAlfaClick:input_type -> treasurer.GetLoginPaymentLinkRequest + 1, // 12: treasurer.TreasurerService.GetPaymentLinkTinkoff:input_type -> treasurer.GetPaymentLinkRequest + 4, // 13: treasurer.TreasurerService.GetPaymentLinkSberbankB2B:input_type -> treasurer.GetB2BPaymentLinkRequest + 1, // 14: treasurer.TreasurerService.GetPaymentLinkSBP:input_type -> treasurer.GetPaymentLinkRequest + 2, // 15: treasurer.TreasurerService.GetPaymentLinkMobile:input_type -> treasurer.GetPhonePaymentLinkRequest + 2, // 16: treasurer.TreasurerService.GetPaymentLinkCash:input_type -> treasurer.GetPhonePaymentLinkRequest + 1, // 17: treasurer.TreasurerService.GetPaymentLinkInstallments:input_type -> treasurer.GetPaymentLinkRequest + 5, // 18: treasurer.TreasurerService.GetPaymentLinkBankCard:output_type -> treasurer.GetPaymentLinkResponse + 5, // 19: treasurer.TreasurerService.GetPaymentLinkYooMoney:output_type -> treasurer.GetPaymentLinkResponse + 5, // 20: treasurer.TreasurerService.GetPaymentLinkQIWI:output_type -> treasurer.GetPaymentLinkResponse + 5, // 21: treasurer.TreasurerService.GetPaymentLinkSberPay:output_type -> treasurer.GetPaymentLinkResponse + 5, // 22: treasurer.TreasurerService.GetPaymentLinkAlfaClick:output_type -> treasurer.GetPaymentLinkResponse + 5, // 23: treasurer.TreasurerService.GetPaymentLinkTinkoff:output_type -> treasurer.GetPaymentLinkResponse + 5, // 24: treasurer.TreasurerService.GetPaymentLinkSberbankB2B:output_type -> treasurer.GetPaymentLinkResponse + 5, // 25: treasurer.TreasurerService.GetPaymentLinkSBP:output_type -> treasurer.GetPaymentLinkResponse + 5, // 26: treasurer.TreasurerService.GetPaymentLinkMobile:output_type -> treasurer.GetPaymentLinkResponse + 5, // 27: treasurer.TreasurerService.GetPaymentLinkCash:output_type -> treasurer.GetPaymentLinkResponse + 5, // 28: treasurer.TreasurerService.GetPaymentLinkInstallments:output_type -> treasurer.GetPaymentLinkResponse + 18, // [18:29] is the sub-list for method output_type + 7, // [7:18] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_treasurer_service_proto_init() } +func file_treasurer_service_proto_init() { + if File_treasurer_service_proto != nil { + return + } + file_treasurer_payment_model_proto_init() + if !protoimpl.UnsafeEnabled { + file_treasurer_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBankCardPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPhonePaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLoginPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetB2BPaymentLinkRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_treasurer_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPaymentLinkResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_treasurer_service_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_treasurer_service_proto_goTypes, + DependencyIndexes: file_treasurer_service_proto_depIdxs, + MessageInfos: file_treasurer_service_proto_msgTypes, + }.Build() + File_treasurer_service_proto = out.File + file_treasurer_service_proto_rawDesc = nil + file_treasurer_service_proto_goTypes = nil + file_treasurer_service_proto_depIdxs = nil +} diff --git a/internal/proto/treasurer/service_grpc.pb.go b/internal/proto/treasurer/service_grpc.pb.go new file mode 100644 index 0000000..89dd5ff --- /dev/null +++ b/internal/proto/treasurer/service_grpc.pb.go @@ -0,0 +1,459 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package treasurer + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// TreasurerServiceClient is the client API for TreasurerService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TreasurerServiceClient interface { + GetPaymentLinkBankCard(ctx context.Context, in *GetBankCardPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkYooMoney(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkQIWI(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberPay(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkAlfaClick(ctx context.Context, in *GetLoginPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkTinkoff(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberbankB2B(ctx context.Context, in *GetB2BPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkSBP(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkMobile(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkCash(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) + GetPaymentLinkInstallments(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) +} + +type treasurerServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTreasurerServiceClient(cc grpc.ClientConnInterface) TreasurerServiceClient { + return &treasurerServiceClient{cc} +} + +func (c *treasurerServiceClient) GetPaymentLinkBankCard(ctx context.Context, in *GetBankCardPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkBankCard", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkYooMoney(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkYooMoney", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkQIWI(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkQIWI", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSberPay(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSberPay", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkAlfaClick(ctx context.Context, in *GetLoginPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkAlfaClick", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkTinkoff(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkTinkoff", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSberbankB2B(ctx context.Context, in *GetB2BPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSberbankB2B", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkSBP(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkSBP", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkMobile(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkMobile", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkCash(ctx context.Context, in *GetPhonePaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkCash", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *treasurerServiceClient) GetPaymentLinkInstallments(ctx context.Context, in *GetPaymentLinkRequest, opts ...grpc.CallOption) (*GetPaymentLinkResponse, error) { + out := new(GetPaymentLinkResponse) + err := c.cc.Invoke(ctx, "/treasurer.TreasurerService/GetPaymentLinkInstallments", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TreasurerServiceServer is the server API for TreasurerService service. +// All implementations should embed UnimplementedTreasurerServiceServer +// for forward compatibility +type TreasurerServiceServer interface { + GetPaymentLinkBankCard(context.Context, *GetBankCardPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkYooMoney(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkQIWI(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberPay(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkAlfaClick(context.Context, *GetLoginPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkTinkoff(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkSberbankB2B(context.Context, *GetB2BPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkSBP(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkMobile(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkCash(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) + GetPaymentLinkInstallments(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) +} + +// UnimplementedTreasurerServiceServer should be embedded to have forward compatible implementations. +type UnimplementedTreasurerServiceServer struct { +} + +func (UnimplementedTreasurerServiceServer) GetPaymentLinkBankCard(context.Context, *GetBankCardPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkBankCard not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkYooMoney(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkYooMoney not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkQIWI(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkQIWI not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSberPay(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSberPay not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkAlfaClick(context.Context, *GetLoginPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkAlfaClick not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkTinkoff(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkTinkoff not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSberbankB2B(context.Context, *GetB2BPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSberbankB2B not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkSBP(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkSBP not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkMobile(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkMobile not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkCash(context.Context, *GetPhonePaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkCash not implemented") +} +func (UnimplementedTreasurerServiceServer) GetPaymentLinkInstallments(context.Context, *GetPaymentLinkRequest) (*GetPaymentLinkResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPaymentLinkInstallments not implemented") +} + +// UnsafeTreasurerServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TreasurerServiceServer will +// result in compilation errors. +type UnsafeTreasurerServiceServer interface { + mustEmbedUnimplementedTreasurerServiceServer() +} + +func RegisterTreasurerServiceServer(s grpc.ServiceRegistrar, srv TreasurerServiceServer) { + s.RegisterService(&TreasurerService_ServiceDesc, srv) +} + +func _TreasurerService_GetPaymentLinkBankCard_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetBankCardPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkBankCard(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkBankCard", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkBankCard(ctx, req.(*GetBankCardPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkYooMoney_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkYooMoney(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkYooMoney", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkYooMoney(ctx, req.(*GetPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkQIWI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkQIWI(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkQIWI", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkQIWI(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSberPay_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSberPay(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSberPay", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSberPay(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkAlfaClick_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLoginPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkAlfaClick(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkAlfaClick", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkAlfaClick(ctx, req.(*GetLoginPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkTinkoff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkTinkoff(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkTinkoff", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkTinkoff(ctx, req.(*GetPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSberbankB2B_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetB2BPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSberbankB2B(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSberbankB2B", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSberbankB2B(ctx, req.(*GetB2BPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkSBP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkSBP(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkSBP", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkSBP(ctx, req.(*GetPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkMobile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkMobile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkMobile", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkMobile(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkCash_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPhonePaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkCash(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkCash", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkCash(ctx, req.(*GetPhonePaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TreasurerService_GetPaymentLinkInstallments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPaymentLinkRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TreasurerServiceServer).GetPaymentLinkInstallments(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/treasurer.TreasurerService/GetPaymentLinkInstallments", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TreasurerServiceServer).GetPaymentLinkInstallments(ctx, req.(*GetPaymentLinkRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TreasurerService_ServiceDesc is the grpc.ServiceDesc for TreasurerService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TreasurerService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "treasurer.TreasurerService", + HandlerType: (*TreasurerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetPaymentLinkBankCard", + Handler: _TreasurerService_GetPaymentLinkBankCard_Handler, + }, + { + MethodName: "GetPaymentLinkYooMoney", + Handler: _TreasurerService_GetPaymentLinkYooMoney_Handler, + }, + { + MethodName: "GetPaymentLinkQIWI", + Handler: _TreasurerService_GetPaymentLinkQIWI_Handler, + }, + { + MethodName: "GetPaymentLinkSberPay", + Handler: _TreasurerService_GetPaymentLinkSberPay_Handler, + }, + { + MethodName: "GetPaymentLinkAlfaClick", + Handler: _TreasurerService_GetPaymentLinkAlfaClick_Handler, + }, + { + MethodName: "GetPaymentLinkTinkoff", + Handler: _TreasurerService_GetPaymentLinkTinkoff_Handler, + }, + { + MethodName: "GetPaymentLinkSberbankB2B", + Handler: _TreasurerService_GetPaymentLinkSberbankB2B_Handler, + }, + { + MethodName: "GetPaymentLinkSBP", + Handler: _TreasurerService_GetPaymentLinkSBP_Handler, + }, + { + MethodName: "GetPaymentLinkMobile", + Handler: _TreasurerService_GetPaymentLinkMobile_Handler, + }, + { + MethodName: "GetPaymentLinkCash", + Handler: _TreasurerService_GetPaymentLinkCash_Handler, + }, + { + MethodName: "GetPaymentLinkInstallments", + Handler: _TreasurerService_GetPaymentLinkInstallments_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "treasurer/service.proto", +} diff --git a/internal/server/grpc.go b/internal/server/grpc.go new file mode 100644 index 0000000..c7c00c0 --- /dev/null +++ b/internal/server/grpc.go @@ -0,0 +1,78 @@ +package server + +import ( + "context" + "fmt" + "net" + + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" + grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" + "go.uber.org/zap" + "google.golang.org/grpc" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" +) + +type DepsGRPC struct { + Logger *zap.Logger +} + +type GRPC struct { + logger *zap.Logger + grpc *grpc.Server +} + +func NewGRPC(deps DepsGRPC) (*GRPC, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger is nil on ", errors.ErrInvalidArgs) + } + + grpcStreamInterceptor := grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( + grpc_zap.StreamServerInterceptor(deps.Logger), + grpc_recovery.StreamServerInterceptor(), + )) + + grpcUnaryInterceptor := grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + grpc_zap.UnaryServerInterceptor(deps.Logger), + grpc_recovery.UnaryServerInterceptor(), + )) + + return &GRPC{ + grpc: grpc.NewServer(grpcStreamInterceptor, grpcUnaryInterceptor), + logger: deps.Logger, + }, nil +} + +func (receiver *GRPC) Run(config *models.ConfigurationGRPC) { + connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port) + + receiver.logger.Info("Starting GRPC Server", zap.String("host", connectionString)) + + if err := receiver.listen(connectionString); err != nil && err != grpc.ErrServerStopped { + receiver.logger.Error("GRPC Listen error", zap.Error(err)) + } +} + +func (receiver *GRPC) Stop(_ context.Context) error { + receiver.grpc.GracefulStop() + receiver.logger.Info("Shutting down GRPC server...") + + return nil +} + +func (receiver *GRPC) Register(server treasurer.TreasurerServiceServer) *GRPC { + treasurer.RegisterTreasurerServiceServer(receiver.grpc, server) + + return receiver +} + +func (receiver *GRPC) listen(address string) error { + listener, err := net.Listen("tcp", address) + if err != nil { + return err + } + + return receiver.grpc.Serve(listener) +} diff --git a/internal/server/http.go b/internal/server/http.go new file mode 100644 index 0000000..3cf1a75 --- /dev/null +++ b/internal/server/http.go @@ -0,0 +1,85 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" + "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/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/swagger" +) + +type DepsHTTP struct { + Logger *zap.Logger + Swagger *openapi3.T +} + +type HTTP struct { + logger *zap.Logger + server *http.Server + echo *echo.Echo +} + +func NewHTTP(deps DepsHTTP) (*HTTP, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("Logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.Swagger == nil { + return nil, errors.NewWithMessage("Swagger is nil on ", errors.ErrInvalidArgs) + } + + echo := echo.New() + + echo.Use(middleware.Recover()) + + return &HTTP{ + echo: echo, + logger: deps.Logger, + server: &http.Server{ + Handler: echo, + MaxHeaderBytes: 1 << 20, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + }, + }, nil +} + +func (receiver *HTTP) Listen(address string) error { + receiver.server.Addr = address + + return receiver.server.ListenAndServe() +} + +func (receiver *HTTP) Run(config *models.ConfigurationHTTP) { + connectionString := fmt.Sprintf("%s:%s", config.Host, config.Port) + startServerMessage := fmt.Sprintf("starting http server on %s", connectionString) + + receiver.logger.Info(startServerMessage) + + if err := receiver.Listen(connectionString); err != nil && err != http.ErrServerClosed { + receiver.logger.Error("http listen error: ", zap.Error(err)) + } +} + +func (receiver *HTTP) Stop(ctx context.Context) error { + receiver.logger.Info("shutting down server...") + + if err := receiver.server.Shutdown(ctx); err != nil { + return fmt.Errorf("failed to shutdown server: %w", err) + } + + return nil +} + +func (receiver *HTTP) Register(api *swagger.API) *HTTP { + swagger.RegisterHandlers(receiver.echo, api) + + return receiver +} diff --git a/internal/service/callback/callback.go b/internal/service/callback/callback.go new file mode 100644 index 0000000..ec10c3b --- /dev/null +++ b/internal/service/callback/callback.go @@ -0,0 +1,127 @@ +package callback + +import ( + "context" + "sync" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +type CallbackClient interface { + SendOnSuccess(ctx context.Context, host string, event *models.Event) errors.Error + SendOnFailure(ctx context.Context, host string, event *models.Event) errors.Error +} + +type PaymentRepository interface { + SetPaymentComplete(ctx context.Context, paymentID string) (*models.Payment, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + CallbackClient CallbackClient + PaymentRepository PaymentRepository +} + +type Service struct { + logger *zap.Logger + callbackClient CallbackClient + paymentRepository PaymentRepository +} + +func New(deps Deps) (*Service, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.CallbackClient == nil { + return nil, errors.NewWithMessage("CallbackClient is nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentRepository == nil { + return nil, errors.NewWithMessage("PaymentRepository is nil on ", errors.ErrInvalidArgs) + } + + return &Service{ + logger: deps.Logger, + callbackClient: deps.CallbackClient, + paymentRepository: deps.PaymentRepository, + }, nil +} + +func (receiver *Service) OnSuccess(ctx context.Context, event *models.Event) errors.Error { + waitGroup := sync.WaitGroup{} + mutex := sync.Mutex{} + executeErrors := make([]error, 0) + + for _, callbackURL := range event.Payment.CallbackHostGRPC { + waitGroup.Add(1) + + go func(url string) { + defer waitGroup.Done() + + if err := receiver.callbackClient.SendOnSuccess(ctx, url, event); err != nil { + receiver.logger.Error("failid to send callback on of ") + + mutex.Lock() + executeErrors = append(executeErrors, err) + mutex.Unlock() + + return + } + + if _, err := receiver.paymentRepository.SetPaymentComplete(ctx, event.Payment.PaymentID); err != nil { + receiver.logger.Error("failid to set payment complete on of ") + + mutex.Lock() + executeErrors = append(executeErrors, err) + mutex.Unlock() + + return + } + }(callbackURL) + } + + waitGroup.Wait() + + if len(executeErrors) >= len(event.Payment.CallbackHostGRPC) { + receiver.logger.Error("failid to success payment on of ", zap.Errors("errors", executeErrors)) + return errors.NewWithMessage("failed to success payment: all operations failed", errors.ErrInternalError) + } + + return nil +} + +func (receiver *Service) OnFailure(ctx context.Context, event *models.Event) errors.Error { + waitGroup := sync.WaitGroup{} + mutex := sync.Mutex{} + executeErrors := make([]error, 0) + + for _, callbackURL := range event.Payment.CallbackHostGRPC { + waitGroup.Add(1) + + go func(url string) { + defer waitGroup.Done() + + if err := receiver.callbackClient.SendOnFailure(ctx, url, event); err != nil { + receiver.logger.Error("failid to send callback on of ") + + mutex.Lock() + executeErrors = append(executeErrors, err) + mutex.Unlock() + + return + } + }(callbackURL) + } + + waitGroup.Wait() + + if len(executeErrors) >= len(event.Payment.CallbackHostGRPC) { + receiver.logger.Error("failid to success payment on of ", zap.Errors("errors", executeErrors)) + return errors.NewWithMessage("failed to success payment: all operations failed", errors.ErrInternalError) + } + + return nil +} diff --git a/internal/service/payment/payment.go b/internal/service/payment/payment.go new file mode 100644 index 0000000..d95b44d --- /dev/null +++ b/internal/service/payment/payment.go @@ -0,0 +1,153 @@ +package payment + +import ( + "context" + "fmt" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/utils" +) + +type PaymentStrategyService interface { + CreatePaymentPhone(ctx context.Context, uuid string, payment *models.CreatePayment[string]) (*models.CreatePaymentResult, errors.Error) + CreatePaymentBankCard(ctx context.Context, uuid string, payment *models.CreatePayment[*models.BankCard]) (*models.CreatePaymentResult, errors.Error) + CreatePayment(ctx context.Context, uuid string, payment *models.CreatePayment[*any]) (*models.CreatePaymentResult, errors.Error) + CreatePaymentLogin(ctx context.Context, uuid string, payment *models.CreatePayment[string]) (*models.CreatePaymentResult, errors.Error) +} + +type PaymentRepository interface { + Insert(context.Context, *models.Payment) (*models.Payment, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + PaymentStrategyService PaymentStrategyService + PaymentRepository PaymentRepository +} + +type Service struct { + logger *zap.Logger + paymentStrategyService PaymentStrategyService + paymentRepository PaymentRepository +} + +func New(deps Deps) (*Service, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentStrategyService == nil { + return nil, errors.NewWithMessage("PaymentStrategyService is nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentRepository == nil { + return nil, errors.NewWithMessage("PaymentRepository is nil on ", errors.ErrInvalidArgs) + } + + return &Service{ + logger: deps.Logger, + paymentStrategyService: deps.PaymentStrategyService, + paymentRepository: deps.PaymentRepository, + }, nil +} + +func (receiver *Service) CreatePaymentPhone(ctx context.Context, request *models.CreatePayment[string]) (string, errors.Error) { + paymentUUID, generateErr := utils.GenerateUUID() + if generateErr != nil { + receiver.logger.Error("failed to generate uuid on of ", zap.Error(generateErr)) + return "", errors.NewWithError(fmt.Errorf("failed to generate uuid: %w", generateErr), errors.ErrInternalError) + } + + response, err := receiver.paymentStrategyService.CreatePaymentPhone(ctx, paymentUUID, request) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return "", err + } + + if _, err := receiver.paymentRepository.Insert(ctx, response.Payment); err != nil { + receiver.logger.Error("failed to insert payment on of ", zap.Error(err)) + return "", err + } + + return response.RedirectURL, nil +} + +func (receiver *Service) CreatePaymentBankCard(ctx context.Context, request *models.CreatePayment[*models.BankCard]) (string, errors.Error) { + paymentUUID, generateErr := utils.GenerateUUID() + if generateErr != nil { + receiver.logger.Error("failed to generate uuid on of ", zap.Error(generateErr)) + return "", errors.NewWithError(fmt.Errorf("failed to generate uuid: %w", generateErr), errors.ErrInternalError) + } + + response, err := receiver.paymentStrategyService.CreatePaymentBankCard(ctx, paymentUUID, request) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return "", err + } + + if _, err := receiver.paymentRepository.Insert(ctx, response.Payment); err != nil { + receiver.logger.Error("failed to insert payment on of ", zap.Error(err)) + return "", err + } + + return response.RedirectURL, nil +} + +func (receiver *Service) CreatePayment(ctx context.Context, request *models.CreatePayment[*any]) (string, errors.Error) { + paymentUUID, generateErr := utils.GenerateUUID() + if generateErr != nil { + receiver.logger.Error("failed to generate uuid on of ", zap.Error(generateErr)) + return "", errors.NewWithError(fmt.Errorf("failed to generate uuid: %w", generateErr), errors.ErrInternalError) + } + + response, err := receiver.paymentStrategyService.CreatePayment(ctx, paymentUUID, request) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return "", err + } + + if _, err := receiver.paymentRepository.Insert(ctx, response.Payment); err != nil { + receiver.logger.Error("failed to insert payment on of ", zap.Error(err)) + return "", err + } + + return response.RedirectURL, nil +} + +func (receiver *Service) CreatePaymentLogin(ctx context.Context, request *models.CreatePayment[string]) (string, errors.Error) { + paymentUUID, generateErr := utils.GenerateUUID() + if generateErr != nil { + receiver.logger.Error("failed to generate uuid on of ", zap.Error(generateErr)) + return "", errors.NewWithError(fmt.Errorf("failed to generate uuid: %w", generateErr), errors.ErrInternalError) + } + + response, err := receiver.paymentStrategyService.CreatePaymentLogin(ctx, paymentUUID, request) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return "", err + } + + if _, err := receiver.paymentRepository.Insert(ctx, response.Payment); err != nil { + receiver.logger.Error("failed to insert payment on of ", zap.Error(err)) + return "", err + } + + return response.RedirectURL, nil +} + +func (receiver *Service) GetAvailablePaymentMethods(_ context.Context) ([]models.PaymentType, errors.Error) { + return []models.PaymentType{ + models.PaymentTypeBankCard, + models.PaymentTypeTinkoff, + models.PaymentTypeQiwi, + models.PaymentTypeSberPay, + models.PaymentTypeYoomoney, + models.PaymentTypeMobile, + models.PaymentTypeInstallments, + models.PaymentTypeCash, + models.PaymentTypeSBP, + models.PaymentTypeSberB2B, + }, nil +} diff --git a/internal/service/payment/yandex.go b/internal/service/payment/yandex.go new file mode 100644 index 0000000..4a23cd2 --- /dev/null +++ b/internal/service/payment/yandex.go @@ -0,0 +1,213 @@ +package payment + +import ( + "context" + + "go.uber.org/zap" + "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" +) + +type YandexPaymentClient interface { + CreatePaymentPhone(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodPhone]) (*yandex.Payment, errors.Error) + CreatePayment(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodType]) (*yandex.Payment, errors.Error) + CreatePaymentBankCard(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodBankCard]) (*yandex.Payment, errors.Error) + CreatePaymentB2B(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodB2B]) (*yandex.Payment, errors.Error) + CreatePaymentLogin(ctx context.Context, idempotenceKey string, request *yandex.CreatePaymentRequest[yandex.PaymentMethodLogin]) (*yandex.Payment, errors.Error) +} + +type YandexPaymentServiceDeps struct { + Logger *zap.Logger + YandexPaymentClient YandexPaymentClient +} + +type Yandex struct { + logger *zap.Logger + yandexPaymentClient YandexPaymentClient +} + +func NewYandex(deps YandexPaymentServiceDeps) (*Yandex, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.YandexPaymentClient == nil { + return nil, errors.NewWithMessage("YandexPaymentClient is nil on ", errors.ErrInvalidArgs) + } + + return &Yandex{ + logger: deps.Logger, + yandexPaymentClient: deps.YandexPaymentClient, + }, nil +} + +func (receiver *Yandex) CreatePaymentPhone(ctx context.Context, uuid string, request *models.CreatePayment[string]) (*models.CreatePaymentResult, errors.Error) { + yandexPayment, err := receiver.yandexPaymentClient.CreatePaymentPhone(ctx, uuid, &yandex.CreatePaymentRequest[yandex.PaymentMethodPhone]{ + Amount: yandex.Amount{ + Value: utils.ConvertAmountToStringFloat(request.Amount), + Currency: request.Currency, + }, + PaymentMethodData: &yandex.PaymentMethodPhone{ + Type: models.YandexPaymentTypeMap[request.Type], + Phone: request.Requisites, + }, + Confirmation: &yandex.CreateConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + Locale: "ru_RU", + ReturnURL: request.ReturnURL, + Enforce: true, + }, + Capture: true, + ClientIP: request.ClientIP, + }) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return nil, err + } + + return &models.CreatePaymentResult{ + Payment: &models.Payment{ + UserID: request.UserID, + PaymentID: yandexPayment.ID, + ClientIP: request.ClientIP, + Currency: request.Currency, + IdempotencePaymentID: uuid, + Amount: request.Amount, + Type: request.Type, + Status: models.PaymentStatusMap[string(yandexPayment.Status)], + Completed: false, + RawPaymentBody: yandexPayment, + CallbackHostGRPC: request.CallbackHostGRPC, + }, + RedirectURL: yandexPayment.Confirmation.ConfirmationURL, + }, nil +} + +func (receiver *Yandex) CreatePaymentBankCard(ctx context.Context, uuid string, request *models.CreatePayment[*models.BankCard]) (*models.CreatePaymentResult, errors.Error) { + yandexPayment, err := receiver.yandexPaymentClient.CreatePaymentBankCard(ctx, uuid, &yandex.CreatePaymentRequest[yandex.PaymentMethodBankCard]{ + Amount: yandex.Amount{ + Value: utils.ConvertAmountToStringFloat(request.Amount), + Currency: request.Currency, + }, + PaymentMethodData: &yandex.PaymentMethodBankCard{ + Type: models.YandexPaymentTypeMap[request.Type], + Card: &yandex.BankCardInformation{ + Number: request.Requisites.Number, + ExpiryYear: request.Requisites.ExpiryYear, + ExpiryMonth: request.Requisites.ExpiryMonth, + }, + }, + Confirmation: &yandex.CreateConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + Locale: "ru_RU", + ReturnURL: request.ReturnURL, + Enforce: true, + }, + Capture: true, + ClientIP: request.ClientIP, + }) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return nil, err + } + + return &models.CreatePaymentResult{ + Payment: &models.Payment{ + UserID: request.UserID, + PaymentID: yandexPayment.ID, + IdempotencePaymentID: uuid, + ClientIP: request.ClientIP, + Currency: request.Currency, + Amount: request.Amount, + Type: request.Type, + Status: models.PaymentStatusMap[string(yandexPayment.Status)], + Completed: false, + RawPaymentBody: yandexPayment, + CallbackHostGRPC: request.CallbackHostGRPC, + }, + RedirectURL: yandexPayment.Confirmation.ConfirmationURL, + }, nil +} + +func (receiver *Yandex) CreatePayment(ctx context.Context, uuid string, request *models.CreatePayment[*any]) (*models.CreatePaymentResult, errors.Error) { + yandexPayment, err := receiver.yandexPaymentClient.CreatePayment(ctx, uuid, &yandex.CreatePaymentRequest[yandex.PaymentMethodType]{ + Amount: yandex.Amount{ + Value: utils.ConvertAmountToStringFloat(request.Amount), + Currency: request.Currency, + }, + PaymentMethodData: &yandex.PaymentMethodType{Type: models.YandexPaymentTypeMap[request.Type]}, + Confirmation: &yandex.CreateConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + Locale: "ru_RU", + ReturnURL: request.ReturnURL, + Enforce: true, + }, + Capture: true, + ClientIP: request.ClientIP, + }) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return nil, err + } + + return &models.CreatePaymentResult{ + Payment: &models.Payment{ + UserID: request.UserID, + PaymentID: yandexPayment.ID, + IdempotencePaymentID: uuid, + ClientIP: request.ClientIP, + Currency: request.Currency, + Amount: request.Amount, + Type: request.Type, + Status: models.PaymentStatusMap[string(yandexPayment.Status)], + Completed: false, + RawPaymentBody: yandexPayment, + CallbackHostGRPC: request.CallbackHostGRPC, + }, + RedirectURL: yandexPayment.Confirmation.ConfirmationURL, + }, nil +} + +func (receiver *Yandex) CreatePaymentLogin(ctx context.Context, uuid string, request *models.CreatePayment[string]) (*models.CreatePaymentResult, errors.Error) { + yandexPayment, err := receiver.yandexPaymentClient.CreatePaymentLogin(ctx, uuid, &yandex.CreatePaymentRequest[yandex.PaymentMethodLogin]{ + Amount: yandex.Amount{ + Value: utils.ConvertAmountToStringFloat(request.Amount), + Currency: request.Currency, + }, + PaymentMethodData: &yandex.PaymentMethodLogin{ + Type: models.YandexPaymentTypeMap[request.Type], + Login: request.Requisites, + }, + Confirmation: &yandex.CreateConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + Locale: "ru_RU", + ReturnURL: request.ReturnURL, + Enforce: true, + }, + Capture: true, + ClientIP: request.ClientIP, + }) + if err != nil { + receiver.logger.Error("failed to create payment on of ", zap.Error(err)) + return nil, err + } + + return &models.CreatePaymentResult{ + Payment: &models.Payment{ + UserID: request.UserID, + PaymentID: yandexPayment.ID, + IdempotencePaymentID: uuid, + ClientIP: request.ClientIP, + Currency: request.Currency, + Amount: request.Amount, + Type: request.Type, + Status: models.PaymentStatusMap[string(yandexPayment.Status)], + Completed: false, + RawPaymentBody: yandexPayment, + CallbackHostGRPC: request.CallbackHostGRPC, + }, + RedirectURL: yandexPayment.Confirmation.ConfirmationURL, + }, nil +} diff --git a/internal/service/status/status.go b/internal/service/status/status.go new file mode 100644 index 0000000..97933ec --- /dev/null +++ b/internal/service/status/status.go @@ -0,0 +1,78 @@ +package status + +import ( + "context" + + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/errors" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" +) + +type PaymentRepository interface { + SetPaymentStatus(ctx context.Context, paymentID string, status models.PaymentStatus) (*models.Payment, errors.Error) +} + +type Deps struct { + Logger *zap.Logger + PaymentRepository PaymentRepository +} + +type Service struct { + logger *zap.Logger + paymentRepository PaymentRepository +} + +func New(deps Deps) (*Service, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.PaymentRepository == nil { + return nil, errors.NewWithMessage("PaymentRepository is nil on ", errors.ErrInvalidArgs) + } + + return &Service{ + logger: deps.Logger, + paymentRepository: deps.PaymentRepository, + }, nil +} + +func (receiver *Service) SetStatusCanceled(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { + payment, err := receiver.paymentRepository.SetPaymentStatus(ctx, paymentID, models.PaymentStatusCanceled) + if err != nil { + receiver.logger.Error("failed to set payment status on of ", zap.Error(err)) + return nil, err + } + + return payment, nil +} + +func (receiver *Service) SetStatusSuccess(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { + payment, err := receiver.paymentRepository.SetPaymentStatus(ctx, paymentID, models.PaymentStatusSuccessfully) + if err != nil { + receiver.logger.Error("failed to set payment status on of ", zap.Error(err)) + return nil, err + } + + return payment, nil +} + +func (receiver *Service) SetStatusWaiting(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { + payment, err := receiver.paymentRepository.SetPaymentStatus(ctx, paymentID, models.PaymentStatusWaiting) + if err != nil { + receiver.logger.Error("failed to set payment status on of ", zap.Error(err)) + return nil, err + } + + return payment, nil +} + +func (receiver *Service) SetStatusRefund(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { + payment, err := receiver.paymentRepository.SetPaymentStatus(ctx, paymentID, models.PaymentStatusRefund) + if err != nil { + receiver.logger.Error("failed to set payment status on of ", zap.Error(err)) + return nil, err + } + + return payment, nil +} diff --git a/internal/service/webhook/yandex.go b/internal/service/webhook/yandex.go new file mode 100644 index 0000000..92696fa --- /dev/null +++ b/internal/service/webhook/yandex.go @@ -0,0 +1,191 @@ +package webhook + +import ( + "context" + "fmt" + "net/url" + + "go.uber.org/zap" + "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/pkg/array" +) + +const ( + onYandexSuccessWebhookEventKey = "e242352d-5921-44b1-99f9-d022734fe514-success" + onYandexCanceledWebhookEventKey = "e242352d-5921-44b1-99f9-d022734fe514-canceled" + onYandexWaitingWebhookEventKey = "e242352d-5921-44b1-99f9-d022734fe514-waiting" + onYandexRefundWebhookEventKey = "e242352d-5921-44b1-99f9-d022734fe514-refund" +) + +type YandexWebhookClient interface { + SetWebhookEvent(ctx context.Context, idempotenceKey string, request *yandex.CreateWebhookRequest) (id string, err errors.Error) + GetWebhookEvents(ctx context.Context) ([]yandex.Webhook, errors.Error) + DeleteWebhook(ctx context.Context, webhookID string) errors.Error +} + +type YandexDeps struct { + Logger *zap.Logger + Configuration *models.ConfigurationHTTP + YandexWebhookClient YandexWebhookClient +} + +type Yandex struct { + logger *zap.Logger + configuration *models.ConfigurationHTTP + yandexWebhookClient YandexWebhookClient +} + +func NewYandex(deps YandexDeps) (*Yandex, errors.Error) { + if deps.Logger == nil { + return nil, errors.NewWithMessage("logger is nil on ", errors.ErrInvalidArgs) + } + + if deps.Configuration == nil { + return nil, errors.NewWithMessage("Configuration is nil on ", errors.ErrInvalidArgs) + } + + if deps.YandexWebhookClient == nil { + return nil, errors.NewWithMessage("PaymentRepository is nil on ", errors.ErrInvalidArgs) + } + + return &Yandex{ + logger: deps.Logger, + configuration: deps.Configuration, + yandexWebhookClient: deps.YandexWebhookClient, + }, nil +} + +func (receiver *Yandex) SetOnPaymentSuccess(ctx context.Context) errors.Error { + url, urlErr := url.JoinPath(receiver.configuration.Domen, "yandex", "payment", "status", "succeeded") + if urlErr != nil { + receiver.logger.Error("failed to join url path on of ", zap.Error(urlErr)) + return errors.NewWithError(fmt.Errorf("failed to join url path: %w", urlErr), errors.ErrInternalError) + } + + if err := receiver.SetWebhook(ctx, onYandexSuccessWebhookEventKey, &yandex.CreateWebhookRequest{ + Event: yandex.WebhookEventPaymentSucceeded, + URL: url, + }); err != nil { + receiver.logger.Error("failed to set webhook on of ", zap.Error(err)) + return err + } + + return nil +} + +func (receiver *Yandex) SetOnPaymentCanceled(ctx context.Context) errors.Error { + url, urlErr := url.JoinPath(receiver.configuration.Domen, "yandex", "payment", "status", "canceled") + if urlErr != nil { + receiver.logger.Error("failed to join url path on of ", zap.Error(urlErr)) + return errors.NewWithError(fmt.Errorf("failed to join url path: %w", urlErr), errors.ErrInternalError) + } + + if err := receiver.SetWebhook(ctx, onYandexCanceledWebhookEventKey, &yandex.CreateWebhookRequest{ + Event: yandex.WebhookEventPaymentCanceled, + URL: url, + }); err != nil { + receiver.logger.Error("failed to set webhook on of ", zap.Error(err)) + return err + } + + return nil +} + +func (receiver *Yandex) SetOnPaymentWaiting(ctx context.Context) errors.Error { + url, urlErr := url.JoinPath(receiver.configuration.Domen, "yandex", "payment", "status", "waiting") + if urlErr != nil { + receiver.logger.Error("failed to join url path on of ", zap.Error(urlErr)) + return errors.NewWithError(fmt.Errorf("failed to join url path: %w", urlErr), errors.ErrInternalError) + } + + if err := receiver.SetWebhook(ctx, onYandexWaitingWebhookEventKey, &yandex.CreateWebhookRequest{ + Event: yandex.WebhookEventPaymentWaiting, + URL: url, + }); err != nil { + receiver.logger.Error("failed to set webhook on of ", zap.Error(err)) + return err + } + + return nil +} + +func (receiver *Yandex) SetOnPaymentRefund(ctx context.Context) errors.Error { + url, urlErr := url.JoinPath(receiver.configuration.Domen, "yandex", "refund", "status", "canceled") + if urlErr != nil { + receiver.logger.Error("failed to join url path on of ", zap.Error(urlErr)) + return errors.NewWithError(fmt.Errorf("failed to join url path: %w", urlErr), errors.ErrInternalError) + } + + if err := receiver.SetWebhook(ctx, onYandexRefundWebhookEventKey, &yandex.CreateWebhookRequest{ + Event: yandex.WebhookEventRefundSucceeded, + URL: url, + }); err != nil { + receiver.logger.Error("failed to set webhook on of ", zap.Error(err)) + return err + } + + return nil +} + +func (receiver *Yandex) SetWebhook(ctx context.Context, idempotenceKey string, request *yandex.CreateWebhookRequest) errors.Error { + webhook, err := receiver.GetWebhookByType(ctx, request.Event) + if err != nil && err.Type() != errors.ErrNotFound { + receiver.logger.Error("failed to get webhook on of ", zap.Error(err)) + return err + } + + if webhook != nil { + receiver.logger.Error("webhook already exist on of ") + return errors.NewWithError( + fmt.Errorf("yandex webhook with type <%s> already exist", request.Event), + errors.ErrConflict, + ) + } + + if _, err := receiver.yandexWebhookClient.SetWebhookEvent(ctx, idempotenceKey, request); err != nil { + receiver.logger.Error("failed to set webhook on of ", zap.Error(err)) + return err + } + + return nil +} + +func (receiver *Yandex) GetWebhookByType(ctx context.Context, event yandex.WebhookEventType) (*yandex.Webhook, errors.Error) { + webhooks, err := receiver.yandexWebhookClient.GetWebhookEvents(ctx) + if err != nil { + receiver.logger.Error("failed to get webhooks list on of ", zap.Error(err)) + return nil, err + } + + if len(webhooks) < 1 { + receiver.logger.Error("empty webhooks on of ") + return nil, errors.NewWithMessage("webhooks empty in yandex server", errors.ErrNotFound) + } + + webhook := array.Find(webhooks, func(webhook yandex.Webhook, _ int, _ []yandex.Webhook) bool { + return webhook.Event == event + }) + if webhook == nil { + receiver.logger.Error("webhook not found on of ", zap.Error(err), zap.String("event", string(event))) + return nil, errors.NewWithError(fmt.Errorf("yandex webhook not found with <%s> event", event), errors.ErrNotFound) + } + + return webhook, nil +} + +func (receiver *Yandex) UnsetWebhook(ctx context.Context, event yandex.WebhookEventType) errors.Error { + webhook, err := receiver.GetWebhookByType(ctx, event) + if err != nil { + receiver.logger.Error("failed to get webhook on of ", zap.Error(err), zap.String("event", string(event))) + return err + } + + if err := receiver.yandexWebhookClient.DeleteWebhook(ctx, webhook.ID); err != nil { + receiver.logger.Error("failed to delete webhook on of ", zap.Error(err), zap.String("id", webhook.ID)) + return err + } + + return nil +} diff --git a/internal/swagger/api.gen.go b/internal/swagger/api.gen.go new file mode 100644 index 0000000..2dca525 --- /dev/null +++ b/internal/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{ + + "H4sIAAAAAAAC/+xYzW7bRhB+FWLbQwIwlpw2DaBb0kMR9NDARlEUSWBsyJXD1CIZknIiGAL0kzRBHTRI", + "0EMPRYq0PReybNm0fuhXmH2FPkkxs0tJlLaOVSC3XBKJImdnvm++b4beY05QCwNf+EnMKnssdh6IGqeP", + "N2pB3U/wUxgFoYgST9B1px5Fwnca+NkVsRN5YeIFPqsw+EO2YCCfwYHswhD6MIAJTOQ+nFowhAyOLOhD", + "D0byZ9mR+xb0LfkUMtmCMfRkBwbWrc1vrnx+df06s5l4wmvhjmAVtvHtTWazpBHilziJPH+bNW22y3fq", + "wpDDO9mFMUbE+NCX+3AgW9DDTCDDTKYpwKBwznp5rVxePqlps0g8qnuRcFnljj7WnqFwb/pEcP+hcBLM", + "7TZv1ISfbCY8qRNmwq/X8OnH3Es8f3urGkRbDg+TeoSxQuG7eJbN4rrjCOEKF0/gviN2hDt3wqz6DeF4", + "oSdM/HDHQeK2PNeAzq9whKzIDqTyKaQwJOQz2bIQMjiEHpxAChMN399f8zjmJvi3eSIe88aqp8i27MIB", + "9GBIF7t0U++9qM/VVDjaBP6mSAr4b4hHdREbgBK7Gr/FDoIMDuQ+Zg8D24KMulcVABmMLdml3j7CLzCS", + "r2AgO3NYzVrKDxKv6jmcIhtA1ElX9tinkaiyCvukNJNjSWux9D33XfFE14SPqTjL6oMUzixK/icYwBCR", + "XbPgLyRAtiGl7PtTKQwsOEGi5XPSaQoD65/WL9Z8ztalYqn5jZdXqHKBSfrV1thPETDxWCx7uc2n9nQe", + "dtrEmignkpu7xU2cv0HngrF8ZcEZOpXsYN2yBceqlSFVP42UU8GxqWlt5gR+1YtqCgrTKXNuRL4o23ie", + "bCNr/+foGVxOJHjy/vLoqBM4ohwo8gmpEXVvLKkQaSnwWziDVLZVtAVDNfy4FF08Cb1IxO/J2rawA4sy", + "PMQLfRwjY8jgGDWILX0AA8JUoTXBm/CZsUayI19akMII0kW0j/Jf55A2ZbyqrRaIK5rqDCoeu9XrPP4C", + "/71WvYZfjWf7TlATW6u2fsiNSf8uW5AqD4Ah+oZOFTE1NsX9INgR3FchSZZbNZE8CFzjGO7IFq4BsiO7", + "2PLIE3KUYe8twnJpRqLiawSpfE7JvJRt7NJMdgpiWYpx2SSKaH5MngfWbJ7SU9W67/L7OyaXfUP6UelO", + "MBvdNdpdB3gBu6tP9/WxdCzqDDLrxu1bRjDVeajdFYmNpwvGefcXtxGcH3ocntsPiCvVklEpp6pBBlTP", + "j5BCaihlwetpWusc7dyw50kpuJbOS7drgYXl8dAkNVQDoyUdUF9oYjKt8Lb6Xw0/TZJsU0F95VLWFeUJ", + "I9mVz3M3UJuQfEbrK20vYxjLrj1nRrjR0I3KpFA61A00czvypW2po5QZZqg1dItTnVIKp4VotC5Po+EC", + "S0nl9mWTh+AhKu0UezFvOq2S6f5CypnpGjH2EmXO72alX6HN75Dc9nDJueRr7HS5L59ZtN6/0FvAKbPZ", + "rohiBfv6WnmtTDtNKHweeqzCPlsrr60Tn8kDatIS3+XeTq6rbWFqwqlJIEijfB4rSvJhOMVyiOQcaRF2", + "4UyniUsO9XKPLDlDwHSwIhi4T9CsvuWyCvtKJDfyBLVmYurDOAz8WG0dV8tlegcK/ES7Cg/DHb38lB7G", + "akgq5dGOmVv8HfbIe+whZJ5aqD3/h6Baxdb2ElGj4Et+ry/wKOIN1fNFtBab6iJAzCOAERO+HWN6IW+w", + "e3ih1KDFq6QtvqQEXJq+juAmFhgN5LdF7mSXGrNH+2YfhqQDvEBDob08B+4yeJtPa/kaJnfZEkubIils", + "hsrWvsyzUw4k4uRm4DZWouo8C/2vd4pmU3necocsQPMnUYXiwY3bgExvARmk/5ox1BuYyC5OV/16/UqN", + "xxeQwgEGWonV2QvnB6Z1HoDswrRuzr0Pf+R1BV71Hxo+uFjhGNL8ZQIGF+b1O53eR1bPZ1XtQB9arIVV", + "dUW5blCGH9V6EV5nV/aYz2s42vGX5r3mvwEAAP//ghXolIcVAAA=", +} + +// 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/swagger/api.go b/internal/swagger/api.go new file mode 100644 index 0000000..060e9fb --- /dev/null +++ b/internal/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/swagger/api.yaml b/internal/swagger/api.yaml new file mode 100644 index 0000000..a2634dd --- /dev/null +++ b/internal/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/swagger/models.gen.go b/internal/swagger/models.gen.go new file mode 100644 index 0000000..1c3615b --- /dev/null +++ b/internal/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/swagger/models.yaml b/internal/swagger/models.yaml new file mode 100644 index 0000000..9ef06be --- /dev/null +++ b/internal/swagger/models.yaml @@ -0,0 +1,4 @@ +output: models.gen.go +package: swagger +generate: + models: true diff --git a/internal/utils/amount.go b/internal/utils/amount.go new file mode 100644 index 0000000..387e834 --- /dev/null +++ b/internal/utils/amount.go @@ -0,0 +1,7 @@ +package utils + +import "strconv" + +func ConvertAmountToStringFloat(amount int64) string { + return strconv.FormatFloat(float64(amount)/100, 'f', 2, 64) +} diff --git a/internal/utils/amount_test.go b/internal/utils/amount_test.go new file mode 100644 index 0000000..039dfd6 --- /dev/null +++ b/internal/utils/amount_test.go @@ -0,0 +1,43 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/utils" +) + +func TestConvertAmountToStringFloat(t *testing.T) { + testCases := []struct { + name string + amount int64 + expected string + }{ + { + name: "Успешная конвертация числа 10020 в 100.20", + amount: 10020, + expected: "100.20", + }, + { + name: "Успешная конвертация числа 10002 в 100.02", + amount: 10002, + expected: "100.02", + }, + { + name: "Успешная конвертация числа 12054 в 120.54", + amount: 12054, + expected: "120.54", + }, + { + name: "Успешная конвертация числа 198531 в 1985.31", + amount: 198531, + expected: "1985.31", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + assert.Equal(t, testCase.expected, utils.ConvertAmountToStringFloat(testCase.amount)) + }) + } +} diff --git a/internal/utils/echotools/bind.go b/internal/utils/echotools/bind.go new file mode 100644 index 0000000..50bfef9 --- /dev/null +++ b/internal/utils/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 +} diff --git a/internal/utils/uuid.go b/internal/utils/uuid.go new file mode 100644 index 0000000..2766a26 --- /dev/null +++ b/internal/utils/uuid.go @@ -0,0 +1,19 @@ +package utils + +import ( + "fmt" + + "github.com/google/uuid" +) + +func GenerateUUID() (result string, err error) { + defer func() { + if recovered := recover(); recovered != nil { + err = fmt.Errorf("recovered generateUUID error: %v", recovered) + } + }() + + generatedUUID := uuid.New() + + return generatedUUID.String(), nil +} diff --git a/internal/utils/yoomoney.go b/internal/utils/yoomoney.go new file mode 100644 index 0000000..54521d6 --- /dev/null +++ b/internal/utils/yoomoney.go @@ -0,0 +1,15 @@ +package utils + +import ( + "encoding/base64" + "fmt" + "strings" +) + +func ConvertYoomoneySercetsToAuth(authType, storeID, secretKey string) string { + credentials := fmt.Sprintf("%s:%s", storeID, secretKey) + encoded := base64.StdEncoding.EncodeToString([]byte(credentials)) + trimedAuthType := strings.TrimSpace(authType) + + return fmt.Sprintf("%s %s", trimedAuthType, encoded) +} diff --git a/internal/utils/yoomoney_test.go b/internal/utils/yoomoney_test.go new file mode 100644 index 0000000..1fe5bf1 --- /dev/null +++ b/internal/utils/yoomoney_test.go @@ -0,0 +1,22 @@ +package utils_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/utils" +) + +func TestConvertYoomoneySercetsToAuth(t *testing.T) { + t.Run("Успешная конвертация секретов в base64 используя метод Basic", func(t *testing.T) { + assert.Equal(t, "Basic aWQ6c2VjcmV0", utils.ConvertYoomoneySercetsToAuth("Basic", "id", "secret")) + }) + + t.Run("Успешная конвертация секретов в base64 используя метод Bearer", func(t *testing.T) { + assert.Equal(t, "Bearer aWQ6c2VjcmV0", utils.ConvertYoomoneySercetsToAuth("Bearer", "id", "secret")) + }) + + t.Run("Успешная конвертация секретов в base64 используя метод Basic, в котором лишние пробелы", func(t *testing.T) { + assert.Equal(t, "Basic aWQ6c2VjcmV0", utils.ConvertYoomoneySercetsToAuth("Basic ", "id", "secret")) + }) +} diff --git a/k8s/deployment.yml b/k8s/deployment.yml deleted file mode 100644 index af3c93e..0000000 --- a/k8s/deployment.yml +++ /dev/null @@ -1,30 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - name: treasurer -spec: - replicas: 1 - selector: - matchLabels: - app: treasurer - template: - metadata: - labels: - app: treasurer - tier: backend - spec: - imagePullSecrets: - - name: regcred - containers: - - name: treasurer - image: 192.168.193.230:31320/treasurer - imagePullPolicy: Always - resources: - requests: - memory: 50Mi - limits: - memory: 1Gi - ports: - - containerPort: 3000 - name: http - restartPolicy: Always \ No newline at end of file diff --git a/k8s/service.yml b/k8s/service.yml deleted file mode 100644 index 51069e0..0000000 --- a/k8s/service.yml +++ /dev/null @@ -1,11 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: treasurer -spec: - ports: - - port: 3000 - targetPort: 3000 - protocol: TCP - selector: - app: treasurer \ No newline at end of file diff --git a/main.go b/main.go deleted file mode 100644 index e794d4c..0000000 --- a/main.go +++ /dev/null @@ -1,10 +0,0 @@ -package main - -import ( - "bitbucket.org/skeris/treasurer/app" - "github.com/skeris/appInit" -) - -func main() { - appInit.Initialize(app.New, app.Options{}) -} diff --git a/migrations/test/001_amocrm_insert.down.json b/migrations/test/001_amocrm_insert.down.json new file mode 100644 index 0000000..7c39d90 --- /dev/null +++ b/migrations/test/001_amocrm_insert.down.json @@ -0,0 +1 @@ +[{ "delete": "amocrm", "deletes": [{ "q": {} }] }] diff --git a/migrations/test/001_amocrm_insert.up.json b/migrations/test/001_amocrm_insert.up.json new file mode 100644 index 0000000..cd07e84 --- /dev/null +++ b/migrations/test/001_amocrm_insert.up.json @@ -0,0 +1,85 @@ +[ + { + "insert": "amocrm", + "ordered": true, + "documents": [ + { + "amocrmId": "1", + "userId": "1", + "information": { + "id": 30228997, + "name": "ООО ПЕНА", + "subdomain": "penadigital", + "created_at": 1683680509, + "created_by": 0, + "updated_at": 1683680509, + "updated_by": 0, + "current_user_id": 8110726, + "country": "RU", + "customers_mode": "disabled", + "is_unsorted_on": true, + "is_loss_reason_enabled": true, + "is_helpbot_enabled": false, + "is_technical_account": true, + "contact_name_display_order": 1, + "amojo_id": "", + "uuid": "", + "version": 0, + "_links": { "self": { "href": "https://penadigital.amocrm.ru/api/v4/account" } }, + "_embedded": { + "amojo_rights": { "can_direct": false, "can_create_groups": false }, + "users_groups": null, + "task_types": null, + "entity_names": { + "leads": { + "ru": { + "gender": "", + "plural_form": { + "dative": "", + "default": "", + "genitive": "", + "accusative": "", + "instrumental": "", + "prepositional": "" + }, + "singular_form": { + "dative": "", + "default": "", + "genitive": "", + "accusative": "", + "instrumental": "", + "prepositional": "" + } + }, + "en": { + "singular_form": { "default": "" }, + "plural_form": { "default": "" }, + "gender": "" + }, + "es": { + "singular_form": { "default": "" }, + "plural_form": { "default": "" }, + "gender": "" + } + } + }, + "datetime_settings": { + "date_pattern": "", + "short_date_pattern": "", + "short_time_pattern": "", + "date_formant": "", + "time_format": "", + "timezone": "", + "timezone_offset": "" + } + } + }, + "audit": { + "createdAt": "2022-12-31T00:00:00.000Z", + "updatedAt": "2022-12-31T00:00:00.000Z", + "deleted": false + } + } + ] + } +] diff --git a/openapi.yaml b/openapi.yaml index 1548223..6183273 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -4,11 +4,15 @@ info: description: |- Область отвественности сервиса - получить на вход сумму, которуб надо запросить, и список действий, который надо выполнить, в зависимости от события оплаты version: 1.0.0 + tags: - name: pay + paths: - /available + + /available: get: + operationId: getAvailablePayments description: метод для получения списка доступных вариантов для оплаты tags: - pay @@ -22,82 +26,166 @@ paths: example: ["qiwi", "visa", "tinkoff"] items: type: string - /invoice: + + /yandex/payment/status/waiting: post: - description: метод для создания запроса на оплату + operationId: setYandexPaymentStatusWaiting + description: Метод для установки статуса платежа "Ожидание" tags: - pay requestBody: content: application/json: schema: - $ref: '#/components/schemas/Request' + $ref: '#/components/schemas/SetPaymentStatusRequest' responses: '200': - description: успешное создание платёжной ссылки - content: - application/json: - schema: - type: object - properties: - link: - type: string - example: "google.ru" - '400': - description: недостаточно данных + description: Успешная установка статуса '500': - description: порой бывает что платёжные решения глючат все сразу, поэтому приходится оповещать о том, что получится запрос только потом + description: Внутренняя ошибка + + /yandex/payment/status/succeeded: + post: + operationId: setYandexPaymentStatusSucceeded + description: Метод для установки статуса платежа "Успешно" + tags: + - pay + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetPaymentStatusRequest' + responses: + '200': + description: Успешная установка статуса + '500': + description: Внутренняя ошибка + + /yandex/payment/status/canceled: + post: + operationId: setYandexPaymentStatusCanceled + description: Метод для установки статуса платежа "Отменён" + tags: + - pay + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetPaymentStatusRequest' + responses: + '200': + description: Успешная установка статуса + '500': + description: Внутренняя ошибка + + /yandex/refund/status/succeeded: + post: + operationId: setYandexRefundStatusSucceeded + description: Метод для установки статуса возврата "Успешно" + tags: + - pay + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetPaymentStatusRequest' + responses: + '200': + description: Успешная установка статуса + '500': + description: Внутренняя ошибка + components: schemas: - Request: - type: object - properties: - requester: - type: string - example: "asdf7as6df7a5f5asdf" - description: айдишник юзера, создавшего запрос - amount: - type: integer - format: int64 - example: 10000 - description: количество денег в сотых долях валюты - currency: - type: string - example: "rub" - description: валюта, в которой юзер хочет запросить деньги. Если для такой валюты нет платёжного решения, через переменные окружения должна передаваться дефолтная валюта, которая будет использоваться в таком случае - payway: - type: string - example: "qiwi" - description: платёжный метод, который хочет использовать пользователь для пополнения баланса - additional: - type: string - example: "+79885895677" - description: некоторым платёжным решениям необходимы дополнительные данные, типа номера телефона или почты. Я пока не знаю, как сделать это красиво, пусть будет так - on_accept: - type: array - items: - $ref: '#/components/schemas/Action' - on_decline: - type: array - items: - $ref: '#/components/schemas/Action' - on_timeout: - type: array - items: - $ref: '#/components/schemas/Action' - Action: + + SetPaymentStatusRequest: type: object + required: [type, event, object] properties: type: type: string - enum: ["post", "get", "put", "delete", "patch"] - description: это варианты запросов по сети. В последствии тут ещё появятся всякие варианты с отправкой смс, почты, оповещений в телегу и прочих методов доставки результата оплаты - example: post - target: + description: Тип объекта. Фиксированное значение — notification (уведомление) + example: "notification" + event: + $ref: '#/components/schemas/Event' + object: + $ref: '#/components/schemas/YandexPayment' + + YandexPayment: + type: object + required: [id, status, amount, recipient, created_at, test, paid, refundable] + properties: + id: type: string - description: в случае с http запросами, это url на который надо сделать запрос - example: "google.ru" - data: + description: Идентификатор платежа в ЮKassa + example: "asdf7as6df7a5f5asdf" + status: + $ref: '#/components/schemas/PaymentStatus' + amount: + $ref: '#/components/schemas/Amount' + income_amount: + $ref: '#/components/schemas/Amount' + recipient: + $ref: '#/components/schemas/Recipient' + description: type: string - description: данные, которые надо передать. в условиях https запросов это json или query string - example: "{\"some\":\"shit\"}" + description: Описание + example: "Описание" + payment_method: + type: object + description: Структура метода платежа (может отличаться от способа платежа) + captured_at: + type: string + description: Время подтверждения платежа + expires_at: + type: string + description: Время, до которого вы можете бесплатно отменить или подтвердить платеж + created_at: + type: string + description: Время создания заказа + confirmation: + type: object + description: Выбранный способ подтверждения платежа + test: + type: boolean + description: Признак тестовой операции + refunded_amount: + $ref: '#/components/schemas/Amount' + paid: + type: boolean + description: Признак оплаты заказа + refundable: + type: boolean + description: Возможность провести возврат по API + + Amount: + type: object + required: [value, currency] + properties: + value: + type: string + description: Сумма в выбранной валюте + example: "10.00" + currency: + type: string + description: Трехбуквенный код валюты в формате ISO-4217 + example: "RUB" + + Recipient: + type: object + required: [account_id, gateway_id] + properties: + account_id: + type: string + description: Идентификатор магазина в ЮKassa + gateway_id: + type: string + description: Идентификатор субаккаунта + + PaymentStatus: + type: string + enum: ["waiting_for_capture", "pending", "succeeded", "canceled"] + + Event: + type: string + enum: ["payment.waiting_for_capture", "payment.succeeded", "payment.canceled", "refund.succeeded"] \ No newline at end of file diff --git a/ops/deploy b/ops/deploy deleted file mode 100755 index b61e3e5..0000000 --- a/ops/deploy +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -e - -cd .. - -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING APP" -echo "################################################################" -echo "" -echo "" - -make build - -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING DOCKER IMAGE" -echo "################################################################" -echo "" -echo "" - -sudo docker build -t 192.168.193.230:31320/treasurer:latest . - -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING PUSH IMAGE" -echo "################################################################" -echo "" -echo "" - -sudo docker push 192.168.193.230:31320/treasurer - -echo "" -echo "" -echo "################################################################" -echo "#### RELOAD PODS" -echo "################################################################" -echo "" -echo "" -# TODO: rolling-update -cd ./k8s; kubectl apply -f ./deployment.yml; kubectl apply -f service.yml - - -kubectl rollout restart deployment treasurer \ No newline at end of file diff --git a/ops/deployStaging b/ops/deployStaging deleted file mode 100755 index 8812263..0000000 --- a/ops/deployStaging +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -e - -cd .. -git checkout dev -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING APP" -echo "################################################################" -echo "" -echo "" - -make build - -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING DOCKER IMAGE" -echo "################################################################" -echo "" -echo "" - -sudo docker build -t 192.168.193.154:31320/treasurer:latest . -f ./DockerfileStaging - -echo "" -echo "" -echo "################################################################" -echo "#### BUILDING PUSH IMAGE" -echo "################################################################" -echo "" -echo "" - -sudo docker push 192.168.193.154:31320/treasurer \ No newline at end of file diff --git a/payway/Dockerfile b/payway/Dockerfile deleted file mode 100644 index 28fc2a3..0000000 --- a/payway/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch -ADD test / -CMD ["/test"] diff --git a/payway/bt.go b/payway/bt.go deleted file mode 100644 index cb48592..0000000 --- a/payway/bt.go +++ /dev/null @@ -1,168 +0,0 @@ -package payway - -import ( - "bitbucket.org/skeris/treasurer/dal" - "context" - "crypto/md5" - "encoding/json" - "fmt" - "github.com/fatih/structs" - "sort" -) - -const UrlBt = "https://merchant.betatransfer.io/api" - -type BetaTransfer struct { - MongoID string - Name string - MerchantID string - WalletID string - Secret1 string // Request key - Secret2 string // Response key - Secret3 string // Wallet API Key - PayoutTypeList []dal.PayoutType - PaymentTypeList []dal.PayWayPayment -} - -func NewBt(ctx context.Context, conn *dal.MongoConnection) (*BetaTransfer, error) { - bt, err := conn.GetPayWayByName(ctx, "BetaTransfer") - - if err != nil { - return nil, err - } - - paymentList, err := conn.GetPayWayPaymentByPayWay(ctx, bt.ShortName) - - if err != nil { - return nil, err - } - - return &BetaTransfer{ - MongoID: bt.ID, - Name: bt.Name, - MerchantID: bt.MerchantID, - WalletID: bt.WalletID, - Secret1: bt.Secret1, - Secret2: bt.Secret2, - PayoutTypeList: bt.PayoutTypeList, - PaymentTypeList: paymentList, - }, nil -} - -func (bt *BetaTransfer) GetMongoID() string { - return bt.MongoID -} - -func (bt *BetaTransfer) GetName() string { - return bt.Name -} - -func (bt *BetaTransfer) UpdateData(data *dal.PayWay) { - bt.MongoID = data.ID - bt.Name = data.MerchantID - bt.MerchantID = data.MerchantID - bt.Secret1 = data.Secret1 - bt.Secret2 = data.Secret2 - bt.Secret3 = data.Secret3 -} - -func (bt *BetaTransfer) UpdatePayment(data []dal.PayWayPayment) { - bt.PaymentTypeList = data -} - -func (bt *BetaTransfer) UpdatePayout(data []dal.PayoutType) { - bt.PayoutTypeList = data -} - -type ReqPaymentListenerBt struct { - Sign string - Amount float64 - OrderId string - UserComment string -} - -// GetPayoutTypeList - возвращает локальные способы вывода средств из кошелька -func (bt *BetaTransfer) GetPayoutTypeList() []dal.PayoutType { - return bt.PayoutTypeList -} - -// GetPayoutType - возвращает локальный указанный способ вывода средств, либо nil если не найдено -func (bt *BetaTransfer) GetPayoutType(payoutType string) *dal.PayoutType { - for _, v := range bt.PayoutTypeList { - if v.ApiId == payoutType { - return &v - } - } - - return nil -} - -// GetPaymentTypeList - возвращает локальные способы пополнения -func (bt *BetaTransfer) GetPaymentTypeList() []dal.PayWayPayment { - return bt.PaymentTypeList -} - -// GetPaymentType - возвращает локальный указанный способ пополнения, либо nil если не найдено -func (bt *BetaTransfer) GetPaymentType(paymentType string) *dal.PayWayPayment { - for _, v := range bt.PaymentTypeList { - if v.ApiId == paymentType { - return &v - } - } - - return nil -} - -// FindPayoutType - возвращает локальный указанный способ вывода средств учитывая amount. -// Возвращает nil если ничего не найдено -func (bt *BetaTransfer) FindPayoutType(payoutType string, amount float64) *dal.PayoutType { - for _, v := range bt.PayoutTypeList { - if v.ApiId == payoutType && amount > v.Minimum && amount < v.Maximum { - return &v - } - } - - return nil -} - -// prepareData - сортирует структуру по имени поля, добавляет подпись и переводит в байты -func (bt *BetaTransfer) prepareData(data interface{}) ([]byte, error) { - s := structs.New(data) - structMap := s.Map() // Преобразуем структуру в map - - // Сортируем по ключу - keys := make([]string, 0, len(structMap)) - for k := range structMap { - keys = append(keys, k) - } - sort.Strings(keys) - - sorted := map[string]interface{}{} - var str string - - for _, k := range keys { - if structMap[k] != "" { - sorted[s.Field(k).Tag("json")] = structMap[k] - str += fmt.Sprintf("%v", structMap[k]) - } - } - - // Добавляем подпись - sorted["sign"] = bt.createSign([]string{str}) - - // Переводим в байты - return json.Marshal(sorted) -} - -// createSign - создает md5-подпись из указанного массива строк. В конец строки добавляет Secret2 -func (bt *BetaTransfer) createSign(s []string) string { - var str string - for _, v := range s { - str += v - } - - str += bt.Secret2 - - h := md5.Sum([]byte(str)) - return fmt.Sprintf("%x", h) -} diff --git a/payway/bt_api.go b/payway/bt_api.go deleted file mode 100644 index 74983ef..0000000 --- a/payway/bt_api.go +++ /dev/null @@ -1,322 +0,0 @@ -package payway - -import ( - "bitbucket.org/skeris/treasurer/dal" - "bytes" - "context" - "crypto/md5" - "encoding/json" - "errors" - "fmt" - "github.com/rs/xid" - "io/ioutil" - "log" - "net/http" - "net/url" - "strconv" -) - -//#region ========= Payment ========= - -type ReqCreatePaymentUrlBt struct { - Amount string `json:"amount"` // Required - Currency string `json:"currency"` // Required - OrderId string `json:"orderId"` // Required - PaymentSystem string `json:"paymentSystem,omitempty"` - UrlResult string `json:"urlResult,omitempty"` - UrlSuccess string `json:"urlSuccess,omitempty"` - UrlFail string `json:"urlFail,omitempty"` - Locale string `json:"locale,omitempty"` - PayerPhone string `json:"payerPhone,omitempty"` - PayerName string `json:"payerName,omitempty"` - PayerEmail string `json:"payerEmail,omitempty"` - PayerFirstName string `json:"payer_firstname,omitempty"` - PayerLastName string `json:"payer_lastname,omitempty"` - PayerPostCode string `json:"payer_postcode,omitempty"` - PayerAddress string `json:"payer_address,omitempty"` - Ip string `json:"ip,omitempty"` - UserComment string `json:"user_comment"` // Returned in Callback - Sign string `json:"sign"` // Required -} - -type RespCreatePaymentUrlBt struct { - Status interface{} `json:"status"` - Id int64 `json:"id"` - Url string `json:"url"` - UrlPayment string `json:"urlPayment"` -} - -// CreatePaymentUrl - создает ссылку для перевода средств на кошелек (пополнение счета клиента) -func (bt *BetaTransfer) CreatePaymentUrl(amount, orderID, paymentType, currency, lang, email, phone, requesterID, - userIP string) (string, error) { - action := UrlBt + "/payment?token=" + bt.Secret1 - - data := ReqCreatePaymentUrlBt{ - Amount: amount, - Currency: currency, - OrderId: orderID, - PaymentSystem: paymentType, - UrlResult: "", - UrlSuccess: "", - UrlFail: "", - Locale: lang, - PayerPhone: phone, - PayerName: "", - PayerEmail: email, - PayerFirstName: "", - PayerLastName: "", - PayerPostCode: "", - PayerAddress: "", - Ip: "", - UserComment: requesterID, - Sign: "", - } - - body, err := bt.prepareData(data) - - fmt.Println("Request:", string(body)) - if err != nil { - fmt.Println("MarshalErr:", err) - } - - params := url.Values{} - params.Add("amount", data.Amount) - params.Add("currency", data.Currency) - params.Add("orderId", xid.New().String()) - params.Add("paymentSystem", data.PaymentSystem) - params.Add("locale", lang) - params.Add("user_comment", orderID) - json.Unmarshal(body, &data) - params.Add("sign", data.Sign) - - resp, err := http.PostForm(action, params) - - fmt.Println("Status:", resp.Status) - - var result RespCreatePaymentUrlBt - bodyr, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Println(" ioutil: ", err) - } - if resp.StatusCode != 200 { - - return "", errors.New("bad response: " + string(bodyr)) - } - - err = json.Unmarshal(bodyr,&result) - - if err != nil { - log.Println(" json: ", err) - } - - fmt.Println(result) - - return result.Url, nil -} - -// PaymentListener - Обрабатывает результат перевода -func (bt *BetaTransfer) PaymentListener(ctx context.Context,ip string, request interface{}, mc *dal.MongoConnection) error { - data := request.(*ReqPaymentListenerBt) - if data.Sign != fmt.Sprintf("%x", - md5.Sum([]byte(fmt.Sprint(data.Amount)+data.OrderId+bt.Secret2))) { - return errors.New(fmt.Sprintf("wrong sign: %v, %s, %s", data.Sign, data.OrderId, bt.Secret2)) - } - - // Проверка существования платежа в БД - payment, err := mc.GetPayment(ctx, data.UserComment) - - if err != nil { - return err - } - - if payment == nil { - return errors.New("payment not found") - } - - // Проверка статуса платежа (должен быть open) - //if payment.Status != "open" || { - // return errors.New(fmt.Sprintf("payment have status: %v", payment.Status)) - //} - - // Проверка суммы платежа - //if data.Amount != payment.Amount { - // return errors.New(fmt.Sprintf("wrong amount: %v", data.Amount)) - //} - // - //// Проверка RequesterID - //if data.RequesterID != payment.RequesterID { - // return errors.New(fmt.Sprintf("wrong us_requesterID: %v", data.RequesterID)) - //} - - // Обновление статуса платежа в БД - if payment.Status != "accepted" { - err = mc.UpdatePaymentStatus(ctx, data.UserComment, "accepted") - if err != nil { - return err - } - } - - return nil -} - -func (bt *BetaTransfer) GetPaymentList() ([]dal.PayWayPayment, error) { - //TODO: implement me - return bt.GetPaymentTypeList(), nil -} - -//#endregion - -//#region ========= Payout ========= - -type ReqCreatePayoutBt struct { - Amount string `json:"amount,omitempty"` // Required - Currency string `json:"currency"` // Required - OrderId string `json:"orderId"` // Required - PaymentSystem string `json:"paymentSystem"` // Required - UrlResult string `json:"urlResult,omitempty"` - Address string `json:"address"` // Required. Purse - Sign string `json:"sign"` // Required -} - -type RespCreatePayoutBt struct { - Status string `json:"status"` - Id int `json:"id"` - PaymentSystem string `json:"paymentSystem"` - OrderId string `json:"orderId"` - OrderAmount string `json:"orderAmount"` - OrderCurrency string `json:"orderCurrency"` - PaymentAmount string `json:"paymentAmount"` - PaymentCurrency string `json:"paymentCurrency"` - Commission string `json:"commission"` - CommissionCurrency string `json:"commissionCurrency"` -} - -// CreatePayout - выполняет запрос для вывода средств из кошелька (вывод средств со счета клиента). -// В ответ получает RespCreatePayoutBt -// purse - кошелек для вывода -// amount - сумма для вывода -// desc - описание/комментарий (OrderId) -// exchange = 1 - отключить автообмен -// currency - валюта для вывода -func (bt *BetaTransfer) CreatePayout(amount, orderID, payoutType, currency, address string) (string, error) { - action := UrlBt + "//withdrawal-payment?token=" + bt.Secret1 - payout := bt.GetPayoutType(payoutType) - if currency == "" { - currency = payout.Currency - } - - data := ReqCreatePayoutBt{ - Amount: amount, - Currency: currency, - OrderId: orderID, - PaymentSystem: payoutType, - UrlResult: "", - Address: address, - Sign: "", - } - - body, err := bt.prepareData(data) - - if err != nil { - fmt.Println("MarshalErr:", err) - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status) - - var result RespCreatePayoutBt - - err = json.NewDecoder(resp.Body).Decode(&result) - - if resp.StatusCode != 200 { - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - return "", errors.New("bad response: " + string(body)) - } - - fmt.Println(result) - - return strconv.Itoa(result.Id), nil -} - -// PayoutListener - обрабатывает результат вывода -func (bt *BetaTransfer) PayoutListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error { - fmt.Println("bt.PayoutListener:", request) - return nil -} - -func (bt *BetaTransfer) GetPayoutList() ([]dal.PayoutType, error) { - //TODO: implement me - return bt.GetPayoutTypeList(), nil -} - -//#endregion - -//#region ========= Wallet ========= - -type RespGetWalletBalanceBt struct { - Account struct { - LockWithdrawal bool `json:"lockWithdrawal"` - LockAccount bool `json:"lockAccount"` - AuthAt int `json:"authAt"` - } `json:"account"` - Balance map[string]string `json:"balance"` - BalanceOnHold map[string]string `json:"balance_on_hold"` -} - -// GetWalletBalance - выполняет запрос чтобы узнать баланс кошелька -func (bt *BetaTransfer) GetWalletBalance() (map[string]float64, error) { - action := UrlBt + "/account-info" - - params := url.Values{ - "token": {bt.Secret1}, - "sign": {bt.createSign([]string{bt.Secret1})}, - } - - action += fmt.Sprintf("?%v", params.Encode()) - - client := &http.Client{} - resp, err := client.Get(action) - - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, errors.New(fmt.Sprintf("got bad status: %v", resp.Status)) - } - - var response RespGetWalletBalanceBt - - err = json.NewDecoder(resp.Body).Decode(&response) - - if err != nil { - return nil, err - } - - var result map[string]float64 - - for key, val := range response.Balance { - if val == "" { - val = "0.0" - } - - toFloat, err := strconv.ParseFloat(val, 64) - if err != nil { - return nil, err - } - result[key] = toFloat - } - - return result, nil -} - -//#endregion diff --git a/payway/fk.go b/payway/fk.go deleted file mode 100644 index 2d6fe6e..0000000 --- a/payway/fk.go +++ /dev/null @@ -1,191 +0,0 @@ -package payway - -import ( - "context" - "crypto/hmac" - "crypto/sha256" - "encoding/json" - "fmt" - "sort" - "strings" - "time" - - "bitbucket.org/skeris/treasurer/dal" - "github.com/fatih/structs" - "go.mongodb.org/mongo-driver/mongo" -) - -//const AllowedIPs = "136.243.38.147 136.243.38.149 136.243.38.150 136.243.38.151 136.243.38.189" - -type FreeKassa struct { - MongoID string - Name string - ShortName string - MerchantID string - WalletID string - Secret1 string // Request key - Secret2 string // Response key - Secret3 string // Wallet API Key - PayoutTypeList []dal.PayoutType - PaymentTypeList []dal.PayWayPayment -} - -func NewFk(ctx context.Context, conn *dal.MongoConnection) (*FreeKassa, error) { - fmt.Println("NewFK0") - fk, err := conn.GetPayWayByName(ctx, "FreeKassa") - - if err != nil { - if err == mongo.ErrNoDocuments { - fk = &dal.PayWay{ - ID: "fk1", - Name: "FreeKassa", - ShortName: "fk", - MerchantID: "20397", - WalletID: "20397", - Secret1: "+%H$+Jb$gNw v.Minimum && amount < v.Maximum { - return &v - } - } - - return nil -} - -// prepareData - сортирует структуру по имени поля, добавляет подпись и переводит в байты -func (fk *FreeKassa) prepareData(data interface{}) ([]byte, error) { - s := structs.New(data) - structMap := s.Map() // Преобразуем структуру в map - - // Сортируем по ключу - keys := make([]string, 0, len(structMap)) - for k := range structMap { - keys = append(keys, k) - } - sort.Strings(keys) - - sorted := map[string]interface{}{} - var arr []string - - for _, k := range keys { - if structMap[k] != "" { - sorted[s.Field(k).Tag("json")] = structMap[k] - arr = append(arr, fmt.Sprintf("%v", structMap[k])) - } - } - - // Добавляем подпись - sorted["signature"] = fk.createSign(arr) - - // Переводим в байты - return json.Marshal(sorted) -} - -// createSign - создает hmac_sha256 подпись по Secret1 -func (fk *FreeKassa) createSign(data []string) string { - str := strings.Join(data, "|") - - sign := hmac.New(sha256.New, []byte("45fb9c7d82e59ba81")) - sign.Write([]byte(str)) - h := sign.Sum(nil) - - return fmt.Sprintf("%x", h) -} diff --git a/payway/fk_api.go b/payway/fk_api.go deleted file mode 100644 index 3fcbab5..0000000 --- a/payway/fk_api.go +++ /dev/null @@ -1,620 +0,0 @@ -package payway - -import ( - "bytes" - "context" - "crypto/md5" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "strconv" - "time" - - "bitbucket.org/skeris/treasurer/dal" -) - -const UrlFk = "https://api.freekassa.ru/v1" -const PrivateKey = "SomePrivateKey1!" //16 byte - -//#region ========= Payment ========= - -type ReqCreatePaymentUrlFk struct { - ShopId int64 `json:"shopId"` // Required - Nonce int64 `json:"nonce"` // Required Уникальный ID запроса, должен всегда быть больше предыдущего значения - Signature string `json:"signature"` // Required - PaymentId string `json:"paymentId"` - I int64 `json:"i"` // Required. ID платежной системы. dal.PaymentType.ApiId - Email string `json:"email"` // Required - IP string `json:"ip"` // Required - Amount float64 `json:"amount"` // Required - Currency string `json:"currency"` // Required - Tel string `json:"tel"` - SuccessUrl string `json:"successUrl,omitempty"` - FailureUrl string `json:"failureUrl,omitempty"` - NotificationUrl string `json:"notificationUrl,omitempty"` -} - -type RespCreatePaymentUrlFk struct { - Type string `json:"type"` - OrderId int `json:"order_id"` - OrderHash string `json:"order_hash"` - Location string `json:"location"` -} - -// CreatePaymentUrl - создает ссылку для перевода средств на кошелек (пополнение счета клиента) -func (fk *FreeKassa) CreatePaymentUrl(amount, orderID, paymentType, currency, lang, email, phone, requesterID, - userIP string) (string, - error) { - action := UrlFk + "/orders/create" - - shopID, err := strconv.ParseInt(fk.MerchantID, 10, 64) - if err != nil { - return "", err - } - - apiId, err := strconv.ParseInt(paymentType, 10, 64) - if err != nil { - return "", err - } - - amountFloat, err := strconv.ParseFloat(amount, 64) - if err != nil { - return "", err - } - - data := ReqCreatePaymentUrlFk{ - ShopId: shopID, - Nonce: time.Now().Unix(), - Signature: "", - PaymentId: orderID, - I: apiId, - Email: email, - IP: userIP, - Amount: amountFloat, - Currency: currency, - Tel: phone, - SuccessUrl: "", - FailureUrl: "", - NotificationUrl: "", - } - - body, err := fk.prepareData(data) - - if err != nil { - return "", err - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status) - - var result RespCreatePaymentUrlFk - bodyR, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - fmt.Println("ERSPONSE", string(bodyR)) - - err = json.Unmarshal(bodyR, &result) - if err != nil { - log.Println(" JSONNNNNioutil: ", err) - } - - if resp.StatusCode != 200 || err != nil { - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - return "", errors.New("bad response: " + string(body)) - } - - if result.Type != "success" { - return "", errors.New(fmt.Sprintf("bad response: %v", result)) - } - - fmt.Println(result) - - return result.Location, nil -} - -type ReqPaymentListenerFk struct { - MerchantID string `schema:"MERCHANT_ID"` - Amount float64 `schema:"AMOUNT"` - IntId string `schema:"intid"` - PaymentID string `schema:"MERCHANT_ORDER_ID"` - PEmail string `schema:"P_EMAIL"` - PPhone string `schema:"P_PHONE"` - CurID string `schema:"CUR_ID"` - Sign string `schema:"SIGN"` - Comission string `schema:"commission"` - PayerAccount string `schema:"payer_account"` - RequesterID string `schema:"us_requesterID"` -} - -// PaymentListener - Обрабатывает результат перевода -func (fk *FreeKassa) PaymentListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error { - data := request.(*ReqPaymentListenerFk) - - // Проверка IP - временно отключен, т.к. получает айпи из зиротира - //if !strings.Contains(ip, AllowedIPs) { - //return errors.New(fmt.Sprintf("this ip not allowed: %v", ip)) - //} - - // Проверка подписи - //fmt.Println("ORAAAA", fk.MerchantID+":"+fmt.Sprint(int64(data.Amount))+":"+fk.Secret2+":"+data.PaymentID) - //fmt.Println("ORAAAA", md5.Sum([]byte(fk.MerchantID+":"+fmt.Sprint(int64(data.Amount))+":"+fk.Secret2+":"+data.PaymentID))) - //fmt.Println("ORAAAA", fmt.Sprintf("%x", - // md5.Sum([]byte(fk.MerchantID+":"+fmt.Sprint(int64(data.Amount))+":"+fk.Secret2+":"+data.PaymentID)))) - if data.Sign != fmt.Sprintf("%x", - md5.Sum([]byte(fk.MerchantID+":"+fmt.Sprint(int64(data.Amount))+":*}/^bkOCGq[C*Ss:"+data.PaymentID))) { - return errors.New(fmt.Sprintf("wrong sign: %v", data)) - } - - // Проверка существования платежа в БД - payment, err := mc.GetPayment(ctx, data.PaymentID) - - if err != nil { - return err - } - - if payment == nil { - return errors.New("payment not found") - } - - // Проверка статуса платежа (должен быть open) - if payment.Status != "open" { - return errors.New(fmt.Sprintf("payment have status: %v", payment.Status)) - } - - // Проверка суммы платежа - //if data.Amount != payment.Amount { - // return errors.New(fmt.Sprintf("wrong amount: %v", data.Amount)) - //} - // - //// Проверка RequesterID - //if data.RequesterID != payment.RequesterID { - // return errors.New(fmt.Sprintf("wrong us_requesterID: %v", data.RequesterID)) - //} - - // Обновление статуса платежа в БД - err = mc.UpdatePaymentStatus(ctx, data.PaymentID, "accepted") - if err != nil { - return err - } - - return nil -} - -type ReqGetPaymentListFk struct { - ShopId int64 `json:"shopId"` // Required - Nonce int64 `json:"nonce"` // Required - Signature string `json:"signature"` // Required -} - -type RespGetPaymentListFk struct { - Type string `json:"type"` - Currencies []struct { - Id int `json:"id"` - Name string `json:"name"` - Currency string `json:"currency"` - IsEnabled int `json:"is_enabled"` - IsFavorite int `json:"is_favorite"` - } `json:"currencies"` -} - -// GetPaymentList - возвращает из API актуальный список доступных платежных систем -func (fk *FreeKassa) GetPaymentList() ([]dal.PayWayPayment, error) { - action := UrlFk + "/currencies" - - shopID, err := strconv.ParseInt(fk.MerchantID, 10, 64) - - if err != nil { - return nil, err - } - - nonce := time.Now().Unix() - - data := ReqGetPaymentListFk{ - ShopId: shopID, - Nonce: nonce, - } - - body, err := fk.prepareData(data) - - if err != nil { - return nil, err - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - if err != nil { - fmt.Println("FKAPIERR", err) - return nil, err - } - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status, data) - - var result RespGetPaymentListFk - - err = json.NewDecoder(resp.Body).Decode(&result) - - if resp.StatusCode != 200 || err != nil { - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - return nil, errors.New("bad response: " + string(body)) - } - - if result.Type != "success" { - return nil, errors.New(fmt.Sprintf("bad response: %v", result)) - } - - fmt.Println(result) - - var formatted []dal.PayWayPayment - - for _, item := range result.Currencies { - status := "active" - - if item.IsEnabled == 0 { - status = "inactive" - } - formatted = append(formatted, dal.PayWayPayment{ - ID: fmt.Sprintf("%v-%v", fk.ShortName, item.Id), - Name: item.Name, - Status: status, - Currency: item.Currency, - ApiId: strconv.Itoa(item.Id), - Commission: 0, - Minimum: 0, - Maximum: 0, - }) - } - - return formatted, nil -} - -//#endregion - -//#region ========= Payout ========= - -type ReqCreatePayoutFk struct { - ShopId int64 `json:"shopId"` // Required - Nonce int64 `json:"nonce"` // Required Уникальный ID запроса, должен всегда быть больше предыдущего значения - Signature string `json:"signature"` // Required - PaymentId string `json:"paymentId"` - I int64 `json:"i"` // Required. ID платежной системы. dal.PayoutType.ApiId - Account string `json:"account"` // Required. Кошелек для зачисления средств - Amount float64 `json:"amount"` // Required - Currency string `json:"currency"` // Required -} - -type RespCreatePayoutFk struct { - Type string `json:"type"` - Data struct { - Id int `json:"id"` - } `json:"data"` -} - -// CreatePayout - выполняет запрос для вывода средств из кошелька (вывод средств со счета клиента). -// В ответ получает RespCreatePayoutFk - -func (fk *FreeKassa) CreatePayout(amount, orderID, payoutType, currency, address string) (string, error) { - action := UrlFk + "/withdrawals/create" - - payout := fk.GetPayoutType(payoutType) - if currency == "" { - currency = payout.Currency - } - - shopID, err := strconv.ParseInt(fk.MerchantID, 10, 64) - if err != nil { - return "", err - } - - apiId, err := strconv.ParseInt(payoutType, 10, 64) - if err != nil { - return "", err - } - - amountFloat, err := strconv.ParseFloat(amount, 64) - if err != nil { - return "", err - } - - data := ReqCreatePayoutFk{ - ShopId: shopID, - Nonce: time.Now().Unix(), - Signature: "", - PaymentId: orderID, - I: apiId, - Account: address, - Amount: amountFloat, - Currency: currency, - } - - body, err := fk.prepareData(data) - - if err != nil { - return "", err - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status) - - var result RespCreatePayoutFk - - err = json.NewDecoder(resp.Body).Decode(&result) - - if resp.StatusCode != 200 || err != nil { - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - return "", errors.New("bad response: " + string(body)) - } - - if result.Type != "success" { - return "", errors.New(fmt.Sprintf("bad response: %v", result)) - } - - fmt.Println(result) - - return strconv.Itoa(result.Data.Id), nil -} - -type ReqPayoutListenerFk struct { - WalletID string `json:"wallet_id" schema:"wallet_id"` - OrderID string `json:"order_id" schema:"order_id"` - Status string `json:"status" schema:"status"` - Amount float64 `json:"amount" schema:"amount"` - UserOrderID string `json:"user_order_id" schema:"user_order_id"` - Sign string `json:"sign" schema:"sign"` -} - -// PayoutListener - обрабатывает результат вывода -func (fk *FreeKassa) PayoutListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error { - data := request.(*ReqPayoutListenerFk) - - // Проверка IP - временно отключен, т.к. получает айпи из зиротира - //if !strings.Contains(ip, AllowedIPs) { - //return errors.New(fmt.Sprintf("this ip not allowed: %v", ip)) - //} - - // Проверка подписи - if data.Sign != fk.createSign([]string{ - fk.WalletID, - data.OrderID, - data.UserOrderID, - data.Status, - fmt.Sprint(data.Amount), - }) { - return errors.New(fmt.Sprintf("wrong sign: %v", data.Sign)) - } - - // Проверка существования платежа в БД - payout, err := mc.GetPaymentByServiceID(ctx, data.OrderID, "false") - - if err != nil { - return err - } - - if payout == nil { - return errors.New("payment not found") - } - - // Проверка статуса платежа (должен быть open) - if payout.Status != "open" { - return errors.New(fmt.Sprintf("payment have status: %v", payout.Status)) - } - - // Проверка суммы платежа - if data.Amount != payout.Amount { - return errors.New(fmt.Sprintf("wrong amount: %v", data.Amount)) - } - - // Обновление статуса платежа в БД - - var newStatus string - switch data.Status { - case "1": - newStatus = "accepted" - case "9": - newStatus = "declined" - default: - newStatus = "" - } - - err = mc.UpdatePaymentStatus(ctx, payout.ID, newStatus) - if err != nil { - return err - } - return nil -} - -type ReqGetPayoutListFk struct { - ShopId int64 `json:"shopId"` // Required - Nonce int64 `json:"nonce"` // Required - Signature string `json:"signature"` // Required -} - -type RespGetPayoutListFk struct { - Type string `json:"type"` - Currencies []struct { - Id int `json:"id"` - Name string `json:"name"` - Min float64 `json:"min"` - Max float64 `json:"max"` - Currency string `json:"currency"` - CanExchange int `json:"can_exchange"` - } `json:"currencies"` -} - -// GetPayoutList - возвращает из API актуальный список доступных платежных систем -func (fk *FreeKassa) GetPayoutList() ([]dal.PayoutType, error) { - action := UrlFk + "/withdrawals/currencies" - - shopID, err := strconv.ParseInt(fk.MerchantID, 10, 64) - - if err != nil { - return nil, err - } - - data := ReqGetPaymentListFk{ - ShopId: shopID, - Nonce: time.Now().Unix(), - Signature: "", - } - - body, err := fk.prepareData(data) - - if err != nil { - return nil, err - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status) - - var result RespGetPayoutListFk - - err = json.NewDecoder(resp.Body).Decode(&result) - - fmt.Println("StatuRR:", result, resp.StatusCode, err) - - if resp.StatusCode != 200 || err != nil { - body, err := ioutil.ReadAll(resp.Body) - - fmt.Println(body) - if err != nil { - log.Println(" ioutil: ", err) - } - return nil, errors.New("bad response: " + string(body)) - } - fmt.Println(result) - - if result.Type != "success" { - return nil, errors.New(fmt.Sprintf("bad response: %v", result)) - } - - var formatted []dal.PayoutType - - for _, item := range result.Currencies { - formatted = append(formatted, dal.PayoutType{ - - Name: item.Name, - IsEnabled: true, - Currency: item.Currency, - ApiId: strconv.Itoa(item.Id), - Commission: 0, - Minimum: item.Min, - Maximum: item.Max, - }) - } - - return formatted, nil -} - -//#endregionn - -//#region ========= Wallet ========= - -type ReqGetWalletBalanceFk struct { - ShopId int64 `json:"shopId"` // Required - Nonce int64 `json:"nonce"` // Required - Signature string `json:"signature"` // Required -} - -type RespGetWalletBalanceFk struct { - Type string `json:"type"` - Balance []struct { - Currency string `json:"currency"` - Value float64 `json:"value"` - } `json:"balance"` -} - -// GetWalletBalance - выполняет запрос чтобы узнать баланс кошелька -// В ответ получает RespGetWalletBalanceFk -func (fk *FreeKassa) GetWalletBalance() (map[string]float64, error) { - action := UrlFk + "/balance" - - shopID, err := strconv.ParseInt(fk.MerchantID, 10, 64) - - if err != nil { - return nil, err - } - - data := ReqGetWalletBalanceFk{ - ShopId: shopID, - Nonce: time.Now().Unix(), - Signature: "", - } - - body, err := fk.prepareData(data) - - if err != nil { - return nil, err - } - - resp, err := http.Post(action, "application/json", bytes.NewReader(body)) - - defer func() { - resp.Body.Close() - }() - - fmt.Println("Status:", resp.Status) - - var result RespGetWalletBalanceFk - - err = json.NewDecoder(resp.Body).Decode(&result) - - if resp.StatusCode != 200 || err != nil { - body, err := ioutil.ReadAll(resp.Body) - - if err != nil { - log.Println(" ioutil: ", err) - } - return nil, errors.New("bad response: " + string(body)) - } - - if result.Type != "success" { - return nil, errors.New(fmt.Sprintf("bad response: %v", result)) - } - - fmt.Println(result) - - var formatted map[string]float64 - - for _, item := range result.Balance { - formatted[item.Currency] = item.Value - } - - return formatted, nil - -} - -//#endregion diff --git a/payway/hlogger.go b/payway/hlogger.go deleted file mode 100644 index 3eb98c6..0000000 --- a/payway/hlogger.go +++ /dev/null @@ -1,18 +0,0 @@ -package payway - -type ErrorType struct { - Err error -} - -type InfoType struct { - Info interface{} -} - -type ErrorQueueWorker ErrorType - -type ErrorCreatePayout struct { - ErrorType - Url string -} - -type InfoQueueWorkerDone InfoType diff --git a/payway/payway.go b/payway/payway.go deleted file mode 100644 index 1693c33..0000000 --- a/payway/payway.go +++ /dev/null @@ -1,393 +0,0 @@ -package payway - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "bitbucket.org/skeris/treasurer/dal" - pw "bitbucket.org/skeris/treasurer/payway/payways" - "bitbucket.org/skeris/treasurer/payway/payways/fk" - "github.com/MarsherSusanin/pena_hub_packages_common/fastconfig" - "github.com/gofiber/fiber/v2" - "github.com/themakers/hlog" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -type Payways struct { - pws []*pw.Payway - conn *mongo.Client - db *mongo.Database - collSvcs *mongo.Collection - available map[string]createPW - templates map[string]string - errChan chan error -} - -const collServices = "services" - -type createPW func() *pw.Payway - -func New( - ctx context.Context, - conn *mongo.Client, - db string, - errChan chan error, - available ...pw.PayWayI, -) (*Payways, error) { - a := map[string]createPW{} - t := map[string]string{} - - for _, p := range available { - name := p.Type() - a[name] = p.Copy - - bts, err := json.Marshal(p) - if err != nil { - return nil, err - } - - t[name] = string(bts) - } - - d := conn.Database(db) - - return &Payways{ - available: a, - templates: t, - conn: conn, - db: d, - errChan: errChan, - collSvcs: d.Collection(collServices), - }, nil -} - -func (p *Payways) Init(ctx context.Context) error { - if err := p.YieldPayways(ctx, p.ProcessPayways); err != nil { - return err - } - - go func() { - if err := p.WatchPayways(ctx, p.ProcessPayways); err != nil { - p.errChan <- err - } - }() - - return nil -} - -func (p *Payways) Register(api *fiber.App) *fiber.App { - api.Get("/services", p.getServices) - return api -} - -func (p *Payways) getServices(c *fiber.Ctx) error { - fmt.Println("getServices") - return c.JSON(p.templates) -} - -func (p *Payways) ProcessPayways(d *pw.Payway) error { - switch d.Svc { - case "fk": - way := fk.FreeKassa{} - - if err := bson.Unmarshal(d.RawCredentials, &way); err != nil { - return err - } - - d.Credentials = way - p.pws = append(p.pws, d) - } - - return nil -} - -func (p *Payways) WatchPayways(ctx context.Context, yield func(d *pw.Payway) error) error { - cs, err := p.collSvcs.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetFullDocument(options.UpdateLookup)) - if err != nil { - return err - } - - for cs.Next(ctx) { - var change fastconfig.Change - - if err := cs.Decode(&change); err != nil { - return err - } - - var piece pw.Payway - - if err := bson.Unmarshal(change.FullDocument, &piece); err != nil { - return err - } - - if err := yield(&piece); err != nil { - return err - } - } - - return nil -} - -func (p *Payways) YieldPayways(ctx context.Context, yield func(d *pw.Payway) error) error { - cur, err := p.collSvcs.Find(ctx, bson.M{}) - if err != nil { - return err - } - - for cur.Next(ctx) { - var piece pw.Payway - - if err := cur.Decode(&piece); err != nil { - return err - } - - if err := yield(&piece); err != nil { - return err - } - } - - return nil -} - -type PaywayAPI interface { - GetWallet() error - CreateInvoice() error - CreatePayout() error - InvoiceListener() error - PayoutListener() error - GetInvoiceCurrencies() error - GetPayoutCurrencies() error -} - -type LayerPayWay interface { - GetMongoID() string - - GetName() string - - UpdateData(data *dal.PayWay) - - UpdatePayment(data []dal.PayWayPayment) - - UpdatePayout(data []dal.PayoutType) - - // Payment - - CreatePaymentUrl(amount, orderID, paymentType, currency, lang, email, phone, requesterID, userIP string) (string, error) - - PaymentListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error - - GetPaymentList() ([]dal.PayWayPayment, error) - - GetPaymentTypeList() []dal.PayWayPayment - - GetPaymentType(paymentType string) *dal.PayWayPayment - - // Payout - - CreatePayout(amount, orderID, payoutType, currency, address string) (string, error) - - PayoutListener(ctx context.Context, ip string, request interface{}, mc *dal.MongoConnection) error - - GetPayoutList() ([]dal.PayoutType, error) - - GetPayoutType(payoutType string) *dal.PayoutType - - FindPayoutType(payType string, amount float64) *dal.PayoutType - - GetWalletBalance() (map[string]float64, error) -} - -type PayWay struct { - mongo *dal.MongoConnection - Cache - *QueuePayout -} - -func NewPayWay( - ctx context.Context, - logger hlog.Logger, - mongoPayWay, mongoTreasurer *dal.MongoConnection) (*PayWay, error) { - - fk, err := NewFk(ctx, mongoPayWay) - if err != nil { - return nil, err - } - - bt, err := NewBt(ctx, mongoPayWay) - if err != nil { - return nil, err - } - - // TODO: пока заполняем всё это вручную, стоит ли автоматизировать? - cache := Cache{ - "fk": fk, - "bt": bt, - } - - queue := NewQueuePayout() - - // Заполняем очередь значениями из бд - ptList, err := mongoTreasurer.GetPaymentListByStatus(ctx, "open", "false") - - if err != nil { - return nil, err - } - - for _, payout := range ptList { - queue.Enqueue(payout.ID) - } - - payWay := &PayWay{ - Cache: cache, - QueuePayout: queue, - mongo: mongoPayWay, - } - - worker := newQueueWorker(ctx, payWay, logger, mongoTreasurer) - - // update workers - - go payWay.WorkerUpdatePayment(ctx) - - go payWay.WorkerUpdatePayout(ctx) - - go worker.routine() - - return payWay, nil -} - -type Cache map[string]LayerPayWay - -func (c Cache) GetPayWayById(id string) LayerPayWay { - for _, way := range c { - fmt.Println("way:", way.GetMongoID()) - if id == way.GetMongoID() { - return way - } - - } - - return nil -} - -func (c Cache) GetPayWayListByPayoutType(payoutType string, amount float64) map[string]LayerPayWay { - result := map[string]LayerPayWay{} - - for name, pw := range c { - pt := pw.FindPayoutType(payoutType, amount) - if pt != nil { - result[name] = pw - } - } - - return result -} - -func (c Cache) GetPaymentList() (map[string][]dal.PayWayPayment, error) { - result := map[string][]dal.PayWayPayment{} - - for k, v := range c { - arr, err := v.GetPaymentList() - - if err != nil { - fmt.Printf("%v.GetPaymentListErr: %v \r\n", k, err) - return nil, err - } - - result[k] = arr - } - - return result, nil -} - -func (p PayWay) WorkerUpdatePayment(ctx context.Context) { - for { - for name, pw := range p.Cache { - data, err := pw.GetPaymentList() - - if err != nil { - fmt.Printf("worker.%v.GetPaymentListErr: %v \r\n", name, err) - } - - for k, item := range data { - id := fmt.Sprintf("%v-%v", name, item.ApiId) - data[k].ID = id - - payment, err := p.mongo.GetPayWayPayment(ctx, id) - - if err != nil { - fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err) - //payment.ID = xid.New().String() - } - - if payment == nil { - payment = &dal.PayWayPayment{ID: id} - } - //if payment != nil { - err = p.mongo.UpdatePayWayPayment(ctx, dal.PayWayPayment{ - ID: payment.ID, - ApiId: item.ApiId, - Name: item.Name, - PayWay: name, - Currency: item.Currency, - Commission: item.Commission, - Minimum: item.Minimum, - Maximum: item.Maximum, - Status: item.Status, - }) - - if err != nil { - fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err) - } - //} else { - // if err != nil { - // fmt.Printf("worker.%v.GetPayWayPaymentByApiIdAndPayWay: %v \r\n", name, err) - // } - //} - } - pw.UpdatePayment(data) - } - - // Прерывание - select { - case <-ctx.Done(): - return - case <-time.After(10 * time.Minute): // Сладкий сон - continue - } - } -} - -func (p PayWay) WorkerUpdatePayout(ctx context.Context) { - for { - for name, pw := range p.Cache { - data, err := pw.GetPayoutList() - - if err != nil { - fmt.Printf("worker.%v.GetPaymentListErr: %v \r\n", name, pw) - } - - fmt.Println("OTTO", name, err, data) - pw.UpdatePayout(data) - - if err := p.mongo.UpdatePayWay(ctx, dal.PayWay{ - ID: pw.GetMongoID(), - PayoutTypeList: data, - }); err != nil { - fmt.Println("UpdatePayoutErr", err) - } - } - - // Прерывание - select { - case <-ctx.Done(): - return - case <-time.After(10 * time.Minute): // Сладкий сон - continue - } - } -} diff --git a/payway/payways/fk/api.go b/payway/payways/fk/api.go deleted file mode 100644 index 028c694..0000000 --- a/payway/payways/fk/api.go +++ /dev/null @@ -1,329 +0,0 @@ -package fk - -import ( - "bytes" - "crypto/hmac" - "crypto/sha256" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "reflect" - "sort" - "strings" - "time" -) - -const UrlFk = "https://api.freekassa.ru/v1" - -type ReqCreateInvoice struct { - ShopId int64 `json:"shopId"` - Nonce int64 `json:"none"` - Signature string `json:"signature"` - PaymentId string `json:"paymentId"` - I int64 `json:"i"` - Email string `json:"email"` - IP string `json:"ip"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - Tel string `json:"tel"` - SuccessUrl string `json:"successUrl,omitempty"` - FailureUrl string `json:"failureUrl,omitempty"` - NotificationUrl string `json:"motificationUrl,omitempty"` -} - -type RespCreateInvoice struct { - Type string `json:"type"` - OrderId int `json:"order_id"` - OrderHash string `json:"order_hash"` - Location string `json:"location"` -} - -func (pw *FreeKassa) CreatePaymentUrl(paymentType int64, amount float64, orderId, email, ip, currency, phone string) (string, error) { - body, err := pw.create(&ReqCreateInvoice{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - PaymentId: orderId, - I: paymentType, - Email: email, - IP: ip, - Amount: amount, - Currency: currency, - Tel: phone, - }) - if err != nil { - return "", err - } - - resp, err := http.Post(UrlFk+"/orders/create", "application/json", bytes.NewReader(body)) - if err != nil { - return "", err - } - - defer resp.Body.Close() - - if resp.StatusCode != 200 { - return "", err - } - - var result RespCreateInvoice - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", err - } - - if result.Type != "success" { - return "", fmt.Errorf("create link bad response: %v", result) - } - - return result.Location, nil -} - -type ReqOrdersHistory struct { - ShopId int64 `json:"id"` - Nonce int64 `json:"nonce"` - Signature string `json:"signature"` - DateFrom string `json:"dateFrom"` - DateTo string `json:"dateTo"` -} - -type RespOrdersHistory struct { - Type string `json:"type"` - Pages int64 `json:"pages"` - Orders []Order `json:"orders"` -} - -type Order struct { - MerchantOrderId string `json:"merchant_order_id"` - FkOrderId int64 `json:"fk_order_id"` - Amount float64 `json:"amount"` - Currency string `json:"currency"` - Email string `json:"email"` - Account string `json:"account"` - Date string `json:"date"` - Status int `json:"status"` -} - -func (pw *FreeKassa) GetOrdersHistory() ([]Order, error) { - var resp RespOrdersHistory - - if err := pw.apiRequest("https://api.freekassa.ru/v1/orders", &pw.getOrders, &ReqOrdersHistory{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return nil, err - } - - return resp.Orders, nil -} - -type ReqGetWallet struct { - ShopId int64 `json:"shopId"` - Nonce int64 `json:"nonce"` - Signature string `json:"signature"` -} - -type WalletResp struct { - Type string `json:"type"` - Balance []Wallet `json:"balance"` -} - -type Wallet struct { - Currency string `json:"currency" bson:"cur"` - Value string `json:"value" bson:"val"` -} - -func (pw *FreeKassa) GetWallet() ([]Wallet, error) { - var resp WalletResp - if err := pw.apiRequest("https://api.freekassa.ru/v1/balance", &pw.wallet, &ReqGetWallet{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return nil, err - } - - fmt.Println("wallet", resp) - - return resp.Balance, nil -} - -type CurreciesResp struct { - Type string `json:"type"` - Currencies []Currency `json:"currencies"` -} - -type Currency struct { - Id int `json:"id" bson:"id"` - Name string `json:"name" bson:"name"` - Currency string `json:"currency" bson:"cur"` - IsEnabled uint8 `json:"is_enabled" bson:"is_enabled"` - IsFavourite uint8 `json:"is_favourite" bson:"is_fav"` -} - -func (pw *FreeKassa) GetCurrencies() ([]Currency, error) { - var resp CurreciesResp - - if err := pw.apiRequest("https://api.freekassa.ru/v1/currencies", &pw.wallet, &ReqGetWallet{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return nil, err - } - - fmt.Println("currencies", resp) - - return resp.Currencies, nil -} - -type ShopResp struct { - Type string `json:"type"` - Shops []Shop `json:"shops"` -} - -type Shop struct { - Id int `json:"id" bson:"id"` - Name string `json:"name" bson:"name"` - Url string `json:"url" bson:"url"` -} - -func (pw *FreeKassa) GetShops() ([]Shop, error) { - var resp ShopResp - - if err := pw.apiRequest("https://api.freekassa.ru/v1/shops", &pw.wallet, &ReqGetWallet{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return nil, err - } - - fmt.Println("shops", resp) - - return resp.Shops, nil -} - -type WithdrawalCurrenciesResp struct { - Type string `json:"type"` - Currencies []WithdrawalCurrencie `json:"currencies"` -} - -type WithdrawalCurrencie struct { - Id int `json:"id" bson:"id"` - Name string `json:"name" bson:"name"` - Min float64 `json:"min" bson:"min"` - Max float64 `json:"max" bson:"max"` - Currency string `json:"currency" bson:"cur"` - CanExcahnge uint8 `json:"can_exchange" bson:"can_exch"` -} - -func (pw *FreeKassa) GetWithdrawalCurrencies() ([]WithdrawalCurrencie, error) { - var resp WithdrawalCurrenciesResp - - if err := pw.apiRequest("https://api.freekassa.ru/v1/withdrawals/currencies", &pw.wallet, &ReqGetWallet{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return nil, err - } - - fmt.Println("withdrawals cur", resp) - - return resp.Currencies, nil -} - -type Access struct { - Type string `json:"type"` -} - -func (pw *FreeKassa) Accesable(id int) (bool, error) { - var resp Access - if err := pw.apiRequest(fmt.Sprintf("https://api.freekassa.ru/currencies/%d/status", id), &pw.wallet, &ReqGetWallet{ - ShopId: pw.ShopId, - Nonce: time.Now().Unix(), - }, &resp); err != nil { - return false, nil - } - - if resp.Type != "success" { - return false, nil - } - - return true, nil -} - -func (pw *FreeKassa) apiRequest(url string, p *preparer, r, dest any) error { - body, err := (*p)(r) - if err != nil { - return err - } - - resp, err := http.Post(url, "application/json", bytes.NewBuffer(body)) - if err != nil { - return err - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return err - } - - return fmt.Errorf("status code: %d\nbad response: %s", resp.StatusCode, string(b)) - } - - if err := json.NewDecoder(resp.Body).Decode(dest); err != nil { - return err - } - - return nil -} - -func (pw *FreeKassa) prepareData(shape any) func(data any) ([]byte, error) { - var ( - sind []int - indexes [][]int - ) - - func() { - var ( - fieldNames []string - ind = map[string][]int{} - ) - - for _, f := range reflect.VisibleFields(reflect.Indirect(reflect.ValueOf(shape)).Type()) { - name := strings.Split(f.Tag.Get("json"), ",")[0] - if name != "signature" { - fieldNames = append(fieldNames, name) - ind[name] = f.Index - } else { - sind = f.Index - } - } - - sort.Strings(fieldNames) - - for _, i := range fieldNames { - indexes = append(indexes, ind[i]) - } - }() - - return func(data any) ([]byte, error) { - val := reflect.Indirect(reflect.ValueOf(data)) - - var forsign []string - - for _, fn := range indexes { - forsign = append(forsign, fmt.Sprint(val.FieldByIndex(fn))) - } - - val.FieldByIndex(sind).Set(reflect.ValueOf(pw.createSign([]byte(strings.Join(forsign, "|"))))) - - return json.Marshal(data) - } -} - -func (pw *FreeKassa) createSign(data []byte) string { - sign := hmac.New(sha256.New, pw.byteKey) - sign.Write(data) - return fmt.Sprintf("%x", sign.Sum(nil)) -} diff --git a/payway/payways/fk/fk_test.go b/payway/payways/fk/fk_test.go deleted file mode 100644 index b288faf..0000000 --- a/payway/payways/fk/fk_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package fk - -import ( - "fmt" - "testing" - - "github.com/themakers/bdd" -) - -func TestFk(t *testing.T) { - fk := New("db9f770bf1b400445fb9c7d82e59ba81", 20397) - bdd.Scenario(t, "sign creating and data preparing", func(t *testing.T, runid string) { - bdd.Test(t, "prepare data", func() { - fk.create(&ReqCreateInvoice{ - ShopId: 1, - Nonce: 2, - Signature: "q", - PaymentId: "w", - I: 3, - Email: "e", - IP: "r", - Amount: 4.5, - Currency: "t", - Tel: "y", - SuccessUrl: "u", - FailureUrl: "i", - NotificationUrl: "o", - }) - fmt.Println(fk.GetWallet()) - fmt.Println(fk.GetCurrencies()) - fmt.Println(fk.GetShops()) - fmt.Println(fk.GetWithdrawalCurrencies()) - }) - }) -} diff --git a/payway/payways/fk/pw.go b/payway/payways/fk/pw.go deleted file mode 100644 index 666b63d..0000000 --- a/payway/payways/fk/pw.go +++ /dev/null @@ -1,43 +0,0 @@ -package fk - -import ( - p "bitbucket.org/skeris/treasurer/payway/payways" -) - -type FreeKassa struct { - Key string `json:"key"` - ShopId int64 `json:"shopId"` - - byteKey []byte - create, wallet, getOrders preparer -} - -type preparer func(any) ([]byte, error) - -func New(sk string, shid int64) *FreeKassa { - fk := &FreeKassa{ - Key: sk, - ShopId: shid, - byteKey: []byte(sk), - } - - fk.create = fk.prepareData(&ReqCreateInvoice{}) - fk.wallet = fk.prepareData(&ReqGetWallet{}) - fk.getOrders = fk.prepareData(&ReqOrdersHistory{}) - - return fk -} - -func (pw *FreeKassa) Type() string { - return "fk" -} - -func (pw *FreeKassa) Copy() *p.Payway { - return &p.Payway{ - Credentials: &FreeKassa{}, - } -} - -//func (pw *FreeKassa) MarshalJSON() ([]byte, error) { -// return json.Marshal(&FreeKassa{}) -//} diff --git a/payway/payways/pw.go b/payway/payways/pw.go deleted file mode 100644 index 7501eee..0000000 --- a/payway/payways/pw.go +++ /dev/null @@ -1,26 +0,0 @@ -package pw - -import ( - "time" - - "go.mongodb.org/mongo-driver/bson" -) - -type PayWayI interface { - Type() string - Copy() *Payway - //MarshalJSON() ([]byte, error) -} - -type Payway struct { - PayWayI - ID string `json:"id" bson:"_id"` - Name string `json:"name" bson:"name"` - Description string `json:"desc" bson:"desc"` - Svc string `json:"svc" bson:"svc"` - - RawCredentials bson.Raw `json:"-" bson:"credentials"` - Credentials any `json:"cred" bson:"-"` - - CreatedAt time.Time `json:"created_at" bson:"created_at"` -} diff --git a/payway/pws_test.go b/payway/pws_test.go deleted file mode 100644 index be510d9..0000000 --- a/payway/pws_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package payway - -import ( - "fmt" - "testing" - - "github.com/themakers/bdd" -) - -func TestPWs(t *testing.T) { - //ctx := context.Background() - bdd.Scenario(t, "add payways", func(t *testing.T, runid string) { - bdd.Test(t, "create pwmanager", func() { - //pm, err := New(ctx, &fk.FreeKassa{}) - //assert.NoError(t, err) - fmt.Println("pws0") - }) - }) -} diff --git a/payway/queue.go b/payway/queue.go deleted file mode 100644 index 647e081..0000000 --- a/payway/queue.go +++ /dev/null @@ -1,261 +0,0 @@ -package payway - -import ( - "bitbucket.org/skeris/treasurer/dal" - "context" - "errors" - "fmt" - "github.com/themakers/hlog" - "math" - "sync" - "time" -) - -type QueuePayout struct { - i int // текущий свободный iterator - len int // длина очереди - queue *sync.Map // key: iterator (int); value: payoutID (string) -} - -func NewQueuePayout() *QueuePayout { - return &QueuePayout{ - i: 0, - len: 0, - queue: &sync.Map{}, - } -} - -func (q *QueuePayout) Enqueue(payoutID string) { - q.queue.Store(q.i, payoutID) - q.i += 1 - q.len += 1 -} - -func (q *QueuePayout) Dequeue() (string, error) { - if q.len == 0 { - return "", errors.New("queue empty") - } - - var result string - for i := 0; i <= math.MaxInt32; i++ { - val, ok := q.queue.LoadAndDelete(i) - if ok { - result = val.(string) - break - } - } - - if result == "" { - return "", errors.New("got empty id") - } - - q.len -= 1 - if q.len == 0 { - q.i = 0 - } - - return result, nil -} - -func (q *QueuePayout) Len() int { - return q.len -} - -type queueWorker struct { - ctx context.Context - pw *PayWay - hl hlog.Logger - mc *dal.MongoConnection -} - -func newQueueWorker(ctx context.Context, - pw *PayWay, - logger hlog.Logger, - mc *dal.MongoConnection) *queueWorker { - return &queueWorker{ - ctx: ctx, - pw: pw, - hl: logger, - mc: mc, - } -} - -func (qw *queueWorker) routine() { - for { - if qw.pw.Len() != 0 { // Опять работать - payoutID, err := qw.pw.Dequeue() - - if err != nil { - qw.hl.Emit(ErrorQueueWorker{err}) - continue - } - - payout, err := qw.mc.GetPayment(qw.ctx, payoutID) - - if payout == nil { - errText := fmt.Sprintf("payout not found id: %v", payoutID) - qw.hl.Emit(ErrorQueueWorker{errors.New(errText)}) - continue - } - - if err != nil { - qw.hl.Emit(ErrorQueueWorker{err}) - continue - } - - // Если статус open продолжаем - if payout.Status != "open" { - qw.hl.Emit(ErrorQueueWorker{errors.New(fmt.Sprintf("payout have status: %v", payout.Status))}) - continue - } - - if payout.IsRefill { - qw.hl.Emit(ErrorQueueWorker{errors.New(fmt.Sprintf("this is a refill, not a payout"))}) - continue - } - - payway := qw.pw.GetPayWayById(payout.PayWayID) - - if payway == nil { - qw.hl.Emit(ErrorQueueWorker{errors.New(fmt.Sprintf("no payway for payout %v", payout.ID))}) - continue - } - //// Ищем самый выгодный способ для вывода средств с наличием нужной суммы - // - //// 1. Находим все PayWay с нужным способом вывода - //pwList := qw.pw.GetPayWayListByPayoutType(payout.PayoutType, payout.Amount) - // - //errNotFound := errors.New(fmt.Sprintf("no payway for payout %v", payout.ID)) - // - //if pwList == nil || len(pwList) == 0 { - // qw.hl.Emit(ErrorQueueWorker{errNotFound}) - // - // err = qw.mc.UpdatePayoutStatus(qw.ctx, payoutID, "declined") - // if err != nil { - // qw.hl.Emit(ErrorQueueWorker{err}) - // } - // - // continue - //} - // - //// 2. Отсекаем те, на которых недостаточно средств или имеют ошибку - //for k, v := range pwList { - // walletBalance, err := v.GetWalletBalance() - // payoutType := v.GetPayoutType(payout.PayoutType) - // - // if err != nil { - // qw.hl.Emit(ErrorQueueWorker{err}) - // delete(pwList, k) - // continue - // } - // - // currencyBalance, ok := walletBalance[payoutType.Currency] - // if !ok { - // errText := fmt.Sprintf("%v not found in %v wallet", payoutType.Currency, v.GetName()) - // delete(pwList, k) - // qw.hl.Emit(ErrorQueueWorker{errors.New(errText)}) - // } - // - // if currencyBalance < payout.Amount { - // delete(pwList, k) - // } - //} - // - //if pwList == nil || len(pwList) == 0 { - // qw.hl.Emit(ErrorQueueWorker{errNotFound}) - // - // err = qw.mc.UpdatePayoutStatus(qw.ctx, payoutID, "declined") - // if err != nil { - // qw.hl.Emit(ErrorQueueWorker{err}) - // } - // - // continue - //} - // - //// 3. Находим из оставшихся наименьшую комиссию для вывода - //commission := 100.0 - //var result LayerPayWay - // - //for _, v := range pwList { - // pt := v.GetPayoutType(payout.PayoutType) - // if pt.Commission < commission { - // commission = pt.Commission - // result = v - // } - //} - - // Используем api для перевода средств - serviceId, err := payway.CreatePayout( - fmt.Sprint(payout.Amount), - payoutID, - payout.PaymentType, - payout.Currency, - payout.Destination, - ) - - if err == nil { - err = qw.mc.UpdatePaymentStatus(qw.ctx, payoutID, "in_progress") - if err != nil { - qw.hl.Emit(ErrorQueueWorker{err}) - } - - err = qw.mc.UpdatePaymentServiceID(qw.ctx, payoutID, serviceId) - if err != nil { - qw.hl.Emit(ErrorQueueWorker{err}) - } - - //err = qw.mc.UpdatePayoutPayWayID(qw.ctx, payoutID, payway.GetMongoID()) - - qw.hl.Emit(InfoQueueWorkerDone{payoutID}) - continue - } - - // TODO: ??? Если есть ошибка, то пробуем другие payWay ? - - qw.hl.Emit(ErrorQueueWorker{err}) - - //for _, v := range pwList { - // _, err = v.CreatePayout( - // fmt.Sprint(payout.Amount), - // payoutID, - // payout.PayoutType, - // "", - // payout.Destination, - // ) - // - // if err == nil { - // err = qw.mc.UpdatePayoutStatus(qw.ctx, payoutID, "in_progress") - // if err != nil { - // qw.hl.Emit(ErrorQueueWorker{err}) - // } - // - // err = qw.mc.UpdatePayoutApiID(qw.ctx, payoutID, apiID) - // if err != nil { - // qw.hl.Emit(ErrorQueueWorker{err}) - // } - // - // err = qw.mc.UpdatePayoutPayWayID(qw.ctx, payoutID, result.GetMongoID()) - // - // qw.hl.Emit(InfoQueueWorkerDone{payoutID}) - // break - // } - // - // qw.hl.Emit(ErrorQueueWorker{err}) - //} - - // Если сервису не удалось выполнить payout меняем статус в БД - err = qw.mc.UpdatePaymentStatus(qw.ctx, payoutID, "declined") - if err != nil { - qw.hl.Emit(ErrorQueueWorker{err}) - } - } - - // Прерывание - select { - case <-qw.ctx.Done(): - return - case <-time.After(3 * time.Second): // Сладкий сон - continue - } - } -} diff --git a/payway/svc_test.go b/payway/svc_test.go deleted file mode 100644 index 217aae2..0000000 --- a/payway/svc_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package payway - -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "os" - "testing" - "time" - - "bitbucket.org/skeris/treasurer/payway/payways/fk" - "github.com/gofiber/fiber/v2" - "github.com/stretchr/testify/assert" - "github.com/themakers/bdd" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -func TestSvc(t *testing.T) { - ctx := context.Background() - - mongouri := os.Getenv("MONGO_URI") - if mongouri == "" { - mongouri = "mongodb://mongo:27017" - } - - time.Sleep(2 * time.Second) - - conn, err := mongo.NewClient(options.Client().ApplyURI(mongouri)) - if err != nil { - t.Fatal("mongo", err) - } - - if err := conn.Connect(ctx); err != nil { - t.Fatal("connect", err) - } - - ec := make(chan error) - - pws, err := New(ctx, conn, "test", ec, &fk.FreeKassa{}) - if err != nil { - t.Fatal("pws", err) - } - - srv := pws.Register(fiber.New()) - go func() { - if err := srv.Listen(":1488"); err != nil { - panic(err) - } - }() - time.Sleep(time.Second) - - bdd.Scenario(t, "svc testing", func(t *testing.T, runid string) { - fmt.Println("sc0") - bdd.Test(t, "get services test", func() { - res, err := http.Get("http://localhost:1488/services") - assert.NoError(t, err) - - bstr, err := ioutil.ReadAll(res.Body) - assert.NoError(t, err) - - assert.Equal(t, `{"fk":"{\"key\":\"\",\"shopId\":0}"}`, string(bstr)) - }) - }) -} diff --git a/payway/test b/payway/test deleted file mode 100755 index f06248a..0000000 Binary files a/payway/test and /dev/null differ diff --git a/pkg/array/find.go b/pkg/array/find.go new file mode 100644 index 0000000..63d0a92 --- /dev/null +++ b/pkg/array/find.go @@ -0,0 +1,11 @@ +package array + +func Find[T any](array []T, callback func(T, int, []T) bool) *T { + for index, element := range array { + if callback(element, index, array) { + return &element + } + } + + return nil +} diff --git a/pkg/array/find_test.go b/pkg/array/find_test.go new file mode 100644 index 0000000..70a3e6a --- /dev/null +++ b/pkg/array/find_test.go @@ -0,0 +1,32 @@ +package array_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" +) + +func TestFind(t *testing.T) { + t.Run("Find element in array (success)", func(t *testing.T) { + result := "test1" + callback := func(element string, index int, array []string) bool { + assert.Equal(t, array, []string{"test1", "test2"}) + + return element == result + } + + assert.Equal(t, &result, array.Find([]string{"test1", "test2"}, callback)) + }) + + t.Run("Find element in array (failure)", func(t *testing.T) { + result := "test" + callback := func(element string, index int, array []string) bool { + assert.Equal(t, array, []string{"test1", "test2"}) + + return element == result + } + + assert.Nil(t, array.Find([]string{"test1", "test2"}, callback)) + }) +} diff --git a/pkg/client/client.go b/pkg/client/client.go new file mode 100644 index 0000000..2674c65 --- /dev/null +++ b/pkg/client/client.go @@ -0,0 +1,37 @@ +package client + +import ( + "context" +) + +type RequestSettings struct { + URL string + Headers map[string]string + QueryParams map[string]string + Body any + Formdata any +} + +type Response[T any, R any] struct { + StatusCode int + Error *R + Body *T +} + +func Post[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Post) +} + +func Get[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Get) +} + +func Delete[T any, R any](ctx context.Context, settings *RequestSettings) (*Response[T, R], error) { + request := buildRequest(ctx, settings) + + return makeRequest[T, R](settings.URL, request.Delete) +} diff --git a/pkg/client/request.go b/pkg/client/request.go new file mode 100644 index 0000000..b1deabe --- /dev/null +++ b/pkg/client/request.go @@ -0,0 +1,56 @@ +package client + +import ( + "context" + + "github.com/go-resty/resty/v2" +) + +func buildRequest(ctx context.Context, settings *RequestSettings) *resty.Request { + request := resty.New().R().SetContext(ctx) + + if settings == nil { + return request + } + + if settings.Body != nil { + request.SetBody(settings.Body) + } + + if settings.QueryParams != nil { + request.SetQueryParams(settings.QueryParams) + } + + request.SetHeaders(settings.Headers) + + return request +} + +func makeRequest[T any, R any](url string, requestMethod func(url string) (*resty.Response, error)) (*Response[T, R], error) { + responseInstance, err := requestMethod(url) + if err != nil { + return nil, err + } + + if responseInstance.IsError() { + responseBody, parseErr := parseResponse[R](responseInstance.Body(), responseInstance.RawResponse) + if parseErr != nil { + return nil, parseErr + } + + return &Response[T, R]{ + StatusCode: responseInstance.StatusCode(), + Error: responseBody, + }, nil + } + + responseBody, parseErr := parseResponse[T](responseInstance.Body(), responseInstance.RawResponse) + if parseErr != nil { + return nil, parseErr + } + + return &Response[T, R]{ + StatusCode: responseInstance.StatusCode(), + Body: responseBody, + }, nil +} diff --git a/pkg/client/response.go b/pkg/client/response.go new file mode 100644 index 0000000..093df05 --- /dev/null +++ b/pkg/client/response.go @@ -0,0 +1,28 @@ +package client + +import ( + "bytes" + "net/http" + + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/json" +) + +func parseResponse[T any](body []byte, response *http.Response) (*T, error) { + isJSONResponse := response.Header.Get("Content-Type") == "application/json" + + if !isJSONResponse { + responseBody, unmarshalErr := json.Unmarshal[T](body) + if unmarshalErr != nil { + return nil, unmarshalErr + } + + return responseBody, nil + } + + responseBody, parseErr := json.Parse[T](bytes.NewReader(body)) + if parseErr != nil { + return nil, parseErr + } + + return responseBody, nil +} diff --git a/pkg/closer/closer.go b/pkg/closer/closer.go new file mode 100644 index 0000000..1e993e9 --- /dev/null +++ b/pkg/closer/closer.go @@ -0,0 +1,50 @@ +package closer + +import ( + "context" + "fmt" + "log" + "sync" +) + +type Callback func(ctx context.Context) error + +type Closer struct { + mutex sync.Mutex + callbacks []Callback +} + +func New() *Closer { + return &Closer{} +} + +func (receiver *Closer) Add(callback Callback) { + receiver.mutex.Lock() + defer receiver.mutex.Unlock() + + receiver.callbacks = append(receiver.callbacks, callback) +} + +func (receiver *Closer) Close(ctx context.Context) error { + receiver.mutex.Lock() + defer receiver.mutex.Unlock() + + complete := make(chan struct{}, 1) + + go func() { + for index, callback := range receiver.callbacks { + if err := callback(ctx); err != nil { + log.Printf("[! (%d)] %v", index, err) + } + } + + complete <- struct{}{} + }() + + select { + case <-complete: + return nil + case <-ctx.Done(): + return fmt.Errorf("shutdown cancelled: %v", ctx.Err()) + } +} diff --git a/pkg/env/parse.go b/pkg/env/parse.go new file mode 100644 index 0000000..b241f03 --- /dev/null +++ b/pkg/env/parse.go @@ -0,0 +1,23 @@ +package env + +import ( + "context" + "fmt" + "log" + + envLoader "github.com/joho/godotenv" + envParser "github.com/sethvargo/go-envconfig" +) + +func Parse[T interface{}](envFilePath string) (*T, error) { + var configuration T + + if err := envLoader.Load(envFilePath); err != nil { + log.Printf("load local env file error: %s", err.Error()) + } + if err := envParser.Process(context.Background(), &configuration); err != nil { + return nil, fmt.Errorf("parsing env error: %s", err.Error()) + } + + return &configuration, nil +} diff --git a/pkg/json/encode.go b/pkg/json/encode.go new file mode 100644 index 0000000..306cb90 --- /dev/null +++ b/pkg/json/encode.go @@ -0,0 +1,16 @@ +package json + +import ( + "bytes" + "encoding/json" +) + +func EncodeBuffer(body interface{}) (*bytes.Buffer, error) { + buffer := new(bytes.Buffer) + + if err := json.NewEncoder(buffer).Encode(body); err != nil { + return nil, err + } + + return buffer, nil +} diff --git a/pkg/json/parse.go b/pkg/json/parse.go new file mode 100644 index 0000000..33a15c4 --- /dev/null +++ b/pkg/json/parse.go @@ -0,0 +1,26 @@ +package json + +import ( + "encoding/json" + "io" +) + +func Parse[T any](reader io.Reader) (*T, error) { + jsonData := new(T) + + if err := json.NewDecoder(reader).Decode(jsonData); err != nil { + return nil, err + } + + return jsonData, nil +} + +func Unmarshal[T any](data []byte) (*T, error) { + unmarshaled := new(T) + + if err := json.Unmarshal(data, unmarshaled); err != nil { + return nil, err + } + + return unmarshaled, nil +} diff --git a/pkg/mongo/config.go b/pkg/mongo/config.go new file mode 100644 index 0000000..56880f0 --- /dev/null +++ b/pkg/mongo/config.go @@ -0,0 +1,22 @@ +package mongo + +import ( + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type Configuration struct { + Host string `env:"MONGO_HOST,default=localhost"` + Port string `env:"MONGO_PORT,default=27017"` + User string `env:"MONGO_USER,required"` + Password string `env:"MONGO_PASSWORD,required"` + Auth string `env:"MONGO_AUTH,required"` + DatabaseName string `env:"MONGO_DB_NAME,required"` +} + +type RequestSettings struct { + Driver *mongo.Collection + Options *options.FindOptions + Filter primitive.M +} diff --git a/pkg/mongo/connection.go b/pkg/mongo/connection.go new file mode 100644 index 0000000..c921b21 --- /dev/null +++ b/pkg/mongo/connection.go @@ -0,0 +1,74 @@ +package mongo + +import ( + "context" + "fmt" + "log" + "net" + "net/url" + "time" + + "go.mongodb.org/mongo-driver/event" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type ConnectDeps struct { + Configuration *Configuration + Timeout time.Duration +} + +func Connect(ctx context.Context, deps *ConnectDeps) (*mongo.Database, error) { + if deps == nil { + return nil, ErrEmptyArgs + } + + mongoURI := &url.URL{ + Scheme: "mongodb", + Host: net.JoinHostPort(deps.Configuration.Host, deps.Configuration.Port), + } + + cmdMonitor := &event.CommandMonitor{ + Started: func(_ context.Context, evt *event.CommandStartedEvent) { + log.Println(evt.Command) + }, + Succeeded: func(_ context.Context, evt *event.CommandSucceededEvent) { + log.Println(evt.Reply) + }, + Failed: func(_ context.Context, evt *event.CommandFailedEvent) { + log.Println(evt.Failure) + }, + } + + connectionOptions := options.Client(). + ApplyURI(mongoURI.String()). + SetAuth(options.Credential{ + AuthMechanism: "SCRAM-SHA-1", + AuthSource: deps.Configuration.Auth, + Username: deps.Configuration.User, + Password: deps.Configuration.Password, + }). + SetMonitor(cmdMonitor) + + ticker := time.NewTicker(1 * time.Second) + timeoutExceeded := time.After(deps.Timeout) + + defer ticker.Stop() + + for { + select { + case <-ticker.C: + connection, err := mongo.Connect(ctx, connectionOptions) + if err == nil { + return connection.Database(deps.Configuration.DatabaseName), nil + } + + log.Printf("failed to connect to db <%s>: %s", mongoURI.String(), err.Error()) + case <-timeoutExceeded: + return nil, fmt.Errorf("db connection <%s> failed after %d timeout", mongoURI, deps.Timeout) + default: + } + + time.Sleep(1 * time.Second) + } +} diff --git a/pkg/mongo/errors.go b/pkg/mongo/errors.go new file mode 100644 index 0000000..2e592f2 --- /dev/null +++ b/pkg/mongo/errors.go @@ -0,0 +1,7 @@ +package mongo + +import "errors" + +var ( + ErrEmptyArgs = errors.New("arguments are empty") +) diff --git a/pkg/mongo/find.go b/pkg/mongo/find.go new file mode 100644 index 0000000..7a46859 --- /dev/null +++ b/pkg/mongo/find.go @@ -0,0 +1,55 @@ +package mongo + +import ( + "context" + "log" +) + +func Find[T any](ctx context.Context, settings *RequestSettings) ([]T, error) { + if settings == nil { + return []T{}, ErrEmptyArgs + } + + results := make([]T, 0) + + cursor, err := settings.Driver.Find(ctx, settings.Filter, settings.Options) + if err != nil { + return []T{}, err + } + + defer func() { + if err := cursor.Close(ctx); err != nil { + log.Printf("failed to close cursor: %v", err) + } + }() + + for cursor.Next(ctx) { + result := new(T) + + if err := cursor.Decode(result); err != nil { + return []T{}, err + } + + results = append(results, *result) + } + + if err := cursor.Err(); err != nil { + return []T{}, err + } + + return results, nil +} + +func FindOne[T any](ctx context.Context, settings *RequestSettings) (*T, error) { + if settings == nil { + return nil, ErrEmptyArgs + } + + result := new(T) + + if err := settings.Driver.FindOne(ctx, settings.Filter).Decode(result); err != nil { + return nil, err + } + + return result, nil +} diff --git a/proto/callback/service.proto b/proto/callback/service.proto new file mode 100644 index 0000000..d9eea36 --- /dev/null +++ b/proto/callback/service.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package payment_callback; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "./payment_callback"; + +service PaymentCallbackService { + rpc OnSuccess(Event) returns (google.protobuf.Empty) {} + rpc OnFailure(Event) returns (google.protobuf.Empty) {} +} + +message Event { + string Key = 1; + string Message = 2; + Payment Payment = 3; +} + +message Payment { + string ID = 1; + string UserID = 2; + string PaymentID = 3; + string IdempotencePaymentID = 4; + int64 Amount = 5; + string Currency = 6; + string Type = 7; + string Status = 8; + bool Completed = 9; +} diff --git a/proto/treasurer/payment.model.proto b/proto/treasurer/payment.model.proto new file mode 100644 index 0000000..f64e166 --- /dev/null +++ b/proto/treasurer/payment.model.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package treasurer; + +option go_package = "./treasurer"; + +message MainPaymentSettings { + string Currency = 1; + int64 Amount = 2; + repeated string CallbackHostGRPC = 3; + string ReturnURL = 4; + string UserID = 5; + string ClientIP = 6; +} + +message BankCardInformation { + string Number = 1; + string ExpiryYear = 2; + string ExpiryMonth = 3; + optional string CSC = 4; + optional string CardHolderName = 5; +} \ No newline at end of file diff --git a/proto/treasurer/service.proto b/proto/treasurer/service.proto new file mode 100644 index 0000000..03da95c --- /dev/null +++ b/proto/treasurer/service.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package treasurer; + +import "google/protobuf/empty.proto"; +import "treasurer/payment.model.proto"; + +option go_package = "./treasurer"; + +service TreasurerService { + rpc GetPaymentLinkBankCard(GetBankCardPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkYooMoney(GetPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkQIWI(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSberPay(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkAlfaClick(GetLoginPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkTinkoff(GetPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSberbankB2B(GetB2BPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkSBP(GetPaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkMobile(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkCash(GetPhonePaymentLinkRequest) returns (GetPaymentLinkResponse) {} + rpc GetPaymentLinkInstallments(GetPaymentLinkRequest) returns (GetPaymentLinkResponse) {} +} + +message GetBankCardPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + BankCardInformation BankCard = 2; +} + +message GetPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; +} + +message GetPhonePaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string Phone = 2; +} + +message GetLoginPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string Login = 2; +} + +message GetB2BPaymentLinkRequest { + MainPaymentSettings MainSettings = 1; + string PaymentPurpose = 2; + google.protobuf.Empty VatData = 3; +} + +message GetPaymentLinkResponse { + string RedirectURL = 1; +} diff --git a/service/service.go b/service/service.go deleted file mode 100644 index 4e41734..0000000 --- a/service/service.go +++ /dev/null @@ -1,72 +0,0 @@ -package service - -import ( - "bitbucket.org/skeris/treasurer/dal" - "github.com/gofiber/fiber/v2" - "github.com/themakers/hlog" -) - -type Service struct { - log hlog.Logger - d *dal.MongoConnection -} - -func New(log hlog.Logger, d *dal.MongoConnection) *Service { - return &Service{ - log: log, - d: d, - } -} - -func (s *Service) Register(mux fiber.Fiber) fiber.Fiber { - api := mux.Group("/treasurer") - api.Post("/invoice") - return mux -} - -type RequestCreateInvoice struct { - RequesterID string `json:"requester_id"` - Amount float64 `json:"amt"` - PayWay string `json:"pw"` - Currency string `json:"cur"` - Email string `json:"email"` - Phone string `json:"phone"` - OnAccept Action `json:"on_accept"` - OnDecline Action `json:"on_decline"` - OnTimeout Action `json:"on_timeout"` -} - -type Action struct { - ActionType string `json:"action_type"` - Target string `json:"target"` - Data string `json:"data"` -} - -func (s *Service) CreateInvoice(c *fiber.Ctx) error { - c.Accepts("application/json") - - req := new(RequestCreateInvoice) - - if err := c.BodyParser(req); err != nil { - return err - } - - id, err := s.d.InsertPayment(c.Context, &dal.Request{ - RequesterID: req.RequesterID, - Email: req.Email, - Phone: req.Phone, - Status: "open", - Amount: req.Amount, - PaymentType: req.PayWay, - Currency: req.Currency, - IsRefill: true, - OnAccept: req.OnAccept, - OnDecline: req.OnDecline, - OnTimeout: req.OnTimeout, - }) - if err != nil { - return err - } - - return nil -} diff --git a/tests/data/confirmation.go b/tests/data/confirmation.go new file mode 100644 index 0000000..8c1a836 --- /dev/null +++ b/tests/data/confirmation.go @@ -0,0 +1,13 @@ +package data + +import "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + +var ( + varTrue = true +) + +var ConfirmationRedirectDefault = yandex.ConfirmationRedirect{ + Type: yandex.ConfirmationTypeRedirect, + ConfirmationURL: "https://www.example.com/confirmation", + Enforce: &varTrue, +} diff --git a/tests/helpers/mongo.go b/tests/helpers/mongo.go new file mode 100644 index 0000000..20d8761 --- /dev/null +++ b/tests/helpers/mongo.go @@ -0,0 +1,42 @@ +package helpers + +import ( + "context" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + mongopkg "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" +) + +type MongoHelper struct { + collection *mongo.Collection +} + +func NewMongoHelper(ctx context.Context, config *mongopkg.Configuration) (*MongoHelper, error) { + database, err := mongopkg.Connect(ctx, &mongopkg.ConnectDeps{ + Configuration: config, + Timeout: 10 * time.Second, + }) + if err != nil { + return nil, err + } + + return &MongoHelper{collection: database.Collection("payments")}, nil +} + +func (receiver *MongoHelper) GetPayments(ctx context.Context) ([]models.Payment, error) { + return mongopkg.Find[models.Payment](ctx, &mongopkg.RequestSettings{ + Driver: receiver.collection, + Filter: bson.M{}, + }) +} + +func (receiver *MongoHelper) Clear(ctx context.Context) error { + if _, err := receiver.collection.DeleteMany(ctx, bson.M{}); err != nil { + return err + } + + return nil +} diff --git a/tests/integration/create_payment_alfaclick_test.go b/tests/integration/create_payment_alfaclick_test.go new file mode 100644 index 0000000..1a53204 --- /dev/null +++ b/tests/integration/create_payment_alfaclick_test.go @@ -0,0 +1,130 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentAlfaClick(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа alfaclick", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeAlfabank, + Status: models.PaymentStatusCanceled, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusCanceled, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodLogin{ + Type: yandex.PaymentTypeAlfabank, + Login: "test_login", + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkAlfaClick(ctx, &treasurer.GetLoginPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + Login: "test_login", + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_bankcard_test.go b/tests/integration/create_payment_bankcard_test.go new file mode 100644 index 0000000..42beaa7 --- /dev/null +++ b/tests/integration/create_payment_bankcard_test.go @@ -0,0 +1,140 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentBankCard(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа по банковской карты", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeBankCard, + Status: models.PaymentStatusWaiting, + Completed: false, + IsDeleted: false, + CallbackHostGRPC: []string{"http://localhost/callback"}, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusWaiting, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodBankCard{ + Type: yandex.PaymentTypeBankCard, + Card: &yandex.BankCardInformation{ + Number: "", + ExpiryYear: "", + ExpiryMonth: "", + CSC: "", + CardHolderName: "", + }, + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkBankCard(ctx, &treasurer.GetBankCardPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + BankCard: &treasurer.BankCardInformation{ + Number: "", + ExpiryYear: "", + ExpiryMonth: "", + }, + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_cash_test.go b/tests/integration/create_payment_cash_test.go new file mode 100644 index 0000000..3fb0c73 --- /dev/null +++ b/tests/integration/create_payment_cash_test.go @@ -0,0 +1,130 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentCash(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа cash", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 115020, + Type: models.PaymentTypeCash, + Status: models.PaymentStatusSuccessfully, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusSuccessfully, + Amount: yandex.Amount{ + Value: "1150.20", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodPhone{ + Type: yandex.PaymentTypeCash, + Phone: "+375309531013", + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkCash(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + Phone: "+375309531013", + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_installments_test.go b/tests/integration/create_payment_installments_test.go new file mode 100644 index 0000000..88889e2 --- /dev/null +++ b/tests/integration/create_payment_installments_test.go @@ -0,0 +1,126 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentInstallments(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа Installments", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 115020, + Type: models.PaymentTypeInstallments, + Status: models.PaymentStatusSuccessfully, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusSuccessfully, + Amount: yandex.Amount{ + Value: "1150.20", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodType{Type: yandex.PaymentTypeInstallments}, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkInstallments(ctx, &treasurer.GetPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_mobile_test.go b/tests/integration/create_payment_mobile_test.go new file mode 100644 index 0000000..63b7e7d --- /dev/null +++ b/tests/integration/create_payment_mobile_test.go @@ -0,0 +1,130 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentMobile(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа Mobile", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 115020, + Type: models.PaymentTypeMobile, + Status: models.PaymentStatusSuccessfully, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusSuccessfully, + Amount: yandex.Amount{ + Value: "1150.20", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodPhone{ + Type: yandex.PaymentTypeMobile, + Phone: "+375309531013", + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkMobile(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + Phone: "+375309531013", + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_qiwi_test.go b/tests/integration/create_payment_qiwi_test.go new file mode 100644 index 0000000..652c337 --- /dev/null +++ b/tests/integration/create_payment_qiwi_test.go @@ -0,0 +1,130 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentQIWI(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа qiwi", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeQiwi, + Status: models.PaymentStatusWaiting, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusWaiting, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodPhone{ + Type: yandex.PaymentTypeQiwi, + Phone: "+79015396311", + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkQIWI(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + Phone: "+79015396311", + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_sberbankb2b_test.go b/tests/integration/create_payment_sberbankb2b_test.go new file mode 100644 index 0000000..01da0a0 --- /dev/null +++ b/tests/integration/create_payment_sberbankb2b_test.go @@ -0,0 +1,29 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" +) + +func TestCreatePaymentSberbankB2B(t *testing.T) { + t.Run("Ошибка при создании платежа через sberbank 2b2 (метод отсутствует)", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + _, requestErr := treasurerClient.GetPaymentLinkSberbankB2B(ctx, &treasurer.GetB2BPaymentLinkRequest{}) + + assert.Error(t, requestErr) + }) +} diff --git a/tests/integration/create_payment_sberpay_test.go b/tests/integration/create_payment_sberpay_test.go new file mode 100644 index 0000000..982428e --- /dev/null +++ b/tests/integration/create_payment_sberpay_test.go @@ -0,0 +1,130 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentSberpay(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа sberpay", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeSberPay, + Status: models.PaymentStatusWaiting, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusWaiting, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodPhone{ + Type: yandex.PaymentTypeSberPay, + Phone: "+79015396311", + }, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkSberPay(ctx, &treasurer.GetPhonePaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + Phone: "+79015396311", + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_sbp_test.go b/tests/integration/create_payment_sbp_test.go new file mode 100644 index 0000000..1db710d --- /dev/null +++ b/tests/integration/create_payment_sbp_test.go @@ -0,0 +1,126 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentSBP(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа SBP", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeSBP, + Status: models.PaymentStatusSuccessfully, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusSuccessfully, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodType{Type: yandex.PaymentTypeSBP}, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkSBP(ctx, &treasurer.GetPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_tinkoff_test.go b/tests/integration/create_payment_tinkoff_test.go new file mode 100644 index 0000000..0fc36c3 --- /dev/null +++ b/tests/integration/create_payment_tinkoff_test.go @@ -0,0 +1,126 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentTinkoff(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа tinkoff", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeTinkoff, + Status: models.PaymentStatusPending, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusPending, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodType{Type: yandex.PaymentTypeTinkoff}, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkTinkoff(ctx, &treasurer.GetPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/tests/integration/create_payment_yoomoney_test.go b/tests/integration/create_payment_yoomoney_test.go new file mode 100644 index 0000000..51b49ab --- /dev/null +++ b/tests/integration/create_payment_yoomoney_test.go @@ -0,0 +1,126 @@ +package integration_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/walkerus/go-wiremock" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/models/yandex" + "penahub.gitlab.yandexcloud.net/external/treasurer/internal/proto/treasurer" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/array" + "penahub.gitlab.yandexcloud.net/external/treasurer/pkg/mongo" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/data" + "penahub.gitlab.yandexcloud.net/external/treasurer/tests/helpers" +) + +func TestCreatePaymentYoomoney(t *testing.T) { + mockClient := wiremock.NewClient("http://localhost:8000") + + t.Run("Успешное создание платежа по yoomoney", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + clientConnection, grpcErr := grpc.Dial("localhost:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if isNoError := assert.NoError(t, grpcErr); !isNoError { + return + } + + mongoHelper, mongoErr := helpers.NewMongoHelper(ctx, &mongo.Configuration{ + Host: "localhost", + Port: "27017", + User: "admin", + Password: "admin", + Auth: "admin", + DatabaseName: "admin", + }) + if isNoError := assert.NoError(t, mongoErr); !isNoError { + return + } + + treasurerClient := treasurer.NewTreasurerServiceClient(clientConnection) + + payment := models.Payment{ + UserID: "217c2d28-0969-11ee-be56-0242ac120002", + PaymentID: "1423d832-0965-11ee-be56-0242ac120002", + ClientIP: "192.186.0.1", + Currency: "RUB", + Amount: 15000, + Type: models.PaymentTypeYoomoney, + Status: models.PaymentStatusWaiting, + CallbackHostGRPC: []string{"http://localhost/callback"}, + Completed: false, + IsDeleted: false, + } + + yandexPayment := yandex.Payment{ + ID: payment.PaymentID, + Status: yandex.PaymentStatusWaiting, + Amount: yandex.Amount{ + Value: "150.00", + Currency: payment.Currency, + }, + PaymentMethod: yandex.PaymentMethodType{Type: yandex.PaymentTypeYoomoney}, + Confirmation: &data.ConfirmationRedirectDefault, + } + + createBankCardPaymentErr := mockClient.StubFor(wiremock. + Post(wiremock.URLPathEqualTo("/payments")). + WillReturnJSON(&yandexPayment, map[string]string{"Content-Type": "application/json"}, 200). + AtPriority(1), + ) + if isNoError := assert.NoError(t, createBankCardPaymentErr); !isNoError { + return + } + + response, requestErr := treasurerClient.GetPaymentLinkYooMoney(ctx, &treasurer.GetPaymentLinkRequest{ + MainSettings: &treasurer.MainPaymentSettings{ + Currency: payment.Currency, + Amount: payment.Amount, + CallbackHostGRPC: payment.CallbackHostGRPC, + ReturnURL: "", + UserID: payment.UserID, + ClientIP: payment.ClientIP, + }, + }) + if isNoError := assert.NoError(t, requestErr); !isNoError { + return + } + + assert.Equal(t, yandexPayment.Confirmation.ConfirmationURL, response.RedirectURL) + + payments, getPaymentsErr := mongoHelper.GetPayments(ctx) + if isNoError := assert.NoError(t, getPaymentsErr); !isNoError { + return + } + + findedPayment := array.Find(payments, func(currentPayment models.Payment, _ int, _ []models.Payment) bool { + return currentPayment.PaymentID == payment.PaymentID + }) + if isNotNil := assert.NotNil(t, findedPayment); !isNotNil { + return + } + + assert.Equal(t, payment.UserID, findedPayment.UserID) + assert.Equal(t, payment.PaymentID, findedPayment.PaymentID) + assert.Equal(t, payment.ClientIP, findedPayment.ClientIP) + assert.Equal(t, payment.Currency, findedPayment.Currency) + assert.Equal(t, payment.Amount, findedPayment.Amount) + assert.Equal(t, payment.Type, findedPayment.Type) + assert.Equal(t, payment.Status, findedPayment.Status) + assert.Equal(t, payment.Completed, findedPayment.Completed) + assert.Equal(t, payment.IsDeleted, findedPayment.IsDeleted) + assert.Equal(t, payment.CallbackHostGRPC, findedPayment.CallbackHostGRPC) + + defer func() { + mongoClearErr := mongoHelper.Clear(ctx) + mockClearErr := mockClient.Clear() + + assert.NoError(t, mongoClearErr) + assert.NoError(t, mockClearErr) + }() + }) +} diff --git a/version/version.go b/version/version.go deleted file mode 100644 index ccbcc51..0000000 --- a/version/version.go +++ /dev/null @@ -1,21 +0,0 @@ -package version - -var ( - BuildTime = "unset" - Commit = "unset" - Release = "unset" -) - -type InfoVersion struct { - BuildTime string - Commit string - Release string -} - -func GetVersion() InfoVersion { - return InfoVersion{ - BuildTime: BuildTime, - Commit: Commit, - Release: Release, - } -}