2024-08-03 19:57:24 +00:00
|
|
|
package main
|
|
|
|
|
2024-08-03 20:26:40 +00:00
|
|
|
import (
|
2024-08-21 10:17:49 +00:00
|
|
|
"fmt"
|
2024-08-07 12:35:30 +00:00
|
|
|
"myapp/internal/cards"
|
2024-08-21 12:22:40 +00:00
|
|
|
"myapp/internal/cards/encryption"
|
2024-08-10 09:10:27 +00:00
|
|
|
"myapp/internal/models"
|
2024-08-21 10:17:49 +00:00
|
|
|
"myapp/internal/urlsigner"
|
2024-08-03 20:26:40 +00:00
|
|
|
"net/http"
|
2024-08-07 09:56:53 +00:00
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi/v5"
|
2024-08-03 20:26:40 +00:00
|
|
|
)
|
2024-08-03 19:57:24 +00:00
|
|
|
|
2024-08-10 09:10:27 +00:00
|
|
|
func (app *application) Home(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := app.renderTemplate(w, r, "home", &templateData{}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-03 19:57:24 +00:00
|
|
|
func (app *application) VirtualTerminal(w http.ResponseWriter, r *http.Request) {
|
2024-08-07 12:35:30 +00:00
|
|
|
if err := app.renderTemplate(w, r, "terminal", &templateData{}); err != nil {
|
2024-08-03 20:26:40 +00:00
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
2024-08-03 19:57:24 +00:00
|
|
|
}
|
2024-08-04 16:17:56 +00:00
|
|
|
|
2024-08-11 10:01:05 +00:00
|
|
|
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
|
|
|
|
|
2024-08-04 16:17:56 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
2024-08-11 10:01:05 +00:00
|
|
|
return txnData, err
|
2024-08-04 16:17:56 +00:00
|
|
|
}
|
|
|
|
|
2024-08-10 09:10:27 +00:00
|
|
|
firstName := r.Form.Get("first_name")
|
|
|
|
lastName := r.Form.Get("last_name")
|
2024-08-04 16:17:56 +00:00
|
|
|
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")
|
2024-08-11 10:01:05 +00:00
|
|
|
amount, _ := strconv.Atoi(paymentAmount)
|
|
|
|
|
|
|
|
// TODO: validation of the data
|
2024-08-04 16:17:56 +00:00
|
|
|
|
2024-08-07 12:35:30 +00:00
|
|
|
card := cards.Card{
|
|
|
|
Secret: app.config.stripe.secret,
|
|
|
|
Key: app.config.stripe.key,
|
|
|
|
}
|
|
|
|
|
|
|
|
pi, err := card.RetrievePaymentIntent(paymentIntent)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
2024-08-11 10:01:05 +00:00
|
|
|
return txnData, err
|
2024-08-07 12:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pm, err := card.GetPaymentMethod(paymentMethod)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
2024-08-11 10:01:05 +00:00
|
|
|
return txnData, err
|
2024-08-07 12:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lastFour := pm.Card.Last4
|
|
|
|
expiryMonth := pm.Card.ExpMonth
|
|
|
|
expiryYear := pm.Card.ExpYear
|
|
|
|
|
2024-08-11 10:01:05 +00:00
|
|
|
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)
|
2024-08-10 09:10:27 +00:00
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
app.infoLog.Printf("custumer id: %d", customerID)
|
|
|
|
|
|
|
|
transaction := models.Transaction{
|
2024-08-11 10:01:05 +00:00
|
|
|
Amount: txnData.PaymentAmount,
|
|
|
|
Currency: txnData.PaymentCurrency,
|
|
|
|
LastFour: txnData.LastFour,
|
|
|
|
ExpiryMonth: txnData.ExpiryMonth,
|
|
|
|
ExpiryYear: txnData.ExpiryYear,
|
|
|
|
PaymentIntent: txnData.PaymentIntentID,
|
|
|
|
PaymentMethod: txnData.PaymentMethodID,
|
|
|
|
BankReturnCode: txnData.BankReturnCode,
|
2024-08-10 09:10:27 +00:00
|
|
|
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,
|
2024-08-11 10:01:05 +00:00
|
|
|
Amount: txnData.PaymentAmount,
|
2024-08-10 09:10:27 +00:00
|
|
|
}
|
|
|
|
orderID, err := app.SaveOrder(order)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
app.infoLog.Printf("order id: %d", orderID)
|
|
|
|
|
2024-08-11 10:01:05 +00:00
|
|
|
app.Session.Put(r.Context(), "txn", txnData)
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/receipt", http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
|
2024-08-11 10:30:57 +00:00
|
|
|
func (app *application) VirtualTerminalPaymentSucceeded(w http.ResponseWriter, r *http.Request) {
|
|
|
|
txnData, err := app.GetTransactionData(r)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction := models.Transaction{
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = app.SaveTransaction(transaction)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
app.Session.Put(r.Context(), "txn", txnData)
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/virtual-terminal-receipt", http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
|
2024-08-11 10:01:05 +00:00
|
|
|
func (app *application) Receipt(w http.ResponseWriter, r *http.Request) {
|
|
|
|
txn := app.Session.Pop(r.Context(), "txn").(TransactionData)
|
2024-08-04 16:17:56 +00:00
|
|
|
data := make(map[string]interface{})
|
2024-08-11 10:01:05 +00:00
|
|
|
data["txn"] = txn
|
|
|
|
if err := app.renderTemplate(w, r, "receipt", &templateData{
|
2024-08-04 16:17:56 +00:00
|
|
|
Data: data,
|
|
|
|
}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-06 11:29:32 +00:00
|
|
|
|
2024-08-11 10:30:57 +00:00
|
|
|
func (app *application) VirtualTerminalReceipt(w http.ResponseWriter, r *http.Request) {
|
|
|
|
txn := app.Session.Pop(r.Context(), "txn").(TransactionData)
|
|
|
|
data := make(map[string]interface{})
|
|
|
|
data["txn"] = txn
|
|
|
|
if err := app.renderTemplate(w, r, "virtual-terminal-receipt", &templateData{
|
|
|
|
Data: data,
|
|
|
|
}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-10 09:10:27 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-08-06 11:29:32 +00:00
|
|
|
// ChargeOnce displays the page to buy one widget
|
|
|
|
func (app *application) ChargeOnce(w http.ResponseWriter, r *http.Request) {
|
2024-08-07 09:56:53 +00:00
|
|
|
id := chi.URLParam(r, "id")
|
|
|
|
widgetID, _ := strconv.Atoi(id)
|
|
|
|
|
|
|
|
widget, err := app.DB.GetWidget(widgetID)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
2024-08-06 12:10:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data := make(map[string]interface{})
|
|
|
|
data["widget"] = widget
|
|
|
|
|
|
|
|
if err := app.renderTemplate(w, r, "buy-once", &templateData{
|
|
|
|
Data: data,
|
2024-08-07 12:35:30 +00:00
|
|
|
}); err != nil {
|
2024-08-06 11:29:32 +00:00
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-11 20:08:55 +00:00
|
|
|
|
|
|
|
func (app *application) BronzePlan(w http.ResponseWriter, r *http.Request) {
|
|
|
|
widget, err := app.DB.GetWidget(2)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
data := make(map[string]interface{})
|
|
|
|
data["widget"] = widget
|
|
|
|
|
|
|
|
if err := app.renderTemplate(w, r, "bronze-plan", &templateData{
|
|
|
|
Data: data,
|
|
|
|
}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-12 17:23:44 +00:00
|
|
|
|
|
|
|
func (app *application) BronzePlanReceipt(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := app.renderTemplate(w, r, "receipt-plan", &templateData{}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-12 19:38:12 +00:00
|
|
|
|
|
|
|
func (app *application) LoginPage(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := app.renderTemplate(w, r, "login", &templateData{}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-20 19:45:13 +00:00
|
|
|
|
|
|
|
func (app *application) PostLoginPage(w http.ResponseWriter, r *http.Request) {
|
|
|
|
app.Session.RenewToken(r.Context())
|
|
|
|
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
email := r.Form.Get("email")
|
|
|
|
password := r.Form.Get("password")
|
|
|
|
|
|
|
|
id, err := app.DB.Authenticate(email, password)
|
|
|
|
if err != nil {
|
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
app.Session.Put(r.Context(), "userID", id)
|
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *application) Logout(w http.ResponseWriter, r *http.Request) {
|
|
|
|
app.Session.Destroy(r.Context())
|
|
|
|
app.Session.RenewToken(r.Context())
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
|
|
}
|
2024-08-20 20:06:04 +00:00
|
|
|
|
|
|
|
func (app *application) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if err := app.renderTemplate(w, r, "forgot-password", &templateData{}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
|
|
|
}
|
2024-08-21 10:17:49 +00:00
|
|
|
|
|
|
|
func (app *application) ShowResetPassword(w http.ResponseWriter, r *http.Request) {
|
2024-08-21 12:22:40 +00:00
|
|
|
email := r.URL.Query().Get("email")
|
2024-08-21 10:17:49 +00:00
|
|
|
theURL := r.RequestURI
|
|
|
|
testURL := fmt.Sprintf("%s%s", app.config.frontend, theURL)
|
|
|
|
|
|
|
|
signer := urlsigner.Signer{
|
|
|
|
Secret: []byte(app.config.secretkey),
|
|
|
|
}
|
|
|
|
valid := signer.VerifyToken(testURL)
|
2024-08-21 10:27:09 +00:00
|
|
|
if !valid {
|
|
|
|
app.errorLog.Println("Invalid url - tampering detected")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-21 10:54:25 +00:00
|
|
|
// make sure not expired
|
|
|
|
expired := signer.Expired(testURL, 10)
|
|
|
|
if expired {
|
|
|
|
app.errorLog.Println("Link expired")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-21 12:22:40 +00:00
|
|
|
encryptor := encryption.Encryption{
|
|
|
|
Key: []byte(app.config.secretkey),
|
|
|
|
}
|
|
|
|
|
|
|
|
encryptedEmail, err := encryptor.Encrypt(email)
|
|
|
|
if err != nil {
|
|
|
|
app.errorLog.Println("Encryption failed", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-21 10:27:09 +00:00
|
|
|
data := make(map[string]interface{})
|
2024-08-21 12:22:40 +00:00
|
|
|
data["email"] = encryptedEmail
|
2024-08-21 10:27:09 +00:00
|
|
|
if err := app.renderTemplate(w, r, "reset-password", &templateData{
|
|
|
|
Data: data,
|
|
|
|
}); err != nil {
|
|
|
|
app.errorLog.Println(err)
|
|
|
|
}
|
2024-08-21 10:17:49 +00:00
|
|
|
}
|