treasurer/internal/yoomoney/provider.go

145 lines
4.8 KiB
Go
Raw Normal View History

package yoomoney
import (
"context"
"encoding/json"
"fmt"
"gitea.pena/PenaSide/treasurer/internal/errors"
"gitea.pena/PenaSide/treasurer/internal/models"
"gitea.pena/PenaSide/treasurer/internal/models/yandex"
"gitea.pena/PenaSide/treasurer/internal/payment_provider"
"gitea.pena/PenaSide/treasurer/internal/utils"
"github.com/go-resty/resty/v2"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"go.uber.org/zap"
"net/http"
)
const (
ProviderName = "yoomoney"
)
type Config struct {
2025-06-04 10:21:42 +00:00
StoreID string `env:"YOOMONEY_STORE_ID"`
SecretKey string `env:"YOOMONEY_SECRET_KEY"`
WebhooksURL string `env:"YOOMONEY_WEBHOOKS_URL"`
PaymentsURL string `env:"YOOMONEY_PAYMENTS_URL"`
}
type Provider struct {
logger *zap.Logger
config *Config
httpClient *resty.Client
repository PaymentRepository
}
type PaymentRepository interface {
Insert(context.Context, *models.Payment) (*models.Payment, errors.Error)
SetPaymentStatus(ctx context.Context, paymentID string, status models.PaymentStatus) (*models.Payment, errors.Error)
SetPaymentComplete(ctx context.Context, paymentID string) (*models.Payment, errors.Error)
}
func New(logger *zap.Logger, config *Config, repository PaymentRepository) (*Provider, errors.Error) {
return &Provider{
logger: logger,
config: config,
httpClient: resty.New(),
repository: repository,
}, nil
}
func (p *Provider) GetName() string {
return ProviderName
}
func (p *Provider) GetSupportedPaymentMethods() []models.PaymentType {
return []models.PaymentType{
models.PaymentTypeBankCard,
models.PaymentTypeTinkoff,
models.PaymentTypeSberPay,
models.PaymentTypeYoomoney,
models.PaymentTypeMobile,
models.PaymentTypeSBP,
models.PaymentTypeSberB2B,
}
}
func (p *Provider) CreateInvoice(ctx context.Context, request *payment_provider.PaymentRequest) (*payment_provider.PaymentResponse, errors.Error) {
idempotenceKey := uuid.New().String()
yandexPayment, err := p.httpClient.R().
SetContext(ctx).
SetHeader("Content-Type", "application/json").
SetHeader("Idempotence-Key", idempotenceKey).
SetHeader("Authorization", utils.ConvertYoomoneySercetsToAuth("Basic", p.config.StoreID, p.config.SecretKey)).
SetBody(&yandex.CreatePaymentRequest[yandex.PaymentMethodType]{
Amount: yandex.Amount{
Value: utils.ConvertAmountToStringFloat(request.Amount),
Currency: request.Currency,
},
//Receipt: yandex.Receipt{
// TaxSystemCode: 2, //https://yookassa.ru/developers/payment-acceptance/receipts/54fz/other-services/parameters-values#tax-systems
// Customer: request.Requisites.Customer,
// Items: request.Requisites.Items,
//},
PaymentMethodData: &yandex.PaymentMethodType{Type: models.YandexPaymentTypeMap[request.PaymentMethod]},
Confirmation: &yandex.CreateConfirmationRedirect{
Type: yandex.ConfirmationTypeRedirect,
Locale: "ru_RU",
ReturnURL: request.ReturnURL,
Enforce: true,
},
Capture: true,
ClientIP: request.ClientIP,
}).
Post(p.config.PaymentsURL)
if err != nil {
p.logger.Error("failed to create payment", zap.Error(err), zap.String("payment_method", string(request.PaymentMethod)))
return nil, errors.NewWithError(fmt.Errorf("failed to create payment: %w", err), errors.ErrInternalError)
}
if yandexPayment.StatusCode() != http.StatusOK {
p.logger.Error("failed to create payment", zap.Int("status_code", yandexPayment.StatusCode()), zap.String("response", yandexPayment.String()))
return nil, errors.NewWithMessage("failed to create payment", errors.ErrInternalError)
}
var payment yandex.Payment
if err := json.Unmarshal(yandexPayment.Body(), &payment); err != nil {
p.logger.Error("failed to unmarshal payment response", zap.Error(err))
return nil, errors.NewWithError(fmt.Errorf("failed to unmarshal payment response: %w", err), errors.ErrInternalError)
}
dbPayment, err := p.repository.Insert(ctx, &models.Payment{
UserID: request.UserID,
PaymentID: payment.ID,
IdempotencePaymentID: idempotenceKey,
ClientIP: request.ClientIP,
Currency: request.Currency,
Amount: request.Amount,
Type: request.PaymentMethod,
Status: models.PaymentStatusMap[string(payment.Status)],
Completed: false,
RawPaymentBody: payment,
CallbackHostGRPC: request.CallbackHostGRPC,
})
if err != nil {
p.logger.Error("failed to save payment to database", zap.Error(err))
return nil, errors.NewWithError(fmt.Errorf("failed to save payment to database: %w", err), errors.ErrInternalError)
}
return &payment_provider.PaymentResponse{
RedirectURL: payment.Confirmation.ConfirmationURL,
Payment: dbPayment,
}, nil
}
func (p *Provider) RegisterWebhookHandlers(router fiber.Router) {
router.Post(p.config.WebhooksURL, p.handleWebhook)
}
func (p *Provider) handleWebhook(c *fiber.Ctx) error {
return nil
}