147 lines
3.3 KiB
Go
147 lines
3.3 KiB
Go
package yookassa
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
resty "github.com/go-resty/resty/v2"
|
|
|
|
"payouts/internal/models"
|
|
"payouts/internal/service/database/orm"
|
|
"payouts/internal/service/yookassa/config"
|
|
)
|
|
|
|
const IdempotenceHeader = "Idempotence-Key"
|
|
|
|
type yookassaService struct {
|
|
conf config.YooKassa
|
|
ctx context.Context
|
|
|
|
client *resty.Client
|
|
}
|
|
|
|
func NewYookassaService(conf config.YooKassa) (Service, error) {
|
|
client := resty.New()
|
|
client.SetBaseURL(conf.BaseUrl)
|
|
client.SetBasicAuth(conf.ApiPaymentKey, conf.ApiPaymentSecret)
|
|
client.SetTimeout(conf.Timeout)
|
|
|
|
if conf.Retry.Enabled {
|
|
client.
|
|
SetRetryCount(conf.Retry.Count).
|
|
SetRetryWaitTime(conf.Retry.WaitTime).
|
|
SetRetryMaxWaitTime(conf.Retry.MaxWaitTime).
|
|
AddRetryCondition(
|
|
func(r *resty.Response, err error) bool {
|
|
// Retry on network errors
|
|
if err != nil {
|
|
return true
|
|
}
|
|
// Retry on specific status codes
|
|
return r.StatusCode() == 429 || // Too Many Requests
|
|
r.StatusCode() >= 500 // Server errors
|
|
},
|
|
)
|
|
}
|
|
|
|
svc := &yookassaService{
|
|
conf: conf,
|
|
client: client,
|
|
ctx: context.Background(),
|
|
}
|
|
return svc, nil
|
|
}
|
|
|
|
func (y *yookassaService) getParams(options ...Optional) *params {
|
|
params := ¶ms{
|
|
ctx: y.ctx,
|
|
}
|
|
for _, opt := range options {
|
|
opt(params)
|
|
}
|
|
return params
|
|
}
|
|
|
|
// GetSbpBanks implements [Service].
|
|
func (y *yookassaService) GetSbpBanks(opts ...Optional) ([]models.SBPBank, error) {
|
|
params := y.getParams(opts...)
|
|
|
|
yResp := &SBPBankResponse{}
|
|
yError := &Error{}
|
|
|
|
restyResp, err := y.client.R().
|
|
SetContext(params.ctx).
|
|
SetResult(yResp).
|
|
SetError(yError).
|
|
Get("/sbp_banks")
|
|
|
|
slog.Debug(fmt.Sprintf("Got response from yookassa: %v", restyResp))
|
|
|
|
if err != nil {
|
|
return nil, errors.Join(errors.New("failed to perform yookassa api post"), err)
|
|
}
|
|
|
|
if restyResp.StatusCode() != http.StatusOK {
|
|
yError.Status = restyResp.StatusCode()
|
|
return nil, yError
|
|
}
|
|
|
|
return yResp.Items, nil
|
|
}
|
|
|
|
// CreatePayout implements [Service].
|
|
func (y *yookassaService) CreatePayout(req models.PayoutReq, userSession *orm.User, idempotenceKey string, opts ...Optional) (*models.PayoutResp, error) {
|
|
params := y.getParams(opts...)
|
|
|
|
yReq := &PayoutRequest{
|
|
Amount: Amount{
|
|
Value: fmt.Sprintf("%.2f", req.Amount),
|
|
Currency: "RUB",
|
|
},
|
|
PayoutDestinationData: PayoutDestination{
|
|
Type: req.PayoutType,
|
|
},
|
|
}
|
|
yResp := &PayoutResponse{}
|
|
yError := &Error{}
|
|
|
|
switch req.PayoutType {
|
|
case models.TypeSBP:
|
|
yReq.PayoutDestinationData.Phone = userSession.Phone
|
|
yReq.PayoutDestinationData.BankID = req.BankID
|
|
case models.TypeYooMoney:
|
|
yReq.PayoutDestinationData.AccountNumber = req.AccountNumber
|
|
default:
|
|
return nil, errors.New("unsupported payout type")
|
|
}
|
|
|
|
restyResp, err := y.client.R().
|
|
SetContext(params.ctx).
|
|
SetHeader(IdempotenceHeader, idempotenceKey).
|
|
SetBody(yReq).
|
|
SetResult(yResp).
|
|
SetError(yError).
|
|
Post("/payouts")
|
|
|
|
slog.Debug(fmt.Sprintf("Got response from yookassa: %v", restyResp))
|
|
|
|
if err != nil {
|
|
return nil, errors.Join(errors.New("failed to perform yookassa api post"), err)
|
|
}
|
|
|
|
if restyResp.StatusCode() != http.StatusOK {
|
|
yError.Status = restyResp.StatusCode()
|
|
return nil, yError
|
|
}
|
|
|
|
return &models.PayoutResp{ID: yResp.ID, Status: yResp.Status}, nil
|
|
}
|
|
|
|
// GetConfig implements [Service].
|
|
func (y *yookassaService) GetConfig() config.YooKassa {
|
|
return y.conf
|
|
}
|