diff --git a/cmd/web/handlers.go b/cmd/web/handlers.go index fd23bc3..bddd642 100644 --- a/cmd/web/handlers.go +++ b/cmd/web/handlers.go @@ -21,14 +21,29 @@ func (app *application) VirtualTerminal(w http.ResponseWriter, r *http.Request) } } -func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) { +type TransactionData struct { + FirstName string + LastName string + Email string + PaymentIntentID string + PaymentMethodID string + PaymentAmount int + PaymentCurrency string + LastFour string + ExpiryMonth int + ExpiryYear int + BankReturnCode string +} + +func (app *application) GetTransactionData(r *http.Request) (TransactionData, error) { + var txnData TransactionData + err := r.ParseForm() if err != nil { app.errorLog.Println(err) - return + return txnData, err } - // read posted data firstName := r.Form.Get("first_name") lastName := r.Form.Get("last_name") email := r.Form.Get("cardholder_email") @@ -36,7 +51,9 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) 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")) + amount, _ := strconv.Atoi(paymentAmount) + + // TODO: validation of the data card := cards.Card{ Secret: app.config.stripe.secret, @@ -46,34 +63,67 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) pi, err := card.RetrievePaymentIntent(paymentIntent) if err != nil { app.errorLog.Println(err) - return + return txnData, err } pm, err := card.GetPaymentMethod(paymentMethod) if err != nil { app.errorLog.Println(err) - return + return txnData, err } lastFour := pm.Card.Last4 expiryMonth := pm.Card.ExpMonth expiryYear := pm.Card.ExpYear - customerID, err := app.SaveCustomer(firstName, lastName, email) + txnData = TransactionData{ + FirstName: firstName, + LastName: lastName, + Email: email, + PaymentIntentID: paymentIntent, + PaymentMethodID: paymentMethod, + PaymentAmount: amount, + PaymentCurrency: paymentCurrency, + LastFour: lastFour, + ExpiryMonth: int(expiryMonth), + ExpiryYear: int(expiryYear), + BankReturnCode: pi.LatestCharge.ID, + } + return txnData, nil +} + +func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) { + // read posted data + err := r.ParseForm() + if err != nil { + app.errorLog.Println(err) + return + } + + widgetID, _ := strconv.Atoi(r.Form.Get("product_id")) + + txnData, err := app.GetTransactionData(r) + if err != nil { + app.errorLog.Println(err) + return + } + + customerID, err := app.SaveCustomer(txnData.FirstName, txnData.LastName, txnData.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, + Amount: txnData.PaymentAmount, + Currency: txnData.PaymentCurrency, + LastFour: txnData.LastFour, + ExpiryMonth: txnData.ExpiryMonth, + ExpiryYear: txnData.ExpiryYear, + PaymentIntent: txnData.PaymentIntentID, + PaymentMethod: txnData.PaymentMethodID, + BankReturnCode: txnData.BankReturnCode, TransactionStatusID: 2, // TODO: use an enum } @@ -90,7 +140,7 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) CustomerID: customerID, StatusID: 1, Quantity: 1, - Amount: amount, + Amount: txnData.PaymentAmount, } orderID, err := app.SaveOrder(order) if err != nil { @@ -99,22 +149,16 @@ func (app *application) PaymentSucceeded(w http.ResponseWriter, r *http.Request) } app.infoLog.Printf("order id: %d", orderID) + app.Session.Put(r.Context(), "txn", txnData) + + http.Redirect(w, r, "/receipt", http.StatusSeeOther) +} + +func (app *application) Receipt(w http.ResponseWriter, r *http.Request) { + txn := app.Session.Pop(r.Context(), "txn").(TransactionData) data := make(map[string]interface{}) - data["email"] = email - data["pi"] = paymentIntent - data["pm"] = paymentMethod - data["pa"] = paymentAmount - data["pc"] = paymentCurrency - data["last_four"] = lastFour - 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["txn"] = txn + if err := app.renderTemplate(w, r, "receipt", &templateData{ Data: data, }); err != nil { app.errorLog.Println(err) diff --git a/cmd/web/main.go b/cmd/web/main.go index 0611536..b255014 100644 --- a/cmd/web/main.go +++ b/cmd/web/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/gob" "flag" "fmt" "html/template" @@ -63,6 +64,8 @@ func (app *application) serve() error { } func main() { + gob.Register(TransactionData{}) + var cfg config flag.IntVar(&cfg.port, "port", 4000, "Server port to listen on") diff --git a/cmd/web/middleware.go b/cmd/web/middleware.go new file mode 100644 index 0000000..a940128 --- /dev/null +++ b/cmd/web/middleware.go @@ -0,0 +1,9 @@ +package main + +import ( + "net/http" +) + +func SessionLoad(next http.Handler) http.Handler { + return session.LoadAndSave(next) +} diff --git a/cmd/web/routes.go b/cmd/web/routes.go index e0aa223..adc51bc 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -13,6 +13,7 @@ func (app *application) routes() http.Handler { mux.Get("/", app.Home) mux.Get("/virtual-terminal", app.VirtualTerminal) mux.Post("/payment-succeeded", app.PaymentSucceeded) + mux.Get("/receipt", app.Receipt) mux.Get("/widget/{id}", app.ChargeOnce) fileServer := http.FileServer(http.Dir("./static")) diff --git a/cmd/web/templates/home.page.gohtml b/cmd/web/templates/home.page.gohtml new file mode 100644 index 0000000..ffae2da --- /dev/null +++ b/cmd/web/templates/home.page.gohtml @@ -0,0 +1,8 @@ +{{ template "base" . }} +{{ define "title" }} +Widget +{{ end }} +{{ define "content" }} +

Widget

+{{ end }} + diff --git a/cmd/web/templates/receipt.page.gohtml b/cmd/web/templates/receipt.page.gohtml new file mode 100644 index 0000000..255ecd4 --- /dev/null +++ b/cmd/web/templates/receipt.page.gohtml @@ -0,0 +1,18 @@ +{{ template "base" . }} +{{ define "title" }} +Payment Succedded! +{{ end }} +{{ define "content" }} +{{$txn := index .Data "txn"}} +

Payment Succeeded

+
+

Payment Intent: {{$txn.PaymentIntentID}}

+

Customer Name: {{ $txn.FirstName }} {{ $txn.LastName }}

+

Email: {{ $txn.Email }}

+

Payment Method: {{ $txn.PaymentMethodID }}

+

Payment Amount: {{ $txn.PaymentAmount }}

+

Currency: {{ $txn.PaymentCurrency }}

+

Last Four: {{ $txn.LastFour }}

+

Bank Return Code: {{ $txn.BankReturnCode }}

+

Expiry Date: {{ $txn.ExpiryMonth }}/{{$txn.ExpiryYear}}

+{{ end }} diff --git a/cmd/web/templates/succeeded.page.gohtml b/cmd/web/templates/succeeded.page.gohtml deleted file mode 100644 index d2066e5..0000000 --- a/cmd/web/templates/succeeded.page.gohtml +++ /dev/null @@ -1,17 +0,0 @@ -{{ template "base" . }} -{{ define "title" }} -Payment Succedded! -{{ end }} -{{ define "content" }} -

Payment Succeeded

-
-

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

-

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

-

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

-

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

-

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

-

Currency: {{ index .Data "pc" }}

-

Last Four: {{ index .Data "last_four" }}

-

Bank Return Code: {{ index .Data "bank_return_code" }}

-

Expiry Date: {{ index .Data "expiry_month" }}/{{index .Data "expiry_year"}}

-{{ end }} diff --git a/internal/models/models.go b/internal/models/models.go index 5c897aa..06a5b86 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -72,6 +72,8 @@ type Transaction struct { LastFour string `json:"last_four"` ExpiryMonth int `json:"expiry_month"` ExpiryYear int `json:"expiry_year"` + PaymentIntent string `json:"payment_intent"` + PaymentMethod string `json:"payment_method"` BankReturnCode string `json:"bank_return_code"` TransactionStatusID int `json:"transaction_status_id"` CreatedAt time.Time `json:"-"` @@ -134,9 +136,9 @@ func (m *DBModel) InsertTransaction(txn Transaction) (int, error) { stmt := `INSERT INTO transactions (amount, currency, last_four, expiry_month, expiry_year, - bank_return_code, + payment_intent, payment_method, bank_return_code, transaction_status_id, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)` + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` result, err := m.DB.ExecContext(ctx, stmt, txn.Amount, @@ -144,6 +146,8 @@ func (m *DBModel) InsertTransaction(txn Transaction) (int, error) { txn.LastFour, txn.ExpiryMonth, txn.ExpiryYear, + txn.PaymentIntent, + txn.PaymentMethod, txn.BankReturnCode, txn.TransactionStatusID, time.Now(), diff --git a/migrations/20240810091357_add_cols_to_txn.down.fizz b/migrations/20240810091357_add_cols_to_txn.down.fizz new file mode 100644 index 0000000..241cae5 --- /dev/null +++ b/migrations/20240810091357_add_cols_to_txn.down.fizz @@ -0,0 +1,2 @@ +drop_column("transactions", "payment_intent") +drop_column("transactions", "payment_method") diff --git a/migrations/20240810091357_add_cols_to_txn.up.fizz b/migrations/20240810091357_add_cols_to_txn.up.fizz new file mode 100644 index 0000000..7a27665 --- /dev/null +++ b/migrations/20240810091357_add_cols_to_txn.up.fizz @@ -0,0 +1,3 @@ +add_column("transactions", "payment_intent", "string", {"default":""}) +add_column("transactions", "payment_method", "string", {"default":""}) +