// Code generated by ogen, DO NOT EDIT. package gen import ( "context" "net/http" "time" "github.com/go-faster/errors" ht "github.com/ogen-go/ogen/http" "github.com/ogen-go/ogen/middleware" "github.com/ogen-go/ogen/ogenerrors" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" ) type codeRecorder struct { http.ResponseWriter status int } func (c *codeRecorder) WriteHeader(status int) { c.status = status c.ResponseWriter.WriteHeader(status) } func (c *codeRecorder) Unwrap() http.ResponseWriter { return c.ResponseWriter } // handleDealsDealIDGetRequest handles GET /deals/{deal_id} operation. // // Запрос позволяет получить информацию о текущем // состоянии сделки по ее уникальному идентификатору. // // GET /deals/{deal_id} func (s *Server) handleDealsDealIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/deals/{deal_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DealsDealIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: DealsDealIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, DealsDealIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, DealsDealIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeDealsDealIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response DealsDealIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: DealsDealIDGetOperation, OperationSummary: "Информация о сделке", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "deal_id", In: "path", }: params.DealID, }, Raw: r, } type ( Request = struct{} Params = DealsDealIDGetParams Response = DealsDealIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackDealsDealIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.DealsDealIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.DealsDealIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeDealsDealIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleDealsGetRequest handles GET /deals operation. // // Запрос позволяет получить список сделок, // отфильтрованный по заданным критериям. Подробнее о // работе со списками: https://yookassa.ru/developers/using-api/lists. // // GET /deals func (s *Server) handleDealsGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/deals"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DealsGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: DealsGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, DealsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, DealsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeDealsGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response DealsGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: DealsGetOperation, OperationSummary: "Список сделок", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "expires_at.gte", In: "query", }: params.ExpiresAtGte, { Name: "expires_at.gt", In: "query", }: params.ExpiresAtGt, { Name: "expires_at.lte", In: "query", }: params.ExpiresAtLte, { Name: "expires_at.lt", In: "query", }: params.ExpiresAtLt, { Name: "status", In: "query", }: params.Status, { Name: "full_text_search", In: "query", }: params.FullTextSearch, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = DealsGetParams Response = DealsGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackDealsGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.DealsGet(ctx, params) return response, err }, ) } else { response, err = s.h.DealsGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeDealsGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleDealsPostRequest handles POST /deals operation. // // Запрос позволяет создать сделку, в рамках которой // необходимо принять оплату от покупателя и // перечислить ее продавцу. // // POST /deals func (s *Server) handleDealsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/deals"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), DealsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: DealsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, DealsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, DealsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeDealsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodeDealsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response DealsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: DealsPostOperation, OperationSummary: "Создание сделки", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *SafeDealRequest Params = DealsPostParams Response = DealsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackDealsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.DealsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.DealsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeDealsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleMeGetRequest handles GET /me operation. // // С помощью этого запроса вы можете получить // информацию о магазине или шлюзе: * Для Сплитования // платежей: https://yookassa.ru/developers/solutions-for-platforms/split-payments/basics: в // запросе необходимо передать параметр on_behalf_of с // идентификатором магазина продавца и ваши данные для // аутентификации: https://yookassa.ru/developers/using-api/interaction-format#auth // (идентификатор и секретный ключ вашей платформы). * // Для партнеров: https://yookassa. // ru/developers/solutions-for-platforms/partners-api/basics: в запросе необходимо // передать OAuth-токен магазина. * Для выплат: https://yookassa. // ru/developers/payouts/overview: в запросе необходимо передать ваши // данные для аутентификации: https://yookassa. // ru/developers/using-api/interaction-format#auth (идентификатор и секретный // ключ вашего шлюза). // // GET /me func (s *Server) handleMeGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/me"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), MeGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: MeGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, MeGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, MeGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeMeGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response MeGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: MeGetOperation, OperationSummary: "Информация о настройках магазина или шлюза", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "on_behalf_of", In: "query", }: params.OnBehalfOf, }, Raw: r, } type ( Request = struct{} Params = MeGetParams Response = MeGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackMeGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.MeGet(ctx, params) return response, err }, ) } else { response, err = s.h.MeGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeMeGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentMethodsPaymentMethodIDGetRequest handles GET /payment_methods/{payment_method_id} operation. // // Используйте этот запрос, чтобы получить информацию о // текущем состоянии способа оплаты по его уникальному // идентификатору. // // GET /payment_methods/{payment_method_id} func (s *Server) handlePaymentMethodsPaymentMethodIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payment_methods/{payment_method_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentMethodsPaymentMethodIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentMethodsPaymentMethodIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentMethodsPaymentMethodIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentMethodsPaymentMethodIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentMethodsPaymentMethodIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PaymentMethodsPaymentMethodIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentMethodsPaymentMethodIDGetOperation, OperationSummary: "Информация о способе оплаты", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "payment_method_id", In: "path", }: params.PaymentMethodID, }, Raw: r, } type ( Request = struct{} Params = PaymentMethodsPaymentMethodIDGetParams Response = PaymentMethodsPaymentMethodIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentMethodsPaymentMethodIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentMethodsPaymentMethodIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.PaymentMethodsPaymentMethodIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentMethodsPaymentMethodIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentMethodsPostRequest handles POST /payment_methods operation. // // Используйте этот запрос, чтобы создать в ЮKassa объект // способа оплаты: https://yookassa.ru/developers/api#payment_method_object. В // запросе необходимо передать код способа оплаты, // который вы хотите сохранить, и при необходимости // дополнительные параметры, связанные с той // функциональностью, которую вы хотите использовать. // Идентификатор созданного способа оплаты вы можете // использовать при проведении автоплатежей: https://yookassa. // ru/developers/payment-acceptance/scenario-extensions/recurring-payments/create-recurring или // выплат: https://yookassa.ru/developers/payouts/scenario-extensions/multipurpose-token. // // POST /payment_methods func (s *Server) handlePaymentMethodsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/payment_methods"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentMethodsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentMethodsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentMethodsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentMethodsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentMethodsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodePaymentMethodsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response PaymentMethodsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentMethodsPostOperation, OperationSummary: "Создание способа оплаты", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = PaymentMethodsPostReq Params = PaymentMethodsPostParams Response = PaymentMethodsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentMethodsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentMethodsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.PaymentMethodsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentMethodsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentsGetRequest handles GET /payments operation. // // Use this request to get a list of payments. You can download payments created over the last 3 // years. You can filter the list by specified criteria. More about working with lists: // https://yookassa.ru/developers/using-api/lists. // // GET /payments func (s *Server) handlePaymentsGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payments"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentsGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentsGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentsGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PaymentsGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentsGetOperation, OperationSummary: "List payments", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "captured_at.gte", In: "query", }: params.CapturedAtGte, { Name: "captured_at.gt", In: "query", }: params.CapturedAtGt, { Name: "captured_at.lte", In: "query", }: params.CapturedAtLte, { Name: "captured_at.lt", In: "query", }: params.CapturedAtLt, { Name: "payment_method", In: "query", }: params.PaymentMethod, { Name: "status", In: "query", }: params.Status, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = PaymentsGetParams Response = PaymentsGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentsGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentsGet(ctx, params) return response, err }, ) } else { response, err = s.h.PaymentsGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentsGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentsPaymentIDCancelPostRequest handles POST /payments/{payment_id}/cancel operation. // // Cancel payments with the waiting_for_capture status. Payment cancelation means you are not ready // to dispatch a product or to provide a service to the user. Once you cancel the payment, we will // start returning the money to the payer’s account. If the payment was made from a bank card, a // YooMoney wallet, or via SberPay, the money will be refunded instantly. If the payment was made // using other payment methods, the process can take up to several days. More about capturing and // canceling payments: https://yookassa. // ru/developers/payment-acceptance/getting-started/payment-process#capture-and-cancel. // // POST /payments/{payment_id}/cancel func (s *Server) handlePaymentsPaymentIDCancelPostRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/payments/{payment_id}/cancel"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentsPaymentIDCancelPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentsPaymentIDCancelPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentsPaymentIDCancelPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentsPaymentIDCancelPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentsPaymentIDCancelPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PaymentsPaymentIDCancelPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentsPaymentIDCancelPostOperation, OperationSummary: "Cancel a payment", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "payment_id", In: "path", }: params.PaymentID, { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = struct{} Params = PaymentsPaymentIDCancelPostParams Response = PaymentsPaymentIDCancelPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentsPaymentIDCancelPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentsPaymentIDCancelPost(ctx, params) return response, err }, ) } else { response, err = s.h.PaymentsPaymentIDCancelPost(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentsPaymentIDCancelPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentsPaymentIDCapturePostRequest handles POST /payments/{payment_id}/capture operation. // // Confirm you’re ready to accept the payment. Once the payment is captured, the status will change // to succeeded. After that, you can provide the customer with the product or service. You can only // capture payments with the waiting_for_capture status, and only for a certain amount of time // (depending on the payment method). If you do not capture the payment within the allotted time, the // status will change to canceled, and the money will be returned to the user. More about capturing // and canceling payments: https://yookassa. // ru/developers/payment-acceptance/getting-started/payment-process#capture-and-cancel. // // POST /payments/{payment_id}/capture func (s *Server) handlePaymentsPaymentIDCapturePostRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/payments/{payment_id}/capture"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentsPaymentIDCapturePostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentsPaymentIDCapturePostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentsPaymentIDCapturePostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentsPaymentIDCapturePostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentsPaymentIDCapturePostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodePaymentsPaymentIDCapturePostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response PaymentsPaymentIDCapturePostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentsPaymentIDCapturePostOperation, OperationSummary: "Capture a payment", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "payment_id", In: "path", }: params.PaymentID, { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *PaymentsPaymentIDCapturePostReq Params = PaymentsPaymentIDCapturePostParams Response = PaymentsPaymentIDCapturePostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentsPaymentIDCapturePostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentsPaymentIDCapturePost(ctx, request, params) return response, err }, ) } else { response, err = s.h.PaymentsPaymentIDCapturePost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentsPaymentIDCapturePostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentsPaymentIDGetRequest handles GET /payments/{payment_id} operation. // // This request allows you to get the information about the current payment status by its unique ID. // // GET /payments/{payment_id} func (s *Server) handlePaymentsPaymentIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payments/{payment_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentsPaymentIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentsPaymentIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentsPaymentIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentsPaymentIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentsPaymentIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PaymentsPaymentIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentsPaymentIDGetOperation, OperationSummary: "Get payment information", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "payment_id", In: "path", }: params.PaymentID, }, Raw: r, } type ( Request = struct{} Params = PaymentsPaymentIDGetParams Response = PaymentsPaymentIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentsPaymentIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentsPaymentIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.PaymentsPaymentIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentsPaymentIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePaymentsPostRequest handles POST /payments operation. // // To accept a payment, you need to create a payment object: https://yookassa. // ru/developers/api#payment_object, Payment. It contains all the necessary payment information // (amount, currency, and status). Payments have a linear life cycle, going from one status to the // next sequentially. // // POST /payments func (s *Server) handlePaymentsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/payments"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PaymentsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PaymentsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PaymentsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PaymentsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePaymentsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodePaymentsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response PaymentsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PaymentsPostOperation, OperationSummary: "Create a payment", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *PaymentsPostReq Params = PaymentsPostParams Response = PaymentsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPaymentsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PaymentsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.PaymentsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePaymentsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePayoutsGetRequest handles GET /payouts operation. // // Use this request to get a list of payouts. You can download payments created over the last 3 years. // // You can filter the list by specified criteria. Request authentication details: https://yookassa. // // ru/developers/using-api/interaction-format#auth depend on which payment solution you are using: // basic payouts: https://yookassa.ru/developers/payouts/overview or payouts within the Safe Deal: // https://yookassa.ru/developers/solutions-for-platforms/safe-deal/basics. More about working with // lists: https://yookassa.ru/developers/using-api/lists. // // GET /payouts func (s *Server) handlePayoutsGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payouts"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PayoutsGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PayoutsGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PayoutsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PayoutsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePayoutsGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PayoutsGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PayoutsGetOperation, OperationSummary: "List of payouts", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "payout_destination.type", In: "query", }: params.PayoutDestinationType, { Name: "status", In: "query", }: params.Status, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = PayoutsGetParams Response = PayoutsGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPayoutsGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PayoutsGet(ctx, params) return response, err }, ) } else { response, err = s.h.PayoutsGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePayoutsGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePayoutsPayoutIDGetRequest handles GET /payouts/{payout_id} operation. // // Используйте этот запрос, чтобы получить информацию о // текущем состоянии выплаты по ее уникальному // идентификатору. Данные для аутентификации: https://yookassa. // ru/developers/using-api/interaction-format#auth запросов зависят от того, // какое платежное решение вы используете — обычные // выплаты: https://yookassa.ru/developers/payouts/overview или выплаты в // рамках Безопасной сделки: https://yookassa. // ru/developers/solutions-for-platforms/safe-deal/basics. // // GET /payouts/{payout_id} func (s *Server) handlePayoutsPayoutIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payouts/{payout_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PayoutsPayoutIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PayoutsPayoutIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PayoutsPayoutIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PayoutsPayoutIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePayoutsPayoutIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PayoutsPayoutIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PayoutsPayoutIDGetOperation, OperationSummary: "Информация о выплате", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "payout_id", In: "path", }: params.PayoutID, }, Raw: r, } type ( Request = struct{} Params = PayoutsPayoutIDGetParams Response = PayoutsPayoutIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPayoutsPayoutIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PayoutsPayoutIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.PayoutsPayoutIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePayoutsPayoutIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePayoutsPostRequest handles POST /payouts operation. // // Используйте этот запрос, чтобы создать в ЮKassa объект // выплаты: https://yookassa.ru/developers/api#payout_object. В запросе // необходимо передать сумму выплаты, данные о способе // получения выплаты (например, номер кошелька ЮMoney), // описание выплаты и при необходимости дополнительные // параметры, связанные с той функциональностью, // которую вы хотите использовать. Передаваемые // параметры и данные для аутентификации: https://yookassa. // ru/developers/using-api/interaction-format#auth запросов зависят от того, // какое платежное решение вы используете — обычные // выплаты: https://yookassa.ru/developers/payouts/overview или выплаты в // рамках Безопасной сделки: https://yookassa. // ru/developers/solutions-for-platforms/safe-deal/basics. // // POST /payouts func (s *Server) handlePayoutsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/payouts"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PayoutsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PayoutsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PayoutsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PayoutsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePayoutsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodePayoutsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response PayoutsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PayoutsPostOperation, OperationSummary: "Создание выплаты", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *PayoutRequest Params = PayoutsPostParams Response = PayoutsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPayoutsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PayoutsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.PayoutsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePayoutsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePayoutsSearchGetRequest handles GET /payouts/search operation. // // Use this request to search for payouts by the specified criteria. Available only for payouts // created over the last 3 months. At this time, only search by the metadata parameter is available. // You can also specify the date and time when the payout was created (the created_at parameter). // Request authentication details: https://yookassa.ru/developers/using-api/interaction-format#auth // depend on which payment solution you are using: basic payouts: https://yookassa. // ru/developers/payouts/overview or payouts within the Safe Deal: https://yookassa. // ru/developers/solutions-for-platforms/safe-deal/basics. // // GET /payouts/search func (s *Server) handlePayoutsSearchGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/payouts/search"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PayoutsSearchGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PayoutsSearchGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PayoutsSearchGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PayoutsSearchGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePayoutsSearchGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PayoutsSearchGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PayoutsSearchGetOperation, OperationSummary: "Search for payouts", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "metadata", In: "query", }: params.Metadata, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = PayoutsSearchGetParams Response = PayoutsSearchGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPayoutsSearchGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PayoutsSearchGet(ctx, params) return response, err }, ) } else { response, err = s.h.PayoutsSearchGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePayoutsSearchGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePersonalDataPersonalDataIDGetRequest handles GET /personal_data/{personal_data_id} operation. // // С помощью этого запроса вы можете получить // информацию о текущем статусе объекта персональных // данных по его уникальному идентификатору. // // GET /personal_data/{personal_data_id} func (s *Server) handlePersonalDataPersonalDataIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/personal_data/{personal_data_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PersonalDataPersonalDataIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PersonalDataPersonalDataIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PersonalDataPersonalDataIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PersonalDataPersonalDataIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePersonalDataPersonalDataIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response PersonalDataPersonalDataIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PersonalDataPersonalDataIDGetOperation, OperationSummary: "Информация о персональных данных", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "personal_data_id", In: "path", }: params.PersonalDataID, }, Raw: r, } type ( Request = struct{} Params = PersonalDataPersonalDataIDGetParams Response = PersonalDataPersonalDataIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPersonalDataPersonalDataIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PersonalDataPersonalDataIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.PersonalDataPersonalDataIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePersonalDataPersonalDataIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handlePersonalDataPostRequest handles POST /personal_data operation. // // Используйте этот запрос, чтобы создать в ЮKassa объект // персональных данных: https://yookassa.ru/developers/api#personal_data_object. В // запросе необходимо указать тип данных (с какой целью // они будут использоваться) и передать информацию о // пользователе: фамилию, имя, отчество и другие — в // зависимости от выбранного типа. Идентификатор // созданного объекта персональных данных необходимо // использовать в запросе на создание выплаты: https://yookassa. // ru/developers/api#create_payout. // // POST /personal_data func (s *Server) handlePersonalDataPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/personal_data"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), PersonalDataPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: PersonalDataPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, PersonalDataPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, PersonalDataPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodePersonalDataPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodePersonalDataPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response PersonalDataPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: PersonalDataPostOperation, OperationSummary: "Создание персональных данных", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = PersonalDataPostReq Params = PersonalDataPostParams Response = PersonalDataPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackPersonalDataPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.PersonalDataPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.PersonalDataPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodePersonalDataPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleReceiptsGetRequest handles GET /receipts operation. // // Запрос позволяет получить список чеков, // отфильтрованный по заданным критериям. Можно // запросить чеки по конкретному платежу, чеки по // конкретному возврату или все чеки магазина. // Подробнее о работе со списками: https://yookassa. // ru/developers/using-api/lists. // // GET /receipts func (s *Server) handleReceiptsGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/receipts"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ReceiptsGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: ReceiptsGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, ReceiptsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, ReceiptsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeReceiptsGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response ReceiptsGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ReceiptsGetOperation, OperationSummary: "Список чеков", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "status", In: "query", }: params.Status, { Name: "payment_id", In: "query", }: params.PaymentID, { Name: "refund_id", In: "query", }: params.RefundID, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = ReceiptsGetParams Response = ReceiptsGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackReceiptsGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ReceiptsGet(ctx, params) return response, err }, ) } else { response, err = s.h.ReceiptsGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeReceiptsGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleReceiptsPostRequest handles POST /receipts operation. // // Используйте этот запрос при оплате с соблюдением // требований 54-ФЗ: https://yookassa. // ru/developers/payment-acceptance/receipts/54fz/basics, чтобы создать чек // зачета предоплаты. Если вы работаете по сценарию // Сначала платеж, потом чек: https://yookassa. // ru/developers/payment-acceptance/receipts/54fz/other-services/basics#receipt-after-payment, в // запросе также нужно передавать данные для // формирования чека прихода и чека возврата прихода. // // POST /receipts func (s *Server) handleReceiptsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/receipts"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ReceiptsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: ReceiptsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, ReceiptsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, ReceiptsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeReceiptsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodeReceiptsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response ReceiptsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ReceiptsPostOperation, OperationSummary: "Создание чека", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = PostReceiptData Params = ReceiptsPostParams Response = ReceiptsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackReceiptsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ReceiptsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.ReceiptsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeReceiptsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleReceiptsReceiptIDGetRequest handles GET /receipts/{receipt_id} operation. // // Запрос позволяет получить информацию о текущем // состоянии чека по его уникальному идентификатору. // // GET /receipts/{receipt_id} func (s *Server) handleReceiptsReceiptIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/receipts/{receipt_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), ReceiptsReceiptIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: ReceiptsReceiptIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, ReceiptsReceiptIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, ReceiptsReceiptIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeReceiptsReceiptIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response ReceiptsReceiptIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: ReceiptsReceiptIDGetOperation, OperationSummary: "Информация о чеке", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "receipt_id", In: "path", }: params.ReceiptID, }, Raw: r, } type ( Request = struct{} Params = ReceiptsReceiptIDGetParams Response = ReceiptsReceiptIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackReceiptsReceiptIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.ReceiptsReceiptIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.ReceiptsReceiptIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeReceiptsReceiptIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleRefundsGetRequest handles GET /refunds operation. // // Use this request to get a list of refunds. You can download refunds created over the last 3 years. // You can filter the list by specified criteria. More about working with lists: https://yookassa. // ru/developers/using-api/lists. // // GET /refunds func (s *Server) handleRefundsGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/refunds"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), RefundsGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: RefundsGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, RefundsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, RefundsGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeRefundsGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response RefundsGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: RefundsGetOperation, OperationSummary: "Список возвратов", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "created_at.gte", In: "query", }: params.CreatedAtGte, { Name: "created_at.gt", In: "query", }: params.CreatedAtGt, { Name: "created_at.lte", In: "query", }: params.CreatedAtLte, { Name: "created_at.lt", In: "query", }: params.CreatedAtLt, { Name: "payment_id", In: "query", }: params.PaymentID, { Name: "status", In: "query", }: params.Status, { Name: "limit", In: "query", }: params.Limit, { Name: "cursor", In: "query", }: params.Cursor, }, Raw: r, } type ( Request = struct{} Params = RefundsGetParams Response = RefundsGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackRefundsGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.RefundsGet(ctx, params) return response, err }, ) } else { response, err = s.h.RefundsGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeRefundsGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleRefundsPostRequest handles POST /refunds operation. // // Создает возврат успешного платежа на указанную сумму. // // Платеж можно вернуть только в течение трех лет с // // момента его создания: https://yookassa.ru/developers/api#create_payment. // Комиссия ЮKassa за проведение платежа не возвращается. // // POST /refunds func (s *Server) handleRefundsPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/refunds"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), RefundsPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: RefundsPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, RefundsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, RefundsPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeRefundsPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodeRefundsPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response RefundsPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: RefundsPostOperation, OperationSummary: "Создание возврата", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *RefundsPostReq Params = RefundsPostParams Response = RefundsPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackRefundsPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.RefundsPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.RefundsPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeRefundsPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleRefundsRefundIDGetRequest handles GET /refunds/{refund_id} operation. // // Запрос позволяет получить информацию о текущем // состоянии возврата по его уникальному // идентификатору. // // GET /refunds/{refund_id} func (s *Server) handleRefundsRefundIDGetRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/refunds/{refund_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), RefundsRefundIDGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: RefundsRefundIDGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, RefundsRefundIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, RefundsRefundIDGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeRefundsRefundIDGetParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response RefundsRefundIDGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: RefundsRefundIDGetOperation, OperationSummary: "Информация о возврате", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "refund_id", In: "path", }: params.RefundID, }, Raw: r, } type ( Request = struct{} Params = RefundsRefundIDGetParams Response = RefundsRefundIDGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackRefundsRefundIDGetParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.RefundsRefundIDGet(ctx, params) return response, err }, ) } else { response, err = s.h.RefundsRefundIDGet(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeRefundsRefundIDGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleSbpBanksGetRequest handles GET /sbp_banks operation. // // С помощью этого запроса вы можете получить // актуальный список всех участников СБП. Список нужно // вывести получателю выплаты, идентификатор // выбранного участника СБП необходимо использовать в // запросе на создание выплаты: https://yookassa. // ru/developers/api#create_payout. Подробнее о выплатах через СБП: // https://yookassa.ru/developers/payouts/making-payouts/sbp. // // GET /sbp_banks func (s *Server) handleSbpBanksGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/sbp_banks"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), SbpBanksGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: SbpBanksGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, SbpBanksGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, SbpBanksGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } var rawBody []byte var response SbpBanksGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: SbpBanksGetOperation, OperationSummary: "Список участников СБП", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = SbpBanksGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.SbpBanksGet(ctx) return response, err }, ) } else { response, err = s.h.SbpBanksGet(ctx) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeSbpBanksGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleWebhooksGetRequest handles GET /webhooks operation. // // Запрос позволяет узнать, какие webhook есть для // переданного OAuth-токена. // // GET /webhooks func (s *Server) handleWebhooksGetRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("GET"), semconv.HTTPRouteKey.String("/webhooks"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), WebhooksGetOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: WebhooksGetOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, WebhooksGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, WebhooksGetOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } var rawBody []byte var response WebhooksGetRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: WebhooksGetOperation, OperationSummary: "Список созданных webhook", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{}, Raw: r, } type ( Request = struct{} Params = struct{} Response = WebhooksGetRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, nil, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.WebhooksGet(ctx) return response, err }, ) } else { response, err = s.h.WebhooksGet(ctx) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeWebhooksGetResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleWebhooksPostRequest handles POST /webhooks operation. // // Запрос позволяет подписаться на уведомления о // событиях: https://yookassa.ru/developers/using-api/webhooks#events (например, // переход платежа в статус succeeded). C помощью webhook можно // подписаться только на события платежей и возвратов. // Если вы хотите получать уведомления о нескольких // событиях, вам нужно для каждого из них создать свой // webhook. Для каждого OAuth-токена нужно создавать свой // набор webhook. // // POST /webhooks func (s *Server) handleWebhooksPostRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("POST"), semconv.HTTPRouteKey.String("/webhooks"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), WebhooksPostOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: WebhooksPostOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, WebhooksPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, WebhooksPostOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeWebhooksPostParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte request, rawBody, close, err := s.decodeWebhooksPostRequest(r) if err != nil { err = &ogenerrors.DecodeRequestError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeRequest", err) s.cfg.ErrorHandler(ctx, w, r, err) return } defer func() { if err := close(); err != nil { recordError("CloseRequest", err) } }() var response WebhooksPostRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: WebhooksPostOperation, OperationSummary: "Создание webhook", OperationID: "", Body: request, RawBody: rawBody, Params: middleware.Parameters{ { Name: "Idempotence-Key", In: "header", }: params.IdempotenceKey, }, Raw: r, } type ( Request = *WebhooksPostReq Params = WebhooksPostParams Response = WebhooksPostRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackWebhooksPostParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.WebhooksPost(ctx, request, params) return response, err }, ) } else { response, err = s.h.WebhooksPost(ctx, request, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeWebhooksPostResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } } // handleWebhooksWebhookIDDeleteRequest handles DELETE /webhooks/{webhook_id} operation. // // Запрос позволяет отписаться от уведомлений о событии // для переданного OAuth-токена. Чтобы удалить webhook, вам // нужно передать в запросе его идентификатор. // // DELETE /webhooks/{webhook_id} func (s *Server) handleWebhooksWebhookIDDeleteRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { statusWriter := &codeRecorder{ResponseWriter: w} w = statusWriter otelAttrs := []attribute.KeyValue{ semconv.HTTPRequestMethodKey.String("DELETE"), semconv.HTTPRouteKey.String("/webhooks/{webhook_id}"), } // Add attributes from config. otelAttrs = append(otelAttrs, s.cfg.Attributes...) // Start a span for this request. ctx, span := s.cfg.Tracer.Start(r.Context(), WebhooksWebhookIDDeleteOperation, trace.WithAttributes(otelAttrs...), serverSpanKind, ) defer span.End() // Add Labeler to context. labeler := &Labeler{attrs: otelAttrs} ctx = contextWithLabeler(ctx, labeler) // Run stopwatch. startTime := time.Now() defer func() { elapsedDuration := time.Since(startTime) attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() code := statusWriter.status if code != 0 { codeAttr := semconv.HTTPResponseStatusCode(code) attrs = append(attrs, codeAttr) span.SetAttributes(codeAttr) } attrOpt := metric.WithAttributes(attrs...) // Increment request counter. s.requests.Add(ctx, 1, attrOpt) // Use floating point division here for higher precision (instead of Millisecond method). s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) }() var ( recordError = func(stage string, err error) { span.RecordError(err) // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, // unless there was another error (e.g., network error receiving the response body; or 3xx codes with // max redirects exceeded), in which case status MUST be set to Error. code := statusWriter.status if code < 100 || code >= 500 { span.SetStatus(codes.Error, stage) } attrSet := labeler.AttributeSet() attrs := attrSet.ToSlice() if code != 0 { attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) } s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) } err error opErrContext = ogenerrors.OperationContext{ Name: WebhooksWebhookIDDeleteOperation, ID: "", } ) { type bitset = [1]uint8 var satisfied bitset { sctx, ok, err := s.securityBasicAuth(ctx, WebhooksWebhookIDDeleteOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "BasicAuth", Err: err, } defer recordError("Security:BasicAuth", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 0 ctx = sctx } } { sctx, ok, err := s.securityOAuth2(ctx, WebhooksWebhookIDDeleteOperation, r) if err != nil { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Security: "OAuth2", Err: err, } defer recordError("Security:OAuth2", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if ok { satisfied[0] |= 1 << 1 ctx = sctx } } if ok := func() bool { nextRequirement: for _, requirement := range []bitset{ {0b00000001}, {0b00000010}, } { for i, mask := range requirement { if satisfied[i]&mask != mask { continue nextRequirement } } return true } return false }(); !ok { err = &ogenerrors.SecurityError{ OperationContext: opErrContext, Err: ogenerrors.ErrSecurityRequirementIsNotSatisfied, } defer recordError("Security", err) s.cfg.ErrorHandler(ctx, w, r, err) return } } params, err := decodeWebhooksWebhookIDDeleteParams(args, argsEscaped, r) if err != nil { err = &ogenerrors.DecodeParamsError{ OperationContext: opErrContext, Err: err, } defer recordError("DecodeParams", err) s.cfg.ErrorHandler(ctx, w, r, err) return } var rawBody []byte var response WebhooksWebhookIDDeleteRes if m := s.cfg.Middleware; m != nil { mreq := middleware.Request{ Context: ctx, OperationName: WebhooksWebhookIDDeleteOperation, OperationSummary: "Удаление webhook", OperationID: "", Body: nil, RawBody: rawBody, Params: middleware.Parameters{ { Name: "webhook_id", In: "path", }: params.WebhookID, }, Raw: r, } type ( Request = struct{} Params = WebhooksWebhookIDDeleteParams Response = WebhooksWebhookIDDeleteRes ) response, err = middleware.HookMiddleware[ Request, Params, Response, ]( m, mreq, unpackWebhooksWebhookIDDeleteParams, func(ctx context.Context, request Request, params Params) (response Response, err error) { response, err = s.h.WebhooksWebhookIDDelete(ctx, params) return response, err }, ) } else { response, err = s.h.WebhooksWebhookIDDelete(ctx, params) } if err != nil { defer recordError("Internal", err) s.cfg.ErrorHandler(ctx, w, r, err) return } if err := encodeWebhooksWebhookIDDeleteResponse(response, w, span); err != nil { defer recordError("EncodeResponse", err) if !errors.Is(err, ht.ErrInternalServerErrorResponse) { s.cfg.ErrorHandler(ctx, w, r, err) } return } }