mirror of
https://github.com/getAlby/lndhub.go.git
synced 2026-01-10 00:16:04 +01:00
135 lines
4.7 KiB
Go
135 lines
4.7 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/getAlby/lndhub.go/lib"
|
|
"github.com/getAlby/lndhub.go/lib/responses"
|
|
"github.com/getAlby/lndhub.go/lib/service"
|
|
"github.com/getsentry/sentry-go"
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// Bolt12Controller : Bolt12Controller struct
|
|
type Bolt12Controller struct {
|
|
svc *service.LndhubService
|
|
}
|
|
type FetchInvoiceRequestBody struct {
|
|
Amount int64 `json:"amt" validate:"required"` // todo: validate properly, amount not strictly needed always amount in Satoshi
|
|
Memo string `json:"memo"`
|
|
Offer string `json:"offer" validate:"required"`
|
|
}
|
|
|
|
func NewBolt12Controller(svc *service.LndhubService) *Bolt12Controller {
|
|
return &Bolt12Controller{svc: svc}
|
|
}
|
|
|
|
// Decode : Decode handler
|
|
func (controller *Bolt12Controller) Decode(c echo.Context) error {
|
|
offer := c.Param("offer")
|
|
decoded, err := controller.svc.LndClient.DecodeBolt12(context.TODO(), offer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.JSON(http.StatusOK, decoded)
|
|
}
|
|
|
|
// FetchInvoice: fetches an invoice from a bolt12 offer for a certain amount
|
|
func (controller *Bolt12Controller) FetchInvoice(c echo.Context) error {
|
|
var body FetchInvoiceRequestBody
|
|
|
|
if err := c.Bind(&body); err != nil {
|
|
c.Logger().Errorf("Failed to load fetchinvoice request body: %v", err)
|
|
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
|
|
}
|
|
|
|
if err := c.Validate(&body); err != nil {
|
|
c.Logger().Errorf("Invalid fetchinvoice request body: %v", err)
|
|
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
|
|
}
|
|
|
|
invoice, err := controller.svc.FetchBolt12Invoice(context.TODO(), body.Offer, body.Memo, body.Amount)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.JSON(http.StatusOK, invoice)
|
|
}
|
|
|
|
// PayOffer: fetches an invoice from a bolt12 offer for a certain amount, and pays it
|
|
func (controller *Bolt12Controller) PayBolt12(c echo.Context) error {
|
|
userID := c.Get("UserID").(int64)
|
|
var body FetchInvoiceRequestBody
|
|
|
|
if err := c.Bind(&body); err != nil {
|
|
c.Logger().Errorf("Failed to load fetchinvoice request body: %v", err)
|
|
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
|
|
}
|
|
|
|
if err := c.Validate(&body); err != nil {
|
|
c.Logger().Errorf("Invalid fetchinvoice request body: %v", err)
|
|
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
|
|
}
|
|
|
|
bolt12, err := controller.svc.FetchBolt12Invoice(c.Request().Context(), body.Offer, body.Memo, body.Amount)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
decodedPaymentRequest, err := controller.svc.TransformBolt12(bolt12)
|
|
if err != nil {
|
|
c.Logger().Errorf("Invalid payment request: %v", err)
|
|
sentry.CaptureException(err)
|
|
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
|
|
}
|
|
|
|
invoice, err := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, bolt12.Encoded, decodedPaymentRequest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
currentBalance, err := controller.svc.CurrentUserBalance(c.Request().Context(), userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if currentBalance < invoice.Amount {
|
|
c.Logger().Errorf("User does not have enough balance invoice_id=%v user_id=%v balance=%v amount=%v", invoice.ID, userID, currentBalance, invoice.Amount)
|
|
|
|
return c.JSON(http.StatusBadRequest, responses.NotEnoughBalanceError)
|
|
}
|
|
|
|
sendPaymentResponse, err := controller.svc.PayInvoice(c.Request().Context(), invoice)
|
|
if err != nil {
|
|
c.Logger().Errorf("Payment failed: %v", err)
|
|
sentry.CaptureException(err)
|
|
return c.JSON(http.StatusBadRequest, echo.Map{
|
|
"error": true,
|
|
"code": 10,
|
|
"message": fmt.Sprintf("Payment failed. Does the receiver have enough inbound capacity? (%v)", err),
|
|
})
|
|
}
|
|
|
|
var responseBody struct {
|
|
RHash *lib.JavaScriptBuffer `json:"payment_hash,omitempty"`
|
|
PaymentRequest string `json:"payment_request,omitempty"`
|
|
PayReq string `json:"pay_req,omitempty"`
|
|
Amount int64 `json:"num_satoshis,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
DescriptionHashStr string `json:"description_hash,omitempty"`
|
|
PaymentError string `json:"payment_error,omitempty"`
|
|
PaymentPreimage *lib.JavaScriptBuffer `json:"payment_preimage,omitempty"`
|
|
PaymentRoute *service.Route `json:"route,omitempty"`
|
|
}
|
|
|
|
responseBody.RHash = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentHash}
|
|
responseBody.PaymentRequest = bolt12.Encoded
|
|
responseBody.PayReq = bolt12.Encoded
|
|
responseBody.Description = bolt12.PayerNote
|
|
responseBody.PaymentError = sendPaymentResponse.PaymentError
|
|
responseBody.PaymentPreimage = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentPreimage}
|
|
responseBody.PaymentRoute = sendPaymentResponse.PaymentRoute
|
|
|
|
return c.JSON(http.StatusOK, &responseBody)
|
|
}
|