Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multi pay invoice method #218

Closed
wants to merge 13 commits into from
89 changes: 51 additions & 38 deletions alby.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net/http"
"net/url"
"strconv"
"sync"
"time"

"github.com/labstack/echo-contrib/session"
"github.com/labstack/echo/v4"
Expand All @@ -18,10 +20,11 @@ import (
)

type AlbyOAuthService struct {
cfg *Config
oauthConf *oauth2.Config
db *gorm.DB
Logger *logrus.Logger
cfg *Config
oauthConf *oauth2.Config
db *gorm.DB
Logger *logrus.Logger
tokenMutex sync.Mutex
}

func NewAlbyOauthService(svc *Service, e *echo.Echo) (result *AlbyOAuthService, err error) {
Expand Down Expand Up @@ -52,12 +55,22 @@ func NewAlbyOauthService(svc *Service, e *echo.Echo) (result *AlbyOAuthService,
}

func (svc *AlbyOAuthService) FetchUserToken(ctx context.Context, app App) (token *oauth2.Token, err error) {
user := app.User
tok, err := svc.oauthConf.TokenSource(ctx, &oauth2.Token{
svc.tokenMutex.Lock()
defer svc.tokenMutex.Unlock()

user := User{}
err = svc.db.First(&user, &User{ID: app.UserId}).Error
currentToken := &oauth2.Token{
AccessToken: user.AccessToken,
RefreshToken: user.RefreshToken,
Expiry: user.Expiry,
}).Token()
}

if user.Expiry.After(time.Now()) {
rolznz marked this conversation as resolved.
Show resolved Hide resolved
return currentToken, nil
}

tok, err := svc.oauthConf.TokenSource(ctx, currentToken).Token()
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": app.NostrPubkey,
Expand All @@ -82,7 +95,7 @@ func (svc *AlbyOAuthService) FetchUserToken(ctx context.Context, app App) (token
func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
// TODO: move to a shared function
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand Down Expand Up @@ -117,7 +130,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Processing make invoice request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
Expand Down Expand Up @@ -154,7 +167,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Errorf("Failed to make invoice: %v", err)
return nil, err
}
Expand All @@ -172,7 +185,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"paymentRequest": responsePayload.PaymentRequest,
"paymentHash": responsePayload.PaymentHash,
}).Info("Make invoice successful")
Expand All @@ -190,7 +203,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Make invoice failed %s", string(errorPayload.Message))
return nil, errors.New(errorPayload.Message)
Expand All @@ -199,7 +212,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (transaction *Nip47Transaction, err error) {
// TODO: move to a shared function
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand All @@ -214,7 +227,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"senderPubkey": senderPubkey,
"paymentHash": paymentHash,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Processing lookup invoice request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
Expand All @@ -240,7 +253,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"senderPubkey": senderPubkey,
"paymentHash": paymentHash,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Errorf("Failed to lookup invoice: %v", err)
return nil, err
}
Expand All @@ -255,7 +268,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"senderPubkey": senderPubkey,
"paymentHash": paymentHash,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"paymentRequest": responsePayload.PaymentRequest,
"settled": responsePayload.Settled,
}).Info("Lookup invoice successful")
Expand All @@ -270,15 +283,15 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"senderPubkey": senderPubkey,
"paymentHash": paymentHash,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Lookup invoice failed %s", string(errorPayload.Message))
return nil, errors.New(errorPayload.Message)
}

