Skip to content

Commit

Permalink
chore: update invoice functions to match NIP-46 extensions spec
Browse files Browse the repository at this point in the history
- fix optional transaction fields
  • Loading branch information
rolznz committed Dec 18, 2023
1 parent 8b30f59 commit 3965ec1
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 116 deletions.
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,9 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light

`pay_keysend`

⚠️ `make_invoice`
- ⚠️ does not match spec
`make_invoice`

⚠️ `lookup_invoice`
- ⚠️ does not match spec
`lookup_invoice`

`list_transactions`
- ⚠️ from and until in request not supported
Expand All @@ -165,12 +163,10 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light
`pay_keysend`
- ⚠️ preimage in request not supported

⚠️ `make_invoice`
- ⚠️ expiry in request not supported
- ⚠️ does not match spec
`make_invoice`

⚠️ `lookup_invoice`
- ⚠️ does not match spec
`lookup_invoice`
- ⚠️ fees_paid in response not supported

`list_transactions`
- ⚠️ offset and unpaid in request not supported
Expand Down
88 changes: 48 additions & 40 deletions alby.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (svc *AlbyOAuthService) FetchUserToken(ctx context.Context, app App) (token
return tok, nil
}

func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) {
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{
Expand All @@ -93,7 +93,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
}).Errorf("App not found: %v", err)
return "", "", err
return nil, err
}

// amount provided in msat, but Alby API currently only supports sats. Will get truncated to a whole sat value
Expand All @@ -107,7 +107,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"descriptionHash": descriptionHash,
"expiry": expiry,
}).Errorf("amount must be 1000 msat or greater")
return "", "", errors.New("amount must be 1000 msat or greater")
return nil, errors.New("amount must be 1000 msat or greater")
}

svc.Logger.WithFields(logrus.Fields{
Expand All @@ -121,7 +121,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
}).Info("Processing make invoice request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
return "", "", err
return nil, err
}
client := svc.oauthConf.Client(ctx, tok)

Expand All @@ -138,7 +138,7 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
req, err := http.NewRequest("POST", fmt.Sprintf("%s/invoices", svc.cfg.AlbyAPIURL), body)
if err != nil {
svc.Logger.WithError(err).Error("Error creating request /invoices")
return "", "", err
return nil, err
}

// TODO: move to creation of HTTP client
Expand All @@ -156,14 +156,14 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"appId": app.ID,
"userId": app.User.ID,
}).Errorf("Failed to make invoice: %v", err)
return "", "", err
return nil, err
}

if resp.StatusCode < 300 {
responsePayload := &MakeInvoiceResponse{}
responsePayload := &AlbyInvoice{}
err = json.NewDecoder(resp.Body).Decode(responsePayload)
if err != nil {
return "", "", err
return nil, err
}
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
Expand All @@ -176,7 +176,9 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"paymentRequest": responsePayload.PaymentRequest,
"paymentHash": responsePayload.PaymentHash,
}).Info("Make invoice successful")
return responsePayload.PaymentRequest, responsePayload.PaymentHash, nil

transaction := albyInvoiceToTransaction(responsePayload)
return transaction, nil
}

errorPayload := &ErrorResponse{}
Expand All @@ -191,10 +193,10 @@ func (svc *AlbyOAuthService) MakeInvoice(ctx context.Context, senderPubkey strin
"userId": app.User.ID,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Make invoice failed %s", string(errorPayload.Message))
return "", "", errors.New(errorPayload.Message)
return nil, errors.New(errorPayload.Message)
}

func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error) {
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{
Expand All @@ -205,7 +207,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"senderPubkey": senderPubkey,
"paymentHash": paymentHash,
}).Errorf("App not found: %v", err)
return "", false, err
return nil, err
}

svc.Logger.WithFields(logrus.Fields{
Expand All @@ -216,7 +218,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
}).Info("Processing lookup invoice request")
tok, err := svc.FetchUserToken(ctx, app)
if err != nil {
return "", false, err
return nil, err
}
client := svc.oauthConf.Client(ctx, tok)

Expand All @@ -226,7 +228,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
req, err := http.NewRequest("GET", fmt.Sprintf("%s/invoices/%s", svc.cfg.AlbyAPIURL, paymentHash), body)
if err != nil {
svc.Logger.WithError(err).Errorf("Error creating request /invoices/%s", paymentHash)
return "", false, err
return nil, err
}

req.Header.Set("User-Agent", "NWC")
Expand All @@ -240,14 +242,14 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"appId": app.ID,
"userId": app.User.ID,
}).Errorf("Failed to lookup invoice: %v", err)
return "", false, err
return nil, err
}

