Implement order placed webhook
This commit is contained in:
parent
36d98f365a
commit
03e1e564f6
|
@ -26,6 +26,8 @@ type MatrixConfig struct {
|
|||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
APIToken string `json:"api_token"`
|
||||
BaseURL string `json:"base_url"`
|
||||
ListenAddress string `json:"listen_address"`
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package pretix
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
eventOrderPlaced = "pretix.event.order.placed"
|
||||
|
||||
urlIndividualOrder = "%s/api/v1/organizers/%s/events/%s/orders/%s/"
|
||||
)
|
||||
|
||||
func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
data := struct{
|
||||
Action string
|
||||
Code string
|
||||
Event string
|
||||
Organizer string
|
||||
}{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
err := decoder.Decode(&data)
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if data.Action != eventOrderPlaced {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, s.buildURL(urlIndividualOrder, data.Organizer, data.Event, data.Code), nil)
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprint("Token ", s.config.APIToken))
|
||||
|
||||
resp, err := s.client.Do(req)
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
decoder = json.NewDecoder(resp.Body)
|
||||
order := struct{
|
||||
Code string
|
||||
Datetime time.Time
|
||||
Positions []struct{
|
||||
Price string
|
||||
}
|
||||
}{}
|
||||
|
||||
err = decoder.Decode(&order)
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := s.db.Exec("INSERT INTO orders VALUES(?);", order.Code)
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if rowsAffected > 0 && order.Datetime.Add(72 * time.Hour).After(time.Now()) { // pretix retries failed webhooks for up to three days
|
||||
log.Print(order)
|
||||
}
|
||||
}
|
|
@ -2,10 +2,12 @@ package pretix
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.luj0ga.de/franconian/matrix-pretix/internal/config"
|
||||
|
@ -14,21 +16,30 @@ import (
|
|||
|
||||
type Server struct {
|
||||
client http.Client
|
||||
config *config.ServerConfig
|
||||
db *sql.DB
|
||||
matrix *matrix.Client
|
||||
}
|
||||
|
||||
func NewServer(matrix *matrix.Client) *Server {
|
||||
func NewServer(config *config.ServerConfig, db *sql.DB, matrix *matrix.Client) *Server {
|
||||
return &Server{
|
||||
config: config,
|
||||
db: db,
|
||||
matrix: matrix,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Server) ListenAndServe(config *config.ServerConfig, ctx context.Context, wg *sync.WaitGroup) error {
|
||||
func (s Server) ListenAndServe(ctx context.Context, wg *sync.WaitGroup) error {
|
||||
err := s.createTables()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
http.HandleFunc("/", http.NotFound)
|
||||
http.HandleFunc("/order_placed", s.orderPlaced)
|
||||
|
||||
server := http.Server{
|
||||
Addr: config.ListenAddress,
|
||||
Addr: s.config.ListenAddress,
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
@ -43,9 +54,9 @@ func (s Server) ListenAndServe(config *config.ServerConfig, ctx context.Context,
|
|||
}
|
||||
}()
|
||||
|
||||
log.Print("listening on ", config.ListenAddress)
|
||||
log.Print("listening on ", s.config.ListenAddress)
|
||||
|
||||
err := server.ListenAndServe()
|
||||
err = server.ListenAndServe()
|
||||
if err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
@ -53,22 +64,41 @@ func (s Server) ListenAndServe(config *config.ServerConfig, ctx context.Context,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s Server) orderPlaced(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
return
|
||||
func (s Server) createTables() error {
|
||||
tables := []string{
|
||||
`CREATE TABLE IF NOT EXISTS orders (
|
||||
code TEXT PRIMARY KEY ON CONFLICT IGNORE
|
||||
);
|
||||
`,
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
var data map[string]interface{}
|
||||
err := decoder.Decode(&data)
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
writeStatus(w, http.StatusBadRequest)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print(data)
|
||||
for _, table := range tables {
|
||||
_, err := tx.Exec(table)
|
||||
if err != nil {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Server) buildURL(endpoint string, args ...any) string {
|
||||
baseURL := strings.TrimSuffix(s.config.BaseURL, "/")
|
||||
return fmt.Sprintf(endpoint, append([]any{baseURL}, args...)...)
|
||||
}
|
||||
|
||||
func writeStatus(w http.ResponseWriter, code int) {
|
||||
|
|
Loading…
Reference in New Issue