package yookassa import ( "context" "errors" "fmt" "log/slog" "net/http" "payouts/internal/models" "payouts/internal/service/database/orm" "payouts/internal/service/yookassa/config" "payouts/internal/service/yookassa/gen" "github.com/ogen-go/ogen/ogenerrors" ) type yookassaService struct { conf config.YooKassa payClient *gen.Client ctx context.Context } func NewYookassaService(conf config.YooKassa) (Service, error) { svc := &yookassaService{ conf: conf, ctx: context.Background(), } payClient, err := gen.NewClient(conf.BaseUrl, svc, gen.WithClient(&http.Client{ Timeout: conf.Timeout, })) if err != nil { return nil, err } svc.payClient = payClient return svc, nil } func (y *yookassaService) getParams(options ...Optional) *params { params := ¶ms{ ctx: y.ctx, } for _, opt := range options { opt(params) } return params } // BasicAuth implements [gen.SecuritySource]. func (y *yookassaService) BasicAuth(ctx context.Context, operationName gen.OperationName) (gen.BasicAuth, error) { return gen.BasicAuth{ Username: y.conf.ApiPaymentKey, Password: y.conf.ApiPaymentSecret, }, nil } // OAuth2 implements [gen.SecuritySource]. func (y *yookassaService) OAuth2(ctx context.Context, operationName gen.OperationName) (gen.OAuth2, error) { return gen.OAuth2{}, ogenerrors.ErrSkipClientSecurity } // CreatePayout implements [Service]. func (y *yookassaService) CreatePayout(req models.PayoutReq, userSession *orm.User, idempotenceKey string, opts ...Optional) (models.PayoutResp, error) { params := y.getParams(opts...) var payoutDestination gen.PayoutRequestPayoutDestinationData switch req.PayoutType { case models.TypeSBP: payoutDestination = gen.NewSbpPayoutRequestPayoutDestinationData(gen.Sbp{ Type: gen.PayoutDestinationDataTypeSbp, Phone: userSession.Phone, }) case models.TypeYooMoney: payoutDestination = gen.NewYooMoneyPayoutRequestPayoutDestinationData(gen.YooMoney{ Type: gen.PayoutDestinationDataTypeYooMoney, AccountNumber: req.AccountNumber, }) default: return models.PayoutResp{Result: "failed", ErrorReason: "unsupported payout type"}, errors.New("unsupported payout type") } payoutOpt := gen.NewOptPayoutRequestPayoutDestinationData(payoutDestination) postResp, err := y.payClient.PayoutsPost(params.ctx, &gen.PayoutRequest{ Amount: gen.PayoutRequestAmount{ Value: fmt.Sprintf("%.2f", req.Amount), Currency: gen.CurrencyCodeRUB, }, PayoutDestinationData: payoutOpt, Test: gen.NewOptTest(gen.Test(y.conf.Test)), }, gen.PayoutsPostParams{ IdempotenceKey: idempotenceKey, }) slog.Debug(fmt.Sprintf("Received from yookassa: error %v; response %v", err, postResp)) if err == nil { switch payoutResp := postResp.(type) { case *gen.Payout: slog.Info(fmt.Sprintf("Succeeded payout. It's id = %s", payoutResp.ID)) return models.PayoutResp{Result: string(payoutResp.Status), PayoutID: string(payoutResp.ID)}, nil default: return models.PayoutResp{Result: "failed", ErrorReason: fmt.Sprintf("%T", postResp)}, nil } } return models.PayoutResp{Result: "failed", ErrorReason: errors.Join(errors.New("failed to call yookassa api"), err).Error()}, err } // GetConfig implements [Service]. func (y *yookassaService) GetConfig() config.YooKassa { return y.conf }