added support auto paid
This commit is contained in:
parent
05089cab41
commit
e30209c739
@ -6,6 +6,7 @@ import (
|
|||||||
"gitea.pena/PenaSide/treasurer/internal/models/yandex"
|
"gitea.pena/PenaSide/treasurer/internal/models/yandex"
|
||||||
"gitea.pena/PenaSide/treasurer/internal/payment_provider"
|
"gitea.pena/PenaSide/treasurer/internal/payment_provider"
|
||||||
"gitea.pena/PenaSide/treasurer/internal/utils"
|
"gitea.pena/PenaSide/treasurer/internal/utils"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -89,6 +90,7 @@ func (r *PaymentController) createPayment(ctx context.Context, paymentType model
|
|||||||
},
|
},
|
||||||
Items: utils.ProtoItems2ReceiptItems(in.MainSettings.Items),
|
Items: utils.ProtoItems2ReceiptItems(in.MainSettings.Items),
|
||||||
}),
|
}),
|
||||||
|
"save_payment_method": strconv.FormatBool(in.MainSettings.Auto),
|
||||||
}
|
}
|
||||||
link, err := provider.CreateInvoice(ctx, request)
|
link, err := provider.CreateInvoice(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,6 +58,7 @@ type CreatePayment[T any] struct {
|
|||||||
UserID string
|
UserID string
|
||||||
ClientIP string
|
ClientIP string
|
||||||
Requisites T
|
Requisites T
|
||||||
|
Auto bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type BankCard struct {
|
type BankCard struct {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package yandex
|
package yandex
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// Payment description https://yookassa.ru/developers/api#payment_object
|
// Payment description https://yookassa.ru/developers/api#payment_object
|
||||||
type Payment struct {
|
type Payment struct {
|
||||||
ID string `json:"id" bson:"id"`
|
ID string `json:"id" bson:"id"`
|
||||||
@ -8,7 +10,7 @@ type Payment struct {
|
|||||||
Confirmation *ConfirmationRedirect `json:"confirmation" bson:"confirmation"`
|
Confirmation *ConfirmationRedirect `json:"confirmation" bson:"confirmation"`
|
||||||
IncomeAmount *Amount `json:"income_amount,omitempty" bson:"income_amount,omitempty"`
|
IncomeAmount *Amount `json:"income_amount,omitempty" bson:"income_amount,omitempty"`
|
||||||
Description string `json:"description,omitempty" bson:"description,omitempty"`
|
Description string `json:"description,omitempty" bson:"description,omitempty"`
|
||||||
PaymentMethod any `json:"payment_method,omitempty" bson:"payment_method,omitempty"`
|
PaymentMethod *PaymentMethod `json:"payment_method,omitempty" bson:"payment_method,omitempty"`
|
||||||
Recipient Recipient `json:"recipient" bson:"recipient"`
|
Recipient Recipient `json:"recipient" bson:"recipient"`
|
||||||
CapturedAt string `json:"captured_at,omitempty" bson:"captured_at,omitempty"`
|
CapturedAt string `json:"captured_at,omitempty" bson:"captured_at,omitempty"`
|
||||||
ExpiresAt string `json:"expires_at,omitempty" bson:"expires_at,omitempty"`
|
ExpiresAt string `json:"expires_at,omitempty" bson:"expires_at,omitempty"`
|
||||||
@ -42,3 +44,31 @@ const (
|
|||||||
PaymentStatusSuccessfully PaymentStatus = "succeeded"
|
PaymentStatusSuccessfully PaymentStatus = "succeeded"
|
||||||
PaymentStatusCanceled PaymentStatus = "canceled"
|
PaymentStatusCanceled PaymentStatus = "canceled"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PaymentMethod struct {
|
||||||
|
ID string `json:"_id" bson:"_id,omitempty"`
|
||||||
|
UserID string `json:"userId" bson:"userId"`
|
||||||
|
Type string `json:"type" bson:"type"`
|
||||||
|
MethodID string `json:"id" bson:"id"`
|
||||||
|
Saved bool `json:"saved" bson:"saved"`
|
||||||
|
Card *Card `json:"card,omitempty" bson:"card,omitempty"`
|
||||||
|
Title string `json:"title" bson:"title"`
|
||||||
|
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card struct {
|
||||||
|
First6 string `json:"first6" bson:"first6"`
|
||||||
|
Last4 string `json:"last4" bson:"last4"`
|
||||||
|
ExpiryMonth string `json:"expiry_month" bson:"expiry_month"`
|
||||||
|
ExpiryYear string `json:"expiry_year" bson:"expiry_year"`
|
||||||
|
CardType string `json:"card_type" bson:"card_type"`
|
||||||
|
CardProduct *CardProduct `json:"card_product,omitempty" bson:"card_product,omitempty"`
|
||||||
|
IssuerCountry string `json:"issuer_country" bson:"issuer_country"`
|
||||||
|
IssuerName string `json:"issuer_name" bson:"issuer_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CardProduct struct {
|
||||||
|
Code string `json:"code" bson:"code"`
|
||||||
|
Name string `json:"name" bson:"name"`
|
||||||
|
}
|
||||||
|
@ -10,33 +10,34 @@ type CreatePaymentRequest[T any] struct {
|
|||||||
ClientIP string `json:"client_ip,omitempty"`
|
ClientIP string `json:"client_ip,omitempty"`
|
||||||
Deal any `json:"deal,omitempty"`
|
Deal any `json:"deal,omitempty"`
|
||||||
MerchantCustomerID any `json:"merchant_customer_id,omitempty"`
|
MerchantCustomerID any `json:"merchant_customer_id,omitempty"`
|
||||||
Receipt Receipt `json:"receipt,omitempty"`
|
Receipt Receipt `json:"receipt,omitempty"`
|
||||||
|
SavePaymentMethod bool `json:"save_payment_method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
Customer Customer `json:"customer,omitempty"`
|
Customer Customer `json:"customer,omitempty"`
|
||||||
Items []Item `json:"items,omitempty"`
|
Items []Item `json:"items,omitempty"`
|
||||||
TaxSystemCode int `json:"tax_system_code,omitempty"`
|
TaxSystemCode int `json:"tax_system_code,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Amount ReceiptAmount `json:"amount,omitempty"`
|
Amount ReceiptAmount `json:"amount,omitempty"`
|
||||||
VatCode int `json:"vat_code,omitempty"`
|
VatCode int `json:"vat_code,omitempty"`
|
||||||
Quantity int64 `json:"quantity,omitempty"`
|
Quantity int64 `json:"quantity,omitempty"`
|
||||||
Measure string `json:"measure,omitempty"`
|
Measure string `json:"measure,omitempty"`
|
||||||
PaymentSubject string `json:"payment_subject,omitempty"`
|
PaymentSubject string `json:"payment_subject,omitempty"`
|
||||||
PaymentMode string `json:"payment_mode,omitempty"`
|
PaymentMode string `json:"payment_mode,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReceiptAmount struct {
|
type ReceiptAmount struct {
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
Currency string `json:"currency,omitempty"`
|
Currency string `json:"currency,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Customer struct {
|
type Customer struct {
|
||||||
FullName string `json:"full_name,omitempty"`
|
FullName string `json:"full_name,omitempty"`
|
||||||
INN string `json:"inn,omitempty"`
|
INN string `json:"inn,omitempty"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Phone string `json:"phone,omitempty"`
|
Phone string `json:"phone,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -29,27 +29,30 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
config *Config
|
config *Config
|
||||||
httpClient *resty.Client
|
httpClient *resty.Client
|
||||||
repository *repository.PaymentRepository
|
repository *repository.PaymentRepository
|
||||||
callbackService *callback.Service
|
callbackService *callback.Service
|
||||||
|
paymentMethodRepository *repository.PaymentMethodRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deps struct {
|
type Deps struct {
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
Config *Config
|
Config *Config
|
||||||
Repository *repository.PaymentRepository
|
Repository *repository.PaymentRepository
|
||||||
CallbackService *callback.Service
|
CallbackService *callback.Service
|
||||||
|
PaymentMethodRepository *repository.PaymentMethodRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(deps Deps) (*Provider, errors.Error) {
|
func New(deps Deps) (*Provider, errors.Error) {
|
||||||
return &Provider{
|
return &Provider{
|
||||||
logger: deps.Logger,
|
logger: deps.Logger,
|
||||||
config: deps.Config,
|
config: deps.Config,
|
||||||
httpClient: resty.New(),
|
httpClient: resty.New(),
|
||||||
repository: deps.Repository,
|
repository: deps.Repository,
|
||||||
callbackService: deps.CallbackService,
|
callbackService: deps.CallbackService,
|
||||||
|
paymentMethodRepository: deps.PaymentMethodRepository,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +103,9 @@ func (p *Provider) CreateInvoice(ctx context.Context, req map[string]string) (st
|
|||||||
ReturnURL: request.ReturnURL,
|
ReturnURL: request.ReturnURL,
|
||||||
Enforce: true,
|
Enforce: true,
|
||||||
},
|
},
|
||||||
Capture: true,
|
Capture: true,
|
||||||
ClientIP: request.ClientIP,
|
ClientIP: request.ClientIP,
|
||||||
|
SavePaymentMethod: request.Auto,
|
||||||
}).
|
}).
|
||||||
Post(p.config.PaymentsURL)
|
Post(p.config.PaymentsURL)
|
||||||
|
|
||||||
@ -163,6 +167,42 @@ func (p *Provider) handleWebhook(ctx *fiber.Ctx) error {
|
|||||||
p.logger.Error("failed to set payment complete", zap.Error(err))
|
p.logger.Error("failed to set payment complete", zap.Error(err))
|
||||||
return errors.HTTP(ctx, err)
|
return errors.HTTP(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if notification.Object.PaymentMethod != nil && notification.Object.PaymentMethod.Saved {
|
||||||
|
method := &yandex.PaymentMethod{
|
||||||
|
UserID: payment.UserID,
|
||||||
|
MethodID: notification.Object.PaymentMethod.ID,
|
||||||
|
Type: notification.Object.PaymentMethod.Type,
|
||||||
|
Title: notification.Object.PaymentMethod.Title,
|
||||||
|
Saved: notification.Object.PaymentMethod.Saved,
|
||||||
|
}
|
||||||
|
|
||||||
|
if notification.Object.PaymentMethod.Card != nil {
|
||||||
|
method.Card = &yandex.Card{
|
||||||
|
First6: notification.Object.PaymentMethod.Card.First6,
|
||||||
|
Last4: notification.Object.PaymentMethod.Card.Last4,
|
||||||
|
ExpiryMonth: notification.Object.PaymentMethod.Card.ExpiryMonth,
|
||||||
|
ExpiryYear: notification.Object.PaymentMethod.Card.ExpiryYear,
|
||||||
|
CardType: notification.Object.PaymentMethod.Card.CardType,
|
||||||
|
IssuerName: notification.Object.PaymentMethod.Card.IssuerName,
|
||||||
|
IssuerCountry: notification.Object.PaymentMethod.Card.IssuerCountry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if notification.Object.PaymentMethod.Card.CardProduct != nil {
|
||||||
|
method.Card.CardProduct = &yandex.CardProduct{
|
||||||
|
Code: notification.Object.PaymentMethod.Card.CardProduct.Code,
|
||||||
|
Name: notification.Object.PaymentMethod.Card.CardProduct.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.paymentMethodRepository.Save(ctx.Context(), method); err != nil {
|
||||||
|
p.logger.Error("failed to save payment method", zap.Error(err),
|
||||||
|
zap.String("userId", payment.UserID), zap.String("methodId", method.MethodID))
|
||||||
|
// todo стоит ли возвращать ошибку?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case yandex.WebhookEventPaymentCanceled:
|
case yandex.WebhookEventPaymentCanceled:
|
||||||
payment, err = p.repository.SetPaymentStatus(ctx.Context(), notification.Object.ID, models.PaymentStatusCanceled)
|
payment, err = p.repository.SetPaymentStatus(ctx.Context(), notification.Object.ID, models.PaymentStatusCanceled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
61
internal/repository/payment_method.go
Normal file
61
internal/repository/payment_method.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gitea.pena/PenaSide/treasurer/internal/models/yandex"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PaymentMethodRepositoryDeps struct {
|
||||||
|
Logger *zap.Logger
|
||||||
|
Collection *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaymentMethodRepository struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
collection *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPaymentMethodRepository(deps PaymentMethodRepositoryDeps) *PaymentMethodRepository {
|
||||||
|
return &PaymentMethodRepository{
|
||||||
|
logger: deps.Logger,
|
||||||
|
collection: deps.Collection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PaymentMethodRepository) Save(ctx context.Context, method *yandex.PaymentMethod) error {
|
||||||
|
_, err := r.collection.InsertOne(ctx, method)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("failed to save payment method", zap.Error(err), zap.String("userId", method.UserID))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PaymentMethodRepository) GetByUserID(ctx context.Context, userID string) ([]*yandex.PaymentMethod, error) {
|
||||||
|
cursor, err := r.collection.Find(ctx, bson.M{"userId": userID})
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("failed to find payment methods", zap.Error(err), zap.String("userId", userID))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cursor.Close(ctx)
|
||||||
|
|
||||||
|
var methods []*yandex.PaymentMethod
|
||||||
|
if err := cursor.All(ctx, &methods); err != nil {
|
||||||
|
r.logger.Error("failed to decode payment methods", zap.Error(err), zap.String("userId", userID))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PaymentMethodRepository) DeleteByUserID(ctx context.Context, userID string) error {
|
||||||
|
_, err := r.collection.DeleteMany(ctx, bson.M{"userId": userID})
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("failed to delete payment methods", zap.Error(err), zap.String("userId", userID))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -29,6 +29,14 @@ func MapToCreatePaymentYandexReceipt(data map[string]string) (*models.CreatePaym
|
|||||||
return nil, fmt.Errorf("missing requisites field")
|
return nil, fmt.Errorf("missing requisites field")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto := false
|
||||||
|
if val, ok := data["save_payment_method"]; ok && val != "" {
|
||||||
|
auto, err = strconv.ParseBool(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid save_payment_method field: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
payment := &models.CreatePayment[yandex.Receipt]{
|
payment := &models.CreatePayment[yandex.Receipt]{
|
||||||
Type: models.PaymentType(data["type"]),
|
Type: models.PaymentType(data["type"]),
|
||||||
Currency: data["currency"],
|
Currency: data["currency"],
|
||||||
@ -38,6 +46,7 @@ func MapToCreatePaymentYandexReceipt(data map[string]string) (*models.CreatePaym
|
|||||||
UserID: data["user_id"],
|
UserID: data["user_id"],
|
||||||
ClientIP: data["client_ip"],
|
ClientIP: data["client_ip"],
|
||||||
Requisites: requisites,
|
Requisites: requisites,
|
||||||
|
Auto: auto,
|
||||||
}
|
}
|
||||||
|
|
||||||
return payment, nil
|
return payment, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user