package wallet import ( "context" "fmt" "github.com/gofiber/fiber/v2" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/treasurer" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" "strings" ) type Deps struct { MiddleWare *http.MiddleWare AuthClient *client.AuthClient PaymentClient *client.PaymentClient GRPC *models.ConfigurationGRPC AccountRepo *repository.AccountRepository CurrencyClient *client.CurrencyClient VerifyClient *client.VerificationClient MailClient *client.MailClient Logger *zap.Logger } type WalletController struct { middleWare *http.MiddleWare authClient *client.AuthClient paymentClient *client.PaymentClient grpc *models.ConfigurationGRPC accountRepo *repository.AccountRepository currencyClient *client.CurrencyClient verifyClient *client.VerificationClient mailClient *client.MailClient logger *zap.Logger } func NewWalletController(deps Deps) *WalletController { return &WalletController{ middleWare: deps.MiddleWare, authClient: deps.AuthClient, paymentClient: deps.PaymentClient, grpc: deps.GRPC, accountRepo: deps.AccountRepo, currencyClient: deps.CurrencyClient, verifyClient: deps.VerifyClient, mailClient: deps.MailClient, logger: deps.Logger, } } func (receiver *WalletController) RequestMoney(ctx *fiber.Ctx) error { userID, ok := receiver.middleWare.ExtractUserID(ctx) if !ok || userID == "" { return receiver.middleWare.NoAuth(ctx) } hlogger := receiver.middleWare.ExtractLogger(ctx) var request models.GetPaymentLinkBody if err := ctx.BodyParser(&request); err != nil { return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind payment link") } if err := utils.ValidateGetPaymentLinkBody(&request); err != nil { return receiver.middleWare.ErrorOld(ctx, err) } link, err := receiver.GetPaymentLink(ctx.Context(), &models.GetPaymentLinkRequest{ Body: &request, UserID: userID, ClientIP: ctx.IP(), }) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } hlogger.Emit(models.InfoRequestMoney{ CtxUserIP: ctx.IP(), CtxUserPort: ctx.Port(), KeyDomain: strings.Join(ctx.Subdomains(), "/"), KeyPath: ctx.Path(), CtxUserID: userID, // todo CtxAccountID: "", KeyPaymentType: string(request.Type), KeyCurrency: request.Currency, CtxPrice: int64(request.Amount), CtxReturnURL: link, }) return ctx.Status(fiber.StatusOK).JSON(&models.GetPaymentLinkResponse{Link: link}) } func (receiver *WalletController) GetPaymentLink(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { if _, userErr := receiver.authClient.GetUser(ctx, request.UserID); userErr != nil { receiver.logger.Error("failed to get user on on ", zap.Error(userErr), zap.String("userID", request.UserID), ) return "", userErr } switch request.Body.Type { case models.PaymentTypeBankCard: return receiver.GetPaymentLinkBankCard(ctx, request) case models.PaymentTypeYoomoney: return receiver.GetPaymentLinkYooMoney(ctx, request) case models.PaymentTypeSberPay: return receiver.GetPaymentLinkSberPay(ctx, request) case models.PaymentTypeTinkoff: return receiver.GetPaymentLinkTinkoff(ctx, request) case models.PaymentTypeSBP: return receiver.GetPaymentLinkSBP(ctx, request) case models.PaymentTypeSberB2B: return receiver.GetPaymentLinkB2B(ctx, request) } return "", errors.NewWithMessage("invalid payment method type", errors.ErrInvalidArgs) } func (receiver *WalletController) GetPaymentLinkBankCard(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkBankCard(ctx, &treasurer.GetBankCardPaymentLinkRequest{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get bankcard payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) GetPaymentLinkYooMoney(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkYooMoney(ctx, &treasurer.GetPaymentLinkBody{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get yoomoney payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) GetPaymentLinkSberPay(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkSberPay(ctx, &treasurer.GetPaymentLinkBody{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get sberpay payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) GetPaymentLinkTinkoff(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkTinkoff(ctx, &treasurer.GetPaymentLinkBody{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get tinkoff payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) GetPaymentLinkSBP(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkSBP(ctx, &treasurer.GetPaymentLinkBody{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get sbp payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) GetPaymentLinkB2B(ctx context.Context, request *models.GetPaymentLinkRequest) (string, errors.Error) { link, err := receiver.paymentClient.GetPaymentLinkSberbankB2B(ctx, &treasurer.GetPaymentLinkBody{ MainSettings: &treasurer.MainPaymentSettings{ Currency: request.Body.Currency, Amount: request.Body.Amount, UserID: request.UserID, ClientIP: request.ClientIP, CallbackHostGRPC: []string{receiver.grpc.Domen}, ReturnURL: request.Body.ReturnURL, }, }) if err != nil { receiver.logger.Error("failed to get sberbankb2b payment link on of ", zap.Error(err)) return "", err } return link, nil } func (receiver *WalletController) ChangeCurrency(ctx *fiber.Ctx) error { userID, ok := receiver.middleWare.ExtractUserID(ctx) if !ok || userID == "" { return receiver.middleWare.NoAuth(ctx) } var request models.ChangeCurrency if err := ctx.BodyParser(&request); err != nil { return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind currency") } if validate.IsStringEmpty(request.Currency) { return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "empty currency") } currency := request.Currency account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } cash, err := receiver.currencyClient.Translate(ctx.Context(), &models.TranslateCurrency{ Money: account.Wallet.Cash, From: account.Wallet.Currency, To: currency, }) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } updatedAccount, err := receiver.accountRepo.ChangeWallet(ctx.Context(), account.UserID, &models.Wallet{ Cash: cash, Currency: currency, Money: account.Wallet.Money, }) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } return ctx.Status(fiber.StatusOK).JSON(updatedAccount) } func (receiver *WalletController) PostWalletRspay(ctx *fiber.Ctx) error { userID, ok := receiver.middleWare.ExtractUserID(ctx) if !ok || userID == "" { return receiver.middleWare.NoAuth(ctx) } hlogger := receiver.middleWare.ExtractLogger(ctx) var req struct { Money *float32 `json:"money,omitempty"` } if err := ctx.BodyParser(&req); err != nil { receiver.logger.Error("failed to bind request", zap.Error(err)) return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request") } user, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } if user.Status != models.AccountStatusNko && user.Status != models.AccountStatusOrg { return receiver.middleWare.Error(ctx, fiber.StatusForbidden, "not allowed for non organizations") } token := ctx.Get("Authorization") fmt.Println("HEADERS", ctx.Request().Header) verification, err := receiver.verifyClient.GetVerification(ctx.Context(), token, userID) if err == errors.ErrNotFound { return receiver.middleWare.Error(ctx, fiber.StatusForbidden, "no verification data found") } if (user.Status == models.AccountStatusOrg && len(verification.Files) != 3) || (user.Status == models.AccountStatusNko && len(verification.Files) != 4) { return receiver.middleWare.Error(ctx, fiber.StatusForbidden, "not enough verification files") } authData, err := receiver.authClient.GetUser(ctx.Context(), userID) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } err = receiver.mailClient.SendMessage(authData.Login, verification, *req.Money) if err != nil { return receiver.middleWare.ErrorOld(ctx, err) } hlogger.Emit(models.InfoRSPay{ CtxUserIP: ctx.IP(), CtxUserPort: ctx.Port(), KeyDomain: strings.Join(ctx.Subdomains(), "/"), KeyPath: ctx.Path(), CtxUserID: userID, CtxAccountID: user.ID, CtxPrice: int64(*req.Money), CtxLogin: authData.Login, }) return ctx.SendStatus(fiber.StatusOK) }