func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) {
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand All @@ -291,7 +304,7 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Info fetch successful")
return &NodeInfo{
Alias: "getalby.com",
Expand All @@ -305,7 +318,7 @@ func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (

func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error) {
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand Down Expand Up @@ -333,7 +346,7 @@ func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Errorf("Failed to fetch balance: %v", err)
return 0, err
}
Expand All @@ -347,7 +360,7 @@ func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Balance fetch successful")
return int64(responsePayload.Balance), nil
}
Expand All @@ -357,15 +370,15 @@ func (svc *AlbyOAuthService) GetBalance(ctx context.Context, senderPubkey string
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Balance fetch failed %s", string(errorPayload.Message))
return 0, errors.New(errorPayload.Message)
}

func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error) {
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand Down Expand Up @@ -419,7 +432,7 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"requestUrl": requestUrl,
}).Errorf("Failed to fetch invoices: %v", err)
return nil, err
Expand All @@ -433,7 +446,7 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"requestUrl": requestUrl,
}).Errorf("Failed to decode invoices: %v", err)
return nil, err
Expand All @@ -449,7 +462,7 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"requestUrl": requestUrl,
}).Info("List transactions successful")
return transactions, nil
Expand All @@ -460,7 +473,7 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
"requestUrl": requestUrl,
}).Errorf("List transactions failed %s", string(errorPayload.Message))
Expand All @@ -469,7 +482,7 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey

func (svc *AlbyOAuthService) SendPaymentSync(ctx context.Context, senderPubkey, payReq string) (preimage string, err error) {
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand All @@ -483,7 +496,7 @@ func (svc *AlbyOAuthService) SendPaymentSync(ctx context.Context, senderPubkey,
"senderPubkey": senderPubkey,
"bolt11": payReq,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Processing payment request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
Expand Down Expand Up @@ -512,7 +525,7 @@ func (svc *AlbyOAuthService) SendPaymentSync(ctx context.Context, senderPubkey,
"senderPubkey": senderPubkey,
"bolt11": payReq,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Errorf("Failed to pay invoice: %v", err)
return "", err
}
Expand All @@ -527,7 +540,7 @@ func (svc *AlbyOAuthService) SendPaymentSync(ctx context.Context, senderPubkey,
"senderPubkey": senderPubkey,
"bolt11": payReq,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"paymentHash": responsePayload.PaymentHash,
}).Info("Payment successful")
return responsePayload.Preimage, nil
Expand All @@ -539,15 +552,15 @@ func (svc *AlbyOAuthService) SendPaymentSync(ctx context.Context, senderPubkey,
"senderPubkey": senderPubkey,
"bolt11": payReq,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Payment failed %s", string(errorPayload.Message))
return "", errors.New(errorPayload.Message)
}

func (svc *AlbyOAuthService) SendKeysend(ctx context.Context, senderPubkey string, amount int64, destination, preimage string, custom_records []TLVRecord) (preImage string, err error) {
app := App{}
err = svc.db.Preload("User").First(&app, &App{
err = svc.db.First(&app, &App{
NostrPubkey: senderPubkey,
}).Error
if err != nil {
Expand All @@ -561,7 +574,7 @@ func (svc *AlbyOAuthService) SendKeysend(ctx context.Context, senderPubkey strin
"senderPubkey": senderPubkey,
"payeePubkey": destination,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Info("Processing keysend request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
Expand Down Expand Up @@ -598,7 +611,7 @@ func (svc *AlbyOAuthService) SendKeysend(ctx context.Context, senderPubkey strin
"senderPubkey": senderPubkey,
"payeePubkey": destination,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
}).Errorf("Failed to pay keysend: %v", err)
return "", err
}
Expand All @@ -613,7 +626,7 @@ func (svc *AlbyOAuthService) SendKeysend(ctx context.Context, senderPubkey strin
"senderPubkey": senderPubkey,
"payeePubkey": destination,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"preimage": responsePayload.Preimage,
"paymentHash": responsePayload.PaymentHash,
}).Info("Keysend payment successful")
Expand All @@ -626,7 +639,7 @@ func (svc *AlbyOAuthService) SendKeysend(ctx context.Context, senderPubkey strin
"senderPubkey": senderPubkey,
"payeePubkey": destination,
"appId": app.ID,
"userId": app.User.ID,
"userId": app.UserId,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Payment failed %s", string(errorPayload.Message))
return "", errors.New(errorPayload.Message)
Expand Down
Loading