Reorganaze modules, add auth processing.
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
|||||||
"payouts/internal/log"
|
"payouts/internal/log"
|
||||||
"payouts/internal/service/cache"
|
"payouts/internal/service/cache"
|
||||||
"payouts/internal/service/database"
|
"payouts/internal/service/database"
|
||||||
|
"payouts/internal/service/yookassa"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -21,6 +22,7 @@ func main() {
|
|||||||
cache.Module,
|
cache.Module,
|
||||||
config.Module,
|
config.Module,
|
||||||
database.Module,
|
database.Module,
|
||||||
|
yookassa.Module,
|
||||||
|
|
||||||
log.Module,
|
log.Module,
|
||||||
fx.WithLogger(func() fxevent.Logger {
|
fx.WithLogger(func() fxevent.Logger {
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ Cache.TTL = 24h
|
|||||||
# Yookassa related props
|
# Yookassa related props
|
||||||
# Base API Url
|
# Base API Url
|
||||||
YooKassa.BaseUrl = https://api.yookassa.ru/v3
|
YooKassa.BaseUrl = https://api.yookassa.ru/v3
|
||||||
|
YooKassa.Timeout = 30s
|
||||||
|
YooKassa.Test = false
|
||||||
# Base API key/secret
|
# Base API key/secret
|
||||||
YooKassa.ApiBaseKey =
|
YooKassa.ApiBaseKey =
|
||||||
YooKassa.ApiBaseSecret =
|
YooKassa.ApiBaseSecret =
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"payouts/internal/api/payment"
|
"payouts/internal/api/payout"
|
||||||
"payouts/internal/api/user"
|
"payouts/internal/api/user"
|
||||||
"payouts/internal/api/version"
|
"payouts/internal/api/version"
|
||||||
appConfig "payouts/internal/config"
|
appConfig "payouts/internal/config"
|
||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
// Module is a fx module
|
// Module is a fx module
|
||||||
var Module = fx.Options(
|
var Module = fx.Options(
|
||||||
user.Module,
|
user.Module,
|
||||||
payment.Module,
|
payout.Module,
|
||||||
version.Module,
|
version.Module,
|
||||||
monitoring.Module,
|
monitoring.Module,
|
||||||
|
|
||||||
@@ -37,9 +37,9 @@ type Params struct {
|
|||||||
|
|
||||||
AppConfig *appConfig.App
|
AppConfig *appConfig.App
|
||||||
|
|
||||||
PaymentHandler payment.Handler
|
PayoutHandler payout.Handler
|
||||||
UserHandler user.Handler
|
UserHandler user.Handler
|
||||||
Version version.Handler
|
Version version.Handler
|
||||||
|
|
||||||
Metrics monitoring.Metrics
|
Metrics monitoring.Metrics
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,9 @@ func RegisterRoutes(p Params, lc fx.Lifecycle) {
|
|||||||
userRouter.HandleFunc(user.RegisterRoute, p.UserHandler.UserRegister).Methods(http.MethodPost)
|
userRouter.HandleFunc(user.RegisterRoute, p.UserHandler.UserRegister).Methods(http.MethodPost)
|
||||||
userRouter.HandleFunc(user.LoginRoute, p.UserHandler.UserLogin).Methods(http.MethodPost)
|
userRouter.HandleFunc(user.LoginRoute, p.UserHandler.UserLogin).Methods(http.MethodPost)
|
||||||
|
|
||||||
paymentRouter := apiRouter.PathPrefix(payment.BaseRoute).Subrouter()
|
payoutRouter := apiRouter.PathPrefix(payout.BaseRoute).Subrouter()
|
||||||
paymentRouter.HandleFunc(payment.CreateRoute, p.PaymentHandler.PaymentCreate).Methods(http.MethodPost)
|
payoutRouter.HandleFunc(payout.CreateRoute, p.PayoutHandler.PayoutCreate).Methods(http.MethodPost)
|
||||||
paymentRouter.HandleFunc(payment.CallbackRoute, p.PaymentHandler.PaymentCallback).Methods(http.MethodPost)
|
payoutRouter.HandleFunc(payout.CallbackRoute, p.PayoutHandler.PayoutCallback).Methods(http.MethodPost)
|
||||||
|
|
||||||
// collect api metrics
|
// collect api metrics
|
||||||
apiRouter.Use(p.Metrics.GetMiddleware())
|
apiRouter.Use(p.Metrics.GetMiddleware())
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package payment
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Module = fx.Options(
|
|
||||||
fx.Provide(NewPaymentHandler),
|
|
||||||
)
|
|
||||||
|
|
||||||
type Handler interface {
|
|
||||||
GetSbpBanks(http.ResponseWriter, *http.Request)
|
|
||||||
PaymentCreate(http.ResponseWriter, *http.Request)
|
|
||||||
PaymentCallback(http.ResponseWriter, *http.Request)
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package payment
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"go.uber.org/fx"
|
|
||||||
|
|
||||||
"payouts/internal/config"
|
|
||||||
"payouts/internal/service/cache"
|
|
||||||
"payouts/internal/service/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BaseRoute = "/payment"
|
|
||||||
CreateRoute = "/create"
|
|
||||||
CallbackRoute = "/callback"
|
|
||||||
BanksRoute = "/sbp/banks"
|
|
||||||
)
|
|
||||||
|
|
||||||
type paymentHandler struct {
|
|
||||||
dbService database.Service
|
|
||||||
cacheService cache.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
// Params represents the module input params
|
|
||||||
type Params struct {
|
|
||||||
fx.In
|
|
||||||
|
|
||||||
AppConfig *config.App
|
|
||||||
DbService database.Service
|
|
||||||
CacheService cache.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPaymentHandler(p Params) (Handler, error) {
|
|
||||||
return &paymentHandler{
|
|
||||||
dbService: p.DbService,
|
|
||||||
cacheService: p.CacheService,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSbpBanks implements [Handler].
|
|
||||||
func (p *paymentHandler) GetSbpBanks(http.ResponseWriter, *http.Request) {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// PaymentCreate implements [Handler].
|
|
||||||
func (p *paymentHandler) PaymentCreate(http.ResponseWriter, *http.Request) {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// PaymentCallback implements [Handler].
|
|
||||||
func (p *paymentHandler) PaymentCallback(http.ResponseWriter, *http.Request) {
|
|
||||||
panic("unimplemented")
|
|
||||||
}
|
|
||||||
17
internal/api/payout/module.go
Normal file
17
internal/api/payout/module.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package payout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.uber.org/fx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Module = fx.Options(
|
||||||
|
fx.Provide(NewPayoutHandler),
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
GetSbpBanks(http.ResponseWriter, *http.Request)
|
||||||
|
PayoutCreate(http.ResponseWriter, *http.Request)
|
||||||
|
PayoutCallback(http.ResponseWriter, *http.Request)
|
||||||
|
}
|
||||||
91
internal/api/payout/payment_handler.go
Normal file
91
internal/api/payout/payment_handler.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package payout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"go.uber.org/fx"
|
||||||
|
|
||||||
|
"payouts/internal/config"
|
||||||
|
"payouts/internal/service/cache"
|
||||||
|
"payouts/internal/service/database"
|
||||||
|
"payouts/internal/service/database/orm"
|
||||||
|
"payouts/internal/service/yookassa"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BaseRoute = "/payment"
|
||||||
|
CreateRoute = "/create"
|
||||||
|
CallbackRoute = "/callback"
|
||||||
|
BanksRoute = "/sbp/banks"
|
||||||
|
)
|
||||||
|
|
||||||
|
var authHeaderRe = regexp.MustCompile(`^Bearer\s+(.*)$`)
|
||||||
|
|
||||||
|
type payoutHandler struct {
|
||||||
|
dbService database.Service
|
||||||
|
cacheService cache.Service
|
||||||
|
yooKassa yookassa.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params represents the module input params
|
||||||
|
type Params struct {
|
||||||
|
fx.In
|
||||||
|
|
||||||
|
AppConfig *config.App
|
||||||
|
DbService database.Service
|
||||||
|
YooKassa yookassa.Service
|
||||||
|
CacheService cache.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPayoutHandler(p Params) (Handler, error) {
|
||||||
|
return &payoutHandler{
|
||||||
|
dbService: p.DbService,
|
||||||
|
cacheService: p.CacheService,
|
||||||
|
yooKassa: p.YooKassa,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *payoutHandler) getSession(r *http.Request) (*orm.User, error) {
|
||||||
|
authHeaderValue := r.Header.Get("Authorization")
|
||||||
|
if len(authHeaderValue) == 0 {
|
||||||
|
return nil, errors.New("no valid auth header")
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := authHeaderRe.FindStringSubmatch(authHeaderValue)
|
||||||
|
if matches == nil {
|
||||||
|
return nil, errors.New("no valid auth header")
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionId := matches[1]
|
||||||
|
userSession, err := p.cacheService.GetSession(sessionId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("session not found")
|
||||||
|
}
|
||||||
|
return &userSession, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSbpBanks implements [Handler].
|
||||||
|
func (p *payoutHandler) GetSbpBanks(w http.ResponseWriter, r *http.Request) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentCreate implements [Handler].
|
||||||
|
func (p *payoutHandler) PayoutCreate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
errResponse := func(message string, err error, status int) {
|
||||||
|
http.Error(w, errors.Join(errors.New(message), err).Error(), status)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := p.getSession(r)
|
||||||
|
if err != nil {
|
||||||
|
errResponse("unautiorized", err, http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentCallback implements [Handler].
|
||||||
|
func (p *payoutHandler) PayoutCallback(http.ResponseWriter, *http.Request) {
|
||||||
|
}
|
||||||
46
internal/models/payout.go
Normal file
46
internal/models/payout.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PayoutType int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
SBP PayoutType = iota
|
||||||
|
YooMoney
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r PayoutType) String() string {
|
||||||
|
switch r {
|
||||||
|
case SBP:
|
||||||
|
return "spb"
|
||||||
|
case YooMoney:
|
||||||
|
return "yoo_money"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r PayoutType) MarshalText() (text []byte, err error) {
|
||||||
|
return []byte(r.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PayoutType) UnmarshalText(text []byte) (err error) {
|
||||||
|
s := strings.ToLower(string(text))
|
||||||
|
switch s {
|
||||||
|
case "spb":
|
||||||
|
*r = SBP
|
||||||
|
case "yoo_money":
|
||||||
|
*r = YooMoney
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid payment type: %s", s)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayoutReq struct {
|
||||||
|
PayoutType PayoutType `json:"payout_type"`
|
||||||
|
AccountNumber string `json:"account_number"`
|
||||||
|
Amount float32 `json:"amount"`
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
type dbService struct {
|
type dbService struct {
|
||||||
dbType string
|
dbType string
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDatabaseService(conf config.Database) (Service, error) {
|
func NewDatabaseService(conf config.Database) (Service, error) {
|
||||||
@@ -51,7 +52,9 @@ func NewDatabaseService(conf config.Database) (Service, error) {
|
|||||||
db.DB()
|
db.DB()
|
||||||
db.AutoMigrate(&orm.User{})
|
db.AutoMigrate(&orm.User{})
|
||||||
}
|
}
|
||||||
result := &dbService{}
|
result := &dbService{
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
result.dbType = conf.Type
|
result.dbType = conf.Type
|
||||||
result.db = db
|
result.db = db
|
||||||
|
|
||||||
@@ -59,9 +62,9 @@ func NewDatabaseService(conf config.Database) (Service, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getParams(options ...Optional) *params {
|
func (d *dbService) getParams(options ...Optional) *params {
|
||||||
params := ¶ms{
|
params := ¶ms{
|
||||||
ctx: context.Background(),
|
ctx: d.ctx,
|
||||||
}
|
}
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
opt(params)
|
opt(params)
|
||||||
@@ -71,14 +74,14 @@ func getParams(options ...Optional) *params {
|
|||||||
|
|
||||||
// AddUser implements [Service].
|
// AddUser implements [Service].
|
||||||
func (d *dbService) CreateUser(userModel orm.User, opts ...Optional) error {
|
func (d *dbService) CreateUser(userModel orm.User, opts ...Optional) error {
|
||||||
p := getParams(opts...)
|
p := d.getParams(opts...)
|
||||||
|
|
||||||
return gorm.G[orm.User](d.db).Create(p.ctx, &userModel)
|
return gorm.G[orm.User](d.db).Create(p.ctx, &userModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser implements [Service].
|
// GetUser implements [Service].
|
||||||
func (d *dbService) GetUser(userModel orm.User, opts ...Optional) (orm.User, error) {
|
func (d *dbService) GetUser(userModel orm.User, opts ...Optional) (orm.User, error) {
|
||||||
p := getParams(opts...)
|
p := d.getParams(opts...)
|
||||||
|
|
||||||
userResp, err := gorm.G[orm.User](d.db).Where(&userModel).First(p.ctx)
|
userResp, err := gorm.G[orm.User](d.db).Where(&userModel).First(p.ctx)
|
||||||
return userResp, err
|
return userResp, err
|
||||||
|
|||||||
1
internal/service/database/orm/payout.go
Normal file
1
internal/service/database/orm/payout.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package orm
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type YooKassa struct {
|
type YooKassa struct {
|
||||||
BaseUrl string
|
BaseUrl string
|
||||||
|
Timeout time.Duration
|
||||||
|
Test bool
|
||||||
|
|
||||||
ApiBaseKey string
|
ApiBaseKey string
|
||||||
ApiBaseSecret string
|
ApiBaseSecret string
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
|
||||||
"payouts/internal/config"
|
"payouts/internal/config"
|
||||||
|
"payouts/internal/models"
|
||||||
|
"payouts/internal/service/database/orm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Module = fx.Options(
|
var Module = fx.Options(
|
||||||
@@ -24,6 +26,7 @@ func WithContext(ctx context.Context) Optional {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
|
CreatePayout(models.PayoutReq, *orm.User, ...Optional)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Param struct {
|
type Param struct {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: "object"
|
type: "object"
|
||||||
title: "CreatePaymentRequest"
|
title: "CreatePayoutRequest"
|
||||||
properties:
|
properties:
|
||||||
amount:
|
amount:
|
||||||
allOf:
|
allOf:
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package yookassa
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"payouts/internal/models"
|
||||||
|
"payouts/internal/service/database/orm"
|
||||||
"payouts/internal/service/yookassa/config"
|
"payouts/internal/service/yookassa/config"
|
||||||
"payouts/internal/service/yookassa/gen"
|
"payouts/internal/service/yookassa/gen"
|
||||||
|
|
||||||
@@ -11,6 +14,36 @@ import (
|
|||||||
type yookassaService struct {
|
type yookassaService struct {
|
||||||
conf config.YooKassa
|
conf config.YooKassa
|
||||||
payClient *gen.Client
|
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
|
||||||
|
|
||||||
|
// payClient.PaymentsPost()
|
||||||
|
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].
|
// BasicAuth implements [gen.SecuritySource].
|
||||||
@@ -26,17 +59,9 @@ func (y *yookassaService) OAuth2(ctx context.Context, operationName gen.Operatio
|
|||||||
return gen.OAuth2{}, ogenerrors.ErrSkipClientSecurity
|
return gen.OAuth2{}, ogenerrors.ErrSkipClientSecurity
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYookassaService(conf config.YooKassa) (Service, error) {
|
// CreatePayout implements [Service].
|
||||||
|
func (y *yookassaService) CreatePayout(req models.PayoutReq, userSession *orm.User, opts ...Optional) {
|
||||||
|
params := y.getParams(opts...)
|
||||||
|
|
||||||
svc := &yookassaService{
|
y.payClient.PayoutsPost(params.ctx, &gen.PayoutRequest{}, gen.PayoutsPostParams{})
|
||||||
conf: conf,
|
|
||||||
}
|
|
||||||
payClient, err := gen.NewClient(conf.BaseUrl, svc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
svc.payClient = payClient
|
|
||||||
|
|
||||||
// payClient.PaymentsPost()
|
|
||||||
return svc, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user