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/payment_provider"
|
||||
"gitea.pena/PenaSide/treasurer/internal/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -89,6 +90,7 @@ func (r *PaymentController) createPayment(ctx context.Context, paymentType model
|
||||
},
|
||||
Items: utils.ProtoItems2ReceiptItems(in.MainSettings.Items),
|
||||
}),
|
||||
"save_payment_method": strconv.FormatBool(in.MainSettings.Auto),
|
||||
}
|
||||
link, err := provider.CreateInvoice(ctx, request)
|
||||
if err != nil {
|
||||
|
@ -58,6 +58,7 @@ type CreatePayment[T any] struct {
|
||||
UserID string
|
||||
ClientIP string
|
||||
Requisites T
|
||||
Auto bool
|
||||
}
|
||||
|
||||
type BankCard struct {
|
||||
|
@ -1,5 +1,7 @@
|
||||
package yandex
|
||||
|
||||
import "time"
|
||||
|
||||
// Payment description https://yookassa.ru/developers/api#payment_object
|
||||
type Payment struct {
|
||||
ID string `json:"id" bson:"id"`
|
||||
@ -8,7 +10,7 @@ type Payment struct {
|
||||
Confirmation *ConfirmationRedirect `json:"confirmation" bson:"confirmation"`
|
||||
IncomeAmount *Amount `json:"income_amount,omitempty" bson:"income_amount,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"`
|
||||
CapturedAt string `json:"captured_at,omitempty" bson:"captured_at,omitempty"`
|
||||
ExpiresAt string `json:"expires_at,omitempty" bson:"expires_at,omitempty"`
|
||||
@ -42,3 +44,31 @@ const (
|
||||
PaymentStatusSuccessfully PaymentStatus = "succeeded"
|
||||
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"`
|
||||
Deal any `json:"deal,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 {
|
||||
Customer Customer `json:"customer,omitempty"`
|
||||
Items []Item `json:"items,omitempty"`
|
||||
TaxSystemCode int `json:"tax_system_code,omitempty"`
|
||||
Customer Customer `json:"customer,omitempty"`
|
||||
Items []Item `json:"items,omitempty"`
|
||||
TaxSystemCode int `json:"tax_system_code,omitempty"`
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
Amount ReceiptAmount `json:"amount,omitempty"`
|
||||
VatCode int `json:"vat_code,omitempty"`
|
||||
Quantity int64 `json:"quantity,omitempty"`
|
||||
Measure string `json:"measure,omitempty"`
|
||||
PaymentSubject string `json:"payment_subject,omitempty"`
|
||||
PaymentMode string `json:"payment_mode,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Amount ReceiptAmount `json:"amount,omitempty"`
|
||||
VatCode int `json:"vat_code,omitempty"`
|
||||
Quantity int64 `json:"quantity,omitempty"`
|
||||
Measure string `json:"measure,omitempty"`
|
||||
PaymentSubject string `json:"payment_subject,omitempty"`
|
||||
PaymentMode string `json:"payment_mode,omitempty"`
|
||||
}
|
||||
|
||||
type ReceiptAmount struct {
|
||||
Value string `json:"value,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
}
|
||||
|
||||
type Customer struct {
|
||||
FullName string `json:"full_name,omitempty"`
|
||||
INN string `json:"inn,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
INN string `json:"inn,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
}
|
||||
|
@ -29,27 +29,30 @@ type Config struct {
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
logger *zap.Logger
|
||||
config *Config
|
||||
httpClient *resty.Client
|
||||
repository *repository.PaymentRepository
|
||||
callbackService *callback.Service
|
||||
logger *zap.Logger
|
||||
config *Config
|
||||
httpClient *resty.Client
|
||||
repository *repository.PaymentRepository
|
||||
callbackService *callback.Service
|
||||
paymentMethodRepository *repository.PaymentMethodRepository
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Config *Config
|
||||
Repository *repository.PaymentRepository
|
||||
CallbackService *callback.Service
|
||||
Logger *zap.Logger
|
||||
Config *Config
|
||||
Repository *repository.PaymentRepository
|
||||
CallbackService *callback.Service
|
||||
PaymentMethodRepository *repository.PaymentMethodRepository
|
||||
}
|
||||
|
||||
func New(deps Deps) (*Provider, errors.Error) {
|
||||
return &Provider{
|
||||
logger: deps.Logger,
|
||||
config: deps.Config,
|
||||
httpClient: resty.New(),
|
||||
repository: deps.Repository,
|
||||
callbackService: deps.CallbackService,
|
||||
logger: deps.Logger,
|
||||
config: deps.Config,
|
||||
httpClient: resty.New(),
|
||||
repository: deps.Repository,
|
||||
callbackService: deps.CallbackService,
|
||||
paymentMethodRepository: deps.PaymentMethodRepository,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -100,8 +103,9 @@ func (p *Provider) CreateInvoice(ctx context.Context, req map[string]string) (st
|
||||
ReturnURL: request.ReturnURL,
|
||||
Enforce: true,
|
||||
},
|
||||
Capture: true,
|
||||
ClientIP: request.ClientIP,
|
||||
Capture: true,
|
||||
ClientIP: request.ClientIP,
|
||||
SavePaymentMethod: request.Auto,
|
||||
}).
|
||||
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))
|
||||
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:
|
||||
payment, err = p.repository.SetPaymentStatus(ctx.Context(), notification.Object.ID, models.PaymentStatusCanceled)
|
||||
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")
|
||||
}
|
||||
|
||||
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]{
|
||||
Type: models.PaymentType(data["type"]),
|
||||
Currency: data["currency"],
|
||||
@ -38,6 +46,7 @@ func MapToCreatePaymentYandexReceipt(data map[string]string) (*models.CreatePaym
|
||||
UserID: data["user_id"],
|
||||
ClientIP: data["client_ip"],
|
||||
Requisites: requisites,
|
||||
Auto: auto,
|
||||
}
|
||||
|
||||
return payment, nil
|
||||
|
Loading…
Reference in New Issue
Block a user