Files
lndhub.go/controllers/keysend.ctrl.go
2023-12-07 16:44:47 +05:30

132 lines
4.9 KiB
Go

package controllers
import (
"encoding/hex"
"fmt"
"net/http"
"strconv"
"github.com/getAlby/lndhub.go/common"
"github.com/getAlby/lndhub.go/lib"
"github.com/getAlby/lndhub.go/lib/responses"
"github.com/getAlby/lndhub.go/lib/service"
"github.com/getAlby/lndhub.go/lnd"
"github.com/getsentry/sentry-go"
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"github.com/lightningnetwork/lnd/lnrpc"
)
// KeySendController : Key send controller struct
type KeySendController struct {
svc *service.LndhubService
}
func NewKeySendController(svc *service.LndhubService) *KeySendController {
return &KeySendController{svc: svc}
}
type KeySendRequestBody struct {
Amount int64 `json:"amount" validate:"required,gt=0"`
Destination string `json:"destination" validate:"required"`
Memo string `json:"memo" validate:"omitempty"`
CustomRecords map[string]string `json:"customRecords" validate:"omitempty"`
}
type KeySendResponseBody struct {
RHash *lib.JavaScriptBuffer `json:"payment_hash,omitempty"`
Amount int64 `json:"num_satoshis,omitempty"`
Description string `json:"description,omitempty"`
Destination string `json:"destination,omitempty"`
DescriptionHashStr string `json:"description_hash,omitempty"`
PaymentError string `json:"payment_error,omitempty"`
PaymentPreimage *lib.JavaScriptBuffer `json:"payment_preimage,omitempty"`
PaymentRoute *service.Route `json:"payment_route,omitempty"`
}
func (controller *KeySendController) KeySend(c echo.Context) error {
userID := c.Get("UserID").(int64)
reqBody := KeySendRequestBody{}
if err := c.Bind(&reqBody); err != nil {
c.Logger().Errorf("Failed to load keysend request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
if err := c.Validate(&reqBody); err != nil {
c.Logger().Errorf("Invalid keysend request body: %v", err)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
lnPayReq := &lnd.LNPayReq{
PayReq: &lnrpc.PayReq{
Destination: reqBody.Destination,
NumSatoshis: reqBody.Amount,
Description: reqBody.Memo,
},
Keysend: true,
}
if controller.svc.LndClient.IsIdentityPubkey(reqBody.Destination) && reqBody.CustomRecords[strconv.Itoa(service.TLV_WALLET_ID)] == "" {
return c.JSON(http.StatusBadRequest, &responses.ErrorResponse{
Error: true,
Code: 8,
Message: fmt.Sprintf("Internal keysend payments require the custom record %d to be present.", service.TLV_WALLET_ID),
HttpStatusCode: 400,
})
}
resp, err := controller.svc.CheckOutgoingPaymentAllowed(c, lnPayReq, userID)
if err != nil {
return c.JSON(http.StatusInternalServerError, responses.GeneralServerError)
}
if resp != nil {
c.Logger().Errorf("Error: %v user_id:%v amount:%v", resp.Message, userID, lnPayReq.PayReq.NumSatoshis)
return c.JSON(resp.HttpStatusCode, resp)
}
invoice, errResp := controller.svc.AddOutgoingInvoice(c.Request().Context(), userID, "", lnPayReq)
if errResp != nil {
return c.JSON(errResp.HttpStatusCode, errResp)
}
if _, err := hex.DecodeString(invoice.DestinationPubkeyHex); err != nil || len(invoice.DestinationPubkeyHex) != common.DestinationPubkeyHexSize {
c.Logger().Errorf("Invalid destination pubkey hex user_id:%v pubkey:%v", userID, len(invoice.DestinationPubkeyHex))
return c.JSON(http.StatusBadRequest, responses.InvalidDestinationError)
}
invoice.DestinationCustomRecords = map[uint64][]byte{}
for key, value := range reqBody.CustomRecords {
intKey, err := strconv.Atoi(key)
if err != nil {
c.Logger().Errorj(
log.JSON{
"message": "invalid custom records",
"error": err,
"lndhub_user_id": userID,
},
)
return c.JSON(http.StatusBadRequest, responses.BadArgumentsError)
}
invoice.DestinationCustomRecords[uint64(intKey)] = []byte(value)
}
sendPaymentResponse, err := controller.svc.PayInvoice(c.Request().Context(), invoice)
if err != nil {
c.Logger().Errorf("Payment failed: user_id:%v error: %v", userID, 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),
})
}
responseBody := &KeySendResponseBody{}
responseBody.RHash = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentHash}
responseBody.Amount = invoice.Amount
responseBody.Destination = invoice.DestinationPubkeyHex
responseBody.Description = invoice.Memo
responseBody.DescriptionHashStr = invoice.DescriptionHash
responseBody.PaymentError = sendPaymentResponse.PaymentError
responseBody.PaymentPreimage = &lib.JavaScriptBuffer{Data: sendPaymentResponse.PaymentPreimage}
responseBody.PaymentRoute = sendPaymentResponse.PaymentRoute
return c.JSON(http.StatusOK, responseBody)
}