// // Created by vfs on 02.05.2024. // package models import ( "database/sql" "errors" "fmt" "time" //import "golang.org/x/crypto/bcrypt" "log" "github.com/alexedwards/argon2id" _ "github.com/lib/pq" "github.com/stripe/stripe-go/v78" "github.com/stripe/stripe-go/v78/customer" ) var ErrNoRecord = errors.New("no matching record found") var ErrInvalidCredentials = errors.New("invalid credentials") var ErrDuplicateEmail = errors.New("duplicate email") var ErrDuplicateUsername = errors.New("duplicate username") type Account struct { ID int32 Username string Password []byte Color int32 Firstname string Lastname string Email string Created time.Time StripeID string } type SubscriptionStatus string const ( incomplete SubscriptionStatus = "incomplete" incomplete_expired SubscriptionStatus = "incomplete_expired" trialing SubscriptionStatus = "trialing" active SubscriptionStatus = "active" past_due SubscriptionStatus = "past_due" canceled SubscriptionStatus = "canceled" unpaid SubscriptionStatus = "unpaid" paused SubscriptionStatus = "paused" ) type Subscription struct { ID int32 AccountID int32 StripeSubscriptionID string StripeCheckoutID string Status SubscriptionStatus } type Usermodel struct { DB *sql.DB } type SubscriptionModel struct { DB *sql.DB } func (m *Usermodel) Insert(username string, password string, firstname string, lastname string, email string) (int32, error) { //hashedpassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) hashedpassword, err := argon2id.CreateHash(password, argon2id.DefaultParams) if err != nil { log.Println(err) return 0, err } //log.Println(hashedpassword) stmt := `INSERT INTO accounts (username, password, firstname, lastname, email, created) VALUES ($1, $2, $3, $4, $5, NOW()) RETURNING id` var insertid int32 row := m.DB.QueryRow(stmt, username, string(hashedpassword), firstname, lastname, email) if row.Err() != nil { log.Println(row.Err()) return 0, row.Err() } err = row.Scan(&insertid) if err != nil { log.Println(err) return 0, err } params := &stripe.CustomerParams{ Name: stripe.String(fmt.Sprintf("%s %s", firstname, lastname)), Email: stripe.String(email), } customer, err := customer.New(params) if err != nil { log.Println(err) return 0, err } stmt = `UPDATE accounts SET stripe_id = $1 WHERE id = $2` //log.Println(customer.ID, insertid) _, err = m.DB.Exec(stmt, customer.ID, insertid) if err != nil { log.Println(err) return 0, err } return insertid, nil } func (m *Usermodel) Delete(id int32) error { account, err := users.GetAccount(id) if err != nil { log.Println(err) return err } if account.StripeID != "" { /*result*/ _, err := customer.Del(account.StripeID, nil) if err != nil { log.Println(err) } //log.Println(result) } stmt := `DELETE FROM accounts WHERE id = $1` _, err = m.DB.Exec(stmt, id) if err != nil { log.Println(err) } return nil } func (m *Usermodel) GetAccount(id int32) (Account, error) { if id == 0 { return Account{}, ErrNoRecord } stmt := `SELECT id, username, password, color, firstname, lastname, email, created, stripe_id FROM accounts WHERE id = $1` row := m.DB.QueryRow(stmt, id) var account Account err := row.Scan(&account.ID, &account.Username, &account.Password, &account.Color, &account.Firstname, &account.Lastname, &account.Email, &account.Created, &account.StripeID) if err == sql.ErrNoRows { return Account{}, sql.ErrNoRows } else if err != nil { return Account{}, err } return account, nil } func (m *Usermodel) Authenticate(username string, password string) (int32, error) { var id int32 var hashedpassword string row := m.DB.QueryRow("SELECT id, password FROM accounts WHERE username = $1", username) err := row.Scan(&id, &hashedpassword) if err == sql.ErrNoRows { return 0, ErrInvalidCredentials } match, err := argon2id.ComparePasswordAndHash(password, hashedpassword) if !match { return 0, ErrInvalidCredentials } else if err != nil { return 0, err } return id, nil } func (m *Usermodel) ExistsAccount(id int32) bool { var exists bool stmt := `SELECT EXISTS(SELECT 1 FROM accounts WHERE id = $1)` row := m.DB.QueryRow(stmt, id) if row.Err() != nil { log.Println(row.Err()) } row.Scan(&exists) //log.Println(exists) return exists } func (m *SubscriptionModel) Insert(stripeid string, stripesubscriptionid string, stripecheckoutid string, status stripe.SubscriptionStatus) (int32, error) { var id int32 stmt := `SELECT id FROM accounts WHERE stripe_id = $1` row := m.DB.QueryRow(stmt, stripeid) if row.Err() != nil { log.Println(row.Err()) return 0, row.Err() } err := row.Scan(&id) if err != nil { log.Println(err) return 0, err } stmt = `INSERT INTO subscriptions (account_id, stripe_subscription_id, stripe_checkout_id, status) VALUES ($1, $2, $3, $4::subscription_status) RETURNING id` var insertid int32 row = m.DB.QueryRow(stmt, id, string(stripesubscriptionid), string(stripecheckoutid), string(status)) if row.Err() != nil { log.Println(row.Err()) return 0, row.Err() } err = row.Scan(&insertid) if err != nil { log.Println(err) return 0, err } return insertid, nil } func (m *SubscriptionModel) Delete(id int32) error { stmt := `DELETE FROM accounts WHERE id = $1` _, err := m.DB.Exec(stmt, id) if err != nil { log.Println(err) return err } return nil } func (m *SubscriptionModel) GetSubscription(id int32) (Subscription, error) { if id == 0 { return Subscription{}, ErrNoRecord } stmt := `SELECT id, account_id, stripe_subscription_id, stripe_checkout_id, status FROM subscriptions WHERE id = $1` row := m.DB.QueryRow(stmt, id) var subscription Subscription err := row.Scan(&subscription.ID, &subscription.AccountID, &subscription.StripeSubscriptionID, &subscription.StripeCheckoutID, &subscription.Status) if err == sql.ErrNoRows { return Subscription{}, sql.ErrNoRows } else if err != nil { return Subscription{}, err } //log.Println(subscription.Status) return subscription, nil } func (m *SubscriptionModel) GetSubscriptionsFromAccount(accountid int32) ([]Subscription, error) { if accountid == 0 { return nil, ErrNoRecord } stmt := `SELECT id, account_id, stripe_subscription_id, stripe_checkout_id, status FROM subscriptions WHERE account_id = $1` rows, err := m.DB.Query(stmt, accountid) if err != nil { return nil, err } defer rows.Close() var subscriptions []Subscription for rows.Next() { var subscription Subscription err := rows.Scan(&subscription.ID, &subscription.AccountID, &subscription.StripeSubscriptionID, &subscription.StripeCheckoutID, &subscription.Status) if err == sql.ErrNoRows { return nil, sql.ErrNoRows } else if err != nil { return nil, err } subscriptions = append(subscriptions, subscription) } return subscriptions, nil } func (m *SubscriptionModel) HasActiveSubscription(accountid int32) bool { subscriptions, err := m.GetSubscriptionsFromAccount(accountid) if err != nil { return false } for _, v := range subscriptions { if v.Status == active { return true } else if v.Status == trialing { return true } } return false }