diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d6a0db8b409d161eb0a688d02fa472b24aa7e560
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,35 @@
+version: “3.8”
+services:
+  emailnotification:
+    build:
+      context: ./src
+      dockerfile: emailnotification/Dockerfile
+    ports:
+      - "8001:8001"
+    depends_on:
+      - nats
+    environment:
+      - NATS_CONNECT=nats:4222
+      - LOG_LEVEL=info # change to trace for debugging
+  highlanderticketing:
+    build:
+      context: ./src
+      dockerfile: highlanderticketing/Dockerfile
+    ports:
+      - "8000:8000"
+    environment:
+      - NATS_CONNECT=nats:4222
+      - EMAILNOT_CONNECT=emailnotification:8001
+      - DB_CONNECT=mongo:27017
+      - LOG_LEVEL=info # change to trace for debugging
+  mongo:
+   container_name: mongo
+   image: mongo:4.4
+   ports:
+     - 27017:27017
+   command: mongod
+  nats:
+    image: nats:latest
+    container_name: nats
+    ports:
+     - 4222:4222
\ No newline at end of file
diff --git a/go.work b/go.work
index b3a61eca966ff63e593268cbb6f88753a902d844..5ef30290575bf47777f03221e4ba6d65154550ec 100644
--- a/go.work
+++ b/go.work
@@ -1 +1,6 @@
 go 1.20
