5981 lines
160 KiB
Go
5981 lines
160 KiB
Go
// 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
|
||
}
|
||
}
|