Skip to content

Commit 8b3fb02

Browse files
committed
resolve rackcash#4 & refactor
1 parent 3a516da commit 8b3fb02

File tree

8 files changed

+129
-13
lines changed

8 files changed

+129
-13
lines changed

api/internal/app/app.go

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"syscall"
1313

1414
"github.com/gin-gonic/gin"
15+
cors "github.com/rs/cors/wrapper/gin"
1516
"gorm.io/gorm"
1617
)
1718

@@ -34,6 +35,8 @@ func (app *App) Start() {
3435

3536
r := gin.New()
3637

38+
r.Use(cors.Default())
39+
3740
services := service.HewServices(app.NatsInfra.Ns, app.Db, app.Log, app.Config)
3841

3942
app.Autostart(services)

api/internal/delivery/rest/v1/invoice_public.go

+62-3
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ func (h *Handler) invoiceCreate(c *gin.Context) {
121121
}
122122

123123
// POST /invoice/info
124-
func (h *Handler) info(c *gin.Context) {
124+
func (h *Handler) invoiceInfo(c *gin.Context) {
125+
// TODO: validation
125126
var data struct {
126127
InvoiceId string `json:"invoice_id"`
127128
ApiKey string `json:"api_key"`
@@ -183,7 +184,7 @@ func (h *Handler) info(c *gin.Context) {
183184
CreatedAt: invoice.CreatedAt.Format("2006-01-02 15:04:05"),
184185
}
185186

186-
if time.Now().Unix() > invoice.EndTimestamp && invoice.Status.IsNotPaid() {
187+
if time.Now().Unix() > invoice.EndTimestamp && invoice.Status.IsNotPaid() && !invoice.Status.IsCancelled() {
187188
response.Status = "end"
188189
}
189190

@@ -228,8 +229,66 @@ func (h *Handler) qrCode(c *gin.Context) {
228229
c.Data(http.StatusOK, "image/png", []byte(qrCode))
229230
}
230231

232+
func (h *Handler) invoiceCancel(c *gin.Context) {
233+
var data struct {
234+
InvoiceId string `json:"invoice_id"`
235+
ApiKey string `json:"api_key"`
236+
}
237+
238+
var errid = logger.GenErrorId()
239+
240+
if err := c.ShouldBindJSON(&data); err != nil {
241+
responseErr(c, http.StatusBadRequest, domain.ErrMsgBadRequest, "")
242+
fmt.Println("unmarshal error: " + err.Error())
243+
return
244+
}
245+
246+
if data.InvoiceId == "" {
247+
responseErr(c, http.StatusBadRequest, fmt.Sprintf(domain.ErrMsgParamsBadRequest, domain.ErrParamEmptyInvoiceId), "")
248+
return
249+
}
250+
251+
if data.ApiKey == "" {
252+
responseErr(c, http.StatusBadRequest, domain.ErrMsgApiKeyInvalid, "")
253+
return
254+
}
255+
256+
exists, err := h.services.Merchants.ApiKeyExists(h.db, data.ApiKey)
257+
if err != nil {
258+
responseErr(c, http.StatusInternalServerError, domain.ErrMsgInternalServerError, errid)
259+
h.log.TemplInvoiceErr("api key exists error: "+err.Error(), errid, data.InvoiceId, decimal.Zero, logger.NA, c.Request.RequestURI, logger.NA, c.ClientIP())
260+
return
261+
}
262+
263+
if !exists {
264+
responseErr(c, http.StatusBadRequest, domain.ErrMsgApiKeyNotFound, "")
265+
return
266+
}
267+
268+
if err = h.services.Invoices.Cancel(data.InvoiceId); err != nil {
269+
var _errid = ""
270+
var errmsg = err.Error()
271+
272+
code := domain.GetStatusByErr(err)
273+
if code == http.StatusInternalServerError {
274+
_errid = errid
275+
errmsg = domain.ErrMsgInternalServerError
276+
h.log.TemplInvoiceErr("invoice cancel error: "+err.Error(), errid, data.InvoiceId, decimal.Zero, logger.NA, c.Request.RequestURI, logger.NA, c.ClientIP())
277+
}
278+
responseErr(c, code, errmsg, _errid)
279+
return
280+
}
281+
282+
c.AbortWithStatusJSON(http.StatusOK, responseInvoiceCancelled{
283+
Error: false,
284+
})
285+
286+
}
287+
231288
func (h *Handler) initPubInvoiceRoutes(g *gin.RouterGroup) {
232289
g.POST("/invoice/create", h.invoiceCreate)
233-
g.POST("/invoice/info", h.info)
290+
g.POST("/invoice/info", h.invoiceInfo)
291+
g.POST("/invoice/cancel", h.invoiceCancel)
292+
234293
g.GET("/invoice/qr-code/:invoice_id", h.qrCode)
235294
}

api/internal/delivery/rest/v1/response.go

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ type responseMerchantCreated struct {
7777
MerchantId string `json:"merchant_id"`
7878
}
7979

80+
type responseInvoiceCancelled struct {
81+
Error bool `json:"error"`
82+
// Message string `json:"message"`
83+
}
84+
8085
func responseErr(c *gin.Context, statusCode int, msg, errorID string) {
8186
// var errorId string
8287

api/internal/domain/invoices.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ const (
2828
STATUS_END
2929
STATUS_PAID_LESS
3030
STATUS_IN_PROCESSING
31+
STATUS_CANCELLED
3132
)
3233

33-
var Statuses = [...]string{"not_paid", "paid", "paid_over", "end", "paid_less", "processing"}
34+
var Statuses = [...]string{"not_paid", "paid", "paid_over", "end", "paid_less", "processing", "cancelled"}
3435

3536
// methods
3637

@@ -48,6 +49,9 @@ func (s Status) ToString() string {
4849
return Statuses[s]
4950
}
5051

52+
func (s Status) IsCancelled() bool {
53+
return s == STATUS_CANCELLED
54+
}
5155
func (s Status) IsPaid() bool {
5256
return s == STATUS_PAID || s == STATUS_PAID_OVER
5357
}

api/internal/domain/responses.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,22 @@ const (
4646
ErrMsgInitBalancesError = "can't init balances"
4747
)
4848

49+
type ResponseError error
50+
4951
var (
50-
ErrInvalidInvoiceId = fmt.Errorf("invalid invoice id")
51-
Err = fmt.Errorf("invalid invoice id")
52-
ErrInternalServerError = fmt.Errorf(ErrMsgInternalServerError)
53-
ErrInvoiceIdNotFound = fmt.Errorf("invoice id not found")
52+
ErrInvalidInvoiceId ResponseError = fmt.Errorf("invalid invoice id")
53+
Err ResponseError = fmt.Errorf("invalid invoice id")
54+
ErrInternalServerError ResponseError = fmt.Errorf(ErrMsgInternalServerError)
55+
ErrInvoiceIdNotFound ResponseError = fmt.Errorf("invoice id not found")
56+
57+
ErrInvoiceAlreadyCancelled ResponseError = fmt.Errorf("invoice already cancelled")
5458
)
5559

5660
const (
5761
ErrParamEmptyInvoiceId = "invoice id is empty"
5862
)
5963

60-
func GetStatusByErr(err error) (status int) {
64+
func GetStatusByErr(err ResponseError) (status int) {
6165
if err == nil {
6266
return 200
6367
}
@@ -69,6 +73,8 @@ func GetStatusByErr(err error) (status int) {
6973
status = http.StatusBadRequest
7074
case errors.Is(err, ErrInvoiceIdNotFound):
7175
status = http.StatusBadRequest
76+
case errors.Is(err, ErrInvoiceAlreadyCancelled):
77+
status = http.StatusBadRequest
7278
default:
7379
status = http.StatusInternalServerError
7480
}

api/internal/service/invoices.go

+38-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type InvoicesService struct {
2828
repo repository.Invoices
2929
wallets repository.Wallets
3030
balances repository.Balances
31+
events repository.Events
3132
locker Locker
3233
n *nats.NatsInfra
3334
db *gorm.DB
@@ -36,8 +37,8 @@ type InvoicesService struct {
3637
config *config.Config
3738
}
3839

39-
func NewInvoicesService(db *gorm.DB, repo repository.Invoices, wallets repository.Wallets, balances repository.Balances, locker Locker, n *nats.NatsInfra, l logger.Logger, cache *cache.Cache, config *config.Config) *InvoicesService {
40-
return &InvoicesService{repo: repo, wallets: wallets, balances: balances, n: n, db: db, l: l, cache: cache, locker: locker, config: config}
40+
func NewInvoicesService(db *gorm.DB, repo repository.Invoices, wallets repository.Wallets, balances repository.Balances, events repository.Events, locker Locker, n *nats.NatsInfra, l logger.Logger, cache *cache.Cache, config *config.Config) *InvoicesService {
41+
return &InvoicesService{repo: repo, wallets: wallets, balances: balances, n: n, db: db, l: l, cache: cache, locker: locker, config: config, events: events}
4142
}
4243

4344
func (s *InvoicesService) Create(tx *gorm.DB, invoice *domain.Invoices) error {
@@ -157,6 +158,11 @@ func (s *InvoicesService) RunCheck(ctx context.Context, cancel context.CancelFun
157158
continue
158159
}
159160

161+
if invoice.Status.IsCancelled() {
162+
fmt.Println("CANCELLED")
163+
return
164+
}
165+
160166
if invoice.Status.IsPaid() || invoice.IsInProcessing() {
161167
fmt.Println("!= NOT PAID: ", invoice.Status)
162168
return
@@ -340,6 +346,36 @@ func (s *InvoicesService) RunCheck(ctx context.Context, cancel context.CancelFun
340346

341347
}
342348

349+
func (s *InvoicesService) Cancel(invoiceId string) domain.ResponseError {
350+
invoice, err := s.repo.FindByID(s.db, invoiceId)
351+
if err != nil {
352+
if postgres.IsNotFound(err) {
353+
return domain.ErrInvoiceIdNotFound
354+
}
355+
return err
356+
}
357+
358+
if invoice.Status.IsCancelled() {
359+
return domain.ErrInvoiceAlreadyCancelled
360+
}
361+
362+
return s.db.Transaction(func(tx *gorm.DB) error {
363+
invoice.Status = domain.STATUS_CANCELLED
364+
365+
// update invoice table
366+
err := s.repo.Update(tx, invoice)
367+
if err != nil {
368+
s.l.TemplInvoiceErr("update invoice error: "+err.Error(), "", invoiceId, invoice.Amount, invoice.Cryptocurrency, logger.NA, invoice.MerchantID, logger.NA)
369+
return err
370+
}
371+
372+
// update cache
373+
s.cache.Set(invoice.InvoiceID, invoice, time.Minute*5)
374+
return nil
375+
})
376+
377+
}
378+
343379
func (s *InvoicesService) CalculateFinAmount(amount, rate decimal.Decimal, ceil ...int32) decimal.Decimal {
344380
var _ceil int32
345381

api/internal/service/service.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Invoices interface {
3434
// Tries to find from cache, if not found, searches the database
3535
FindGlobal(tx *gorm.DB, invoiceId string) (*domain.Invoices, error)
3636

37+
Cancel(invoiceId string) domain.ResponseError
3738
// for autostart only
3839
RunFindEnd()
3940
RunAutostartCheck()
@@ -119,9 +120,10 @@ func HewServices(ns *natsdomain.Ns, db *gorm.DB, l logger.Logger, config *config
119120

120121
webhookSender := NewWebhookSenderService(nil, l)
121122

122-
invoiceService := NewInvoicesService(db, repository.InitInvoicesRepo(), walletsRepo, balancesRepo, lockerService, n, l, cache.InitStorage(), config)
123-
124123
eventsRepo := repository.InitEventsRepo()
124+
125+
invoiceService := NewInvoicesService(db, repository.InitInvoicesRepo(), walletsRepo, balancesRepo, eventsRepo, lockerService, n, l, cache.InitStorage(), config)
126+
125127
GetWithdrawalService := NewGetWithdrawalService(db, n, l, eventsRepo, walletsRepo, balancesRepo, invoiceService, webhookSender, config)
126128

127129
withdrawalsRepo := repository.InitWithdrawalsRepo()

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ require (
1515
github.com/joho/godotenv v1.5.1
1616
github.com/kelseyhightower/envconfig v1.4.0
1717
github.com/nats-io/nats.go v1.36.0
18+
github.com/rs/cors v1.7.0
1819
github.com/shopspring/decimal v1.4.0
1920
github.com/xssnick/tonutils-go v1.9.8
2021
github.com/yeqown/go-qrcode/v2 v2.2.4

0 commit comments

Comments
 (0)