diff --git a/db/structs.go b/db/structs.go index 4491fbd88..4e37fc974 100644 --- a/db/structs.go +++ b/db/structs.go @@ -390,6 +390,8 @@ type Bounty struct { CodingLanguages pq.StringArray `gorm:"type:text[];not null default:'[]'" json:"coding_languages"` PhaseUuid *string `json:"phase_uuid"` PhasePriority *int `json:"phase_priority"` + PaymentPending bool `gorm:"default:false" json:"payment_pending"` + PaymentFailed bool `gorm:"default:false" json:"payment_failed"` } // Todo: Change back to Bounty @@ -427,6 +429,8 @@ type NewBounty struct { CodingLanguages pq.StringArray `gorm:"type:text[];not null default:'[]'" json:"coding_languages"` PhaseUuid string `json:"phase_uuid"` PhasePriority int `json:"phase_priority"` + PaymentPending bool `gorm:"default:false" json:"payment_pending"` + PaymentFailed bool `gorm:"default:false" json:"payment_failed"` } type BountyOwners struct { @@ -718,6 +722,7 @@ type PaymentHistory struct { ReceiverPubKey string `json:"receiver_pubkey"` Tag string `json:"tag,omitempty"` PaymentStatus string `json:"payment_status,omitempty"` + Error string `json:"error,omitempty"` Created *time.Time `json:"created"` Updated *time.Time `json:"updated"` Status bool `json:"status"` @@ -734,6 +739,7 @@ type NewPaymentHistory struct { ReceiverPubKey string `json:"receiver_pubkey"` Tag string `json:"tag,omitempty"` PaymentStatus string `json:"payment_status,omitempty"` + Error string `json:"error,omitempty"` Created *time.Time `json:"created"` Updated *time.Time `json:"updated"` Status bool `json:"status"` diff --git a/db/structsv2.go b/db/structsv2.go index f6df64bba..c0ba2f0e5 100644 --- a/db/structsv2.go +++ b/db/structsv2.go @@ -29,6 +29,7 @@ type V2SendOnionRes struct { Tag string `json:"tag"` Preimage string `json:"preimage"` PaymentHash string `json:"payment_hash"` + Message string `json:"message,omitempty"` } type V2PayInvoiceBody struct { diff --git a/db/workspaces.go b/db/workspaces.go index 4b352a1a7..5b322d6d9 100644 --- a/db/workspaces.go +++ b/db/workspaces.go @@ -440,17 +440,6 @@ func (db database) WithdrawBudget(sender_pubkey string, workspace_uuid string, a func (db database) AddPaymentHistory(payment NewPaymentHistory) NewPaymentHistory { db.db.Create(&payment) - // get Workspace budget and subtract payment from total budget - WorkspaceBudget := db.GetWorkspaceBudget(payment.WorkspaceUuid) - totalBudget := WorkspaceBudget.TotalBudget - - // deduct amount if it's a bounty payment - if payment.PaymentType == "payment" { - WorkspaceBudget.TotalBudget = totalBudget - payment.Amount - } - - db.UpdateWorkspaceBudget(WorkspaceBudget) - return payment } diff --git a/handlers/bounty.go b/handlers/bounty.go index 4216a61d5..422aaefec 100644 --- a/handlers/bounty.go +++ b/handlers/bounty.go @@ -256,13 +256,22 @@ func (h *bountyHandler) CreateOrEditBounty(w http.ResponseWriter, r *http.Reques // get bounty from DB dbBounty := h.db.GetBounty(bounty.ID) + // check if the bounty has a pending payment + if dbBounty.PaymentPending { + msg := "You cannot update a bounty with a pending payment" + fmt.Println("[bounty]", msg) + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode(msg) + return + } + // trying to update // check if bounty belongs to user if pubKeyFromAuth != dbBounty.OwnerID { if bounty.WorkspaceUuid != "" { hasBountyRoles := h.userHasManageBountyRoles(pubKeyFromAuth, bounty.WorkspaceUuid) if !hasBountyRoles { - msg := "You don't have a=the right permission ton update bounty" + msg := "You don't have the right permission ton update bounty" fmt.Println("[bounty]", msg) w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(msg) @@ -360,6 +369,12 @@ func UpdatePaymentStatus(w http.ResponseWriter, r *http.Request) { created, _ := strconv.ParseUint(createdParam, 10, 32) bounty, _ := db.DB.GetBountyByCreated(uint(created)) + if bounty.PaymentPending { + w.WriteHeader(http.StatusBadGateway) + json.NewEncoder(w).Encode("Cannot update a bounty with a pending payment") + return + } + if bounty.ID != 0 && bounty.Created == int64(created) { bounty.Paid = !bounty.Paid now := time.Now() @@ -369,6 +384,7 @@ func UpdatePaymentStatus(w http.ResponseWriter, r *http.Request) { bounty.Completed = true bounty.CompletionDate = &now bounty.MarkAsPaidDate = &now + if bounty.PaidDate == nil { bounty.PaidDate = &now } @@ -382,8 +398,14 @@ func UpdatePaymentStatus(w http.ResponseWriter, r *http.Request) { func UpdateCompletedStatus(w http.ResponseWriter, r *http.Request) { createdParam := chi.URLParam(r, "created") created, _ := strconv.ParseUint(createdParam, 10, 32) - bounty, _ := db.DB.GetBountyByCreated(uint(created)) + + if bounty.PaymentPending { + w.WriteHeader(http.StatusBadGateway) + json.NewEncoder(w).Encode("Cannot update a bounty with a pending payment") + return + } + if bounty.ID != 0 && bounty.Created == int64(created) { now := time.Now() // set bounty as completed @@ -436,6 +458,8 @@ func (h *bountyHandler) GenerateBountyResponse(bounties []db.NewBounty) []db.Bou Updated: bounty.Updated, CodingLanguages: bounty.CodingLanguages, Completed: bounty.Completed, + PaymentPending: bounty.PaymentPending, + PaymentFailed: bounty.PaymentFailed, }, Assignee: db.Person{ ID: assignee.ID, @@ -531,6 +555,13 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request return } + if bounty.PaymentPending { + w.WriteHeader(http.StatusBadRequest) + json.NewEncoder(w).Encode("Bounty payemnt is pending, cannot retry payment") + h.m.Unlock() + return + } + // check if user is the admin of the workspace // or has a pay bounty role hasRole := h.userHasAccess(pubKeyFromAuth, bounty.WorkspaceUuid, db.PayBounty) @@ -637,7 +668,7 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request BountyId: id, Created: &now, Updated: &now, - Status: true, + Status: false, PaymentType: "payment", Tag: v2KeysendRes.Tag, PaymentStatus: v2KeysendRes.Status, @@ -649,6 +680,7 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request bounty.PaidDate = &now bounty.Completed = true bounty.CompletionDate = &now + paymentHistory.Status = true h.db.ProcessBountyPayment(paymentHistory, bounty) @@ -659,10 +691,42 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request if err == nil { socket.Conn.WriteJSON(msg) } + + h.m.Unlock() + return + } else if v2KeysendRes.Status == db.PaymentPending { + // Send payment status + log.Printf("[bounty] V2 Status is pending: %s", v2KeysendRes.Status) + bounty.Paid = false + bounty.PaymentPending = true + bounty.PaidDate = &now + bounty.Completed = true + bounty.CompletionDate = &now + paymentHistory.Status = true + + h.db.ProcessBountyPayment(paymentHistory, bounty) + + msg["msg"] = "keysend_pending" + msg["invoice"] = "" + + socket, err := h.getSocketConnections(request.Websocket_token) + if err == nil { + socket.Conn.WriteJSON(msg) + } + + h.m.Unlock() + return } else { // Send payment status log.Printf("[bounty] V2 Status Was not completed: %s", v2KeysendRes.Status) + bounty.Paid = false + bounty.PaymentPending = false + bounty.PaymentFailed = true + + // set the error message + paymentHistory.Error = v2KeysendRes.Message + h.db.AddPaymentHistory(paymentHistory) log.Println("Keysend payment not completed ===") @@ -775,8 +839,6 @@ func (h *bountyHandler) MakeBountyPayment(w http.ResponseWriter, r *http.Request return } } - - h.m.Unlock() } func (h *bountyHandler) GetBountyPaymentStatus(w http.ResponseWriter, r *http.Request) { @@ -878,6 +940,7 @@ func (h *bountyHandler) UpdateBountyPaymentStatus(w http.ResponseWriter, r *http now := time.Now() bounty.Paid = true + bounty.PaymentPending = false bounty.PaidDate = &now bounty.Completed = true bounty.CompletionDate = &now diff --git a/routes/bounty.go b/routes/bounty.go index d6d83d2e0..74f969d52 100644 --- a/routes/bounty.go +++ b/routes/bounty.go @@ -38,7 +38,6 @@ func BountyRoutes() chi.Router { r.Post("/pay/{id}", bountyHandler.MakeBountyPayment) r.Get("/payment/status/{id}", bountyHandler.GetBountyPaymentStatus) r.Put("/payment/status/{id}", bountyHandler.UpdateBountyPaymentStatus) - r.Get("/payment/status/{id}", bountyHandler.GetBountyPaymentStatus) r.Post("/", bountyHandler.CreateOrEditBounty) r.Delete("/assignee", handlers.DeleteBountyAssignee)