if resp.StatusCode < 300 {
responsePayload := &LookupInvoiceResponse{}
responsePayload := &AlbyInvoice{}
err = json.NewDecoder(resp.Body).Decode(responsePayload)
if err != nil {
return "", false, err
return nil, err
}
svc.Logger.WithFields(logrus.Fields{
"senderPubkey": senderPubkey,
Expand All @@ -257,7 +259,9 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"paymentRequest": responsePayload.PaymentRequest,
"settled": responsePayload.Settled,
}).Info("Lookup invoice successful")
return responsePayload.PaymentRequest, responsePayload.Settled, nil

transaction = albyInvoiceToTransaction(responsePayload)
return transaction, nil
}

errorPayload := &ErrorResponse{}
Expand All @@ -269,7 +273,7 @@ func (svc *AlbyOAuthService) LookupInvoice(ctx context.Context, senderPubkey str
"userId": app.User.ID,
"APIHttpStatus": resp.StatusCode,
}).Errorf("Lookup invoice failed %s", string(errorPayload.Message))
return "", false, errors.New(errorPayload.Message)
return nil, errors.New(errorPayload.Message)
}

func (svc *AlbyOAuthService) GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error) {
Expand Down Expand Up @@ -437,27 +441,9 @@ func (svc *AlbyOAuthService) ListTransactions(ctx context.Context, senderPubkey

transactions = []Nip47Transaction{}
for _, invoice := range invoices {
description := invoice.Comment
if description == "" {
description = invoice.Memo
}

transaction := Nip47Transaction{
Type: invoice.Type,
Invoice: invoice.PaymentRequest,
Description: description,
DescriptionHash: invoice.DescriptionHash,
Preimage: invoice.Preimage,
PaymentHash: invoice.PaymentHash,
Amount: invoice.Amount * 1000,
FeesPaid: 0, // TODO: support fees
CreatedAt: invoice.CreatedAt,
ExpiresAt: invoice.ExpiresAt,
SettledAt: invoice.SettledAt,
Metadata: invoice.Metadata,
}

transactions = append(transactions, transaction)
transaction := albyInvoiceToTransaction(&invoice)

transactions = append(transactions, *transaction)
}

svc.Logger.WithFields(logrus.Fields{
Expand Down Expand Up @@ -712,3 +698,25 @@ func (svc *AlbyOAuthService) CallbackHandler(c echo.Context) error {
sess.Save(c.Request(), c.Response())
return c.Redirect(302, "/")
}

func albyInvoiceToTransaction(invoice *AlbyInvoice) *Nip47Transaction {
description := invoice.Comment
if description == "" {
description = invoice.Memo
}

return &Nip47Transaction{
Type: invoice.Type,
Invoice: invoice.PaymentRequest,
Description: description,
DescriptionHash: invoice.DescriptionHash,
Preimage: invoice.Preimage,
PaymentHash: invoice.PaymentHash,
Amount: invoice.Amount * 1000,
FeesPaid: 0, // TODO: support fees
CreatedAt: invoice.CreatedAt,
ExpiresAt: invoice.ExpiresAt,
SettledAt: invoice.SettledAt,
Metadata: invoice.Metadata,
}
}
5 changes: 2 additions & 3 deletions handle_lookup_invoice_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, request *Nip47
paymentHash = paymentRequest.PaymentHash
}

invoice, paid, err := svc.lnClient.LookupInvoice(ctx, event.PubKey, paymentHash)
transaction, err := svc.lnClient.LookupInvoice(ctx, event.PubKey, paymentHash)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
Expand All @@ -105,8 +105,7 @@ func (svc *Service) HandleLookupInvoiceEvent(ctx context.Context, request *Nip47
}

responsePayload := &Nip47LookupInvoiceResponse{
Invoice: invoice,
Paid: paid,
Nip47Transaction: *transaction,
}

nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED
Expand Down
5 changes: 2 additions & 3 deletions handle_make_invoice_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, request *Nip47Re
"expiry": makeInvoiceParams.Expiry,
}).Info("Making invoice")

invoice, paymentHash, err := svc.lnClient.MakeInvoice(ctx, event.PubKey, makeInvoiceParams.Amount, makeInvoiceParams.Description, makeInvoiceParams.DescriptionHash, makeInvoiceParams.Expiry)
transaction, err := svc.lnClient.MakeInvoice(ctx, event.PubKey, makeInvoiceParams.Amount, makeInvoiceParams.Description, makeInvoiceParams.DescriptionHash, makeInvoiceParams.Expiry)
if err != nil {
svc.Logger.WithFields(logrus.Fields{
"eventId": event.ID,
Expand All @@ -104,8 +104,7 @@ func (svc *Service) HandleMakeInvoiceEvent(ctx context.Context, request *Nip47Re
}

responsePayload := &Nip47MakeInvoiceResponse{
Invoice: invoice,
PaymentHash: paymentHash,
Nip47Transaction: *transaction,
}

nostrEvent.State = NOSTR_EVENT_STATE_HANDLER_EXECUTED
Expand Down
Loading

0 comments on commit 3965ec1

Please sign in to comment.