diff --git a/cmd/web/handlers.go b/cmd/web/handlers.go index e5cde27..fd23bc3 100644 --- a/cmd/web/handlers.go +++ b/cmd/web/handlers.go @@ -2,12 +2,19 @@ package main import ( "myapp/internal/cards" + "myapp/internal/models" "net/http" "strconv" "github.com/go-chi/chi/v5" ) +func (app *application) Home(w http.ResponseWriter, r *http.Request) { + if err := app.renderTemplate(w, r, "home", &templateData{}); err != nil { + app.errorLog.Println(err) + } +} + func (app *application) VirtualTerminal(w http.ResponseWriter, r *http.Request) { if err := app.renderTemplate(w, r, "terminal", &templateData{}); err != nil { app.errorLog.Println(err) @@ -22,12 +29,14 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) } // read posted data - cardHolder := r.Form.Get("cardholder_name") + firstName := r.Form.Get("first_name") + lastName := r.Form.Get("last_name") email := r.Form.Get("cardholder_email") paymentIntent := r.Form.Get("payment_intent") paymentMethod := r.Form.Get("payment_method") paymentAmount := r.Form.Get("payment_amount") paymentCurrency := r.Form.Get("payment_currency") + widgetID, _ := strconv.Atoi(r.Form.Get("product_id")) card := cards.Card{ Secret: app.config.stripe.secret, @@ -50,8 +59,47 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) expiryMonth := pm.Card.ExpMonth expiryYear := pm.Card.ExpYear + customerID, err := app.SaveCustomer(firstName, lastName, email) + if err != nil { + app.errorLog.Println(err) + return + } + app.infoLog.Printf("custumer id: %d", customerID) + + amount, _ := strconv.Atoi(paymentAmount) + transaction := models.Transaction{ + Amount: amount, + Currency: paymentCurrency, + LastFour: lastFour, + ExpiryMonth: int(expiryMonth), + ExpiryYear: int(expiryYear), + BankReturnCode: pi.LatestCharge.ID, + TransactionStatusID: 2, // TODO: use an enum + } + + txnID, err := app.SaveTransaction(transaction) + if err != nil { + app.errorLog.Println(err) + return + } + app.infoLog.Printf("transaction id: %d", txnID) + + order := models.Order{ + WidgetID: widgetID, + TransactionID: txnID, + CustomerID: customerID, + StatusID: 1, + Quantity: 1, + Amount: amount, + } + orderID, err := app.SaveOrder(order) + if err != nil { + app.errorLog.Println(err) + return + } + app.infoLog.Printf("order id: %d", orderID) + data := make(map[string]interface{}) - data["cardholder"] = cardHolder data["email"] = email data["pi"] = paymentIntent data["pm"] = paymentMethod @@ -61,6 +109,10 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) data["expiry_month"] = expiryMonth data["expiry_year"] = expiryYear data["bank_return_code"] = pi.LatestCharge.ID + data["first_name"] = firstName + data["last_name"] = lastName + + // should write this data to session, and then redirect user to the new page if err := app.renderTemplate(w, r, "succeeded", &templateData{ Data: data, @@ -69,6 +121,41 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) } } +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 +} + // ChargeOnce displays the page to buy one widget func (app *application) ChargeOnce(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") diff --git a/cmd/web/main.go b/cmd/web/main.go index 0973c6e..0611536 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -10,6 +10,8 @@ import ( "net/http" "os" "time" + + "github.com/alexedwards/scs/v2" ) const ( @@ -17,6 +19,8 @@ const ( cssVersion = "1" ) +var session *scs.SessionManager + type config struct { port int env string @@ -37,6 +41,7 @@ type application struct { templateCache map[string]*template.Template version string DB models.DBModel + Session *scs.SessionManager } func (app *application) serve() error { @@ -83,6 +88,10 @@ func main() { infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime) errorLog := log.New(os.Stdout, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile) + // set up session + session = scs.New() + session.Lifetime = 24 * time.Hour + conn, err := driver.OpenDB(cfg.db.dsn) if err != nil { errorLog.Fatal(err) @@ -98,6 +107,7 @@ func main() { templateCache: tc, version: version, DB: models.DBModel{DB: conn}, + Session: session, } app.infoLog.Println("Connected to MariaDB") diff --git a/cmd/web/routes.go b/cmd/web/routes.go index d9c28f9..e0aa223 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -8,7 +8,9 @@ import ( func (app *application) routes() http.Handler { mux := chi.NewRouter() + mux.Use(SessionLoad) + mux.Get("/", app.Home) mux.Get("/virtual-terminal", app.VirtualTerminal) mux.Post("/payment-succeeded", app.PaymentSucceeded) mux.Get("/widget/{id}", app.ChargeOnce) diff --git a/cmd/web/templates/buy-once.page.gohtml b/cmd/web/templates/buy-once.page.gohtml index f83b71a..7b82b9b 100644 --- a/cmd/web/templates/buy-once.page.gohtml +++ b/cmd/web/templates/buy-once.page.gohtml @@ -23,7 +23,33 @@ Buy one widget

