added logic recurrent payment

This commit is contained in:
pasha1coil 2025-06-15 10:48:37 +03:00
parent a1f734a87d
commit 8f7f0db142
5 changed files with 96 additions and 0 deletions

@ -97,6 +97,7 @@ func (r *PaymentController) createPayment(ctx context.Context, paymentType model
Items: utils.ProtoItems2ReceiptItems(in.MainSettings.Items),
}),
"save_payment_method": strconv.FormatBool(in.MainSettings.Auto),
"recurrent": strconv.FormatBool(in.MainSettings.Recurrent),
}
link, err := provider.CreateInvoice(ctx, request)
if err != nil {

@ -59,6 +59,7 @@ type CreatePayment[T any] struct {
ClientIP string
Requisites T
Auto bool
Recurrent bool
}
type BankCard struct {

@ -41,3 +41,10 @@ type Customer struct {
Email string `json:"email,omitempty"`
Phone string `json:"phone,omitempty"`
}
type CreateRecurrentPayment struct {
Amount Amount `json:"amount"`
Capture bool `json:"capture"`
PaymentMethodID string `json:"payment_method_id"`
Description string `json:"description,omitempty"`
}

@ -79,6 +79,11 @@ func (p *Provider) CreateInvoice(ctx context.Context, req map[string]string) (st
p.logger.Error("failed to create payment yandex receipt by parse map", zap.Error(err))
return "", errors.NewWithMessage("failed to parse input request by parse map", errors.ErrInvalidArgs)
}
if request.Recurrent {
return p.CreateRecurrentPayment(ctx, request)
}
idempotenceKey := uuid.New().String()
yandexPayment, err := p.httpClient.R().
@ -250,3 +255,77 @@ func (p *Provider) handleWebhook(ctx *fiber.Ctx) error {
return ctx.SendStatus(http.StatusOK)
}
func (p *Provider) CreateRecurrentPayment(ctx context.Context, request *models.CreatePayment[yandex.Receipt]) (string, errors.Error) {
methods, err := p.paymentMethodRepository.GetByUserID(ctx, request.UserID)
if err != nil {
p.logger.Error("failed to get payment methods", zap.Error(err), zap.String("userId", request.UserID))
return "", errors.NewWithError(err, errors.ErrInternalError)
}
if len(methods) == 0 {
p.logger.Warn("no saved payment methods found", zap.String("userId", request.UserID))
return "", errors.NewWithMessage("no saved payment methods found", errors.ErrInvalidArgs)
}
for _, method := range methods {
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.CreateRecurrentPayment{
Amount: yandex.Amount{
Value: utils.ConvertAmountToStringFloat(request.Amount),
Currency: request.Currency,
},
PaymentMethodID: method.MethodID,
Capture: true,
}).
Post(p.config.PaymentsURL)
if err != nil {
p.logger.Error("failed to create recurrent payment", zap.Error(err), zap.String("userId", request.UserID))
return "", errors.NewWithError(fmt.Errorf("failed to create recurrent payment"), errors.ErrInternalError)
}
if yandexPayment.StatusCode() != http.StatusOK {
p.logger.Error("unexpected status code from yandex", zap.Int("statusCode", yandexPayment.StatusCode()), zap.String("userId", request.UserID))
return "", errors.NewWithError(fmt.Errorf("unexpected status code: %d", yandexPayment.StatusCode()), 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), zap.String("userId", request.UserID), zap.String("methodId", method.MethodID))
return "", errors.NewWithError(err, errors.ErrInternalError)
}
if payment.Status != yandex.PaymentStatusSuccessfully {
p.logger.Error("payment not succeeded", zap.String("userId", request.UserID), zap.String("status", string(payment.Status)))
continue
}
_, 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.Type,
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 "", errors.NewWithError(fmt.Errorf("failed to save payment to database: %w", err), errors.ErrInternalError)
}
return payment.ID, nil
}
return "", errors.NewWithMessage("failed to create recurrent payment with any saved method", errors.ErrInternalError)
}

@ -36,6 +36,13 @@ func MapToCreatePaymentYandexReceipt(data map[string]string) (*models.CreatePaym
return nil, fmt.Errorf("invalid save_payment_method field: %w", err)
}
}
recurrent := false
if val, ok := data["recurrent"]; ok && val != "" {
recurrent, err = strconv.ParseBool(val)
if err != nil {
return nil, fmt.Errorf("invalid recurrent field: %w", err)
}
}
payment := &models.CreatePayment[yandex.Receipt]{
Type: models.PaymentType(data["type"]),
@ -47,6 +54,7 @@ func MapToCreatePaymentYandexReceipt(data map[string]string) (*models.CreatePaym
ClientIP: data["client_ip"],
Requisites: requisites,
Auto: auto,
Recurrent: recurrent,
}
return payment, nil