chore: update invoice functions to match NIP-46 extensions spec

- fix optional transaction fields
This commit is contained in:
Roland Bewick 2023-12-18 19:58:36 +07:00
parent 8b30f59d11
commit 3965ec12aa
7 changed files with 137 additions and 115 deletions

View file

@ -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
@ -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

86
alby.go
View file

@ -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{
@ -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
@ -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{
@ -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)
@ -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
@ -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,
@ -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{}
@ -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{
@ -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{
@ -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)
@ -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")
@ -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,
@ -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{}
@ -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) {
@ -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 := albyInvoiceToTransaction(&invoice)
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)
transactions = append(transactions, *transaction)
}
svc.Logger.WithFields(logrus.Fields{
@ -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,
}
}

View file

@ -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,
@ -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

View file

@ -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,
@ -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

87
lnd.go
View file

@ -25,9 +25,9 @@ type LNClient interface {
SendKeysend(ctx context.Context, senderPubkey string, amount int64, destination, preimage string, custom_records []TLVRecord) (preImage string, err error)
GetBalance(ctx context.Context, senderPubkey string) (balance int64, err error)
GetInfo(ctx context.Context, senderPubkey string) (info *NodeInfo, err error)
MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error)
LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error)
ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (invoices []Nip47Transaction, err error)
MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error)
LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (transaction *Nip47Transaction, err error)
ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (transactions []Nip47Transaction, err error)
}
// wrap it again :sweat_smile:
@ -75,21 +75,8 @@ func (svc *LNDService) ListTransactions(ctx context.Context, senderPubkey string
continue
}
transaction := Nip47Transaction{
Type: "incoming",
Invoice: invoice.PaymentRequest,
Description: invoice.Memo,
DescriptionHash: hex.EncodeToString(invoice.DescriptionHash),
Preimage: hex.EncodeToString(invoice.RPreimage),
PaymentHash: hex.EncodeToString(invoice.RHash),
Amount: invoice.ValueMsat,
FeesPaid: invoice.AmtPaidMsat,
CreatedAt: time.Unix(invoice.CreationDate, 0),
SettledAt: time.Unix(invoice.SettleDate, 0),
ExpiresAt: time.Unix(invoice.CreationDate+invoice.Expiry, 0),
// TODO: Metadata (e.g. keysend)
}
transactions = append(transactions, transaction)
transaction := lndInvoiceToTransaction(invoice)
transactions = append(transactions, *transaction)
}
// Fetch payments
var payments []*lnrpc.Payment
@ -108,7 +95,7 @@ func (svc *LNDService) ListTransactions(ctx context.Context, senderPubkey string
continue
}
var paymentRequest decodepay.Bolt11
var expiresAt time.Time
var expiresAt *time.Time
var description string
var descriptionHash string
if payment.PaymentRequest != "" {
@ -120,15 +107,17 @@ func (svc *LNDService) ListTransactions(ctx context.Context, senderPubkey string
return nil, err
}
expiresAt = time.UnixMilli(int64(paymentRequest.CreatedAt) * 1000).Add(time.Duration(paymentRequest.Expiry) * time.Second)
expiresAt = &time.Time{}
*expiresAt = time.UnixMilli(int64(paymentRequest.CreatedAt) * 1000).Add(time.Duration(paymentRequest.Expiry) * time.Second)
description = paymentRequest.Description
descriptionHash = paymentRequest.DescriptionHash
}
var settledAt time.Time
var settledAt *time.Time
if payment.Status == lnrpc.Payment_SUCCEEDED {
// FIXME: how to get the actual settled at time?
settledAt = time.Unix(0, payment.CreationTimeNs)
settledAt = &time.Time{}
*settledAt = time.Unix(0, payment.CreationTimeNs)
}
transaction := Nip47Transaction{
@ -171,7 +160,7 @@ func (svc *LNDService) GetInfo(ctx context.Context, senderPubkey string) (info *
}, nil
}
func (svc *LNDService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) {
func (svc *LNDService) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
var descriptionHashBytes []byte
if descriptionHash != "" {
@ -185,34 +174,41 @@ func (svc *LNDService) MakeInvoice(ctx context.Context, senderPubkey string, amo
"descriptionHash": descriptionHash,
"expiry": expiry,
}).Errorf("Invalid description hash")
return "", "", errors.New("Description hash must be 32 bytes hex")
return nil, errors.New("Description hash must be 32 bytes hex")
}
}
resp, err := svc.client.AddInvoice(ctx, &lnrpc.Invoice{ValueMsat: amount, Memo: description, DescriptionHash: descriptionHashBytes, Expiry: expiry})
if err != nil {
return "", "", err
return nil, err
}
return resp.GetPaymentRequest(), hex.EncodeToString(resp.GetRHash()), nil
inv, err := svc.client.LookupInvoice(ctx, &lnrpc.PaymentHash{RHash: resp.RHash})
if err != nil {
return nil, err
}
transaction = lndInvoiceToTransaction(inv)
return transaction, nil
}
func (svc *LNDService) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error) {
func (svc *LNDService) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (transaction *Nip47Transaction, err error) {
paymentHashBytes, err := hex.DecodeString(paymentHash)
if err != nil || len(paymentHashBytes) != 32 {
svc.Logger.WithFields(logrus.Fields{
"paymentHash": paymentHash,
}).Errorf("Invalid payment hash")
return "", false, errors.New("Payment hash must be 32 bytes hex")
return nil, errors.New("Payment hash must be 32 bytes hex")
}
lndInvoice, err := svc.client.LookupInvoice(ctx, &lnrpc.PaymentHash{RHash: paymentHashBytes})
if err != nil {
return "", false, err
return nil, err
}
return lndInvoice.PaymentRequest, lndInvoice.State == *lnrpc.Invoice_SETTLED.Enum(), nil
transaction = lndInvoiceToTransaction(lndInvoice)
return transaction, nil
}
func (svc *LNDService) SendPaymentSync(ctx context.Context, senderPubkey, payReq string) (preimage string, err error) {
@ -358,3 +354,34 @@ func NewLNDService(ctx context.Context, svc *Service, e *echo.Echo) (result *LND
return lndService, nil
}
func lndInvoiceToTransaction(invoice *lnrpc.Invoice) *Nip47Transaction {
var settledAt *time.Time
var preimage string
if invoice.State == lnrpc.Invoice_SETTLED {
settledAt = &time.Time{}
*settledAt = time.Unix(invoice.SettleDate, 0)
// only set preimage if invoice is settled
preimage = hex.EncodeToString(invoice.RPreimage)
}
var expiresAt *time.Time
if invoice.Expiry > 0 {
expiresAt = &time.Time{}
*expiresAt = time.Unix(invoice.SettleDate, 0)
}
return &Nip47Transaction{
Type: "incoming",
Invoice: invoice.PaymentRequest,
Description: invoice.Memo,
DescriptionHash: hex.EncodeToString(invoice.DescriptionHash),
Preimage: preimage,
PaymentHash: hex.EncodeToString(invoice.RHash),
Amount: invoice.ValueMsat,
FeesPaid: invoice.AmtPaidMsat,
CreatedAt: time.Unix(invoice.CreationDate, 0),
SettledAt: settledAt,
ExpiresAt: expiresAt,
// TODO: Metadata (e.g. keysend)
}
}

View file

@ -136,8 +136,8 @@ type Nip47Transaction struct {
Amount int64 `json:"amount"`
FeesPaid int64 `json:"fees_paid"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
SettledAt time.Time `json:"settled_at"`
ExpiresAt *time.Time `json:"expires_at"`
SettledAt *time.Time `json:"settled_at"`
Metadata interface{} `json:"metadata,omitempty"`
}
@ -150,9 +150,9 @@ type AlbyInvoice struct {
// CreationDate uint64 `json:"creation_date"`
Currency string `json:"currency"`
// custom_records
DescriptionHash string `json:"description_hash"`
ExpiresAt time.Time `json:"expires_at"`
Expiry uint32 `json:"expiry"`
DescriptionHash string `json:"description_hash"`
ExpiresAt *time.Time `json:"expires_at"`
Expiry uint32 `json:"expiry"`
// Identifier string
KeysendMessage string `json:"keysend_message"`
Memo string `json:"memo"`
@ -163,10 +163,10 @@ type AlbyInvoice struct {
PaymentRequest string `json:"payment_request"`
Preimage string `json:"preimage"`
// r_hash_str
Settled bool `json:"settled"`
SettledAt time.Time `json:"settled_at"`
State string `json:"state"`
Type string `json:"type"`
Settled bool `json:"settled"`
SettledAt *time.Time `json:"settled_at"`
State string `json:"state"`
Type string `json:"type"`
// value
}
@ -198,18 +198,12 @@ type MakeInvoiceRequest struct {
DescriptionHash string `json:"description_hash"`
}
// TODO: this should have the same content as Nip47Transaction
type MakeInvoiceResponse struct {
// Nip47Transaction
PaymentRequest string `json:"payment_request"`
PaymentHash string `json:"payment_hash"`
Nip47Transaction
}
// TODO: this should have the same content as Nip47Transaction
type LookupInvoiceResponse struct {
// Nip47Transaction
PaymentRequest string `json:"payment_request"`
Settled bool `json:"settled"`
Nip47Transaction
}
type ErrorResponse struct {
@ -293,8 +287,7 @@ type Nip47MakeInvoiceParams struct {
Expiry int64 `json:"expiry"`
}
type Nip47MakeInvoiceResponse struct {
Invoice string `json:"invoice"`
PaymentHash string `json:"payment_hash"`
Nip47Transaction
}
type Nip47LookupInvoiceParams struct {
@ -303,8 +296,7 @@ type Nip47LookupInvoiceParams struct {
}
type Nip47LookupInvoiceResponse struct {
Invoice string `json:"invoice"`
Paid bool `json:"paid"`
Nip47Transaction
}
type Nip47ListTransactionsParams struct {

View file

@ -110,6 +110,8 @@ var mockNodeInfo = NodeInfo{
BlockHash: "123blockhash",
}
var mockTime = time.Unix(1693876963, 0)
var mockTransactions = []Nip47Transaction{
{
Type: "incoming",
@ -120,7 +122,7 @@ var mockTransactions = []Nip47Transaction{
PaymentHash: "payment_hash_1",
Amount: 1000,
FeesPaid: 50,
SettledAt: time.Unix(1693876963, 0),
SettledAt: &mockTime,
Metadata: map[string]interface{}{
"key1": "value1",
"key2": 42,
@ -135,9 +137,10 @@ var mockTransactions = []Nip47Transaction{
PaymentHash: "payment_hash_2",
Amount: 2000,
FeesPaid: 75,
SettledAt: time.Unix(1693876965, 0),
SettledAt: &mockTime,
},
}
var mockTransaction = &mockTransactions[0]
// TODO: split up into individual tests
func TestHandleEvent(t *testing.T) {
@ -480,8 +483,7 @@ func TestHandleEvent(t *testing.T) {
}
err = json.Unmarshal([]byte(decrypted), received)
assert.NoError(t, err)
assert.Equal(t, mockInvoice, received.Result.(*Nip47MakeInvoiceResponse).Invoice)
assert.Equal(t, mockPaymentHash, received.Result.(*Nip47MakeInvoiceResponse).PaymentHash)
assert.Equal(t, mockTransaction.Preimage, received.Result.(*Nip47MakeInvoiceResponse).Preimage)
// lookup_invoice: without permission
newPayload, err = nip04.Encrypt(nip47LookupInvoiceJson, ss)
@ -520,8 +522,7 @@ func TestHandleEvent(t *testing.T) {
}
err = json.Unmarshal([]byte(decrypted), received)
assert.NoError(t, err)
assert.Equal(t, mockInvoice, received.Result.(*Nip47LookupInvoiceResponse).Invoice)
assert.Equal(t, false, received.Result.(*Nip47LookupInvoiceResponse).Paid)
assert.Equal(t, mockTransaction.Preimage, received.Result.(*Nip47LookupInvoiceResponse).Preimage)
// list_transactions: without permission
newPayload, err = nip04.Encrypt(nip47ListTransactionsJson, ss)
@ -672,12 +673,12 @@ func (mln *MockLn) GetInfo(ctx context.Context, senderPubkey string) (info *Node
return &mockNodeInfo, nil
}
func (mln *MockLn) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (invoice string, paymentHash string, err error) {
return mockInvoice, mockPaymentHash, nil
func (mln *MockLn) MakeInvoice(ctx context.Context, senderPubkey string, amount int64, description string, descriptionHash string, expiry int64) (transaction *Nip47Transaction, err error) {
return mockTransaction, nil
}
func (mln *MockLn) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (invoice string, paid bool, err error) {
return mockInvoice, false, nil
func (mln *MockLn) LookupInvoice(ctx context.Context, senderPubkey string, paymentHash string) (transaction *Nip47Transaction, err error) {
return mockTransaction, nil
}
func (mln *MockLn) ListTransactions(ctx context.Context, senderPubkey string, from, until, limit, offset uint64, unpaid bool, invoiceType string) (invoices []Nip47Transaction, err error) {