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

chore: update invoice functions to match NIP-46 extensions spec, fix optional transaction fields #196

Merged
merged 3 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 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,11 @@ You can also contribute to our [bounty program](https://github.com/getAlby/light
✅ `pay_keysend`
- ⚠️ preimage in request not supported

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

⚠️ `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
92 changes: 52 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,29 @@ 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
}
var preimage string
if invoice.SettledAt != nil {
preimage = invoice.Preimage
}

return &Nip47Transaction{
Type: invoice.Type,
Invoice: invoice.PaymentRequest,
Description: description,
DescriptionHash: invoice.DescriptionHash,
Preimage: 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
Loading