mirror of
https://github.com/bumi/lntip
synced 2025-06-21 19:53:47 +00:00
Merge branch 'master' into tor-support
This commit is contained in:
commit
7efef4a2b6
46
README.md
46
README.md
@ -1,26 +1,20 @@
|
|||||||
# LnMe - your friendly ⚡ payment page
|
# LnMe - your friendly ⚡ payment page
|
||||||
|
|
||||||
LnMe is a personal Bitcoin Lightning payment website and payment widget.
|
LnMe is a personal Bitcoin Lightning payment page/widget and self-hosted [Lightning Address](https://lightningaddress.com/) server.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It is a small service written in Go that connects to a [lnd node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md) and exposes a simple HTTP JSON API to create and monitor invoices.
|
**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
|
||||||
It comes with a configurable personal payment website and offers a JavaScript widget to integrate in existing websites.
|
|
||||||
|
|
||||||
If [webln](https://github.com/wbobeirne/webln) is available the widget automatically use webln to request the payment;
|
LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported).
|
||||||
otherwise an overlay will be shown with the payment request and a QR code.
|
|
||||||
|
|
||||||
## Motivation
|
LnMe is one [simple executable](https://github.com/bumi/lnme/releases) file that can be deployed anywhere with no dependencies. (on your own node or for example with [one click on Heroku](#heroku))
|
||||||
|
|
||||||
I wanted a simple way for people to send Lightning payments using my own lightning node.
|
|
||||||
|
|
||||||
BTCPay Server is too big and hard to run for that and I do not need most of its features.
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
LnMe connects to your [LND node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md), so a running LND node is required.
|
LnMe connects to your [LND node](https://github.com/lightningnetwork/lnd/blob/master/docs/INSTALL.md), so a running LND node is required.
|
||||||
LnMe can easily run next to LND on the same system.
|
LnMe can easily run next to LND on the same system or any other hosting provider.
|
||||||
|
|
||||||
1. Download the latest [release](https://github.com/bumi/lnme/releases)
|
1. Download the latest [release](https://github.com/bumi/lnme/releases)
|
||||||
2. Run `lnme`
|
2. Run `lnme`
|
||||||
@ -55,6 +49,7 @@ Instead of the path to the macaroon and cert files you can also provide the hex
|
|||||||
* `static-path`: Path to a folder that you want to serve with LnMe (e.g. /home/bitcoin/lnme/website). Use this if you want to customize your ⚡website. default: disabled
|
* `static-path`: Path to a folder that you want to serve with LnMe (e.g. /home/bitcoin/lnme/website). Use this if you want to customize your ⚡website. default: disabled
|
||||||
* `disable-website`: Disable the default LnMe website. Disable the website if you only want to embed the LnMe widget on your existing website.
|
* `disable-website`: Disable the default LnMe website. Disable the website if you only want to embed the LnMe widget on your existing website.
|
||||||
* `disable-cors`: Disable CORS headers. (default: false)
|
* `disable-cors`: Disable CORS headers. (default: false)
|
||||||
|
* `disable-ln-address`: Disable [Lightning Address](https://lightningaddress.com/) handling.
|
||||||
* `port`: Port to listen on. (default: 1323)
|
* `port`: Port to listen on. (default: 1323)
|
||||||
* `request-limit`: Limit the allowed requests per second. (default: 5)
|
* `request-limit`: Limit the allowed requests per second. (default: 5)
|
||||||
|
|
||||||
@ -84,6 +79,16 @@ All environment variables must be prefixed by `LNME_` use `_` instead of `-`
|
|||||||
|
|
||||||
$ LNME_LND_ADDRESS=127.0.0.1:10005 lnme
|
$ LNME_LND_ADDRESS=127.0.0.1:10005 lnme
|
||||||
|
|
||||||
|
### LND Permissions
|
||||||
|
|
||||||
|
LnMe needs the following LND permissions:
|
||||||
|
|
||||||
|
* Read/Write permission for invoices
|
||||||
|
* Write permission for onchain address (if you want to use the onchain option)
|
||||||
|
|
||||||
|
Use the LND [macaroon bakery](http://macaroon-bakery.freedomnode.com/) to create a new macaroon for LnMe.
|
||||||
|
|
||||||
|
To get the HEX versions of the files use `xxd -plain` e.g. `xxd -plain invoice.macaroon | tr -d '\n'`
|
||||||
|
|
||||||
### TOR
|
### TOR
|
||||||
|
|
||||||
@ -97,8 +102,12 @@ It is the easiest to run LnMe on the same node as LND. But you can run it anywhe
|
|||||||
#### Heroku
|
#### Heroku
|
||||||
One click deployment with Heroku:
|
One click deployment with Heroku:
|
||||||
|
|
||||||
|
You will need your LND address, the LND tls certificate (HEX) and the macaroon (HEX).
|
||||||
|
|
||||||
[](https://heroku.com/deploy?template=https://github.com/bumi/lnme)
|
[](https://heroku.com/deploy?template=https://github.com/bumi/lnme)
|
||||||
|
|
||||||
|
Here is a [Video Demo of the Heroku deployment](https://www.youtube.com/watch?v=hSFXhnLp_Rc)
|
||||||
|
|
||||||
#### Notes
|
#### Notes
|
||||||
|
|
||||||
To run LnMe as systemd service have a look at the [systemd service example config](https://github.com/bumi/lnme/blob/master/examples/lnme.service)
|
To run LnMe as systemd service have a look at the [systemd service example config](https://github.com/bumi/lnme/blob/master/examples/lnme.service)
|
||||||
@ -114,6 +123,16 @@ lnme.michaelbumann.com {
|
|||||||
`$ caddy --config /etc/caddy/Caddyfile`
|
`$ caddy --config /etc/caddy/Caddyfile`
|
||||||
|
|
||||||
|
|
||||||
|
### Lightning Address
|
||||||
|
|
||||||
|
The Lightning Address is an Internet Identifier that allows anyone to send you Bitcoin over the Lightning Network.
|
||||||
|
Lightning Address builds on [LNURL-pay](https://github.com/fiatjaf/lnurl-rfc/blob/luds/06.md) LnMe handles the necessary requests for you.
|
||||||
|
|
||||||
|
For more information check out the website: [lightningaddress.com](https://lightningaddress.com/)
|
||||||
|
|
||||||
|
Your Lightning Address: `{anything}@{your domain}`
|
||||||
|
|
||||||
|
|
||||||
### Customize your ⚡ website
|
### Customize your ⚡ website
|
||||||
|
|
||||||
LnMe comes with a default website but you can easily configure and build your own using the the LnMe JavaScript widget or JSON API.
|
LnMe comes with a default website but you can easily configure and build your own using the the LnMe JavaScript widget or JSON API.
|
||||||
@ -173,6 +192,11 @@ lnme.watchPayment().then(invoice => {
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
I wanted a simple way for people to send Lightning payments using my own lightning node.
|
||||||
|
BTCPay Server is too big and hard to run for that and I do not need most of its features.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
Use `go run` to ron the service locally:
|
Use `go run` to ron the service locally:
|
||||||
|
45
lnme.go
45
lnme.go
@ -1,14 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
rice "github.com/GeertJohan/go.rice"
|
rice "github.com/GeertJohan/go.rice"
|
||||||
"github.com/bumi/lnme/ln"
|
"github.com/bumi/lnme/ln"
|
||||||
|
"github.com/bumi/lnme/lnurl"
|
||||||
"github.com/didip/tollbooth/v6"
|
"github.com/didip/tollbooth/v6"
|
||||||
"github.com/didip/tollbooth/v6/limiter"
|
"github.com/didip/tollbooth/v6/limiter"
|
||||||
"github.com/knadh/koanf"
|
"github.com/knadh/koanf"
|
||||||
@ -137,6 +142,45 @@ func main() {
|
|||||||
return c.JSON(http.StatusOK, invoice)
|
return c.JSON(http.StatusOK, invoice)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !cfg.Bool("disable-ln-address") {
|
||||||
|
e.GET("/.well-known/lnurlp/:name", func(c echo.Context) error {
|
||||||
|
name := c.Param("name")
|
||||||
|
lightningAddress := name + "@" + c.Request().Host
|
||||||
|
lnurlMetadata := "[[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]]"
|
||||||
|
|
||||||
|
if amount := c.QueryParam("amount"); amount == "" {
|
||||||
|
lnurlPayResponse1 := lnurl.LNURLPayResponse1{
|
||||||
|
LNURLResponse: lnurl.LNURLResponse{Status: "OK"},
|
||||||
|
Callback: fmt.Sprintf("%s://%s%s", c.Scheme(), c.Request().Host, c.Request().URL.Path),
|
||||||
|
MinSendable: 1000,
|
||||||
|
MaxSendable: 100000000,
|
||||||
|
EncodedMetadata: lnurlMetadata,
|
||||||
|
CommentAllowed: 0,
|
||||||
|
Tag: "payRequest",
|
||||||
|
}
|
||||||
|
return c.JSON(http.StatusOK, lnurlPayResponse1)
|
||||||
|
} else {
|
||||||
|
stdOutLogger.Printf("New LightningAddress request amount: %s", amount)
|
||||||
|
msats, err := strconv.ParseInt(amount, 10, 64)
|
||||||
|
if err != nil || msats < 1000 {
|
||||||
|
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid Amount"})
|
||||||
|
}
|
||||||
|
sats := msats / 1000 // we need sats
|
||||||
|
metadataHash := sha256.Sum256([]byte(lnurlMetadata))
|
||||||
|
memo := hex.EncodeToString(metadataHash[:])
|
||||||
|
invoice, err := lnClient.AddInvoice(sats, memo)
|
||||||
|
lnurlPayResponse2 := lnurl.LNURLPayResponse2{
|
||||||
|
LNURLResponse: lnurl.LNURLResponse{Status: "OK"},
|
||||||
|
PR: invoice.PaymentRequest,
|
||||||
|
Routes: make([][]lnurl.RouteInfo, 0),
|
||||||
|
Disposable: false,
|
||||||
|
SuccessAction: &lnurl.SuccessAction{Tag: "message", Message: "Thanks, payment received!"},
|
||||||
|
}
|
||||||
|
return c.JSON(http.StatusOK, lnurlPayResponse2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Debug test endpoint
|
// Debug test endpoint
|
||||||
e.GET("/ping", func(c echo.Context) error {
|
e.GET("/ping", func(c echo.Context) error {
|
||||||
return c.JSON(http.StatusOK, "pong")
|
return c.JSON(http.StatusOK, "pong")
|
||||||
@ -157,6 +201,7 @@ func LoadConfig() *koanf.Koanf {
|
|||||||
f.String("lnd-macaroon-path", "~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon", "Path to the LND macaroon file.")
|
f.String("lnd-macaroon-path", "~/.lnd/data/chain/bitcoin/mainnet/invoice.macaroon", "Path to the LND macaroon file.")
|
||||||
f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.")
|
f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.")
|
||||||
f.Bool("disable-website", false, "Disable default embedded website.")
|
f.Bool("disable-website", false, "Disable default embedded website.")
|
||||||
|
f.Bool("disable-ln-address", false, "Disable Lightning Address handling")
|
||||||
f.Bool("disable-cors", false, "Disable CORS headers.")
|
f.Bool("disable-cors", false, "Disable CORS headers.")
|
||||||
f.Float64("request-limit", 5, "Request limit per second.")
|
f.Float64("request-limit", 5, "Request limit per second.")
|
||||||
f.String("static-path", "", "Path to a static assets directory.")
|
f.String("static-path", "", "Path to a static assets directory.")
|
||||||
|
52
lnurl/types.go
Normal file
52
lnurl/types.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// THANKS: https://github.com/fiatjaf/go-lnurl/blob/d50a8e916232580895822178fe36e0f5cf400554/base.go
|
||||||
|
// only using the LNURL types here
|
||||||
|
package lnurl
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
type LNURLResponse struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LNURLPayResponse1 struct {
|
||||||
|
LNURLResponse
|
||||||
|
Callback string `json:"callback"`
|
||||||
|
CallbackURL *url.URL `json:"-"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
MaxSendable int64 `json:"maxSendable"`
|
||||||
|
MinSendable int64 `json:"minSendable"`
|
||||||
|
EncodedMetadata string `json:"metadata"`
|
||||||
|
Metadata Metadata `json:"-"`
|
||||||
|
CommentAllowed int64 `json:"commentAllowed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LNURLPayResponse2 struct {
|
||||||
|
LNURLResponse
|
||||||
|
SuccessAction *SuccessAction `json:"successAction"`
|
||||||
|
Routes [][]RouteInfo `json:"routes"`
|
||||||
|
PR string `json:"pr"`
|
||||||
|
Disposable bool `json:"disposable,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RouteInfo struct {
|
||||||
|
NodeId string `json:"nodeId"`
|
||||||
|
ChannelUpdate string `json:"channelUpdate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuccessAction struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Ciphertext string `json:"ciphertext,omitempty"`
|
||||||
|
IV string `json:"iv,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LNURLErrorResponse struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
URL *url.URL `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metadata [][]string
|
Loading…
x
Reference in New Issue
Block a user