1
1
mirror of https://github.com/bumi/lntip synced 2025-06-16 17:55:35 +00:00

Compare commits

..

No commits in common. "master" and "1.5.1" have entirely different histories.

8 changed files with 20 additions and 109 deletions

View File

@ -5,7 +5,7 @@ LnMe is a personal Bitcoin Lightning payment page/widget and self-hosted [Lightn
![demo](./lnme-demo.gif)
**See it in action: [ln.michaelbumann.com](http://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
**See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported).
@ -19,7 +19,6 @@ LnMe is one [simple executable](https://github.com/bumi/lnme/releases) file that
- [x] [JavaScript widget](#javascript-widget-integration) for existing websites
- [x] [Invoice API](https://github.com/bumi/lnme/wiki/API) - simple REST API to create LN invoices from existing JS code
- [x] [LNURL-pay](https://github.com/fiatjaf/lnurl-rfc/blob/luds/06.md) support
- [x] [LNURL-pay comment](https://github.com/fiatjaf/lnurl-rfc/blob/luds/12.md) support
## Installation
@ -63,12 +62,10 @@ Instead of the path to the macaroon and cert files you can also provide the hex
#### Other configuration
- `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
- `lnurlp-comment-allowed`: Allowed length of LNURL-pay comments, maximum around [~2000 characters](https://stackoverflow.com/a/417184). (default: 210)
- `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-ln-address`: Disable [Lightning Address](https://lightningaddress.com/) handling.
- `port`: Port to listen on. (default: 1323)
- `listen`: IP and port to listen to. Supersedes `port`. (default: :1323).
- `request-limit`: Limit the allowed requests per second. (default: 5)
Depending on your deployment needs LnMe can be configured using the following options:
@ -124,7 +121,6 @@ The TLS cert is located in the lnd directory:
- ~/umbrel/lnd/tls.cert on Umbrel
- /mnt/hdd/lnd/tls.cert on Raspiblitz
- /embassy-data/package-data/volumes/lnd/data/main/tls.cert on Start9 Embassy
- Can also be located in ~/.lnd
You should find the macaroon files in the LND data dir (e.g. ~.lnd/data/chain/bitcoin/mainnet/) or see "LND Permissions" how to create a new one.
@ -142,41 +138,7 @@ This buildpack can be disabled and removed if not needed or desired, through the
Lastly, using the Heroku deployment, you can link the app to your own domain by following the directions here: https://help.heroku.com/MTG1BIA7/how-do-i-connect-a-domain-to-my-heroku-app
### Fly.io
#### 0. Clone the repo
$ git clone https://github.com/bumi/lnme.git
$ cd lnme
#### 1. Create a new app
$ flyctl launch --generate-name // or set a custom app name: flyctl launch --name lnme-test-1
You will be asked a few things:
* Copy the configuration to the new app
* You do NOT need to create a Postgresql Database
* Do NOT deploy it directly, we first need to set some configs
#### 2. Set the configuration using environment variables:
The LND config variablse are required. Others are optional:
$ flyctl secrets set LNME_LND_ADDRESS="xxx.xxx.xxx.xxx:10009" LNME_LND_CERT=xxx LNME_LND_MACAROON=xxx
$ flyctl secrets set DISABLE_WEBSITE=1 // etc.
#### 3. Launch the app:
$ flyctl deploy
#### 4.Configure your domain
To configure a custom domain check the [fly.io guides](https://fly.io/docs/app-guides/custom-domains-with-fly/)
### Custom deployment notes
### Deployment 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)
@ -209,8 +171,6 @@ if you got the Lightning Address enabled you also get a LNURL-pay URL:
https://`{your domain}/lnurlp/{anything}`
If you need an bech32 encoded version you can use this online tool: [https://lnurl.fiatjaf.com/codec/](https://lnurl.fiatjaf.com/codec/)
### 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.
@ -221,13 +181,6 @@ Take a look at the [embedded default website](https://github.com/bumi/lnme/blob/
2. Create your index.html
3. Run lnme: `lnme --static-path=/home/satoshi/my-ln-page
### Usage with 21 Payment Widgets
[widgets.twentyuno.net](https://widgets.twentyuno.net/) is a beautiful embeddable payment widget for any existing website.
You can use your LnMe instance with the widget by using your [LnMe LNURL](https://github.com/bumi/lnme#lnurl) with the widget.
Use your bech32 encoded [LNURL](https://github.com/bumi/lnme#lnurl) as `Receiver` in the [widget configuration](https://widgets.twentyuno.net/get-started)
### JavaScript Widget integration
You can integrate the LnMe widget in your existing website.

View File

@ -120,11 +120,15 @@ class LnMe {
}
payWithWebln() {
webln.enable().then((weblnResponse) => {
if (!webln.isEnabled) {
webln.enable().then((weblnResponse) => {
return webln.sendPayment(this.invoice.payment_request);
}).catch((e) => {
return this.showPaymentRequest();
})
} else {
return webln.sendPayment(this.invoice.payment_request);
}).catch((e) => {
return this.showPaymentRequest();
})
}
}
populatePaymentRequest() {

View File

@ -8,7 +8,7 @@
<meta property="og:image" content="/lnme/icon.svg">
<title>Send me some sats</title>
<title>Send me some Sats</title>
<style>
html {
width: 100%;
@ -118,7 +118,7 @@
<div class="form" id="form">
<p>
Send me<br>
<input type="number" placeholder="10000" class="amount" id="amount" autofocus="true" autocomplete="off" min="100"> sats
<input type="number" placeholder="10000" class="amount" id="amount" autofocus="true" autocomplete="off" min="100"> Sats
<br>
for
<br>
@ -159,7 +159,7 @@
}
var siteDescription = document.querySelector('head > meta[property="og:description"]');
if (siteDescription && !siteDescription.content) {
siteDescription.content = "sats for " + window.location.host;
siteDescription.content = "Sats for " + window.location.host;
}
document.getElementById("get-new-address").addEventListener('click', function(e) {
e.preventDefault();

View File

@ -1,37 +0,0 @@
# fly.toml file generated
kill_signal = "SIGINT"
kill_timeout = 5
processes = []
[env]
[experimental]
allowed_public_ports = []
auto_rollback = true
[[services]]
http_checks = []
internal_port = 1323
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"

2
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/cretz/bine v0.2.0
github.com/didip/tollbooth/v6 v6.1.2
github.com/knadh/koanf v1.4.1
github.com/labstack/echo/v4 v4.9.0
github.com/labstack/echo/v4 v4.7.2
github.com/lightningnetwork/lnd v0.14.1-beta
google.golang.org/grpc v1.45.0
gopkg.in/macaroon.v2 v2.1.0

4
go.sum
View File

@ -463,8 +463,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=

View File

@ -47,7 +47,7 @@ type LNDclient struct {
}
// AddInvoice generates an invoice with the given price and memo.
func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte, private bool) (Invoice, error) {
func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte) (Invoice, error) {
result := Invoice{}
stdOutLogger.Printf("Adding invoice: memo=%s value=%v", memo, value)
@ -55,7 +55,6 @@ func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte,
Memo: memo,
DescriptionHash: descriptionHash,
Value: value,
Private: private,
}
res, err := c.lndClient.AddInvoice(c.ctx, &invoice)
if err != nil {

14
lnme.go
View File

@ -117,7 +117,7 @@ func main() {
return c.JSON(http.StatusBadRequest, "Bad request")
}
invoice, err := lnClient.AddInvoice(i.Value, i.Memo, nil, cfg.Bool("enable-private-channels"))
invoice, err := lnClient.AddInvoice(i.Value, i.Memo, nil)
if err != nil {
stdOutLogger.Printf("Error creating invoice: %s", err)
return c.JSON(http.StatusInternalServerError, "Error adding invoice")
@ -163,7 +163,6 @@ func main() {
name := c.Param("name")
lightningAddress := name + "@" + host
lnurlMetadata := "[[\"text/identifier\", \"" + lightningAddress + "\"], [\"text/plain\", \"Sats for " + lightningAddress + "\"]]"
lnurlpCommentAllowed := cfg.Int64("lnurlp-comment-allowed")
if amount := c.QueryParam("amount"); amount == "" {
lnurlPayResponse1 := lnurl.LNURLPayResponse1{
@ -172,7 +171,7 @@ func main() {
MinSendable: 1000,
MaxSendable: 100000000,
EncodedMetadata: lnurlMetadata,
CommentAllowed: lnurlpCommentAllowed,
CommentAllowed: 0,
Tag: "payRequest",
}
return c.JSON(http.StatusOK, lnurlPayResponse1)
@ -184,13 +183,8 @@ func main() {
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid Amount"})
}
sats := msats / 1000 // we need sats
comment := c.QueryParam("comment")
if commentLength := int64(len(comment)); commentLength > lnurlpCommentAllowed {
stdOutLogger.Printf("Invalid comment length: %d", commentLength)
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Invalid comment length"})
}
metadataHash := sha256.Sum256([]byte(lnurlMetadata))
invoice, err := lnClient.AddInvoice(sats, comment, metadataHash[:], cfg.Bool("enable-private-channels"))
invoice, err := lnClient.AddInvoice(sats, lightningAddress, metadataHash[:])
if err != nil {
stdOutLogger.Printf("Error creating invoice: %s", err)
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Server Error"})
@ -244,11 +238,9 @@ func LoadConfig() *koanf.Koanf {
f.String("lnd-macaroon", "", "HEX string of LND macaroon file.")
f.String("lnd-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.")
f.String("lnd-cert", "", "HEX string of LND tls cert file.")
f.Int64("lnurlp-comment-allowed", 210, "Allowed length of LNURL-pay comments.")
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("enable-private-channels", false, "Adds private routing hints to invoices")
f.Float64("request-limit", 5, "Request limit per second.")
f.String("static-path", "", "Path to a static assets directory.")
f.String("port", "", "Port to bind on (deprecated - use listen).")