save orders

This commit is contained in:
vinchent 2024-08-10 11:10:27 +02:00
parent 88be25d77f
commit 8e482aa712
8 changed files with 172 additions and 17 deletions

View File

@ -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")

View File

@ -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")

View File

@ -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)

View File

@ -23,7 +23,33 @@ Buy one widget
<p class="mt-2 mb-3 text-center">{{$widget.Description}}</p>
<hr>
<div class="mb-3">
<label for="cardholder-name" class="form-label">Cardholder Name</label>
<label for="first-name" class="form-label">First Name</label>
<input type="text"
id="first-name"
name="first_name"
autocomplete="first-name-new"
required=""
class="form-control">
</div>
<div class="mb-3">
<label for="last-name" class="form-label">Last Name</label>
<input type="text"
id="last-name"
name="last_name"
autocomplete="last-name-new"
required=""
class="form-control">
</div>
<div class="mb-3">
<label for="cardholder-email" class="form-label">Email</label>
<input type="text"
id="cardholder-email"
name="cardholder_email"
autocomplete="cardholder-email-new"
required=""
class="form-control">
<div class="mb-3">
<label for="cardholder-name" class="form-label">Name on card</label>
<input type="text"
id="cardholder-name"
name="cardholder_name"
@ -31,14 +57,6 @@ Buy one widget
required=""
class="form-control">
</div>
<div class="mb-3">
<label for="cardholder-email" class="form-label">Cardholder Email</label>
<input type="text"
id="cardholder-email"
name="cardholder_email"
autocomplete="cardholder-email-new"
required=""
class="form-control">
</div>
<!-- card number will be built by stripe -->
<div class="mb-3">

View File

@ -6,7 +6,7 @@ Payment Succedded!
<h2 class="mt-5">Payment Succeeded</h2>
<hr>
<p>Payment Intent: {{ index .Data "pi" }}</p>
<p>Cardholder: {{ index .Data "cardholder" }}</p>
<p>Customer Name: {{ index .Data "first_name" }} {{ index .Data "last_name" }}</p>
<p>Email: {{ index .Data "email" }}</p>
<p>Payment Method: {{ index .Data "pm" }}</p>
<p>Payment Amount: {{ index .Data "pa" }}</p>

5
go.mod
View File

@ -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
)

2
go.sum
View File

@ -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=

View File

@ -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