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

Compare commits

..

16 Commits

Author SHA1 Message Date
70028e4264
Merge pull request #60 from reneaaron/fix/isenabled
fix: implement webln spec
2024-05-31 20:54:03 +03:00
René Aaron
47efbfcc96 fix: implement webln spec 2024-05-21 15:30:47 +02:00
b9b2ce8d6b
Merge pull request #58 from crc32/patch-3
Update README.md
2023-06-05 09:20:46 +02:00
Arceris
f604ca21ce
Update README.md
Added location of LND TLS for Start9
2023-06-03 16:54:49 -06:00
530c7c0942
Merge pull request #50 from ziggie1984/add-private-route-hints
add private routing hints for invoices
2023-01-28 13:02:18 +02:00
ziggie
23a134b28e
add private routing hints for invoices 2023-01-23 10:08:26 +01:00
c6dc9308c3
Merge pull request #46 from bumi/dependabot/go_modules/github.com/labstack/echo/v4-4.9.0
Bump github.com/labstack/echo/v4 from 4.7.2 to 4.9.0
2022-11-06 23:24:54 +01:00
dependabot[bot]
897217995a
Bump github.com/labstack/echo/v4 from 4.7.2 to 4.9.0
Bumps [github.com/labstack/echo/v4](https://github.com/labstack/echo) from 4.7.2 to 4.9.0.
- [Release notes](https://github.com/labstack/echo/releases)
- [Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/labstack/echo/compare/v4.7.2...v4.9.0)

---
updated-dependencies:
- dependency-name: github.com/labstack/echo/v4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-30 16:05:20 +00:00
a21e8b96b7 Add fly.io deployment notes 2022-08-19 10:55:00 +02:00
a383a747ef
Merge pull request #40 from yanascz/lnurl-comment-allowed
Add configuration property for allowed length of LNURL-pay comments
2022-08-11 09:02:27 +02:00
yanas
9ceea9e6bf Add configuration property for allowed length of LNURL-pay comments 2022-08-11 08:45:19 +02:00
d42c12be9b
Merge pull request #43 from ok300/patch-1
Document `listen` startup config
2022-08-11 02:34:25 +02:00
ok300
53253eba76
Document listen startup config 2022-08-09 07:30:02 +02:00
a3675ec746
Update README.md 2022-07-13 20:22:12 +02:00
3c8a76a1c1
Update README.md 2022-05-17 14:14:26 +02:00
f6d089c194
lowercase sats 2022-05-13 11:06:13 +02:00
8 changed files with 109 additions and 20 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](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com**
**See it in action: [ln.michaelbumann.com](http://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,6 +19,7 @@ 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
@ -62,10 +63,12 @@ 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:
@ -121,6 +124,7 @@ 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.
@ -138,7 +142,41 @@ 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
### Deployment Notes
### 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
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)
@ -171,6 +209,8 @@ 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.
@ -181,6 +221,13 @@ 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,15 +120,11 @@ class LnMe {
}
payWithWebln() {
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);
}
}
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();

37
fly.toml Normal file
View File

@ -0,0 +1,37 @@
# 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.7.2
github.com/labstack/echo/v4 v4.9.0
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.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
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/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) (Invoice, error) {
func (c LNDclient) AddInvoice(value int64, memo string, descriptionHash []byte, private bool) (Invoice, error) {
result := Invoice{}
stdOutLogger.Printf("Adding invoice: memo=%s value=%v", memo, value)
@ -55,6 +55,7 @@ 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)
invoice, err := lnClient.AddInvoice(i.Value, i.Memo, nil, cfg.Bool("enable-private-channels"))
if err != nil {
stdOutLogger.Printf("Error creating invoice: %s", err)
return c.JSON(http.StatusInternalServerError, "Error adding invoice")
@ -163,6 +163,7 @@ 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{
@ -171,7 +172,7 @@ func main() {
MinSendable: 1000,
MaxSendable: 100000000,
EncodedMetadata: lnurlMetadata,
CommentAllowed: 0,
CommentAllowed: lnurlpCommentAllowed,
Tag: "payRequest",
}
return c.JSON(http.StatusOK, lnurlPayResponse1)
@ -183,8 +184,13 @@ 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, lightningAddress, metadataHash[:])
invoice, err := lnClient.AddInvoice(sats, comment, metadataHash[:], cfg.Bool("enable-private-channels"))
if err != nil {
stdOutLogger.Printf("Error creating invoice: %s", err)
return c.JSON(http.StatusOK, lnurl.LNURLErrorResponse{Status: "ERROR", Reason: "Server Error"})
@ -238,9 +244,11 @@ 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).")