This commit is contained in:
tavo-wasd 2024-09-01 22:44:34 -06:00
parent 7c50792f26
commit eec6e358db
4 changed files with 151 additions and 116 deletions

View file

@ -1,6 +1,10 @@
BIN = builder BIN = builder
SRCDIR = server SRCDIR = server
SRC = ${SRCDIR}/main.go SRC = ${SRCDIR}/init.go \
${SRCDIR}/main.go \
${SRCDIR}/paypal.go \
${SRCDIR}/db.go \
GOFILES = ${SRCDIR}/go.sum ${SRCDIR}/go.mod GOFILES = ${SRCDIR}/go.sum ${SRCDIR}/go.mod
GOMODS = github.com/joho/godotenv github.com/lib/pq GOMODS = github.com/joho/godotenv github.com/lib/pq

View file

@ -3,13 +3,13 @@ package main
import ( import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"log" "fmt"
"time" "time"
_ "github.com/lib/pq" _ "github.com/lib/pq"
) )
func RegisterSite(capture Capture, directory string, editorData json.RawMessage) { func RegisterSitePayment(capture Capture, directory string, editorData json.RawMessage) error {
var ( var (
// Payment // Payment
id string id string
@ -41,7 +41,6 @@ func RegisterSite(capture Capture, directory string, editorData json.RawMessage)
country = capture.Payer.Address.CountryCode country = capture.Payer.Address.CountryCode
var pkey int var pkey int
newSite := db.QueryRow(`SELECT id FROM sites WHERE folder = $1`, directory).Scan(&pkey) newSite := db.QueryRow(`SELECT id FROM sites WHERE folder = $1`, directory).Scan(&pkey)
if newSite == sql.ErrNoRows { if newSite == sql.ErrNoRows {
@ -52,8 +51,7 @@ func RegisterSite(capture Capture, directory string, editorData json.RawMessage)
directory, wstatus, due, directory, wstatus, due,
name, surname, email, phone, country, name, surname, email, phone, country,
editorData).Scan(&pkey); err != nil { editorData).Scan(&pkey); err != nil {
log.Printf("Error: Could not register site to database: %v", err) return fmt.Errorf("Error: Could not register site to database: %v", err)
return
} }
} else { } else {
if err := db.QueryRow( if err := db.QueryRow(
@ -61,8 +59,7 @@ func RegisterSite(capture Capture, directory string, editorData json.RawMessage)
WHERE id = $1 WHERE id = $1
RETURNING id`, RETURNING id`,
pkey).Scan(&pkey); err != nil { pkey).Scan(&pkey); err != nil {
log.Fatalf("Error: Could not update due date: %v", err) return fmt.Errorf("Error: Could not update due date: %v", err)
return
} }
} }
@ -70,9 +67,47 @@ func RegisterSite(capture Capture, directory string, editorData json.RawMessage)
`INSERT INTO payments (capture, site, amount, currency, date, status) `INSERT INTO payments (capture, site, amount, currency, date, status)
VALUES ($1, $2, $3, $4, $5, $6)`, VALUES ($1, $2, $3, $4, $5, $6)`,
id, pkey, amount, currency, date, pstatus); err != nil { id, pkey, amount, currency, date, pstatus); err != nil {
log.Printf("Error: Could not register payment to database: %v", err) return fmt.Errorf("Error: Could not register payment to database: %v", err)
return
} }
return return nil
}
func UpdateSite(by string, pkey int, editorData json.RawMessage) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("Error: Could not start transaction: %v", err)
}
defer func() {
if err != nil {
tx.Rollback()
}
}()
var prev json.RawMessage
if err := tx.QueryRow(
`SELECT raw FROM sites WHERE id = $1`,
pkey).Scan(&prev); err != nil {
return fmt.Errorf("Error: Could not retrieve old value: %v", err)
}
if _, err = tx.Exec(
`UPDATE sites SET raw = $1 WHERE id = $2`,
editorData, pkey); err != nil {
return fmt.Errorf("Error: Could not update raw column: %v", err)
}
if _, err = tx.Exec(
`INSERT INTO changes (by, site, payment, col, prev, next, date)
VALUES ($1, $2, NULL, 'raw', $3, $4, CURRENT_DATE);`,
by, pkey, prev, editorData); err != nil {
return fmt.Errorf("Error: Could not register change to database: %v", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("Error: Could not commit transaction: %v", err)
}
return nil
} }