{{$widget.Description}}


- + + +
+
+ + +
+
+ + +
+
-
- -
diff --git a/cmd/web/templates/succeeded.page.gohtml b/cmd/web/templates/succeeded.page.gohtml index 27366a5..d2066e5 100644 --- a/cmd/web/templates/succeeded.page.gohtml +++ b/cmd/web/templates/succeeded.page.gohtml @@ -6,7 +6,7 @@ Payment Succedded!

Payment Succeeded


Payment Intent: {{ index .Data "pi" }}

-

Cardholder: {{ index .Data "cardholder" }}

+

Customer Name: {{ index .Data "first_name" }} {{ index .Data "last_name" }}

Email: {{ index .Data "email" }}

Payment Method: {{ index .Data "pm" }}

Payment Amount: {{ index .Data "pa" }}

diff --git a/go.mod b/go.mod index 4e4c45d..83b74e4 100644 --- a/go.mod +++ b/go.mod @@ -9,4 +9,7 @@ require ( github.com/stripe/stripe-go/v79 v79.6.0 ) -require filippo.io/edwards25519 v1.1.0 // indirect +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/alexedwards/scs/v2 v2.8.0 // indirect +) diff --git a/go.sum b/go.sum index 9d8e030..a98f487 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= +github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= diff --git a/internal/models/models.go b/internal/models/models.go index 0a0ee5f..5c897aa 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -70,6 +70,8 @@ type Transaction struct { Amount int `json:"amount"` Currency string `json:"currency"` LastFour string `json:"last_four"` + ExpiryMonth int `json:"expiry_month"` + ExpiryYear int `json:"expiry_year"` BankReturnCode string `json:"bank_return_code"` TransactionStatusID int `json:"transaction_status_id"` CreatedAt time.Time `json:"-"` @@ -131,14 +133,17 @@ func (m *DBModel) InsertTransaction(txn Transaction) (int, error) { defer cancel() stmt := `INSERT INTO transactions - (amount, currency, last_four, bank_return_code, + (amount, currency, last_four, expiry_month, expiry_year, + bank_return_code, transaction_status_id, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?)` + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` result, err := m.DB.ExecContext(ctx, stmt, txn.Amount, txn.Currency, txn.LastFour, + txn.ExpiryMonth, + txn.ExpiryYear, txn.BankReturnCode, txn.TransactionStatusID, time.Now(), @@ -160,17 +165,45 @@ func (m *DBModel) InsertOrder(order Order) (int, error) { defer cancel() stmt := `INSERT INTO orders - (widget_id, transaction_id, status_id, quantity, + (widget_id, transaction_id, customer_id, status_id, quantity, amount, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?)` + VALUES (?, ?, ?, ?, ?, ?, ?, ?)` result, err := m.DB.ExecContext(ctx, stmt, order.WidgetID, order.TransactionID, + order.CustomerID, order.StatusID, order.Quantity, order.Amount, time.Now(), + time.Now(), + ) + if err != nil { + return 0, err + } + id, err := result.LastInsertId() + if err != nil { + return 0, err + } + return int(id), nil +} + +// InsertCustomer inserts a new customer, and returns its id +func (m *DBModel) InsertCustomer(customer Customer) (int, error) { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + stmt := `INSERT INTO customers + (first_name, last_name, email, created_at, updated_at) + VALUES (?, ?, ?, ?, ?)` + + result, err := m.DB.ExecContext(ctx, stmt, + customer.FirstName, + customer.LastName, + customer.Email, + time.Now(), + time.Now(), ) if err != nil { return 0, err