package main import ( "embed" "fmt" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/joho/godotenv" "html/template" "log" "net/http" "payment-poc/migration" "payment-poc/wspay" "strconv" "strings" "time" ) //go:embed db/dev/*.sql var devMigrations embed.FS var BackendUrl string var ShopId string var ShopSecret string func init() { godotenv.Load() BackendUrl = envMustExist("BACKEND_URL") ShopId = envMustExist("SHOP_ID") ShopSecret = envMustExist("SHOP_SECRET") log.SetPrefix("") log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) } func main() { client, err := connectToDb() if err != nil { log.Fatalf("couldn't connect to db: %v", err) } if err := migration.InitializeMigrations(client, devMigrations); err != nil { log.Fatalf("couldn't execute migrations: %v", err) } g := gin.Default() g.Use(gin.BasicAuth(getAccounts())) g.SetFuncMap(template.FuncMap{ "formatCurrency": func(current int64) string { return fmt.Sprintf("%d,%02d", current/100, current%100) }, "formatState": func(state wspay.PaymentState) string { switch state { case wspay.StateCanceled: return "Otkazano" case wspay.StateAccepted: return "Prihvačeno" case wspay.StateError: return "Greška" case wspay.StateInitialized: return "Inicijalna izrada" case wspay.StateCanceledInitialization: return "Otkazano tijekom izrade" case wspay.StateCompleted: return "Završeno" } return "nepoznato stanje '" + string(state) + "'" }, "omitempty": func(value string) string { if value == "" { return "-" } return value }, }) g.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{"message": "no action on given url", "created": time.Now()}) }) g.NoMethod(func(c *gin.Context) { c.JSON(http.StatusMethodNotAllowed, gin.H{"message": "no action on given method", "created": time.Now()}) }) wspayService := wspay.Service{ DB: client, } g.LoadHTMLGlob("./templates/*.gohtml") g.GET("/", func(c *gin.Context) { entries, err := wspayService.FetchAll() log.Printf("%v", err) c.HTML(200, "index.gohtml", gin.H{"Entries": entries}) }) g.GET("/initial", func(c *gin.Context) { amount, err := strconv.ParseFloat(c.Query("amount"), 64) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } entry, err := wspayService.CreateEntry(ShopId, int64(amount*100)) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } form := wspay.WsPayForm{ ShopID: ShopId, ShoppingCartID: entry.ShoppingCartID, Version: "2.0", TotalAmount: entry.TotalAmount, ReturnURL: BackendUrl + "/initial/success", ReturnErrorURL: BackendUrl + "/initial/error", CancelURL: BackendUrl + "/initial/cancel", Signature: wspay.CalculateFormSignature(ShopId, ShopSecret, entry.ShoppingCartID, entry.TotalAmount), } c.HTML(200, "initial.gohtml", gin.H{"Action": wspay.AuthorisationForm, "Form": form}) }) g.GET("/initial/success", func(c *gin.Context) { response := wspay.WsPayFormReturn{} if err := c.ShouldBind(&response); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if err := wspay.CompareFormReturnSignature(response.Signature, ShopId, ShopSecret, response.ShoppingCartID, response.Success, response.ApprovalCode); err != nil { c.AbortWithError(http.StatusBadRequest, err) return } entry.Lang = response.Lang entry.CustomerFirstName = response.CustomerFirstName entry.CustomerLastName = response.CustomerSurname entry.CustomerAddress = response.CustomerAddress entry.CustomerCity = response.CustomerCity entry.CustomerZIP = response.CustomerZIP entry.CustomerCountry = response.CustomerCountry entry.CustomerPhone = response.CustomerPhone entry.PaymentPlan = response.PaymentPlan entry.CreditCardNumber = response.CreditCardNumber entry.DateTime = parseDateTime(response.DateTime) entry.ECI = response.ECI entry.STAN = response.STAN entry.Success = response.Success entry.ApprovalCode = response.ApprovalCode entry.ErrorMessage = response.ErrorMessage entry.State = wspay.StateAccepted if err := wspayService.Update(entry); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if c.Query("iframe") != "" { c.HTML(200, "iframe_handler.gohtml", gin.H{}) } else { c.Redirect(http.StatusTemporaryRedirect, "/") } }) g.GET("/iframe", func(c *gin.Context) { c.HTML(200, "iframe_handler.gohtml", gin.H{}) }) g.GET("/initial/error", func(c *gin.Context) { response := wspay.WsPayFormError{} if err := c.ShouldBind(&response); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } entry.Lang = response.Lang entry.CustomerFirstName = response.CustomerFirstName entry.CustomerLastName = response.CustomerSurname entry.CustomerAddress = response.CustomerAddress entry.CustomerCity = response.CustomerCity entry.CustomerZIP = response.CustomerZIP entry.CustomerCountry = response.CustomerCountry entry.CustomerPhone = response.CustomerPhone entry.PaymentPlan = response.PaymentPlan entry.DateTime = parseDateTime(response.DateTime) entry.ECI = response.ECI entry.Success = response.Success entry.ApprovalCode = response.ApprovalCode entry.ErrorMessage = response.ErrorMessage entry.ErrorCodes = response.ErrorCodes entry.State = wspay.StateError if err := wspayService.Update(entry); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if c.Query("iframe") != "" { c.HTML(200, "iframe_handler.gohtml", gin.H{}) } else { c.Redirect(http.StatusTemporaryRedirect, "/") } }) g.GET("info/:id", func(c *gin.Context) { id := uuid.MustParse(c.Param("id")) entry, err := wspayService.FetchById(id) if err != nil { c.AbortWithError(http.StatusNotFound, err) return } c.HTML(200, "info.gohtml", gin.H{"Entry": entry}) }) g.GET("/initial/cancel", func(c *gin.Context) { response := wspay.WsPayFormCancel{} if err := c.ShouldBind(&response); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } entry, err := wspayService.FetchByShoppingCartID(response.ShoppingCartID) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } entry.State = wspay.StateCanceledInitialization if err := wspayService.Update(entry); err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } if c.Query("iframe") != "" { c.HTML(200, "iframe_handler.gohtml", gin.H{}) } else { c.Redirect(http.StatusTemporaryRedirect, "/") } }) log.Fatal(http.ListenAndServe(":5281", g)) } func getAccounts() gin.Accounts { auth := strings.Split(envMustExist("AUTH"), ":") return gin.Accounts{auth[0]: auth[1]} } func parseDateTime(dateTime string) time.Time { t, err := time.Parse("20060102150405", dateTime) if err != nil { log.Printf("couldn't parse response time %s: %v", dateTime, err) } return t }