mirror of
				https://github.com/bumi/lntip
				synced 2025-10-31 04:32:31 +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 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. | ||||
| It comes with a configurable personal payment website and offers a JavaScript widget to integrate in existing websites. | ||||
| **See it in action: [ln.michaelbumann.com](https://ln.michaelbumann.com/) - my lightning address: bumi@ln.michaelbumann.com** | ||||
| 
 | ||||
| If [webln](https://github.com/wbobeirne/webln) is available the widget automatically use webln to request the payment; | ||||
| otherwise an overlay will be shown with the payment request and a QR code. | ||||
| LnMe focusses on simplicity and ease of deployment. It connects to an existing lightning node (currently LND is supported). | ||||
| 
 | ||||
| ## 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. | ||||
| 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)) | ||||
| 
 | ||||
| 
 | ||||
| ## 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 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) | ||||
| 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 | ||||
| * `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) | ||||
| * `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 | ||||
| 
 | ||||
| ### 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 | ||||
| 
 | ||||
| @ -97,8 +102,12 @@ It is the easiest to run LnMe on the same node as LND. But you can run it anywhe | ||||
| #### 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) | ||||
| 
 | ||||
| Here is a [Video Demo of the Heroku deployment](https://www.youtube.com/watch?v=hSFXhnLp_Rc) | ||||
| 
 | ||||
| #### 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) | ||||
| @ -114,6 +123,16 @@ lnme.michaelbumann.com { | ||||
| `$ 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 | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| Use `go run` to ron the service locally: | ||||
|  | ||||
							
								
								
									
										45
									
								
								lnme.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								lnme.go
									
									
									
									
									
								
							| @ -1,14 +1,19 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/hex" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	rice "github.com/GeertJohan/go.rice" | ||||
| 	"github.com/bumi/lnme/ln" | ||||
| 	"github.com/bumi/lnme/lnurl" | ||||
| 	"github.com/didip/tollbooth/v6" | ||||
| 	"github.com/didip/tollbooth/v6/limiter" | ||||
| 	"github.com/knadh/koanf" | ||||
| @ -137,6 +142,45 @@ func main() { | ||||
| 		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 | ||||
| 	e.GET("/ping", func(c echo.Context) error { | ||||
| 		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-cert-path", "~/.lnd/tls.cert", "Path to the LND tls.cert file.") | ||||
| 	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.Float64("request-limit", 5, "Request limit per second.") | ||||
| 	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