+
+use (
+	./src/emailnotification
+	./src/highlanderticketing
+)
diff --git a/go.work.sum b/go.work.sum
new file mode 100644
index 0000000000000000000000000000000000000000..537ca644d7c487f6a9a2be7a24fb98ec9a9ce754
--- /dev/null
+++ b/go.work.sum
@@ -0,0 +1,6 @@
+cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
+cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
+google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
diff --git a/src/emailnotification/Dockerfile b/src/emailnotification/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..2946e191d1e3ce3334463deaaacd1d3e4d03e3de
--- /dev/null
+++ b/src/emailnotification/Dockerfile
@@ -0,0 +1,18 @@
+FROM golang:1.20-buster
+# non-go modules dependencies
+RUN apt update
+
+# copy code
+WORKDIR /go/src/app
+COPY ./emailnotification .
+
+RUN go mod download
+RUN go install
+
+RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh
+RUN chmod +x ./wait-for-it.sh ./docker-entrypoint.sh
+
+ENTRYPOINT ["./docker-entrypoint.sh"]
+CMD ["emailnotification"]
+
+EXPOSE 8001
\ No newline at end of file
diff --git a/src/emailnotification/docker-entrypoint.sh b/src/emailnotification/docker-entrypoint.sh
new file mode 100644
index 0000000000000000000000000000000000000000..9e499df2a2095fb4dc62ba9e4ab62f1bfb0506b0
--- /dev/null
+++ b/src/emailnotification/docker-entrypoint.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Abort on any error (including if wait-for-it fails).
+set -e
+# Wait for nats
+if [ -n "$NATS_CONNECT" ]; then
+/go/src/app/wait-for-it.sh "$NATS_CONNECT" -t 20
+fi
+# Run the main container command.
+exec "$@"
\ No newline at end of file
diff --git a/src/emailnotification/go.mod b/src/emailnotification/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..a360529a0794f0e09f51c469d39dd951a2f06ca3
--- /dev/null
+++ b/src/emailnotification/go.mod
@@ -0,0 +1,19 @@
+module gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/emailnotification
+
+go 1.20
+
+require (
+	github.com/joho/godotenv v1.5.1
+	github.com/nats-io/nats.go v1.27.1
+)
+
+require (
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/klauspost/compress v1.16.5 // indirect
+	github.com/nats-io/nats-server/v2 v2.9.19 // indirect
+	github.com/nats-io/nkeys v0.4.4 // indirect
+	github.com/nats-io/nuid v1.0.1 // indirect
+	golang.org/x/crypto v0.9.0 // indirect
+	golang.org/x/sys v0.8.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
+)
diff --git a/src/emailnotification/go.sum b/src/emailnotification/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..2d4b752023f0f3424adb139e9c5abec6dafdee45
--- /dev/null
+++ b/src/emailnotification/go.sum
@@ -0,0 +1,28 @@
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
+github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4=
+github.com/nats-io/nats-server/v2 v2.9.19 h1:OF9jSKZGo425C/FcVVIvNgpd36CUe7aVTTXEZRJk6kA=
+github.com/nats-io/nats-server/v2 v2.9.19/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw=
+github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo=
+github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
+github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
+github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
+github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
diff --git a/src/emailnotification/main.go b/src/emailnotification/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..d5723b7859b2932cbf44856d8b6a243baba639e0
--- /dev/null
+++ b/src/emailnotification/main.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+
+	"github.com/nats-io/nats.go"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/emailnotification/model"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/emailnotification/service"
+)
+
+func main() {
+	nc, err := service.ConnectToNats()
+	if err != nil {
+		log.Fatalf("unable to connect to nats %v", err)
+	}
+	nc.Subscribe("confirmOrder.*", func(m *nats.Msg) {
+		var (
+			req model.EmialContent
+			res model.Response
+		)
+		if err := json.Unmarshal(m.Data, &req); err != nil {
+			panic(err)
+		}
+
+		emailadre, emailcontent, emailtype := service.CreateEmail(req, "confirm")
+		err := service.SendEmail(emailadre, emailcontent, emailtype)
+		if err != nil {
+			res.Send = false
+		} else {
+			res.Send = true
+		}
+		e, errMarshal := json.Marshal(res)
+		if errMarshal != nil {
+			fmt.Println(errMarshal)
+			return
+		}
+		nc.Publish(m.Reply, []byte(e))
+	})
+
+	nc.Subscribe("confirmCancel.*", func(m *nats.Msg) {
+		var (
+			req model.EmialContent
+			res model.Response
+		)
+		if err := json.Unmarshal(m.Data, &req); err != nil {
+			panic(err)
+		}
+		emailadre, emailcontent, emailtype := service.CreateEmail(req, "cancel")
+		if err := service.SendEmail(emailadre, emailcontent, emailtype); err != nil {
+			res.Send = false
+		} else {
+			res.Send = true
+		}
+		e, errMarshal := json.Marshal(res)
+		if errMarshal != nil {
+			fmt.Println(errMarshal)
+			return
+		}
+		nc.Publish(m.Reply, []byte(e))
+	})
+
+	if err := http.ListenAndServe(":8181", nil); err != nil {
+		log.Fatal(err)
+	}
+
+}
diff --git a/src/emailnotification/model/emailContent.go b/src/emailnotification/model/emailContent.go
new file mode 100644
index 0000000000000000000000000000000000000000..5a7867cd142ec0802e44c25d342aed04a441285c
--- /dev/null
+++ b/src/emailnotification/model/emailContent.go
@@ -0,0 +1,10 @@
+package model
+
+type EmialContent struct {
+	OrderID     string `json:"orderid"`
+	Name        string `json:"name"`
+	AwayMatch   bool   `json:"awaymatch"`
+	Location    string `json:"location"`
+	Date        string `json:"date"`
+	Emailadress string `json:"emailadress"`
+}
diff --git a/src/emailnotification/model/natsResponse.go b/src/emailnotification/model/natsResponse.go
new file mode 100644
index 0000000000000000000000000000000000000000..ea4856d8c7a413929becc22fe1eff1dc742d89f7
--- /dev/null
+++ b/src/emailnotification/model/natsResponse.go
@@ -0,0 +1,5 @@
+package model
+
+type Response struct {
+	Send bool `json:"send"`
+}
diff --git a/src/emailnotification/service/email.go b/src/emailnotification/service/email.go
new file mode 100644
index 0000000000000000000000000000000000000000..cacd6c2a4b1874e91b5f84e1ec534944ab0911d7
--- /dev/null
+++ b/src/emailnotification/service/email.go
@@ -0,0 +1,62 @@
+package service
+
+import (
+	"fmt"
+	"log"
+	"net/mail"
+	"net/smtp"
+	"os"
+	"strings"
+
+	"github.com/joho/godotenv"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/emailnotification/model"
+)
+
+func CreateEmail(emailContenct model.EmialContent, subject string) (string, string, string) {
+	if subject == "confirm" {
+		return emailContenct.Emailadress, fmt.Sprintf("Hallo Herr/Frau, %s\r\nHiermit bestaetigen wird deine Bestellung fuer das VFB Spiel", emailContenct.Name), "Confirm Order"
+	}
+	if subject == "cancel" {
+		return emailContenct.Emailadress, fmt.Sprintf("Hallo Herr/Frau, %s\r\nHiermit bestaetigen wird die Stornierung deiner Bestellung fuer das VFB Spiel in %s, am %s", emailContenct.Name, emailContenct.Location, emailContenct.Date), "Confirm Cancelation"
+	}
+	return "", "", ""
+}
+func SendEmail(receiver string, body string, subject string) error {
+	err := godotenv.Load(".env")
+
+	if err != nil {
+		log.Fatalf("Error loading .env file")
+		return fmt.Errorf("Error loading .env file")
+	}
+
+	from := mail.Address{
+		Name:    "Highlander Ticketing",
+		Address: os.Getenv("EMAIL_ADRESS"),
+	}
+
+	toList := []string{}
+	toList = append(toList, receiver)
+
+	header := make(map[string]string)
+	header["From"] = from.String()
+	header["To"] = strings.Join(toList, ", ")
+	header["Subject"] = subject
+
+	message := ""
+	for key, value := range header {
+		message += fmt.Sprintf("%s: %s\r\n", key, value)
+	}
+	message += "\r\n" + body
+
+	smtpServer := "smtp.web.de"
+	smtpPort := "587"
+	password := os.Getenv("EMAIL_PW")
+
+	auth := smtp.PlainAuth("", from.Address, password, smtpServer)
+
+	err1 := smtp.SendMail(smtpServer+":"+smtpPort, auth, from.Address, toList, []byte(message))
+	if err1 != nil {
+		return err1
+	}
+	return nil
+}
diff --git a/src/emailnotification/service/nats.go b/src/emailnotification/service/nats.go
new file mode 100644
index 0000000000000000000000000000000000000000..f2a82a6d1f5c71e0c2c27c9b5803cb768179f35b
--- /dev/null
+++ b/src/emailnotification/service/nats.go
@@ -0,0 +1,23 @@
+package service
+
+import (
+	"log"
+	"os"
+
+	"github.com/joho/godotenv"
+	"github.com/nats-io/nats.go"
+)
+
+func ConnectToNats() (*nats.Conn, error) {
+	if err := godotenv.Load(".env"); err != nil {
+		log.Fatalf("Error loading .env file")
+	}
+	uri := os.Getenv("NATS_URI")
+	var err error
+	var nc *nats.Conn
+	nc, err = nats.Connect(uri)
+	if err != nil {
+		log.Fatal("Error establishing connection to NATS:", err)
+	}
+	return nc, nil
+}
diff --git a/src/emailnotification/wait-for-it.sh b/src/emailnotification/wait-for-it.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3974640b053e6f84a21c292c01e3674348445831
--- /dev/null
+++ b/src/emailnotification/wait-for-it.sh
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+# Use this script to test if a given TCP host/port are available
+
+WAITFORIT_cmdname=${0##*/}
+
+echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
+
+usage()
+{
+    cat << USAGE >&2
+Usage:
+    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
+    -h HOST | --host=HOST       Host or IP under test
+    -p PORT | --port=PORT       TCP port under test
+                                Alternatively, you specify the host and port as host:port
+    -s | --strict               Only execute subcommand if the test succeeds
+    -q | --quiet                Don't output any status messages
+    -t TIMEOUT | --timeout=TIMEOUT
+                                Timeout in seconds, zero for no timeout
+    -- COMMAND ARGS             Execute command with args after the test finishes
+USAGE
+    exit 1
+}
+
+wait_for()
+{
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    else
+        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
+    fi
+    WAITFORIT_start_ts=$(date +%s)
+    while :
+    do
+        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
+            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
+            WAITFORIT_result=$?
+        else
+            (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
+            WAITFORIT_result=$?
+        fi
+        if [[ $WAITFORIT_result -eq 0 ]]; then
+            WAITFORIT_end_ts=$(date +%s)
+            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
+            break
+        fi
+        sleep 1
+    done
+    return $WAITFORIT_result
+}
+
+wait_for_wrapper()
+{
+    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
+    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    else
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    fi
+    WAITFORIT_PID=$!
+    trap "kill -INT -$WAITFORIT_PID" INT
+    wait $WAITFORIT_PID
+    WAITFORIT_RESULT=$?
+    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    fi
+    return $WAITFORIT_RESULT
+}
+
+# process arguments
+while [[ $# -gt 0 ]]
+do
+    case "$1" in
+        *:* )
+        WAITFORIT_hostport=(${1//:/ })
+        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
+        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
+        shift 1
+        ;;
+        --child)
+        WAITFORIT_CHILD=1
+        shift 1
+        ;;
+        -q | --quiet)
+        WAITFORIT_QUIET=1
+        shift 1
+        ;;
+        -s | --strict)
+        WAITFORIT_STRICT=1
+        shift 1
+        ;;
+        -h)
+        WAITFORIT_HOST="$2"
+        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
+        shift 2
+        ;;
+        --host=*)
+        WAITFORIT_HOST="${1#*=}"
+        shift 1
+        ;;
+        -p)
+        WAITFORIT_PORT="$2"
+        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --port=*)
+        WAITFORIT_PORT="${1#*=}"
+        shift 1
+        ;;
+        -t)
+        WAITFORIT_TIMEOUT="$2"
+        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --timeout=*)
+        WAITFORIT_TIMEOUT="${1#*=}"
+        shift 1
+        ;;
+        --)
+        shift
+        WAITFORIT_CLI=("$@")
+        break
+        ;;
+        --help)
+        usage
+        ;;
+        *)
+        echoerr "Unknown argument: $1"
+        usage
+        ;;
+    esac
+done
+
+if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
+    echoerr "Error: you need to provide a host and port to test."
+    usage
+fi
+
+WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
+WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
+WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
+WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
+
+# Check to see if timeout is from busybox?
+WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
+WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
+
+WAITFORIT_BUSYTIMEFLAG=""
+if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
+    WAITFORIT_ISBUSY=1
+    # Check if busybox timeout uses -t flag
+    # (recent Alpine versions don't support -t anymore)
+    if timeout &>/dev/stdout | grep -q -e '-t '; then
+        WAITFORIT_BUSYTIMEFLAG="-t"
+    fi
+else
+    WAITFORIT_ISBUSY=0
+fi
+
+if [[ $WAITFORIT_CHILD -gt 0 ]]; then
+    wait_for
+    WAITFORIT_RESULT=$?
+    exit $WAITFORIT_RESULT
+else
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        wait_for_wrapper
+        WAITFORIT_RESULT=$?
+    else
+        wait_for
+        WAITFORIT_RESULT=$?
+    fi
+fi
+
+if [[ $WAITFORIT_CLI != "" ]]; then
+    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
+        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
+        exit $WAITFORIT_RESULT
+    fi
+    exec "${WAITFORIT_CLI[@]}"
+else
+    exit $WAITFORIT_RESULT
+fi
\ No newline at end of file
diff --git a/src/highlanderticketing/Dockerfile b/src/highlanderticketing/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..13e739190a4ac6d3e89a01e274f3cc61f1e1519a
--- /dev/null
+++ b/src/highlanderticketing/Dockerfile
@@ -0,0 +1,18 @@
+FROM golang:1.20-buster
+# non-go modules dependencies
+RUN apt update
+
+# copy code and protobuf
+WORKDIR /go/src/app
+COPY ./highlanderticketing .
+
+RUN go mod download
+RUN go install
+
+RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/81b1373f17855a4dc21156cfe1694c31d7d1792e/wait-for-it.sh
+RUN chmod +x ./wait-for-it.sh ./docker-entrypoint.sh
+
+ENTRYPOINT ["./docker-entrypoint.sh"]
+CMD ["highlanderticketing"]
+
+EXPOSE 8000
\ No newline at end of file
diff --git a/src/highlanderticketing/api/match.go b/src/highlanderticketing/api/match.go
new file mode 100644
index 0000000000000000000000000000000000000000..cd4fea9151aacb9c822cd97a8224a6637a08a732
--- /dev/null
+++ b/src/highlanderticketing/api/match.go
@@ -0,0 +1,121 @@
+package api
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"time"
+
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+)
+
+func GetMatchesOfApi(apiUrl string) ([]*model.Match, error) {
+	data, err := getData(apiUrl)
+	if err != nil {
+		log.Errorf("Failure loading data of match %v", err)
+		return []*model.Match{}, err
+	}
+	matches, err := formatJsonToMatch(data)
+	if err != nil {
+		log.Errorf("Failure formating match %v", err)
+		return []*model.Match{}, err
+	}
+	log.Info("Got matches of API")
+	return matches, nil
+}
+
+func GetlatestMatchesOfApi(url string, updateChan chan<- *model.Match) error {
+	data, err := getData(url)
+	if err != nil {
+		log.Errorf("Failure loading data of match %v", err)
+		return err
+	}
+	matches, err := formatJsonToMatch(data)
+	if err != nil {
+		log.Errorf("Failure formating match %v", err)
+		return err
+	}
+	for _, match := range matches {
+		updateChan <- match
+	}
+	log.Info("added matches to chain")
+	return nil
+}
+
+func getData(apiUrl string) ([]byte, error) {
+	request, err := http.NewRequest("GET", apiUrl, nil)
+
+	if err != nil {
+		log.Errorf("Failure creating request %v", err)
+		return []byte{}, err
+	}
+	client := &http.Client{}
+	response, err := client.Do(request)
+
+	if err != nil {
+		log.Errorf("Failure making request %v", err)
+		return []byte{}, err
+	}
+
+	responseBody, err := io.ReadAll(response.Body)
+
+	if err != nil {
+		log.Errorf("Failure searalizing request %v", err)
+		return []byte{}, err
+	}
+	defer response.Body.Close()
+	log.Info("Got data of API")
+	return responseBody, nil
+}
+
+func formatJsonToMatch(jsonArray []byte) ([]*model.Match, error) {
+	var matches []*model.Match
+	var results []map[string]interface{}
+
+	if err := json.Unmarshal(jsonArray, &results); err != nil {
+		log.Errorf("Failure unmarshaling data %v", err)
+		return nil, err
+	}
+
+	for _, result := range results {
+		var match model.Match
+		match.ExternalID = int64(result["matchID"].(float64))
+		match.LeagueName = result["leagueName"].(string)
+
+		matchDate, err := time.Parse("2006-01-02T15:04:05", result["matchDateTime"].(string))
+		if err != nil {
+			log.Errorf("Failure parsing date %v", err)
+			return nil, err
+		}
+		match.Date = matchDate
+
+		if team1, ok := result["team1"].(map[string]interface{}); ok {
+			if name, ok := team1["shortName"].(string); ok {
+				match.Location = name
+			}
+		}
+
+		if team1, ok := result["team1"].(map[string]interface{}); ok {
+			if name, ok := team1["teamName"].(string); ok {
+				if name != "VfB Stuttgart" {
+					match.Opponenent = name
+				}
+			}
+		}
+
+		if team2, ok := result["team2"].(map[string]interface{}); ok {
+			if name, ok := team2["teamName"].(string); ok {
+				if name == "VfB Stuttgart" {
+					match.AwayMatch = true
+				} else {
+					match.AwayMatch = false
+					match.Opponenent = name
+				}
+			}
+		}
+		matches = append(matches, &match)
+	}
+	log.Info("formated matches in json")
+	return matches, nil
+}
diff --git a/src/highlanderticketing/config/oauthconf.go b/src/highlanderticketing/config/oauthconf.go
new file mode 100644
index 0000000000000000000000000000000000000000..60cc802a1013437dcfdd3cc5fc396e184d35dccc
--- /dev/null
+++ b/src/highlanderticketing/config/oauthconf.go
@@ -0,0 +1,42 @@
+package config
+
+import (
+	"log"
+	"os"
+
+	"github.com/joho/godotenv"
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/google"
+)
+
+func GetOAuthConfigLogin() *oauth2.Config {
+	if err := godotenv.Load(".env"); err != nil {
+		log.Fatalf("Error loading .env file")
+	}
+	return &oauth2.Config{
+		ClientID:     os.Getenv("CLIENT_ID"),
+		ClientSecret: os.Getenv("CLIENT_SECRET"),
+		RedirectURL:  "http://localhost:8000/callback/login",
+		Scopes: []string{
+			"https://www.googleapis.com/auth/userinfo.email",
+			"https://www.googleapis.com/auth/userinfo.profile",
+		},
+		Endpoint: google.Endpoint,
+	}
+}
+
+func GetOAuthConfigRegister() *oauth2.Config {
+	if err := godotenv.Load(".env"); err != nil {
+		log.Fatalf("Error loading .env file")
+	}
+	return &oauth2.Config{
+		ClientID:     os.Getenv("CLIENT_ID"),
+		ClientSecret: os.Getenv("CLIENT_SECRET"),
+		RedirectURL:  "http://localhost:8000/callback/register",
+		Scopes: []string{
+			"https://www.googleapis.com/auth/userinfo.email",
+			"https://www.googleapis.com/auth/userinfo.profile",
+		},
+		Endpoint: google.Endpoint,
+	}
+}
diff --git a/src/highlanderticketing/db/db.go b/src/highlanderticketing/db/db.go
new file mode 100644
index 0000000000000000000000000000000000000000..a2e6500b670b4ef7bc59be021edb2fec238be262
--- /dev/null
+++ b/src/highlanderticketing/db/db.go
@@ -0,0 +1,60 @@
+package db
+
+import (
+	"context"
+	"sync"
+
+	log "github.com/sirupsen/logrus"
+
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+var (
+	clientInstance      *mongo.Client
+	clientInstanceError error
+	clientOnce          sync.Once
+)
+
+const (
+	CONNECTIONSTRING = "mongodb://mongo:27017"
+	DB               = "db_issue_manager"
+	DBUSER           = "db_user"
+	MATCHES          = "col_matches"
+	USERS            = "col_users"
+	USERS_TEST       = "col_users_test"
+	POOL_SIZE        = 10000 // Anzahl der Verbindungen im Pool
+)
+
+func GetMongoClient() (*mongo.Client, error) {
+	clientOnce.Do(func() {
+		// Erstelle den Verbindungspool
+		clientOptions := options.Client().ApplyURI(CONNECTIONSTRING)
+		clientOptions.SetMaxPoolSize(POOL_SIZE)
+		client, err := mongo.Connect(context.TODO(), clientOptions)
+		if err != nil {
+
+			clientInstanceError = err
+			log.Errorf("Failure instancing client %v", err)
+		}
+		err = client.Ping(context.TODO(), nil)
+		if err != nil {
+			clientInstanceError = err
+			log.Errorf("Failure pinging client %v", err)
+		}
+		clientInstance = client
+	})
+	log.Info("client returned")
+	return clientInstance, clientInstanceError
+}
+func CloseMongoClient() error {
+	if clientInstance != nil {
+		err := clientInstance.Disconnect(context.Background())
+		if err != nil {
+			log.Errorf("Failure disconnecting client %v", err)
+			return err
+		}
+	}
+	log.Info("client disconnected")
+	return nil
+}
diff --git a/src/highlanderticketing/docker-entrypoint.sh b/src/highlanderticketing/docker-entrypoint.sh
new file mode 100644
index 0000000000000000000000000000000000000000..8e1940f55f098449f0d5294e970b18d26ddb4358
--- /dev/null
+++ b/src/highlanderticketing/docker-entrypoint.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+# Abort on any error (including if wait-for-it fails).
+set -e
+# Wait for DB
+if [ -n "$DB_CONNECT" ]; then
+/go/src/app/wait-for-it.sh "$DB_CONNECT" -t 20
+fi
+# Wait for nats
+if [ -n "$NATS_CONNECT" ]; then
+/go/src/app/wait-for-it.sh "$DB_CONNECT" -t 20
+fi
+# Wait for emailnotification
+if [ -n "$NATS_CONNECT" ]; then
+/go/src/app/wait-for-it.sh "$DB_CONNECT" -t 20
+fi
+# Run the main container command.
+exec "$@"
\ No newline at end of file
diff --git a/src/highlanderticketing/go.mod b/src/highlanderticketing/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..442c668bd6498691f7998b87d59e3e00c1b2f548
--- /dev/null
+++ b/src/highlanderticketing/go.mod
@@ -0,0 +1,39 @@
+module gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing
+
+go 1.20
+
+require (
+	github.com/joho/godotenv v1.5.1
+	github.com/nats-io/nats.go v1.27.1
+	golang.org/x/oauth2 v0.9.0
+)
+
+require (
+	cloud.google.com/go/compute/metadata v0.2.0 // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/golang/snappy v0.0.1 // indirect
+	github.com/klauspost/compress v1.16.5 // indirect
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/nats-io/nats-server/v2 v2.9.19 // indirect
+	github.com/nats-io/nkeys v0.4.4 // indirect
+	github.com/nats-io/nuid v1.0.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.1 // indirect
+	github.com/xdg-go/stringprep v1.0.3 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	golang.org/x/crypto v0.10.0 // indirect
+	golang.org/x/net v0.11.0 // indirect
+	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
+	golang.org/x/sys v0.9.0 // indirect
+	golang.org/x/text v0.10.0 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
+)
+
+require (
+	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/gorilla/mux v1.8.0
+	github.com/sirupsen/logrus v1.9.3
+	go.mongodb.org/mongo-driver v1.11.7
+	google.golang.org/protobuf v1.30.0 // indirect
+)
diff --git a/src/highlanderticketing/go.sum b/src/highlanderticketing/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..ab1e0c32aedf6b58739c90b12d316f49d1775018
--- /dev/null
+++ b/src/highlanderticketing/go.sum
@@ -0,0 +1,102 @@
+cloud.google.com/go/compute/metadata v0.2.0 h1:nBbNSZyDpkNlo3DepaaLKVuO7ClyifSAmNloSCZrHnQ=
+cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
+github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4=
+github.com/nats-io/nats-server/v2 v2.9.19 h1:OF9jSKZGo425C/FcVVIvNgpd36CUe7aVTTXEZRJk6kA=
+github.com/nats-io/nats-server/v2 v2.9.19/go.mod h1:aTb/xtLCGKhfTFLxP591CMWfkdgBmcUUSkiSOe5A3gw=
+github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo=
+github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc=
+github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA=
+github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64=
+github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
+github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs=
+go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
+golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
+golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
+golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/src/highlanderticketing/handler/health.go b/src/highlanderticketing/handler/health.go
new file mode 100644
index 0000000000000000000000000000000000000000..61dc8dd4750ec01a3a1c6d24be603c003870ab18
--- /dev/null
+++ b/src/highlanderticketing/handler/health.go
@@ -0,0 +1,14 @@
+package handler
+
+import (
+	"io"
+	"net/http"
+
+	log "github.com/sirupsen/logrus"
+)
+
+func Health(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	log.Infof("healthy")
+	io.WriteString(w, `{"alive": true}`)
+}
diff --git a/src/highlanderticketing/handler/match.go b/src/highlanderticketing/handler/match.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ef79c80c0cba4694cde131765719ea888934fac
--- /dev/null
+++ b/src/highlanderticketing/handler/match.go
@@ -0,0 +1,170 @@
+package handler
+
+import (
+	"encoding/json"
+	"net/http"
+
+	log "github.com/sirupsen/logrus"
+
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/service"
+)
+
+func CreateMatch(w http.ResponseWriter, r *http.Request) {
+	var match *model.Match
+	if err, _ := CheckAccessToken(w, r, true); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	match, err := getMatch(r)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+
+	if err := service.CreateMatch(match); err != nil {
+		log.Errorf("Error calling service CreateMatch: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, match)
+}
+
+func UpdateMatch(w http.ResponseWriter, r *http.Request) {
+	if err, _ := CheckAccessToken(w, r, true); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Please parse in ID at the url %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	MatchToUpdate, err := getMatch(r)
+	if err != nil {
+		log.Errorf("Match not found %v", err)
+		return
+	}
+	MatchUpdated, err := service.UpdateMatch(id, MatchToUpdate)
+	if err != nil {
+		log.Errorf("Campaign could not be updated %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	sendJson(w, MatchUpdated)
+}
+
+func GetAllMatches(w http.ResponseWriter, r *http.Request) {
+	if err, _ := CheckAccessToken(w, r, false); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	matches, err := service.GetAllMatches()
+	if err != nil {
+		log.Errorf("Error calling service GetAllMatches: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, matches)
+}
+
+func GetMatchByID(w http.ResponseWriter, r *http.Request) {
+	if err, _ := CheckAccessToken(w, r, false); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Please parse in ID at the url %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+	}
+	campaign, err := service.GetMatchByID(id)
+	if err != nil {
+		log.Errorf("No Match with this ID %v", err)
+		return
+	}
+	sendJson(w, campaign)
+}
+
+func DeleteMatch(w http.ResponseWriter, r *http.Request) {
+	if err, _ := CheckAccessToken(w, r, true); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Please parse in ID at the url %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	} else {
+		log.Infof("ID to delete was found in struct")
+	}
+	err1 := service.DeleteMatch(id)
+	if err1 != nil {
+		log.Errorf("Match could not be deleted %v", err1)
+		http.Error(w, err1.Error(), http.StatusInternalServerError)
+		return
+	} else {
+		log.Infof("ID deleted")
+		log.Tracef("ID: %v deleted", id)
+	}
+	sendJson(w, result{Success: "OK"})
+}
+
+func UpdateTickets(w http.ResponseWriter, r *http.Request) {
+	if err, _ := CheckAccessToken(w, r, true); err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Please parse in ID at the url %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	MatchToUpdate, err := getMatch(r)
+	if err != nil {
+		log.Errorf("Match not found %v", err)
+		return
+	}
+	MatchUpdated, err := service.UpdateTickets(id, MatchToUpdate)
+	if err != nil {
+		log.Errorf("Match could not be updated %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	sendJson(w, MatchUpdated)
+}
+
+// nur intern
+func DeleteAllMatches(w http.ResponseWriter, r *http.Request) {
+	err := service.DeleteAllMatches()
+	if err != nil {
+		log.Errorf("Match could not be deleted %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	} else {
+		log.Infof("Matches deleted")
+	}
+	sendJson(w, result{Success: "OK"})
+}
+
+func getMatch(r *http.Request) (*model.Match, error) {
+	var match *model.Match
+	err := json.NewDecoder(r.Body).Decode(&match)
+	if err != nil {
+		log.Errorf("Can't serialize request body to campaign struct: %v", err)
+		return nil, err
+	} else {
+		log.Infof("request body seralized to campaign struct")
+		log.Tracef("body seralized in struct campaign: %v", match)
+	}
+	return match, nil
+}
diff --git a/src/highlanderticketing/handler/oauth.go b/src/highlanderticketing/handler/oauth.go
new file mode 100644
index 0000000000000000000000000000000000000000..6fbeb9141f4ac0f92160ddba7c1ba6d8e357f1bd
--- /dev/null
+++ b/src/highlanderticketing/handler/oauth.go
@@ -0,0 +1,138 @@
+package handler
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"time"
+
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/config"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/service"
+
+	"github.com/dgrijalva/jwt-go"
+	"golang.org/x/oauth2"
+)
+
+var secretKey = []byte("mysecretkey")
+
+// aufgerufen, wenn der benutzer sich ein loggt, ruft oauth conf auf, leitet zu authorisisuerngsseite um
+func HandleLogin(w http.ResponseWriter, r *http.Request) {
+	oauthConfig := config.GetOAuthConfigLogin()
+	url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline)
+	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+}
+
+// aufgerufen, wenn der benutzer sich registriert, ruft oauth conf auf, leitet zu authorisisuerngsseite um
+func HandleRegister(w http.ResponseWriter, r *http.Request) {
+	oauthConfig := config.GetOAuthConfigRegister()
+	url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline)
+	http.Redirect(w, r, url, http.StatusTemporaryRedirect)
+}
+
+// aufgerufen, wenn autorisierungscode von google kommt, tauscht code gegen access token ein, und registriert den benutzer
+func HandleCallbackRegister(w http.ResponseWriter, r *http.Request) {
+	oauthConfig := config.GetOAuthConfigRegister()
+	code := r.URL.Query().Get("code")
+	token, err := oauthConfig.Exchange(context.Background(), code)
+	if err != nil {
+		log.Errorf("Failure exchanging authorisingcode: %v", err)
+		http.Error(w, "Fehler beim Authentifizieren", http.StatusInternalServerError)
+		return
+	}
+	err = service.Register(token.AccessToken)
+	if err != nil {
+		log.Errorf("Failure registrating: %v", err)
+		io.WriteString(w, `user besteht bereits`)
+	} else {
+		sendJson(w, "user erfolgreich angelegt")
+	}
+
+}
+
+// aufgerufen, wenn autorisierungscode von google kommt, tauscht code gegen Zugriffstoken ein, generiert jwt token
+func HandleCallbackLogin(w http.ResponseWriter, r *http.Request) {
+
+	oauthConfig := config.GetOAuthConfigLogin()
+	code := r.URL.Query().Get("code")
+	token, err := oauthConfig.Exchange(context.Background(), code)
+	if err != nil {
+		log.Errorf("Failure exchanging authorisingcode: %v", err)
+		http.Error(w, "Fehler beim Authentifizieren", http.StatusInternalServerError)
+		return
+	}
+
+	user, err := service.GetUserInfoByToken(token.AccessToken)
+	if err != nil {
+		sendJson(w, err)
+		return
+	}
+	userfound, errUser := service.GetUserByEmail(user.Email)
+	if errUser != nil {
+		sendJson(w, err)
+		sendJson(w, "user nicht registriert")
+		return
+	}
+
+	tokenJwt := jwt.New(jwt.SigningMethodHS256)
+	claims := tokenJwt.Claims.(jwt.MapClaims)
+	claims["username"] = userfound.Email
+	claims["exp"] = time.Now().Add(time.Minute * 5).Unix()
+
+	tokenString, err := tokenJwt.SignedString(secretKey)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+
+	log.Info("Login erfolgreich")
+	sendJson(w, tokenString)
+}
+
+// überprüft den jwt token
+func CheckAccessToken(w http.ResponseWriter, r *http.Request, needAdmin bool) (error, string) {
+	tokenString, err := getBearerToken(r)
+	if err != nil {
+		log.Errorf("no bearer token handed over: %v", err)
+		return err, ""
+	}
+
+	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
+		return secretKey, nil
+	})
+
+	if err != nil {
+		w.WriteHeader(http.StatusUnauthorized)
+		fmt.Fprint(w, "Ungültiges Authorization-Token")
+		return err, ""
+	}
+	var username string
+	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
+		username = claims["username"].(string)
+	} else {
+		w.WriteHeader(http.StatusUnauthorized)
+		fmt.Fprint(w, "Ungültiges Authorization-Token")
+	}
+	if needAdmin {
+		err := checkAdmin(username)
+		if err != nil {
+			return err, ""
+		}
+	}
+	log.Info("token checked succesfully")
+	return nil, username
+}
+
+func checkAdmin(userEmail string) error {
+	user, err := service.GetUserByEmail(userEmail)
+	if err != nil {
+		log.Errorf("failure getting user by email: %v", err)
+		return err
+	}
+	if user.IsAdmin {
+		return nil
+	} else {
+		return fmt.Errorf("User has not adminrights")
+	}
+}
diff --git a/src/highlanderticketing/handler/order.go b/src/highlanderticketing/handler/order.go
new file mode 100644
index 0000000000000000000000000000000000000000..a5868eebedb660bbfa00d5005ab5f99487ee563a
--- /dev/null
+++ b/src/highlanderticketing/handler/order.go
@@ -0,0 +1,102 @@
+package handler
+
+import (
+	"encoding/json"
+	"net/http"
+
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/service"
+)
+
+func AddMatchOrder(w http.ResponseWriter, r *http.Request) {
+	err, userOfOrder := CheckAccessToken(w, r, false)
+	if err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Eror gettin id in request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	order, err := getOrder(r)
+	if err != nil {
+		log.Errorf("Eror gettin order in request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	internalUser, err := service.GetUserByEmail(userOfOrder)
+	if err != nil {
+		log.Errorf("Failure loading internal user Info %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+	order.User = *internalUser
+	err = service.AddMatchOrder(id, order)
+	if err != nil {
+		log.Errorf("Failure adding order to match with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, order)
+
+}
+func CancelOrder(w http.ResponseWriter, r *http.Request) {
+	err, userOfOrder := CheckAccessToken(w, r, false)
+	if err != nil {
+		log.Errorf("Eror checking AccessToken: %v", err)
+		http.Error(w, err.Error(), http.StatusUnauthorized)
+		return
+	}
+	id, err := getID(r)
+	if err != nil {
+		log.Errorf("Eror gettin id in request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	orderId, err := getOrderID(r)
+	if err != nil {
+		log.Errorf("Eror gettin order in request: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	order, err := service.GetOrderById(orderId)
+	if err != nil {
+		log.Errorf("Eror order internal: %v", err)
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	internalUser, err := service.GetUserByEmail(userOfOrder)
+	if err != nil {
+		log.Errorf("Failure loading internal user Info %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+	if order.User != *internalUser {
+		http.Error(w, "can not cancel order with this user", http.StatusInternalServerError)
+		sendJson(w, "user is not allowed to cancel this order")
+		return
+	}
+
+	err = service.CancelOrder(id, order)
+	if err != nil {
+		log.Errorf("Failure adding donation to campaign with ID %v: %v", id, err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	sendJson(w, order)
+}
+
+func getOrder(r *http.Request) (*model.Order, error) {
+	var order model.Order
+	err := json.NewDecoder(r.Body).Decode(&order)
+	if err != nil {
+		log.Errorf("Can't serialize request body to order struct: %v", err)
+		return nil, err
+	} else {
+		log.Infof("request body seralized to order struct")
+		log.Tracef("body seralized in struct order: %v", order)
+	}
+	return &order, nil
+}
diff --git a/src/highlanderticketing/handler/utils.go b/src/highlanderticketing/handler/utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..8304c975d6c2d3b06ccaaf286f003477ebed1de0
--- /dev/null
+++ b/src/highlanderticketing/handler/utils.go
@@ -0,0 +1,65 @@
+package handler
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	"github.com/gorilla/mux"
+	log "github.com/sirupsen/logrus"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
+
+type result struct {
+	Success string `json:"success"`
+}
+
+func sendJson(w http.ResponseWriter, value interface{}) {
+	w.Header().Set("Content-Type", "application/json")
+	if err := json.NewEncoder(w).Encode(value); err != nil {
+		log.Errorf("Failure encoding value to JSON: %v", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+}
+func getID(r *http.Request) (primitive.ObjectID, error) {
+	vars := mux.Vars(r)
+	id := vars["id"]
+
+	objectID, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		log.Errorf("Can't get ObjectID from request: %v", err)
+		return primitive.NilObjectID, err
+	}
+
+	return objectID, nil
+}
+func getOrderID(r *http.Request) (primitive.ObjectID, error) {
+	vars := mux.Vars(r)
+	id := vars["orderid"]
+
+	objectID, err := primitive.ObjectIDFromHex(id)
+	if err != nil {
+		log.Errorf("Can't get ObjectID from request: %v", err)
+		return primitive.NilObjectID, err
+	}
+
+	return objectID, nil
+}
+
+func getBearerToken(r *http.Request) (string, error) {
+	reqToken := r.Header.Get("Authorization")
+	if reqToken == "" {
+		log.Error("no Bearer Token in Request")
+		return "", fmt.Errorf("Please parse in Bearer Token")
+
+	}
+	splitToken := strings.Split(reqToken, "Bearer")
+	if len(splitToken) != 2 {
+		log.Error("Beaerer Token could not be extracted")
+		return "", fmt.Errorf("Can not extract Token")
+	}
+
+	reqToken = strings.TrimSpace(splitToken[1])
+	return reqToken, nil
+}
diff --git a/src/highlanderticketing/main.go b/src/highlanderticketing/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..7e829b137531a2857b1657834c8558de9b2741d9
--- /dev/null
+++ b/src/highlanderticketing/main.go
@@ -0,0 +1,116 @@
+package main
+
+import (
+	"net/http"
+	"os"
+	"time"
+
+	"github.com/gorilla/mux"
+	"github.com/joho/godotenv"
+	"github.com/nats-io/nats.go"
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/api"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/db"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/handler"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/service"
+)
+
+func main() {
+	updateChan := make(chan *model.Match)
+	service.DeleteAllMatches()
+	service.DeleteAllUsers()
+	/* var userArray []model.User
+	userArray, _ = service.GetAllUsers()
+	fmt.Println(userArray)*/
+
+	go func() {
+		for {
+			err := api.GetlatestMatchesOfApi("https://api.openligadb.de/getmatchesbyteamid/16/10/0", updateChan)
+			if err != nil {
+				log.Println("Fehler beim Abrufen der Matches:", err)
+			}
+			time.Sleep(3 * time.Minute)
+		}
+	}()
+
+	go func() {
+		for {
+			match := <-updateChan
+
+			service.InserExternalMatch(match)
+		}
+	}()
+
+	/*
+		matches, errMatches := api.GetMatchesOfApi("https://api.openligadb.de/getmatchesbyteamid/16/10/0")
+		if errMatches != nil {
+			return
+		}
+		for _, match := range matches {
+			service.CreateMatch(match)
+		}
+	*/
+
+	if err := godotenv.Load(".env"); err != nil {
+		log.Fatalf("Error loading .env file")
+	}
+
+	var natsServer service.NatsServer
+
+	uri := os.Getenv("NATS_URI")
+
+	nc, err := nats.Connect(uri)
+	if err == nil {
+		natsServer.Nc = nc
+	}
+	if err != nil {
+		log.Fatal("Error establishing connection to NATS:", err)
+	}
+
+	log.Println("Starting Highlander Ticketing server")
+	router := mux.NewRouter()
+	router.HandleFunc("/register", handler.HandleRegister).Methods("GET")
+	router.HandleFunc("/callback/register", handler.HandleCallbackRegister).Methods("GET")
+	router.HandleFunc("/login", handler.HandleLogin).Methods("GET")
+	router.HandleFunc("/callback/login", handler.HandleCallbackLogin).Methods("GET")
+	router.HandleFunc("/health", handler.Health).Methods("GET")
+	router.HandleFunc("/match", handler.CreateMatch).Methods("POST")
+	router.HandleFunc("/matches", handler.GetAllMatches).Methods("GET")
+	router.HandleFunc("/match/{id}", handler.GetMatchByID).Methods("GET")
+	router.HandleFunc("/match/{id}", handler.UpdateMatch).Methods("PUT")
+	router.HandleFunc("/match/{id}", handler.DeleteMatch).Methods("DELETE")
+	router.HandleFunc("/match/{id}/updatetickets", handler.UpdateTickets).Methods("PUT")
+	router.HandleFunc("/match/{id}/matchorder", handler.AddMatchOrder).Methods("POST")
+	router.HandleFunc("/match/{id}/cancelorder/{orderid}", handler.CancelOrder).Methods("PUT")
+	if err := http.ListenAndServe(":8000", router); err != nil {
+		log.Fatal(err)
+	}
+
+	err = db.CloseMongoClient()
+	if err != nil {
+		log.Fatal(err)
+	}
+
+}
+
+func init() {
+	//init db
+	_, err := db.GetMongoClient()
+	if err != nil {
+		log.Fatal(err)
+	}
+	// init logger
+	log.SetFormatter(&log.TextFormatter{})
+	log.SetReportCaller(true)
+
+	os.Setenv("LOG_LEVEL", "INFO")
+
+	level, err := log.ParseLevel(os.Getenv("LOG_LEVEL"))
+	if err != nil {
+		log.Info("Log level not specified, set default to: INFO")
+		log.SetLevel(log.InfoLevel)
+		return
+	}
+	log.SetLevel(level)
+}
diff --git a/src/highlanderticketing/model/emailContent.go b/src/highlanderticketing/model/emailContent.go
new file mode 100644
index 0000000000000000000000000000000000000000..5a7867cd142ec0802e44c25d342aed04a441285c
--- /dev/null
+++ b/src/highlanderticketing/model/emailContent.go
@@ -0,0 +1,10 @@
+package model
+
+type EmialContent struct {
+	OrderID     string `json:"orderid"`
+	Name        string `json:"name"`
+	AwayMatch   bool   `json:"awaymatch"`
+	Location    string `json:"location"`
+	Date        string `json:"date"`
+	Emailadress string `json:"emailadress"`
+}
diff --git a/src/highlanderticketing/model/match.go b/src/highlanderticketing/model/match.go
index 82a92f562ffdd3b603cce61fd8389c2c3ed9f058..7c063dd0ae4f401c958eacd0b79658cde7cbc438 100644
--- a/src/highlanderticketing/model/match.go
+++ b/src/highlanderticketing/model/match.go
@@ -1,17 +1,21 @@
 package model
 
-import "google.golang.org/genproto/googleapis/type/date"
+import (
+	"time"
+
+	"go.mongodb.org/mongo-driver/bson/primitive"
+)
 
 type Match struct {
-	ID                    uint
-	InitialTicketAmount   int32
-	AvailableTicketAmount int32
-	AwayMatch             bool
-	Location              string
-	Date                  date.Date
-	Travel                Travel
-	Orders                []Order
+	ID                    primitive.ObjectID `bson:"_id, omitempty"`
+	ExternalID            int64              `bson:"externalID"`
+	Price                 int32              `bson:"price, omitempty"`
+	InitialTicketAmount   int32              `bson:"initial_ticket_amount"`
+	AvailableTicketAmount int32              `bson:"available_ticket_amount"`
+	Opponenent            string             `bson:"opponent"`
+	LeagueName            string             `bson:"league_name"`
+	AwayMatch             bool               `bson:"away_match"`
+	Location              string             `bson:"location"`
+	Date                  time.Time          `bson:"date"`
+	Orders                []Order            `bson:"orders"`
 }
-
-// Funktion ins Modell (siehe
-//Myaktion), welche den available_ Ticket_Amount berechnet
diff --git a/src/highlanderticketing/model/natsResponse.go b/src/highlanderticketing/model/natsResponse.go
new file mode 100644
index 0000000000000000000000000000000000000000..ea4856d8c7a413929becc22fe1eff1dc742d89f7
--- /dev/null
+++ b/src/highlanderticketing/model/natsResponse.go
@@ -0,0 +1,5 @@
+package model
+
+type Response struct {
+	Send bool `json:"send"`
+}
diff --git a/src/highlanderticketing/model/order.go b/src/highlanderticketing/model/order.go
index b4a4fb48b7bf2fa20748d4fd1cd2b0dc2767d1ad..4ccab1876b6cf53b41edc8c6c6e5fa2902a5dcff 100644
--- a/src/highlanderticketing/model/order.go
+++ b/src/highlanderticketing/model/order.go
@@ -1,13 +1,12 @@
 package model
 
+import "go.mongodb.org/mongo-driver/bson/primitive"
+
 type Order struct {
-	OrderType OrderType
-	Amount    int32
-	User      User
+	ID             primitive.ObjectID `bson:"_id, omitempty"`
+	Amount         int32              `bson:"amount"`
+	User           User               `bson:"user, omitempty"`
+	Ordernotified  bool               `bson:"ordernotified, omitempty"`
+	Canceled       bool               `bson:"canceled, omitempty"`
+	Cancelnotified bool               `bson:"cancelnotified, omitempty"`
 }
-type OrderType string
-
-const (
-	MATCHTICKET OrderType = "MATCHTICKET"
-	BUSTICKET   OrderType = "BUSTICKET"
-)
diff --git a/src/highlanderticketing/model/travel.go b/src/highlanderticketing/model/travel.go
deleted file mode 100644
index 9b36343aef09d9ac4d1237c4246ca4d3c3aaffcb..0000000000000000000000000000000000000000
--- a/src/highlanderticketing/model/travel.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package model
-
-import (
-	"google.golang.org/genproto/googleapis/type/date"
-)
-
-type Travel struct {
-	ID                  uint
-	TravelType          TravelType
-	InitialSeatAmount   int32
-	AvailableSeatAmount int32
-	StartLocation       string
-	EndLocation         string
-	StartDate           date.Date
-	Orders              []Order
-}
-
-type TravelType string
-
-const (
-	CAR   TravelType = "CAR"
-	BUS   TravelType = "BUS"
-	PLANE TravelType = "PLANE"
-)
diff --git a/src/highlanderticketing/model/user.go b/src/highlanderticketing/model/user.go
index 799191784f96313c91795144ba074ee015ccb67e..1583dceefa58fcf68079367ebfd0b5d40fd48803 100644
--- a/src/highlanderticketing/model/user.go
+++ b/src/highlanderticketing/model/user.go
@@ -1,7 +1,12 @@
 package model
 
+import "go.mongodb.org/mongo-driver/bson/primitive"
+
 type User struct {
-	FirstName string
-	LastName  string
-	Role      string
+	ID         primitive.ObjectID `bson:"_id,omitempty"`
+	GoogleID   string             `json:"id,omitempty" bson:"google_id"`
+	Email      string             `json:"email" bson:"email"`
+	Name       string             `json:"name" bson:"name"`
+	FamilyName string             `json:"family_name" bson:"family_name"`
+	IsAdmin    bool               `json:"is_admin" bson:"is_admin"`
 }
diff --git a/src/highlanderticketing/service/match.go b/src/highlanderticketing/service/match.go
new file mode 100644
index 0000000000000000000000000000000000000000..8b1c2c9fbe2ae4658c5a8618ffc72586e739f92a
--- /dev/null
+++ b/src/highlanderticketing/service/match.go
@@ -0,0 +1,223 @@
+package service
+
+import (
+	"context"
+	"fmt"
+
+	log "github.com/sirupsen/logrus"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/db"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+)
+
+func CreateMatch(match *model.Match) error {
+	match.ID = primitive.NewObjectID()
+	match.Orders = []model.Order{}
+	client, err := db.GetMongoClient()
+	if err != nil {
+		log.Errorf("Eror gettin db client: %v", err)
+		return err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	_, err = collection.InsertOne(context.TODO(), match)
+	if err != nil {
+		log.Errorf("Erorr inserting match: %v", err)
+		return nil
+	}
+	log.Info("match inserted: %v", match)
+	return nil
+}
+
+func InserExternalMatch(match *model.Match) error {
+	match.ID = primitive.NewObjectID()
+	match.Orders = []model.Order{}
+	existingMatch := &model.Match{}
+	filter := bson.M{"externalID": match.ExternalID}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		log.Errorf("Eror gettin db client: %v", err)
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+	err = collection.FindOne(context.TODO(), filter).Decode(existingMatch)
+	if err == nil {
+		log.Info("match already exists")
+		return fmt.Errorf("Match mit ExternalID %d exists already", match.ExternalID)
+	} else if err != mongo.ErrNoDocuments {
+		return err
+	}
+	_, err = collection.InsertOne(context.TODO(), match)
+	if err != nil {
+		return nil
+	}
+	log.Info("match inserted: %v", match)
+	return nil
+}
+
+func UpdateMatch(matchID primitive.ObjectID, match *model.Match) (*model.Match, error) {
+	result := model.Match{}
+
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+
+	updater := bson.D{primitive.E{Key: "$set", Value: bson.D{
+		primitive.E{Key: "initial_ticket_amount", Value: match.InitialTicketAmount},
+		primitive.E{Key: "external_id", Value: match.ExternalID},
+		primitive.E{Key: "price", Value: match.Price},
+		primitive.E{Key: "opponent", Value: match.Opponenent},
+		primitive.E{Key: "league_name", Value: match.LeagueName},
+		primitive.E{Key: "available_ticket_amount", Value: match.AvailableTicketAmount},
+		primitive.E{Key: "away_match", Value: match.AwayMatch},
+		primitive.E{Key: "location", Value: match.Location},
+		primitive.E{Key: "date", Value: match.Date},
+	}}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		log.Errorf("Eror gettin db client: %v", err)
+		return nil, err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	updateResult, err := collection.UpdateOne(context.TODO(), filter, updater)
+	if err != nil {
+		log.Errorf("Erorr getting collection of db: %v", err)
+		return nil, err
+	}
+
+	if updateResult.ModifiedCount == 0 {
+		return nil, fmt.Errorf("no document was updated")
+	}
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		log.Errorf("Erorr finding match in db: %v", err)
+		return nil, err
+	}
+	log.Info("match updated: %v", match)
+	return &result, nil
+}
+
+func UpdateTickets(matchID primitive.ObjectID, match *model.Match) (*model.Match, error) {
+	result := model.Match{}
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+
+	existingmatch, err := GetMatchByID(matchID)
+	if err != nil {
+		log.Errorf("Erorr getting match by id: %v", err)
+		return &result, err
+	}
+
+	match.AvailableTicketAmount = existingmatch.AvailableTicketAmount + match.InitialTicketAmount
+	match.InitialTicketAmount = existingmatch.InitialTicketAmount + match.InitialTicketAmount
+
+	updater := bson.D{primitive.E{Key: "$set", Value: bson.D{
+		primitive.E{Key: "initial_ticket_amount", Value: match.InitialTicketAmount},
+		primitive.E{Key: "price", Value: match.Price},
+		primitive.E{Key: "available_ticket_amount", Value: match.AvailableTicketAmount},
+	}}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		log.Errorf("Eror gettin db client: %v", err)
+		return nil, err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	updateResult, err := collection.UpdateOne(context.TODO(), filter, updater)
+	if err != nil {
+		log.Errorf("Erorr getting collection of db: %v", err)
+		return nil, err
+	}
+
+	if updateResult.ModifiedCount == 0 {
+		return nil, fmt.Errorf("no document was updated")
+	}
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return nil, err
+	}
+	log.Info("tickets updated: %v", match)
+	return &result, nil
+}
+
+func GetAllMatches() ([]model.Match, error) {
+	filter := bson.D{{}}
+	matches := []model.Match{}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return matches, err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+	cur, err := collection.Find(context.TODO(), filter)
+	if err != nil {
+		return matches, err
+	}
+	defer cur.Close(context.TODO())
+
+	for cur.Next(context.TODO()) {
+		var match model.Match
+		if err := cur.Decode(&match); err != nil {
+			return matches, err
+		}
+		matches = append(matches, match)
+	}
+
+	if len(matches) == 0 {
+		return matches, mongo.ErrNoDocuments
+	}
+
+	return matches, nil
+}
+
+func GetMatchByID(matchID primitive.ObjectID) (*model.Match, error) {
+	result := model.Match{}
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return &result, err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return &result, err
+	}
+	return &result, nil
+}
+
+func DeleteMatch(matchID primitive.ObjectID) error {
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+	_, err = collection.DeleteOne(context.TODO(), filter)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func DeleteAllMatches() error {
+	selector := bson.D{{}}
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+	_, err = collection.DeleteMany(context.TODO(), selector)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/src/highlanderticketing/service/nats.go b/src/highlanderticketing/service/nats.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ad15824451e471f8937c00f21fc8f48ad11d4a3
--- /dev/null
+++ b/src/highlanderticketing/service/nats.go
@@ -0,0 +1,80 @@
+package service
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"os"
+	"time"
+
+	"github.com/joho/godotenv"
+	"github.com/nats-io/nats.go"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+)
+
+type NatsServer struct {
+	Nc *nats.Conn
+}
+
+func ConnectToNats() (NatsServer, error) {
+	var natsServer NatsServer
+	if err := godotenv.Load(".env"); err != nil {
+		log.Fatalf("Error loading .env file")
+	}
+	uri := os.Getenv("NATS_URI")
+	nc, err := nats.Connect(uri)
+	if err != nil {
+		log.Fatal("Error establishing connection to NATS:", err)
+		return natsServer, err
+	}
+	natsServer.Nc = nc
+	fmt.Println("Connected to NATS at:", natsServer.Nc.ConnectedUrl())
+	return natsServer, nil
+
+}
+
+func (s NatsServer) ConfirmOrder(e *model.EmialContent) (error, bool) {
+	var res *model.Response
+	emailContenct, errMarshal := json.Marshal(e)
+	if errMarshal != nil {
+		fmt.Println(errMarshal)
+		return fmt.Errorf(errMarshal.Error()), false
+	}
+	response, err := s.Nc.Request("confirmOrder."+string(e.OrderID), []byte(emailContenct), 2*time.Second)
+	if err != nil {
+		log.Println("Error making NATS request:", err)
+		return fmt.Errorf(err.Error()), false
+	}
+
+	if err := json.Unmarshal(response.Data, &res); err != nil {
+		return fmt.Errorf(err.Error()), false
+	}
+	if res.Send != true {
+		return fmt.Errorf("emain not succesfuly send"), false
+	}
+	fmt.Println("hier die nats response", *res)
+	return nil, true
+}
+
+func (s NatsServer) ConfirmCancel(e *model.EmialContent) (error, bool) {
+	var res *model.Response
+	emailContenct, errMarshal := json.Marshal(e)
+	if errMarshal != nil {
+		fmt.Println(errMarshal)
+		return fmt.Errorf(errMarshal.Error()), false
+	}
+	response, err := s.Nc.Request("confirmCancel."+string(e.OrderID), []byte(emailContenct), 2*time.Second)
+	if err != nil {
+		log.Println("Error making NATS request:", err)
+		return fmt.Errorf(err.Error()), false
+	}
+
+	if err := json.Unmarshal(response.Data, &res); err != nil {
+		return fmt.Errorf(err.Error()), false
+	}
+	if res.Send != true {
+		return fmt.Errorf("emain not succesfuly send"), false
+	}
+	fmt.Println("hier die nats response", *res)
+	return nil, true
+}
diff --git a/src/highlanderticketing/service/oauth.go b/src/highlanderticketing/service/oauth.go
new file mode 100644
index 0000000000000000000000000000000000000000..7c7167cd00990d27e84efa5b3c51716edf32f210
--- /dev/null
+++ b/src/highlanderticketing/service/oauth.go
@@ -0,0 +1,78 @@
+package service
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+)
+
+func ValidateGoogleAccessToken(accessToken string) (bool, error) {
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token="+accessToken, nil)
+	if err != nil {
+		return false, err
+	}
+
+	resp, err := client.Do(req)
+	if err != nil {
+		return false, err
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return false, err
+	}
+
+	var tokenInfo struct {
+		ExpiresIn int    `json:"expires_in"`
+		Error     string `json:"error"`
+	}
+
+	err = json.Unmarshal(body, &tokenInfo)
+	if err != nil {
+		return false, err
+	}
+
+	if tokenInfo.Error != "" {
+		return false, fmt.Errorf("Fehler bei der Überprüfung des Tokens: %s", tokenInfo.Error)
+	}
+	//fmt.Println(tokenInfo.ExpiresIn)
+
+	if tokenInfo.ExpiresIn > 0 {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+func GetUserInfoByToken(accessToken string) (model.User, error) {
+	var userInfo model.User
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", "https://www.googleapis.com/oauth2/v1/userinfo", nil)
+	if err != nil {
+		return userInfo, err
+	}
+
+	req.Header.Set("Authorization", "Bearer "+accessToken)
+
+	resp, err := client.Do(req)
+	if err != nil {
+		return userInfo, err
+	}
+	defer resp.Body.Close()
+
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return userInfo, err
+	}
+	err = json.Unmarshal(body, &userInfo)
+	if err != nil {
+		return userInfo, err
+	}
+
+	return userInfo, nil
+}
diff --git a/src/highlanderticketing/service/order.go b/src/highlanderticketing/service/order.go
new file mode 100644
index 0000000000000000000000000000000000000000..09100c0abefb6981708dc8b9223a63a055b577a7
--- /dev/null
+++ b/src/highlanderticketing/service/order.go
@@ -0,0 +1,243 @@
+package service
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"time"
+
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/db"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+func AddMatchOrder(matchID primitive.ObjectID, order *model.Order) error {
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+	order.ID = primitive.NewObjectID()
+	matchToFind := &model.Match{}
+	emailContent := model.EmialContent{Name: order.User.Name, AwayMatch: matchToFind.AwayMatch, Location: matchToFind.Location, Date: matchToFind.Date.String(), Emailadress: order.User.Email, OrderID: matchToFind.ID.String()}
+
+	updater := bson.D{primitive.E{Key: "$push", Value: bson.D{
+		primitive.E{Key: "orders", Value: order},
+	}}}
+
+	updaterNotification := bson.D{primitive.E{Key: "$set", Value: bson.D{
+		primitive.E{Key: "orders.$[element]", Value: order},
+	}}}
+
+	options := options.Update().SetArrayFilters(options.ArrayFilters{
+		Filters: []interface{}{
+			bson.D{{Key: "element._id", Value: order.ID}},
+		},
+	})
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+	// find match
+	err = collection.FindOne(context.TODO(), filter).Decode(&matchToFind)
+	if err != nil {
+		return err
+	}
+	if matchToFind.AvailableTicketAmount < order.Amount {
+		return fmt.Errorf("ticket amount not available")
+	} else {
+		matchToFind.AvailableTicketAmount = matchToFind.AvailableTicketAmount - order.Amount
+	}
+	// push order
+	updateResult, err := collection.UpdateOne(context.TODO(), filter, updater)
+	if err != nil {
+		return err
+	}
+	if updateResult.ModifiedCount == 0 {
+		return fmt.Errorf("no document was updated, please send order again")
+	}
+	// update match with new available ticketamount
+	_, errUpdate := UpdateMatch(matchToFind.ID, matchToFind)
+	if errUpdate != nil {
+		errUpdate = fmt.Errorf("can not update match amount, please send order again")
+		err := deleteOrder(order.ID, matchToFind.ID)
+		natsServer, err := ConnectToNats()
+		if err != nil {
+			return err
+		}
+		// send email cancel notification
+		defer natsServer.Nc.Close()
+		order.Cancelnotified = true
+		if err, _ := natsServer.ConfirmCancel(&emailContent); err != nil {
+			time.Sleep(1 * time.Second)
+			err, _ := natsServer.ConfirmCancel(&emailContent)
+			if err != nil {
+				order.Ordernotified = false
+				err = fmt.Errorf("error sending cancel email: %v", err)
+			}
+			return err
+		}
+	}
+
+	natsServer, err := ConnectToNats()
+	if err != nil {
+		return err
+	}
+
+	defer natsServer.Nc.Close()
+	// send confirm email
+	if err, _ := natsServer.ConfirmOrder(&emailContent); err != nil {
+		time.Sleep(1 * time.Second)
+		err, _ := natsServer.ConfirmOrder(&emailContent)
+		if err != nil {
+			order.Ordernotified = false
+			err = fmt.Errorf("error sending confirm email: %v", err)
+		}
+		return err
+	} else {
+		order.Ordernotified = true
+	}
+	// update order notification to true
+	updateNotification, err := collection.UpdateOne(context.TODO(), filter, updaterNotification, options)
+	if err != nil {
+		err = fmt.Errorf("no document was updated, please send order again")
+		return err
+	}
+
+	if updateNotification.ModifiedCount == 0 {
+		err = fmt.Errorf("notification was not updated")
+		return err
+	}
+
+	return nil
+}
+
+func CancelOrder(matchID primitive.ObjectID, order *model.Order) error {
+	if order.Canceled == true {
+		return fmt.Errorf("order already canceled")
+	}
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+	// find match with id
+	matchToFind, err := GetMatchByID(matchID)
+	if err != nil {
+		return err
+	} else {
+		matchToFind.AvailableTicketAmount = matchToFind.AvailableTicketAmount + order.Amount
+	}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	order.Canceled = true
+
+	updaterMatchCancel := bson.D{primitive.E{Key: "$set", Value: bson.D{
+		primitive.E{Key: "orders.$[element]", Value: order},
+	}}}
+
+	options := options.Update().SetArrayFilters(options.ArrayFilters{
+		Filters: []interface{}{
+			bson.D{{Key: "element._id", Value: order.ID}},
+		},
+	})
+	// update order to canceled
+	updateMatchCancel, err := collection.UpdateOne(context.TODO(), filter, updaterMatchCancel, options)
+	if err != nil {
+		return err
+	}
+	if updateMatchCancel.ModifiedCount == 0 {
+		return fmt.Errorf("not updated")
+
+	}
+	// update match with new available tickets
+	_, errUpdateAmount := UpdateMatch(matchToFind.ID, matchToFind)
+	if errUpdateAmount != nil {
+		order.Canceled = false
+		updateMatchCancel, err := collection.UpdateOne(context.TODO(), filter, updaterMatchCancel, options)
+		if err != nil {
+			return err
+		}
+		if updateMatchCancel.ModifiedCount == 0 {
+			return fmt.Errorf("not updated")
+
+		}
+		return fmt.Errorf("error canceling match internal, please try again %v", err)
+	}
+
+	natsServer, err := ConnectToNats()
+	defer natsServer.Nc.Close()
+	// send notification mail
+	emailContent := model.EmialContent{Name: order.User.Name, AwayMatch: matchToFind.AwayMatch, Location: matchToFind.Location, Date: matchToFind.Date.String(), Emailadress: order.User.Email, OrderID: order.ID.String()}
+	if err, _ := natsServer.ConfirmCancel(&emailContent); err != nil {
+		time.Sleep(1 * time.Second)
+		err, _ := natsServer.ConfirmOrder(&emailContent)
+		if err != nil {
+			order.Ordernotified = false
+			err = fmt.Errorf("error sending confirm email: %v", err)
+		}
+	} else {
+		order.Cancelnotified = true
+	}
+	// update cancel notification
+	updateMatchCancelNotifi, err := collection.UpdateOne(context.TODO(), filter, updaterMatchCancel, options)
+	if err != nil {
+		return err
+	}
+	if updateMatchCancelNotifi.ModifiedCount == 0 {
+		return fmt.Errorf("orderid not in system")
+	}
+	return nil
+
+}
+
+func GetOrderById(orderID primitive.ObjectID) (*model.Order, error) {
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return nil, err
+	}
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	filter := bson.M{"orders._id": orderID}
+
+	var result model.Match
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, order := range result.Orders {
+		if order.ID == orderID {
+			return &order, nil
+		}
+	}
+
+	return nil, errors.New("Order not found")
+}
+
+func deleteOrder(matchID primitive.ObjectID, orderID primitive.ObjectID) error {
+	filter := bson.D{primitive.E{Key: "_id", Value: matchID}}
+	updater := bson.D{primitive.E{Key: "$pull", Value: bson.D{
+		primitive.E{Key: "orders", Value: bson.D{
+			primitive.E{Key: "_id", Value: orderID},
+		}},
+	}}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	_, err = collection.UpdateOne(context.TODO(), filter, updater)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/src/highlanderticketing/service/register.go b/src/highlanderticketing/service/register.go
new file mode 100644
index 0000000000000000000000000000000000000000..40ca19207a832a6848a6c5e7cf2f95d74df4c019
--- /dev/null
+++ b/src/highlanderticketing/service/register.go
@@ -0,0 +1,22 @@
+package service
+
+import (
+	"fmt"
+
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+)
+
+func Register(accessToken string) error {
+	user, err := GetUserInfoByToken(accessToken)
+	if err != nil {
+		return err
+	}
+	err1 := CreateUser(&user)
+	if err1 != nil {
+		return err1
+	}
+	var userArray []model.User
+	userArray, _ = GetAllUsers()
+	fmt.Println(userArray)
+	return nil
+}
diff --git a/src/highlanderticketing/service/user.go b/src/highlanderticketing/service/user.go
new file mode 100644
index 0000000000000000000000000000000000000000..c9c703c7e5d9411b5d621439b9dc073935dad4ea
--- /dev/null
+++ b/src/highlanderticketing/service/user.go
@@ -0,0 +1,195 @@
+package service
+
+import (
+	"context"
+	"fmt"
+
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/db"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+var isFirstCall = true
+
+func CreateUser(user *model.User) error {
+	if isFirstCall == false {
+		user.IsAdmin = false
+	} else if isFirstCall == true {
+		_, err := GetAllUsers()
+		if err == mongo.ErrNoDocuments {
+			user.IsAdmin = true
+		} else if err != nil {
+			return err
+		} else {
+			user.IsAdmin = false
+		}
+		isFirstCall = false
+	}
+	user.ID = primitive.NewObjectID()
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+	filter := bson.M{"email": user.Email}
+	update := bson.M{
+		"$setOnInsert": bson.M{
+			"_id":         user.ID,
+			"email":       user.Email,
+			"name":        user.Name,
+			"family_name": user.FamilyName,
+			"is_admin":    user.IsAdmin,
+		},
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+	options := options.FindOneAndUpdate().SetUpsert(true)
+
+	result := collection.FindOneAndUpdate(context.TODO(), filter, update, options)
+
+	if result.Err() == mongo.ErrNoDocuments {
+		return nil // dokument wurd erstellt
+	} else if result.Err() != nil {
+		return result.Err() // fehler beim process an sich
+	} else {
+		return fmt.Errorf("Der Benutzer existiert bereits")
+	}
+}
+
+func UpdateUser(userID primitive.ObjectID, user *model.User) (*model.User, error) {
+	result := model.User{}
+	existingUser, err := GetUserByID(userID)
+	if existingUser == nil || err != nil {
+		return existingUser, err
+	}
+
+	filter := bson.D{primitive.E{Key: "_id", Value: userID}}
+
+	updater := bson.D{primitive.E{Key: "$set", Value: bson.D{
+		primitive.E{Key: "email", Value: user.Email},
+		primitive.E{Key: "name", Value: user.Name},
+		primitive.E{Key: "family_name", Value: user.FamilyName},
+		primitive.E{Key: "is_admin", Value: user.IsAdmin},
+	}}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return nil, err
+	}
+
+	collection := client.Database(db.DB).Collection(db.MATCHES)
+
+	updateResult, err := collection.UpdateOne(context.TODO(), filter, updater)
+	if err != nil {
+		return nil, err
+	}
+
+	if updateResult.ModifiedCount == 0 {
+		return nil, fmt.Errorf("no document was updated")
+	}
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return nil, err
+	}
+
+	return &result, nil
+}
+
+func GetAllUsers() ([]model.User, error) {
+	filter := bson.D{{}}
+	users := []model.User{}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return users, err
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+	cur, err := collection.Find(context.TODO(), filter)
+	if err != nil {
+		return users, err
+	}
+	defer cur.Close(context.TODO())
+
+	for cur.Next(context.TODO()) {
+		var user model.User
+		if err := cur.Decode(&user); err != nil {
+			return users, err
+		}
+		users = append(users, user)
+	}
+
+	if len(users) == 0 {
+		return users, mongo.ErrNoDocuments
+	}
+
+	return users, nil
+}
+
+func GetUserByID(userID primitive.ObjectID) (*model.User, error) {
+	result := model.User{}
+	filter := bson.D{primitive.E{Key: "_id", Value: userID}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return &result, err
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return &result, err
+	}
+	return &result, nil
+}
+func GetUserByEmail(email string) (*model.User, error) {
+	result := model.User{}
+	filter := bson.D{primitive.E{Key: "email", Value: email}}
+
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return &result, err
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+
+	err = collection.FindOne(context.TODO(), filter).Decode(&result)
+	if err != nil {
+		return &result, err
+	}
+	return &result, nil
+}
+
+func DeleteUser(UserID primitive.ObjectID) error {
+	filter := bson.D{primitive.E{Key: "_id", Value: UserID}}
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+	_, err = collection.DeleteOne(context.TODO(), filter)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func DeleteAllUsers() error {
+	selector := bson.D{{}}
+	client, err := db.GetMongoClient()
+	if err != nil {
+		return err
+	}
+
+	collection := client.Database(db.DB).Collection(db.USERS)
+	_, err = collection.DeleteMany(context.TODO(), selector)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/src/highlanderticketing/service/user_test.go b/src/highlanderticketing/service/user_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d3c4404abb4239a6f09e347f522a68317d0be6db
--- /dev/null
+++ b/src/highlanderticketing/service/user_test.go
@@ -0,0 +1,39 @@
+package service_test
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/model"
+	"gitlab.reutlingen-university.de/ege/highlander-ticketing-go-ss2023/src/highlanderticketing/service"
+)
+
+func TestCreateUserIntegration(t *testing.T) {
+	user := &model.User{
+		Email:      "test@example.com",
+		Name:       "John",
+		FamilyName: "Doe",
+		IsAdmin:    false,
+	}
+
+	// Testfall 1: der benutzer wird angelegt
+	err2 := service.CreateUser(user)
+
+	assert.Nil(t, err2)
+	assert.False(t, user.IsAdmin)
+
+	if !reflect.DeepEqual(t, user) {
+		t.Errorf("Expected %+v, but got %+v", t, err2)
+	}
+
+	// Testfall 2: es gibt bereits den Benutzer
+	err3 := service.CreateUser(user)
+
+	assert.Error(t, err3)
+	assert.Equal(t, "Der Benutzer existiert bereits", err3.Error())
+
+	if !reflect.DeepEqual(err2, user) {
+		t.Errorf("Expected %+v, but got %+v", t, err2)
+	}
+}
diff --git a/src/highlanderticketing/wait-for-it.sh b/src/highlanderticketing/wait-for-it.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3974640b053e6f84a21c292c01e3674348445831
--- /dev/null
+++ b/src/highlanderticketing/wait-for-it.sh
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+# Use this script to test if a given TCP host/port are available
+
+WAITFORIT_cmdname=${0##*/}
+
+echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
+
+usage()
+{
+    cat << USAGE >&2
+Usage:
+    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
+    -h HOST | --host=HOST       Host or IP under test
+    -p PORT | --port=PORT       TCP port under test
+                                Alternatively, you specify the host and port as host:port
+    -s | --strict               Only execute subcommand if the test succeeds
+    -q | --quiet                Don't output any status messages
+    -t TIMEOUT | --timeout=TIMEOUT
+                                Timeout in seconds, zero for no timeout
+    -- COMMAND ARGS             Execute command with args after the test finishes
+USAGE
+    exit 1
+}
+
+wait_for()
+{
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    else
+        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
+    fi
+    WAITFORIT_start_ts=$(date +%s)
+    while :
+    do
+        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
+            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
+            WAITFORIT_result=$?
+        else
+            (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
+            WAITFORIT_result=$?
+        fi
+        if [[ $WAITFORIT_result -eq 0 ]]; then
+            WAITFORIT_end_ts=$(date +%s)
+            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
+            break
+        fi
+        sleep 1
+    done
+    return $WAITFORIT_result
+}
+
+wait_for_wrapper()
+{
+    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
+    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    else
+        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
+    fi
+    WAITFORIT_PID=$!
+    trap "kill -INT -$WAITFORIT_PID" INT
+    wait $WAITFORIT_PID
+    WAITFORIT_RESULT=$?
+    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
+        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
+    fi
+    return $WAITFORIT_RESULT
+}
+
+# process arguments
+while [[ $# -gt 0 ]]
+do
+    case "$1" in
+        *:* )
+        WAITFORIT_hostport=(${1//:/ })
+        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
+        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
+        shift 1
+        ;;
+        --child)
+        WAITFORIT_CHILD=1
+        shift 1
+        ;;
+        -q | --quiet)
+        WAITFORIT_QUIET=1
+        shift 1
+        ;;
+        -s | --strict)
+        WAITFORIT_STRICT=1
+        shift 1
+        ;;
+        -h)
+        WAITFORIT_HOST="$2"
+        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
+        shift 2
+        ;;
+        --host=*)
+        WAITFORIT_HOST="${1#*=}"
+        shift 1
+        ;;
+        -p)
+        WAITFORIT_PORT="$2"
+        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --port=*)
+        WAITFORIT_PORT="${1#*=}"
+        shift 1
+        ;;
+        -t)
+        WAITFORIT_TIMEOUT="$2"
+        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
+        shift 2
+        ;;
+        --timeout=*)
+        WAITFORIT_TIMEOUT="${1#*=}"
+        shift 1
+        ;;
+        --)
+        shift
+        WAITFORIT_CLI=("$@")
+        break
+        ;;
+        --help)
+        usage
+        ;;
+        *)
+        echoerr "Unknown argument: $1"
+        usage
+        ;;
+    esac
+done
+
+if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
+    echoerr "Error: you need to provide a host and port to test."
+    usage
+fi
+
+WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
+WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
+WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
+WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
+
+# Check to see if timeout is from busybox?
+WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
+WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
+
+WAITFORIT_BUSYTIMEFLAG=""
+if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
+    WAITFORIT_ISBUSY=1
+    # Check if busybox timeout uses -t flag
+    # (recent Alpine versions don't support -t anymore)
+    if timeout &>/dev/stdout | grep -q -e '-t '; then
+        WAITFORIT_BUSYTIMEFLAG="-t"
+    fi
+else
+    WAITFORIT_ISBUSY=0
+fi
+
+if [[ $WAITFORIT_CHILD -gt 0 ]]; then
+    wait_for
+    WAITFORIT_RESULT=$?
+    exit $WAITFORIT_RESULT
+else
+    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
+        wait_for_wrapper
+        WAITFORIT_RESULT=$?
+    else
+        wait_for
+        WAITFORIT_RESULT=$?
+    fi
+fi
+
+if [[ $WAITFORIT_CLI != "" ]]; then
+    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
+        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
+        exit $WAITFORIT_RESULT
+    fi
+    exec "${WAITFORIT_CLI[@]}"
+else
+    exit $WAITFORIT_RESULT
+fi
\ No newline at end of file
diff --git a/todo b/todo
new file mode 100644
index 0000000000000000000000000000000000000000..6e6c4125f6086c02060056cd323745a22d60c0d2
--- /dev/null
+++ b/todo
@@ -0,0 +1,11 @@
+ - handler , order , travel 
+ - user werden aus google oauth2 erzeugt (Plan)
+ - services von user travel order
+ - context definieren
+ - schauen wann verbindung zu mongodb unterbrechen
+ - docker von api 
+ - reihenfolge von den docker files (shell von schmollinger verwenden)
+ - google oauth2 anbinden
+ - schauen wie thread save machen (evtl. mit abfrage von api)
+ - api anbinden
+