package main import ( "encoding/json" "errors" "fmt" "myapp/internal/cards" "myapp/internal/models" "net/http" "strconv" "strings" "time" "github.com/go-chi/chi/v5" "github.com/stripe/stripe-go/v79" ) type stripePayload struct { Currency string `json:"currency"` Amount string `json:"amount"` PaymentMethod string `json:"payment_method"` Email string `json:"email"` CardBrand string `json:"card_brand"` ExpiryMonth int `json:"expiry_month"` ExpiryYear int `json:"expiry_year"` LastFour string `json:"last_four"` Plan string `json:"plan"` ProductID string `json:"product_id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } type jsonResponse struct { OK bool `json:"ok"` Message string `json:"message,omitempty"` Content string `json:"content,omitempty"` ID int `json:"id,omitempty"` } func (app *application) GetPaymentIntent(w http.ResponseWriter, r *http.Request) { var payload stripePayload err := json.NewDecoder(r.Body).Decode(&payload) if err != nil { app.errorLog.Println(err) return // TODO: return a valid json } amount, err := strconv.Atoi(payload.Amount) if err != nil { app.errorLog.Println(err) return // TODO: return a valid json } card := cards.Card{ Secret: app.config.stripe.secret, Key: app.config.stripe.key, Currency: payload.Currency, } pi, msg, err := card.Charge(payload.Currency, amount) if err != nil { j := jsonResponse{ OK: false, Message: msg, Content: "", } out, err := json.MarshalIndent(j, "", " ") if err != nil { app.errorLog.Println(err) } w.Header().Set("Content-Type", "application/json") w.Write(out) return } out, err := json.MarshalIndent(pi, "", " ") if err != nil { app.errorLog.Println(err) return // TODO: return a valid json } w.Header().Set("Content-Type", "application/json") w.Write(out) } func (app *application) GetWidgetByID(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") widgetID, _ := strconv.Atoi(id) widget, err := app.DB.GetWidget(widgetID) if err != nil { app.errorLog.Println(err) } out, err := json.MarshalIndent(widget, "", " ") if err != nil { app.errorLog.Println(err) } w.Header().Set("Content-Type", "application/json") w.Write(out) } func (app *application) CreateCustomerAndSubscribeToPlan(w http.ResponseWriter, r *http.Request) { var data stripePayload err := json.NewDecoder(r.Body).Decode(&data) if err != nil { app.errorLog.Println(err) return } app.infoLog.Println(data.Email, data.LastFour, data.PaymentMethod, data.Plan) card := cards.Card{ Secret: app.config.stripe.secret, Key: app.config.stripe.key, Currency: data.Currency, } okay := true var subscription *stripe.Subscription txnMsg := "Transaction successful" stripeCustomer, msg, err := card.CreateCustomer(data.PaymentMethod, data.Email) if err != nil { app.errorLog.Println(err) okay = false txnMsg = msg } if okay { subscription, err = card.SubscribeToPlan( stripeCustomer, data.Plan, data.Email, data.LastFour, "", ) if err != nil { app.errorLog.Println(err) okay = false txnMsg = "Error subscribing customer" } app.infoLog.Println("subscription id is", subscription.ID) } if okay { productID, _ := strconv.Atoi(data.ProductID) customerID, err := app.SaveCustomer(data.FirstName, data.LastName, data.Email) if err != nil { app.errorLog.Println(err) return } // create a new txn amount, _ := strconv.Atoi(data.Amount) txn := models.Transaction{ Amount: amount, Currency: "eur", LastFour: data.LastFour, ExpiryMonth: data.ExpiryMonth, ExpiryYear: data.ExpiryYear, TransactionStatusID: 2, } txnID, err := app.SaveTransaction(txn) if err != nil { app.errorLog.Println(err) return } // create order order := models.Order{ WidgetID: productID, TransactionID: txnID, CustomerID: customerID, StatusID: 1, Quantity: 1, Amount: amount, } _, err = app.SaveOrder(order) if err != nil { app.errorLog.Println(err) return } } resp := jsonResponse{ OK: okay, Message: txnMsg, } out, err := json.MarshalIndent(resp, "", " ") if err != nil { app.errorLog.Println(err) return } w.Header().Set("Content-Type", "application/json") w.Write(out) } func (app *application) SaveCustomer(firstName, lastName, email string) (int, error) { customer := models.Customer{ FirstName: firstName, LastName: lastName, Email: email, } id, err := app.DB.InsertCustomer(customer) if err != nil { return 0, err } return id, nil } func (app *application) SaveTransaction(txn models.Transaction) (int, error) { txnID, err := app.DB.InsertTransaction(txn) if err != nil { app.errorLog.Println(err) return 0, err } return txnID, nil } func (app *application) SaveOrder(order models.Order) (int, error) { id, err := app.DB.InsertOrder(order) if err != nil { app.errorLog.Println(err) return 0, err } return id, nil } func (app *application) CreateAuthToken(w http.ResponseWriter, r *http.Request) { var userInput struct { Email string `json:"email"` Password string `json:"password"` } err := app.readJSON(w, r, &userInput) if err != nil { app.errorLog.Println(err) app.badRequest(w, r, err) return } // get the user from the db by email, send error if invalid email user, err := app.DB.GetUserByEmail(userInput.Email) if err != nil { app.errorLog.Println(err) app.invalidCredentials(w) return } // validate the password, send error if invalid password validPassword, err := app.passwordMatches(user.Password, userInput.Password) if err != nil { app.errorLog.Println(err) app.invalidCredentials(w) return } if !validPassword { app.invalidCredentials(w) return } // generate the token token, err := models.GenerateToken(user.ID, 24*time.Hour, models.ScopeAuthentication) if err != nil { app.errorLog.Println(err) app.badRequest(w, r, err) return } // save to DB err = app.DB.InsertToken(token, user) if err != nil { app.errorLog.Println(err) app.badRequest(w, r, err) return } // send response var payload struct { Error bool `json:"error"` Message string `json:"message"` Token *models.Token `json:"authentication_token"` } payload.Error = false payload.Message = fmt.Sprintf("token for %s created", userInput.Email) payload.Token = token _ = app.writeJSON(w, http.StatusOK, payload) } func (app *application) authenticateToken(r *http.Request) (*models.User, error) { authorizationHeader := r.Header.Get("Authorization") if authorizationHeader == "" { return nil, errors.New("no authorization header received") } headerParts := strings.Split(authorizationHeader, " ") if len(headerParts) != 2 || headerParts[0] != "Bearer" { return nil, errors.New("no authorization header received") } token := headerParts[1] if len(token) != 26 { return nil, errors.New("authentication token wrong size") } // get the user from the tokens table user, err := app.DB.GetUserForToken(token) if err != nil { return nil, errors.New("no matching user found") } return user, nil } func (app *application) CheckAuthentication(w http.ResponseWriter, r *http.Request) { // validate the token, and get associated user user, err := app.authenticateToken(r) if err != nil { app.errorLog.Println(err) app.invalidCredentials(w) return } // valid user var payload struct { Error bool `json:"error"` Message string `json:"message"` } payload.Error = false payload.Message = fmt.Sprintf("authenticated user %s", user.Email) app.writeJSON(w, http.StatusOK, payload) }