View file

@ -1,18 +1,21 @@
package main package main
import ( import (
"encoding/json"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
"strings"
"syscall" "syscall"
) )
func main() { func main() {
initialize() initialize()
http.HandleFunc("/api/orders", CreateOrder) http.HandleFunc("/api/orders", CreateOrderHandler)
http.HandleFunc("/api/orders/", CaptureOrder) http.HandleFunc("/api/orders/", CaptureOrderHandler)
http.Handle("/", http.FileServer(http.Dir("./public"))) http.Handle("/", http.FileServer(http.Dir("./public")))
stop := make(chan os.Signal, 1) stop := make(chan os.Signal, 1)
@ -31,3 +34,71 @@ func main() {
shutdown() shutdown()
log.Println("Server shutdown gracefully.") log.Println("Server shutdown gracefully.")
} }
func fail(w http.ResponseWriter, err error, notice string) {
log.Printf("%s: %v", notice, err)
http.Error(w, notice, http.StatusInternalServerError)
}
func CreateOrderHandler(w http.ResponseWriter, r *http.Request) {
orderID, err := CreateOrder()
if err != nil {
fail(w, err, "Failed to obtain orderID")
return
}
var response struct {
ID string `json:"id"`
}
response.ID = orderID
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
func CaptureOrderHandler(w http.ResponseWriter, r *http.Request) {
info, err := io.ReadAll(r.Body)
if err != nil {
fail(w, err, "Failed to read request body")
return
}
var cart struct {
Directory string `json:"directory"`
EditorData json.RawMessage `json:"editor_data"`
}
err = json.Unmarshal(info, &cart)
if err != nil {
fail(w, err, "Failed to parse request body")
return
}
directory := cart.Directory
editorData := cart.EditorData
path := strings.TrimPrefix(r.URL.Path, "/api/orders/")
parts := strings.Split(path, "/")
orderID := parts[0]
if orderID == "" {
fail(w, err, "Failed to get orderID from client URL")
return
}
capture, receipt, err := CaptureOrder(orderID)
if err != nil {
fail(w, err, "Failed to capture order")
return
}
if err := RegisterSitePayment(capture, directory, editorData); err != nil {
fail(w, err, "Failed to register '"+directory+"'in database")
return
}
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(receipt); err != nil {
fail(w, err, "Failed to encode response")
return
}
return
}

View file

@ -45,6 +45,17 @@ type Capture struct {
} `json:"payer"` } `json:"payer"`
} }
type Receipt struct {
PurchaseUnits []struct {
Payments struct {
Captures []struct {
ID string `json:"id"`
Status string `json:"status"`
} `json:"captures"`
} `json:"payments"`
} `json:"purchase_units"`
}
func Token() (string, error) { func Token() (string, error) {
req, err := http.NewRequest("POST", req, err := http.NewRequest("POST",
os.Getenv("BASE_URL")+"/v1/oauth2/token", os.Getenv("BASE_URL")+"/v1/oauth2/token",
@ -69,13 +80,10 @@ func Token() (string, error) {
return response.AccessToken, nil return response.AccessToken, nil
} }
func CreateOrder(w http.ResponseWriter, r *http.Request) { func CreateOrder() (string, error) {
token, err := Token() token, err := Token()
if err != nil { if err != nil {
http.Error(w, return "", fmt.Errorf("Failed to get acess token: %v", err)
"Failed to get access token",
http.StatusInternalServerError)
return
} }
type Amount struct { type Amount struct {
@ -129,10 +137,7 @@ func CreateOrder(w http.ResponseWriter, r *http.Request) {
raw, err := http.DefaultClient.Do(req) raw, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
http.Error(w, return "", fmt.Errorf("Failed to send request: %v", err)
"Failed to send request",
http.StatusInternalServerError)
return
} }
defer raw.Body.Close() defer raw.Body.Close()
@ -141,64 +146,16 @@ func CreateOrder(w http.ResponseWriter, r *http.Request) {
} }
if err := json.NewDecoder(raw.Body).Decode(&response); err != nil { if err := json.NewDecoder(raw.Body).Decode(&response); err != nil {
http.Error(w, return "", fmt.Errorf("Failed to decode response: %v", err)
"Failed to decode response",
http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json") return response.ID, nil
json.NewEncoder(w).Encode(response)
return
} }
func CaptureOrder(w http.ResponseWriter, r *http.Request) { func CaptureOrder(orderID string) (Capture, Receipt, error) {
info, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w,
"Failed to read request body",
http.StatusInternalServerError)
return
}
var cart struct {
Directory string `json:"directory"`
EditorData json.RawMessage `json:"editor_data"`
}
err = json.Unmarshal(info, &cart)
if err != nil {
http.Error(w,
"Failed to parse request body",
http.StatusBadRequest)
return
}
directory := cart.Directory
editorData := cart.EditorData
if err != nil {
http.Error(w,
"Failed to parse request body",
http.StatusBadRequest)
return
}
path := strings.TrimPrefix(r.URL.Path, "/api/orders/")
parts := strings.Split(path, "/")
orderID := parts[0]
if orderID == "" {
http.Error(w,
"Failed to get orderID from client URL",
http.StatusInternalServerError)
return
}
token, err := Token() token, err := Token()
if err != nil { if err != nil {
http.Error(w, return Capture{}, Receipt{}, fmt.Errorf("Failed to get acess token: %v", err)
"Failed to get access token",
http.StatusInternalServerError)
return
} }
req, err := http.NewRequest("POST", req, err := http.NewRequest("POST",
@ -209,57 +166,25 @@ func CaptureOrder(w http.ResponseWriter, r *http.Request) {
raw, err := http.DefaultClient.Do(req) raw, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
http.Error(w, return Capture{}, Receipt{}, fmt.Errorf("Failed to send request: %v", err)
"Failed to send request",
http.StatusInternalServerError)
return
} }
defer raw.Body.Close() defer raw.Body.Close()
var capture Capture
var receipt Receipt
body, err := io.ReadAll(raw.Body) body, err := io.ReadAll(raw.Body)
if err != nil { if err != nil {
http.Error(w, return Capture{}, Receipt{}, fmt.Errorf("Failed to read response body: %v", err)
"Failed to read response body",
http.StatusInternalServerError)
return
} }
var capture Capture if err := json.NewDecoder(bytes.NewReader(body)).Decode(&capture); err != nil {
if err := json.Unmarshal(body, &capture); err != nil { return Capture{}, Receipt{}, fmt.Errorf("Failed to decode into capture: %v", err)
http.Error(w,
"Failed to decode response into capture",
http.StatusInternalServerError)
return
} }
var receipt = struct { if err := json.NewDecoder(bytes.NewReader(body)).Decode(&receipt); err != nil {
PurchaseUnits []struct { return Capture{}, Receipt{}, fmt.Errorf("Failed to decode into capture: %v", err)
Payments struct {
Captures []struct {
ID string `json:"id"`
Status string `json:"status"`
} `json:"captures"`
} `json:"payments"`
} `json:"purchase_units"`
}{}
if err := json.Unmarshal(body, &receipt); err != nil {
http.Error(w,
"Failed to decode response into receipt",
http.StatusInternalServerError)
return
} }
RegisterSite(capture, directory, editorData) return capture, receipt, nil
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(receipt); err != nil {
http.Error(w,
"Failed to encode response",
http.StatusInternalServerError)
return
}
return
} }