feat: set up project
This commit is contained in:
23
internal/api/handler/helper.go
Normal file
23
internal/api/handler/helper.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func RenderError(c *fiber.Ctx, err error, message string) error {
|
||||
if err != nil {
|
||||
log.Printf("renderError: %s\n", err)
|
||||
}
|
||||
return c.Render("error", fiber.Map{
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func JSONError(c *fiber.Ctx, status int, err error, message string) error {
|
||||
if err != nil {
|
||||
log.Printf("JSONError: %s\n", err)
|
||||
}
|
||||
return c.Status(status).SendString("error: " + message)
|
||||
}
|
||||
58
internal/api/handler/payment.go
Normal file
58
internal/api/handler/payment.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/domain"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/price"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type OrderHandler struct {
|
||||
walletAddress string
|
||||
orderSrv *services.Order
|
||||
conversor *price.PriceConversor
|
||||
}
|
||||
|
||||
func NewOrderHandler(walletAddress string, orderSrv *services.Order, conversor *price.PriceConversor) *OrderHandler {
|
||||
return &OrderHandler{
|
||||
walletAddress: walletAddress,
|
||||
orderSrv: orderSrv,
|
||||
conversor: conversor,
|
||||
}
|
||||
}
|
||||
|
||||
type orderReq struct {
|
||||
OrderID string `json:"order_id"`
|
||||
ClientID string `json:"client_id"`
|
||||
Amount float64 `json:"amount"`
|
||||
Currency domain.FiatCurrency `json:"currency"`
|
||||
}
|
||||
|
||||
func (hdl *OrderHandler) Post(c *fiber.Ctx) error {
|
||||
req := orderReq{}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return RenderError(c, fmt.Errorf("id is empty"), "")
|
||||
}
|
||||
|
||||
btcAmount, err := hdl.conversor.UsdToBtc(req.Amount)
|
||||
if err != nil {
|
||||
return RenderError(c, fmt.Errorf("hdl.conversor.UsdToBtc %w", err), "")
|
||||
}
|
||||
|
||||
order, err := hdl.orderSrv.NewOrder(c.Context(), req.OrderID, req.ClientID, btcAmount)
|
||||
if err != nil {
|
||||
return RenderError(c, fmt.Errorf("hdl.orderSrv.NewOrder %w", err), "")
|
||||
}
|
||||
|
||||
return c.Render("order",
|
||||
fiber.Map{
|
||||
"order_id": order.ID.Hex(),
|
||||
"amount": btcAmount,
|
||||
"wallet_address": hdl.walletAddress,
|
||||
"expires_at": order.ExpiresAt.Format(time.RFC3339),
|
||||
},
|
||||
)
|
||||
}
|
||||
152
internal/api/server.go
Normal file
152
internal/api/server.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/limiter"
|
||||
"github.com/gofiber/template/handlebars/v2"
|
||||
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/api/handler"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/domain"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/btc"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/mail"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/internal/services/price"
|
||||
"gitea.urkob.com/urko/btc-pay-checker/kit/cfg"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_FILE_SIZE = 25
|
||||
MAX_FILE_SIZE_MiB = MAX_FILE_SIZE * units.MiB
|
||||
)
|
||||
|
||||
type RestServer struct {
|
||||
app *fiber.App
|
||||
config *cfg.Config
|
||||
btcService *btc.BitcoinService
|
||||
orderSrv *services.Order
|
||||
mailSrv *mail.MailService
|
||||
priceSrv *price.PriceConversor
|
||||
}
|
||||
|
||||
func NewRestServer(
|
||||
config *cfg.Config,
|
||||
orderSrv *services.Order,
|
||||
btcService *btc.BitcoinService,
|
||||
priceSrv *price.PriceConversor,
|
||||
mailSrv *mail.MailService,
|
||||
) *RestServer {
|
||||
return &RestServer{
|
||||
config: config,
|
||||
orderSrv: orderSrv,
|
||||
btcService: btcService,
|
||||
priceSrv: priceSrv,
|
||||
mailSrv: mailSrv,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RestServer) Start(ctx context.Context, apiPort, views string) error {
|
||||
engine := handlebars.New(views, ".hbs")
|
||||
s.app = fiber.New(fiber.Config{
|
||||
Views: engine,
|
||||
BodyLimit: MAX_FILE_SIZE_MiB,
|
||||
})
|
||||
|
||||
s.app.Use(limiter.New(limiter.Config{
|
||||
Max: 5,
|
||||
Expiration: 1 * time.Hour,
|
||||
LimiterMiddleware: limiter.SlidingWindow{},
|
||||
}))
|
||||
|
||||
s.app.Use(cors.New(cors.Config{
|
||||
AllowMethods: "GET,POST,OPTIONS",
|
||||
AllowOrigins: "*",
|
||||
AllowHeaders: "Origin, Accept, Content-Type, X-CSRF-Token, Authorization",
|
||||
ExposeHeaders: "Origin",
|
||||
}))
|
||||
|
||||
s.loadViews(views + "/images")
|
||||
|
||||
orderHandler := handler.NewOrderHandler(s.config.WalletAddress, s.orderSrv, s.priceSrv)
|
||||
|
||||
notifChan := make(chan domain.Notification)
|
||||
go s.btcService.Notify(ctx, notifChan)
|
||||
go s.onNotification(ctx, notifChan)
|
||||
|
||||
s.app.Post("/order", orderHandler.Post)
|
||||
|
||||
if err := s.app.Listen(":" + apiPort); err != nil {
|
||||
log.Fatalln("app.Listen:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// onNotification Step
|
||||
// 1- update the block where tx has been forged
|
||||
// 2- send email to client alerting his block has been forged
|
||||
// 3- to upload file into arweave
|
||||
func (s *RestServer) onNotification(ctx context.Context, notifChan chan domain.Notification) {
|
||||
for notif := range notifChan {
|
||||
order, err := s.orderSrv.FromAmount(ctx, notif.Amount, notif.DoneAt)
|
||||
if err != nil {
|
||||
log.Println("error while retrieve transaction from forged block: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
order.Block = notif.BlockHash
|
||||
order.Tx = notif.Tx
|
||||
|
||||
if err := s.orderSrv.OrderCompleted(ctx, order); err != nil {
|
||||
log.Println("OrderCompleted:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Send email to client and provider
|
||||
if err := s.mailSrv.SendProviderConfirm(mail.SendOK{
|
||||
TxID: order.Tx,
|
||||
BlockHash: order.Block,
|
||||
DocHash: "",
|
||||
To: "doc.Email",
|
||||
}); err != nil {
|
||||
log.Println("error while send confirm email:", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RestServer) loadViews(imagesDir string) {
|
||||
s.app.Static("/images", imagesDir)
|
||||
|
||||
s.app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.Render("upload", fiber.Map{})
|
||||
})
|
||||
|
||||
s.app.Get("/error", func(c *fiber.Ctx) error {
|
||||
message := c.Query("message")
|
||||
return renderError(c, nil, message)
|
||||
})
|
||||
}
|
||||
|
||||
func renderError(c *fiber.Ctx, err error, message string) error {
|
||||
if err != nil {
|
||||
log.Printf("renderError: %s\n", err)
|
||||
}
|
||||
return c.Render("error", fiber.Map{
|
||||
"message": message,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *RestServer) Shutdown() error {
|
||||
if err := s.app.Server().Shutdown(); err != nil {
|
||||
log.Printf("app.Server().Shutdown(): %s\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user