package repository import ( "context" "fmt" "time" "gitea.pena/PenaSide/treasurer/internal/errors" "gitea.pena/PenaSide/treasurer/internal/models" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" ) var PaymentFields = struct { ID string UserID string PaymentID string IdempotencePaymentID string ClientIP string Currency string Amount string Type string Status string Completed string IsDeleted string CreatedAt string UpdatedAt string DeletedAt string RawPaymentBody string CallbackHostGRPC string WalletAddress string // for crypto CryptoAmount string // for crypto ToWalletAddress string FromWalletAddress string }{ ID: "_id", UserID: "userId", PaymentID: "paymentId", IdempotencePaymentID: "idempotencePaymentId", ClientIP: "clientIp", Currency: "currency", Amount: "amount", Type: "type", Status: "status", Completed: "completed", IsDeleted: "isDeleted", CreatedAt: "createdAt", UpdatedAt: "updatedAt", DeletedAt: "deletedAt", RawPaymentBody: "rawPaymentBody", CallbackHostGRPC: "callbackHostGrpc", WalletAddress: "walletAddress", CryptoAmount: "cryptoAmount", ToWalletAddress: "toWalletAddress", FromWalletAddress: "fromWalletAddress", } type PaymentRepositoryDeps struct { Logger *zap.Logger Collection *mongo.Collection } type PaymentRepository struct { logger *zap.Logger collection *mongo.Collection } func NewPaymentRepository(deps PaymentRepositoryDeps) (*PaymentRepository, errors.Error) { if deps.Logger == nil { return nil, errors.NewWithMessage("Logger in nil on ", errors.ErrInvalidArgs) } if deps.Collection == nil { return nil, errors.NewWithMessage("Collection in nil on ", errors.ErrInvalidArgs) } return &PaymentRepository{ logger: deps.Logger, collection: deps.Collection, }, nil } func (r *PaymentRepository) SetPaymentComplete(ctx context.Context, paymentID string) (*models.Payment, errors.Error) { payment := models.Payment{} options := options.FindOneAndUpdate().SetReturnDocument(options.After) filter := bson.M{PaymentFields.PaymentID: paymentID} update := bson.M{"$set": bson.M{ PaymentFields.Completed: true, PaymentFields.UpdatedAt: time.Now(), }} if err := r.collection.FindOneAndUpdate(ctx, filter, update, options).Decode(&payment); err != nil { r.logger.Error("failed to set payment complete on of ", zap.Error(err), zap.String("paymentID", paymentID), ) removeErr := errors.NewWithError( fmt.Errorf("failed to set payment complete with <%s> on of : %w", paymentID, err), errors.ErrInternalError, ) if err == mongo.ErrNoDocuments { return nil, removeErr.SetType(errors.ErrNotFound) } return nil, removeErr } return &payment, nil } func (r *PaymentRepository) Insert(ctx context.Context, payment *models.Payment) (*models.Payment, errors.Error) { sanitizedPayment := payment.Sanitize() result, err := r.collection.InsertOne(ctx, sanitizedPayment) if err != nil { r.logger.Error("failed to insert payment on of ", zap.Any("payment", sanitizedPayment), zap.Error(err), ) return nil, errors.NewWithError( fmt.Errorf("failed to insert payment on of : %w", err), errors.ErrInternalError, ) } insertedID := result.InsertedID.(primitive.ObjectID).Hex() sanitizedPayment.ID = insertedID return sanitizedPayment, nil } func (r *PaymentRepository) SetPaymentStatus(ctx context.Context, paymentID string, status models.PaymentStatus) (*models.Payment, errors.Error) { payment := models.Payment{} options := options.FindOneAndUpdate().SetReturnDocument(options.After) filter := bson.M{PaymentFields.PaymentID: paymentID} update := bson.M{"$set": bson.M{ PaymentFields.Status: status, PaymentFields.UpdatedAt: time.Now(), }} if err := r.collection.FindOneAndUpdate(ctx, filter, update, options).Decode(&payment); err != nil { r.logger.Error("failed to set payment status on of ", zap.Error(err), zap.String("paymentID", paymentID), ) removeErr := errors.NewWithError( fmt.Errorf("failed to set payment status with <%s> on of : %w", paymentID, err), errors.ErrInternalError, ) if err == mongo.ErrNoDocuments { return nil, removeErr.SetType(errors.ErrNotFound) } return nil, removeErr } return &payment, nil } func (r *PaymentRepository) FindByWalletsAndAmount(ctx context.Context, toWalletAddress, fromWalletAddress string, cryptoAmount float64) (*models.Payment, errors.Error) { payment := models.Payment{} filter := bson.M{ PaymentFields.ToWalletAddress: toWalletAddress, PaymentFields.FromWalletAddress: fromWalletAddress, PaymentFields.CryptoAmount: cryptoAmount, PaymentFields.Status: models.PaymentStatusWaiting, } if err := r.collection.FindOne(ctx, filter).Decode(&payment); err != nil { r.logger.Error("failed to find payment by wallets and amount on of ", zap.Error(err), zap.String("toWalletAddress", toWalletAddress), zap.String("fromWalletAddress", fromWalletAddress), zap.Float64("cryptoAmount", cryptoAmount), ) findErr := errors.NewWithError( fmt.Errorf("failed to find payment by wallets and amount: %w", err), errors.ErrInternalError, ) if err == mongo.ErrNoDocuments { return nil, findErr.SetType(errors.ErrNotFound) } return nil, findErr } return &payment, nil }