This commit is contained in:
Peter Rounce 2023-02-19 06:47:28 +00:00
commit f3949b1b59
23 changed files with 586 additions and 969 deletions

View file

@ -7,6 +7,8 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/resp_err"
) )
func random_hex() string { func random_hex() string {
@ -21,10 +23,10 @@ func random_hex() string {
} }
func createboltcard(w http.ResponseWriter, r *http.Request) { func createboltcard(w http.ResponseWriter, r *http.Request) {
if db_get_setting("FUNCTION_INTERNAL_API") != "ENABLE" { if db.Get_setting("FUNCTION_INTERNAL_API") != "ENABLE" {
msg := "createboltcard: internal API function is not enabled" msg := "createboltcard: internal API function is not enabled"
log.Debug(msg) log.Debug(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -33,7 +35,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
msg := "createboltcard: tx_max is not a valid integer" msg := "createboltcard: tx_max is not a valid integer"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -42,7 +44,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
msg := "createboltcard: day_max is not a valid integer" msg := "createboltcard: day_max is not a valid integer"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -51,7 +53,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
msg := "createboltcard: enable is not a valid boolean" msg := "createboltcard: enable is not a valid boolean"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -62,7 +64,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
msg := "createboltcard: uid_privacy is not a valid boolean" msg := "createboltcard: uid_privacy is not a valid boolean"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -71,13 +73,13 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
msg := "createboltcard: allow_neg_bal is not a valid boolean" msg := "createboltcard: allow_neg_bal is not a valid boolean"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
// check if card_name already exists // check if card_name already exists
card_count, err := db_get_card_name_count(card_name) card_count, err := db.Get_card_name_count(card_name)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
@ -86,7 +88,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
if card_count > 0 { if card_count > 0 {
msg := "createboltcard: the card name already exists in the database" msg := "createboltcard: the card name already exists in the database"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -107,7 +109,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
// create the new card record // create the new card record
err = db_insert_card(one_time_code, k0_auth_key, k2_cmac_key, k3, k4, err = db.Insert_card(one_time_code, k0_auth_key, k2_cmac_key, k3, k4,
tx_max, day_max, enable_flag, card_name, tx_max, day_max, enable_flag, card_name,
uid_privacy_flag, allow_neg_bal_flag) uid_privacy_flag, allow_neg_bal_flag)
if err != nil { if err != nil {
@ -117,7 +119,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
// return the URI + one_time_code // return the URI + one_time_code
hostdomain := db_get_setting("HOST_DOMAIN") hostdomain := db.Get_setting("HOST_DOMAIN")
url := "" url := ""
if strings.HasSuffix(hostdomain, ".onion") { if strings.HasSuffix(hostdomain, ".onion") {
url = "http://" + hostdomain + "/new?a=" + one_time_code url = "http://" + hostdomain + "/new?a=" + one_time_code

View file

@ -1,4 +1,4 @@
package main package crypto
import ( import (
"bytes" "bytes"
@ -9,7 +9,7 @@ import (
"github.com/aead/cmac" "github.com/aead/cmac"
) )
func create_k1() (string, error) { func Create_k1() (string, error) {
// 16 bytes = 128 bits // 16 bytes = 128 bits
b := make([]byte, 16) b := make([]byte, 16)
@ -24,7 +24,7 @@ func create_k1() (string, error) {
} }
// decrypt p with aes_dec // decrypt p with aes_dec
func crypto_aes_decrypt(key_sdm_file_read []byte, ba_p []byte) ([]byte, error) { func Aes_decrypt(key_sdm_file_read []byte, ba_p []byte) ([]byte, error) {
dec_p := make([]byte, 16) dec_p := make([]byte, 16)
iv := make([]byte, 16) iv := make([]byte, 16)
@ -38,7 +38,7 @@ func crypto_aes_decrypt(key_sdm_file_read []byte, ba_p []byte) ([]byte, error) {
return dec_p, nil return dec_p, nil
} }
func crypto_aes_cmac(key_sdm_file_read_mac []byte, sv2 []byte, ba_c []byte) (bool, error) { func Aes_cmac(key_sdm_file_read_mac []byte, sv2 []byte, ba_c []byte) (bool, error) {
c2, err := aes.NewCipher(key_sdm_file_read_mac) c2, err := aes.NewCipher(key_sdm_file_read_mac)
if err != nil { if err != nil {

View file

@ -1,4 +1,4 @@
package main package db
import ( import (
"database/sql" "database/sql"
@ -8,45 +8,55 @@ import (
"os" "os"
) )
type card struct { type Card struct {
card_id int Card_id int
card_guid string Card_guid string
k0_auth_key string K0_auth_key string
k1_decrypt_key string K1_decrypt_key string
k2_cmac_key string K2_cmac_key string
k3 string K3 string
k4 string K4 string
db_uid string Db_uid string
last_counter_value uint32 Last_counter_value uint32
lnurlw_request_timeout_sec int Lnurlw_request_timeout_sec int
lnurlw_enable string Lnurlw_enable string
tx_limit_sats int Tx_limit_sats int
day_limit_sats int Day_limit_sats int
lnurlp_enable string Lnurlp_enable string
email_address string Email_address string
email_enable string Email_enable string
uid_privacy string Uid_privacy string
one_time_code string One_time_code string
card_name string Card_name string
allow_negative_balance string Allow_negative_balance string
} }
type payment struct { type Payment struct {
card_payment_id int Card_payment_id int
card_id int Card_id int
lnurlw_k1 string Lnurlw_k1 string
paid_flag string Paid_flag string
} }
type transaction struct { type Transaction struct {
card_id int Card_id int
tx_id int Tx_id int
tx_type string Tx_type string
tx_amount_msats int Tx_amount_msats int
tx_time string Tx_time string
} }
func db_open() (*sql.DB, error) { type Card_wipe_info struct {
Id int
K0 string
K1 string
K2 string
K3 string
K4 string
Uid string
}
func open() (*sql.DB, error) {
// get connection string from environment variables // get connection string from environment variables
@ -65,11 +75,11 @@ func db_open() (*sql.DB, error) {
return db, nil return db, nil
} }
func db_get_setting(setting_name string) string { func Get_setting(setting_name string) string {
setting_value := "" setting_value := ""
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return "" return ""
} }
@ -86,11 +96,11 @@ func db_get_setting(setting_name string) string {
return setting_value return setting_value
} }
func db_get_new_card(one_time_code string) (*card, error) { func Get_new_card(one_time_code string) (*Card, error) {
c := card{} c := Card{}
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -101,12 +111,12 @@ func db_get_new_card(one_time_code string) (*card, error) {
` one_time_code_expiry > NOW() AND one_time_code_used = 'N' AND wiped = 'N';` ` one_time_code_expiry > NOW() AND one_time_code_used = 'N' AND wiped = 'N';`
row := db.QueryRow(sqlStatement, one_time_code) row := db.QueryRow(sqlStatement, one_time_code)
err = row.Scan( err = row.Scan(
&c.k0_auth_key, &c.K0_auth_key,
&c.k2_cmac_key, &c.K2_cmac_key,
&c.k3, &c.K3,
&c.k4, &c.K4,
&c.card_name, &c.Card_name,
&c.uid_privacy) &c.Uid_privacy)
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -120,11 +130,11 @@ func db_get_new_card(one_time_code string) (*card, error) {
return &c, nil return &c, nil
} }
func db_get_card_count_for_uid(uid string) (int, error) { func Get_card_count_for_uid(uid string) (int, error) {
card_count := 0 card_count := 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -141,11 +151,11 @@ func db_get_card_count_for_uid(uid string) (int, error) {
return card_count, nil return card_count, nil
} }
func db_get_card_count_for_name_lnurlp(name string) (int, error) { func Get_card_count_for_name_lnurlp(name string) (int, error) {
card_count := 0 card_count := 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -162,11 +172,11 @@ func db_get_card_count_for_name_lnurlp(name string) (int, error) {
return card_count, nil return card_count, nil
} }
func db_get_card_id_for_name(name string) (int, error) { func Get_card_id_for_name(name string) (int, error) {
card_id := 0 card_id := 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -183,10 +193,10 @@ func db_get_card_id_for_name(name string) (int, error) {
return card_id, nil return card_id, nil
} }
func db_get_card_id_for_card_payment_id(card_payment_id int) (int, error) { func Get_card_id_for_card_payment_id(card_payment_id int) (int, error) {
card_id := 0 card_id := 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -203,10 +213,10 @@ func db_get_card_id_for_card_payment_id(card_payment_id int) (int, error) {
return card_id, nil return card_id, nil
} }
func db_get_card_id_for_r_hash(r_hash string) (int, error) { func Get_card_id_for_r_hash(r_hash string) (int, error) {
card_id := 0 card_id := 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -223,11 +233,11 @@ func db_get_card_id_for_r_hash(r_hash string) (int, error) {
return card_id, nil return card_id, nil
} }
func db_get_cards_blank_uid() ([]card, error) { func Get_cards_blank_uid() ([]Card, error) {
// open the database // open the database
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return nil, err return nil, err
@ -249,12 +259,12 @@ func db_get_cards_blank_uid() ([]card, error) {
// prepare the results // prepare the results
var cards []card var cards []Card
// Loop through rows, using Scan to assign column data to struct fields. // Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() { for rows.Next() {
var c card var c Card
err := rows.Scan(&c.card_id, &c.k2_cmac_key) err := rows.Scan(&c.Card_id, &c.K2_cmac_key)
if err != nil { if err != nil {
return cards, err return cards, err
@ -271,8 +281,8 @@ func db_get_cards_blank_uid() ([]card, error) {
return cards, nil return cards, nil
} }
func db_update_card_uid_ctr(card_id int, uid string, ctr uint32) error { func Update_card_uid_ctr(card_id int, uid string, ctr uint32) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -294,11 +304,11 @@ func db_update_card_uid_ctr(card_id int, uid string, ctr uint32) error {
return nil return nil
} }
func db_get_card_from_uid(card_uid string) (*card, error) { func Get_card_from_uid(card_uid string) (*Card, error) {
c := card{} c := Card{}
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -310,14 +320,14 @@ func db_get_card_from_uid(card_uid string) (*card, error) {
` FROM cards WHERE uid=$1 AND wiped='N';` ` FROM cards WHERE uid=$1 AND wiped='N';`
row := db.QueryRow(sqlStatement, card_uid) row := db.QueryRow(sqlStatement, card_uid)
err = row.Scan( err = row.Scan(
&c.card_id, &c.Card_id,
&c.k2_cmac_key, &c.K2_cmac_key,
&c.db_uid, &c.Db_uid,
&c.last_counter_value, &c.Last_counter_value,
&c.lnurlw_request_timeout_sec, &c.Lnurlw_request_timeout_sec,
&c.lnurlw_enable, &c.Lnurlw_enable,
&c.tx_limit_sats, &c.Tx_limit_sats,
&c.day_limit_sats) &c.Day_limit_sats)
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -325,11 +335,11 @@ func db_get_card_from_uid(card_uid string) (*card, error) {
return &c, nil return &c, nil
} }
func db_get_card_from_card_id(card_id int) (*card, error) { func Get_card_from_card_id(card_id int) (*Card, error) {
c := card{} c := Card{}
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -342,18 +352,18 @@ func db_get_card_from_card_id(card_id int) (*card, error) {
`allow_negative_balance FROM cards WHERE card_id=$1;` `allow_negative_balance FROM cards WHERE card_id=$1;`
row := db.QueryRow(sqlStatement, card_id) row := db.QueryRow(sqlStatement, card_id)
err = row.Scan( err = row.Scan(
&c.card_id, &c.Card_id,
&c.k2_cmac_key, &c.K2_cmac_key,
&c.db_uid, &c.Db_uid,
&c.last_counter_value, &c.Last_counter_value,
&c.lnurlw_request_timeout_sec, &c.Lnurlw_request_timeout_sec,
&c.lnurlw_enable, &c.Lnurlw_enable,
&c.tx_limit_sats, &c.Tx_limit_sats,
&c.day_limit_sats, &c.Day_limit_sats,
&c.email_enable, &c.Email_enable,
&c.email_address, &c.Email_address,
&c.card_name, &c.Card_name,
&c.allow_negative_balance) &c.Allow_negative_balance)
if err != nil { if err != nil {
return &c, err return &c, err
} }
@ -361,9 +371,9 @@ func db_get_card_from_card_id(card_id int) (*card, error) {
return &c, nil return &c, nil
} }
func db_check_lnurlw_timeout(card_payment_id int) (bool, error) { func Check_lnurlw_timeout(card_payment_id int) (bool, error) {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return true, err return true, err
} }
@ -383,9 +393,9 @@ func db_check_lnurlw_timeout(card_payment_id int) (bool, error) {
return lnurlw_timeout, nil return lnurlw_timeout, nil
} }
func db_check_and_update_counter(card_id int, new_counter_value uint32) (bool, error) { func Check_and_update_counter(card_id int, new_counter_value uint32) (bool, error) {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return false, err return false, err
} }
@ -408,9 +418,9 @@ func db_check_and_update_counter(card_id int, new_counter_value uint32) (bool, e
return true, nil return true, nil
} }
func db_insert_payment(card_id int, lnurlw_k1 string) error { func Insert_payment(card_id int, lnurlw_k1 string) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -436,13 +446,13 @@ func db_insert_payment(card_id int, lnurlw_k1 string) error {
return nil return nil
} }
func db_insert_receipt( func Insert_receipt(
card_id int, card_id int,
ln_invoice string, ln_invoice string,
r_hash_hex string, r_hash_hex string,
amount_msat int64) error { amount_msat int64) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -468,8 +478,8 @@ func db_insert_receipt(
return nil return nil
} }
func db_update_receipt_state(r_hash_hex string, invoice_state string) error { func Update_receipt_state(r_hash_hex string, invoice_state string) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -493,10 +503,10 @@ func db_update_receipt_state(r_hash_hex string, invoice_state string) error {
return nil return nil
} }
func db_get_payment_k1(lnurlw_k1 string) (*payment, error) { func Get_payment_k1(lnurlw_k1 string) (*Payment, error) {
p := payment{} p := Payment{}
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return &p, err return &p, err
} }
@ -506,9 +516,9 @@ func db_get_payment_k1(lnurlw_k1 string) (*payment, error) {
` FROM card_payments WHERE lnurlw_k1=$1;` ` FROM card_payments WHERE lnurlw_k1=$1;`
row := db.QueryRow(sqlStatement, lnurlw_k1) row := db.QueryRow(sqlStatement, lnurlw_k1)
err = row.Scan( err = row.Scan(
&p.card_payment_id, &p.Card_payment_id,
&p.card_id, &p.Card_id,
&p.paid_flag) &p.Paid_flag)
if err != nil { if err != nil {
return &p, err return &p, err
} }
@ -516,9 +526,9 @@ func db_get_payment_k1(lnurlw_k1 string) (*payment, error) {
return &p, nil return &p, nil
} }
func db_update_payment_invoice(card_payment_id int, ln_invoice string, amount_msats int64) error { func Update_payment_invoice(card_payment_id int, ln_invoice string, amount_msats int64) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -540,9 +550,9 @@ func db_update_payment_invoice(card_payment_id int, ln_invoice string, amount_ms
return nil return nil
} }
func db_update_payment_paid(card_payment_id int) error { func Update_payment_paid(card_payment_id int) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -564,9 +574,9 @@ func db_update_payment_paid(card_payment_id int) error {
return nil return nil
} }
func db_update_payment_status(card_payment_id int, payment_status string, failure_reason string) error { func Update_payment_status(card_payment_id int, payment_status string, failure_reason string) error {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
@ -596,9 +606,9 @@ func db_update_payment_status(card_payment_id int, payment_status string, failur
return nil return nil
} }
func db_get_card_totals(card_id int) (int, error) { func Get_card_totals(card_id int) (int, error) {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -620,10 +630,10 @@ func db_get_card_totals(card_id int) (int, error) {
return day_total_sats, nil return day_total_sats, nil
} }
func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) { func Get_card_txs(card_id int, max_txs int) ([]Transaction, error) {
// open the database // open the database
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return nil, err return nil, err
@ -655,12 +665,12 @@ func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) {
// prepare the results // prepare the results
var transactions []transaction var transactions []Transaction
// Loop through rows, using Scan to assign column data to struct fields. // Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() { for rows.Next() {
var t transaction var t Transaction
err := rows.Scan(&t.card_id, &t.tx_id, &t.tx_type, &t.tx_amount_msats, &t.tx_time) err := rows.Scan(&t.Card_id, &t.Tx_id, &t.Tx_type, &t.Tx_amount_msats, &t.Tx_time)
if err != nil { if err != nil {
return transactions, err return transactions, err
@ -677,9 +687,9 @@ func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) {
return transactions, nil return transactions, nil
} }
func db_get_card_total_sats(card_id int) (int, error) { func Get_card_total_sats(card_id int) (int, error) {
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -707,11 +717,11 @@ func db_get_card_total_sats(card_id int) (int, error) {
return card_total_sats, nil return card_total_sats, nil
} }
func db_get_card_name_count(card_name string) (card_count int, err error) { func Get_card_name_count(card_name string) (card_count int, err error) {
card_count = 0 card_count = 0
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -728,7 +738,7 @@ func db_get_card_name_count(card_name string) (card_count int, err error) {
return card_count, nil return card_count, nil
} }
func db_insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string, k3 string, k4 string, func Insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string, k3 string, k4 string,
tx_max_sats int, day_max_sats int, lnurlw_enable bool, card_name string, uid_privacy bool, tx_max_sats int, day_max_sats int, lnurlw_enable bool, card_name string, uid_privacy bool,
allow_neg_bal_ptr bool) error { allow_neg_bal_ptr bool) error {
@ -747,7 +757,7 @@ func db_insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string
allow_neg_bal_yn = "Y" allow_neg_bal_yn = "Y"
} }
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return err return err
} }
@ -777,11 +787,11 @@ func db_insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string
return nil return nil
} }
func db_wipe_card(card_name string) (*card_wipe_info, error) { func Wipe_card(card_name string) (*Card_wipe_info, error) {
card_wipe_info := card_wipe_info{} card_wipe_info := Card_wipe_info{}
db, err := db_open() db, err := open()
if err != nil { if err != nil {
return &card_wipe_info, err return &card_wipe_info, err
} }
@ -810,17 +820,17 @@ func db_wipe_card(card_name string) (*card_wipe_info, error) {
` FROM cards WHERE card_name = $1;` ` FROM cards WHERE card_name = $1;`
row := db.QueryRow(sqlStatement, card_name) row := db.QueryRow(sqlStatement, card_name)
err = row.Scan( err = row.Scan(
&card_wipe_info.id, &card_wipe_info.Id,
&card_wipe_info.uid, &card_wipe_info.Uid,
&card_wipe_info.k0, &card_wipe_info.K0,
&card_wipe_info.k2, &card_wipe_info.K2,
&card_wipe_info.k3, &card_wipe_info.K3,
&card_wipe_info.k4) &card_wipe_info.K4)
if err != nil { if err != nil {
return &card_wipe_info, err return &card_wipe_info, err
} }
card_wipe_info.k1 = db_get_setting("AES_DECRYPT_KEY") card_wipe_info.K1 = Get_setting("AES_DECRYPT_KEY")
return &card_wipe_info, nil return &card_wipe_info, nil
} }

View file

@ -1,4 +1,4 @@
package main package email
import ( import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
@ -9,35 +9,36 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"strconv" "strconv"
"strings" "strings"
"github.com/boltcard/boltcard/db"
) )
func send_balance_email(recipient_email string, card_id int) { func Send_balance_email(recipient_email string, card_id int) {
c, err := db_get_card_from_card_id(card_id) c, err := db.Get_card_from_card_id(card_id)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
} }
card_total_sats, err := db_get_card_total_sats(card_id) card_total_sats, err := db.Get_card_total_sats(card_id)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
} }
email_max_txs, err := strconv.Atoi(db_get_setting("EMAIL_MAX_TXS")) email_max_txs, err := strconv.Atoi(db.Get_setting("EMAIL_MAX_TXS"))
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
} }
txs, err := db_get_card_txs(card_id, email_max_txs+1) txs, err := db.Get_card_txs(card_id, email_max_txs+1)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
} }
subject := c.card_name + " balance = " + strconv.Itoa(card_total_sats) + " sats" subject := c.Card_name + " balance = " + strconv.Itoa(card_total_sats) + " sats"
// add transactions to the email body // add transactions to the email body
@ -56,9 +57,9 @@ func send_balance_email(recipient_email string, card_id int) {
if i < email_max_txs { if i < email_max_txs {
html_body_sb.WriteString( html_body_sb.WriteString(
"<tr>" + "<tr>" +
"<td>" + tx.tx_time + "</td>" + "<td>" + tx.Tx_time + "</td>" +
"<td>" + tx.tx_type + "</td>" + "<td>" + tx.Tx_type + "</td>" +
"<td style='text-align:right'>" + strconv.Itoa(tx.tx_amount_msats/1000) + "</td>" + "<td style='text-align:right'>" + strconv.Itoa(tx.Tx_amount_msats/1000) + "</td>" +
"</tr>") "</tr>")
} else { } else {
html_body_sb.WriteString( html_body_sb.WriteString(
@ -69,8 +70,8 @@ func send_balance_email(recipient_email string, card_id int) {
"</tr>") "</tr>")
} }
text_body_sb.WriteString(tx.tx_type + text_body_sb.WriteString(tx.Tx_type +
" " + strconv.Itoa(tx.tx_amount_msats/1000)) " " + strconv.Itoa(tx.Tx_amount_msats/1000))
} }
html_body_sb.WriteString("</table></body></html>") html_body_sb.WriteString("</table></body></html>")
@ -78,7 +79,7 @@ func send_balance_email(recipient_email string, card_id int) {
html_body := html_body_sb.String() html_body := html_body_sb.String()
text_body := text_body_sb.String() text_body := text_body_sb.String()
send_email(recipient_email, Send_email(recipient_email,
subject, subject,
html_body, html_body,
text_body) text_body)
@ -86,11 +87,11 @@ func send_balance_email(recipient_email string, card_id int) {
// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/ses-example-send-email.html // https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/ses-example-send-email.html
func send_email(recipient string, subject string, htmlBody string, textBody string) { func Send_email(recipient string, subject string, htmlBody string, textBody string) {
aws_ses_id := db_get_setting("AWS_SES_ID") aws_ses_id := db.Get_setting("AWS_SES_ID")
aws_ses_secret := db_get_setting("AWS_SES_SECRET") aws_ses_secret := db.Get_setting("AWS_SES_SECRET")
sender := db_get_setting("AWS_SES_EMAIL_FROM") sender := db.Get_setting("AWS_SES_EMAIL_FROM")
sess, err := session.NewSession(&aws.Config{ sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1"), Region: aws.String("us-east-1"),

21
go.mod
View file

@ -16,9 +16,10 @@ require (
) )
require ( require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/aead/siphash v1.0.1 // indirect github.com/aead/siphash v1.0.1 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcd v0.23.4 // indirect github.com/btcsuite/btcd v0.23.4 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
@ -26,7 +27,6 @@ require (
github.com/btcsuite/btcd/btcutil/psbt v1.1.6 // indirect github.com/btcsuite/btcd/btcutil/psbt v1.1.6 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
github.com/btcsuite/btcutil v1.0.2 // indirect
github.com/btcsuite/btcwallet v0.16.4 // indirect github.com/btcsuite/btcwallet v0.16.4 // indirect
github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect github.com/btcsuite/btcwallet/wallet/txauthor v1.3.2 // indirect
github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect github.com/btcsuite/btcwallet/wallet/txrules v1.2.0 // indirect
@ -44,7 +44,6 @@ require (
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/decred/dcrd/lru v1.1.1 // indirect github.com/decred/dcrd/lru v1.1.1 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 // indirect github.com/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 // indirect
github.com/fergusstrange/embedded-postgres v1.19.0 // indirect github.com/fergusstrange/embedded-postgres v1.19.0 // indirect
@ -75,10 +74,7 @@ require (
github.com/jonboulle/clockwork v0.3.0 // indirect github.com/jonboulle/clockwork v0.3.0 // indirect
github.com/jrick/logrotate v1.0.0 // indirect github.com/jrick/logrotate v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/juju/loggo v1.0.0 // indirect
github.com/kkdai/bstream v1.0.0 // indirect github.com/kkdai/bstream v1.0.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf // indirect
github.com/lightninglabs/neutrino v0.14.2 // indirect github.com/lightninglabs/neutrino v0.14.2 // indirect
github.com/lightningnetwork/lightning-onion v1.2.0 // indirect github.com/lightningnetwork/lightning-onion v1.2.0 // indirect
@ -89,15 +85,12 @@ require (
github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect github.com/lightningnetwork/lnd/ticker v1.1.0 // indirect
github.com/lightningnetwork/lnd/tlv v1.0.3 // indirect github.com/lightningnetwork/lnd/tlv v1.0.3 // indirect
github.com/lightningnetwork/lnd/tor v1.1.0 // indirect github.com/lightningnetwork/lnd/tor v1.1.0 // indirect
github.com/lncm/lnd-rpc v1.0.2 // indirect
github.com/ltcsuite/ltcd v0.22.1-beta // indirect github.com/ltcsuite/ltcd v0.22.1-beta // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/miekg/dns v1.1.50 // indirect github.com/miekg/dns v1.1.50 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect github.com/nbd-wtf/ln-decodepay v1.11.1 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
@ -110,7 +103,6 @@ require (
github.com/stretchr/testify v1.8.1 // indirect github.com/stretchr/testify v1.8.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect go.etcd.io/bbolt v1.3.6 // indirect
@ -121,18 +113,12 @@ require (
go.etcd.io/etcd/pkg/v3 v3.5.5 // indirect go.etcd.io/etcd/pkg/v3 v3.5.5 // indirect
go.etcd.io/etcd/raft/v3 v3.5.5 // indirect go.etcd.io/etcd/raft/v3 v3.5.5 // indirect
go.etcd.io/etcd/server/v3 v3.5.5 // indirect go.etcd.io/etcd/server/v3 v3.5.5 // indirect
go.opentelemetry.io/contrib v1.9.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.36.4 // indirect
go.opentelemetry.io/otel v1.11.1 // indirect go.opentelemetry.io/otel v1.11.1 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.1 // indirect
go.opentelemetry.io/otel/internal/metric v0.27.0 // indirect
go.opentelemetry.io/otel/metric v0.33.0 // indirect
go.opentelemetry.io/otel/sdk v1.11.1 // indirect go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.28.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.33.0 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect go.opentelemetry.io/otel/trace v1.11.1 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
@ -146,7 +132,6 @@ require (
golang.org/x/text v0.7.0 // indirect golang.org/x/text v0.7.0 // indirect
golang.org/x/time v0.2.0 // indirect golang.org/x/time v0.2.0 // indirect
golang.org/x/tools v0.3.0 // indirect golang.org/x/tools v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/errgo.v1 v1.0.1 // indirect gopkg.in/errgo.v1 v1.0.1 // indirect

489
go.sum

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
package main package lnd
import ( import (
"context" "context"
@ -20,6 +20,9 @@ import (
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"gopkg.in/macaroon.v2" "gopkg.in/macaroon.v2"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/email"
) )
type rpcCreds map[string]string type rpcCreds map[string]string
@ -72,9 +75,9 @@ func getGrpcConn(hostname string, port int, tlsFile, macaroonFile string) *grpc.
// https://api.lightning.community/?shell#addinvoice // https://api.lightning.community/?shell#addinvoice
func add_invoice(amount_sat int64, metadata string) (payment_request string, r_hash []byte, return_err error) { func Add_invoice(amount_sat int64, metadata string) (payment_request string, r_hash []byte, return_err error) {
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT")) ln_port, err := strconv.Atoi(db.Get_setting("LN_PORT"))
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -82,10 +85,10 @@ func add_invoice(amount_sat int64, metadata string) (payment_request string, r_h
dh := sha256.Sum256([]byte(metadata)) dh := sha256.Sum256([]byte(metadata))
connection := getGrpcConn( connection := getGrpcConn(
db_get_setting("LN_HOST"), db.Get_setting("LN_HOST"),
ln_port, ln_port,
db_get_setting("LN_TLS_FILE"), db.Get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE")) db.Get_setting("LN_MACAROON_FILE"))
l_client := lnrpc.NewLightningClient(connection) l_client := lnrpc.NewLightningClient(connection)
@ -106,23 +109,23 @@ func add_invoice(amount_sat int64, metadata string) (payment_request string, r_h
// https://api.lightning.community/?shell#subscribesingleinvoice // https://api.lightning.community/?shell#subscribesingleinvoice
func monitor_invoice_state(r_hash []byte) { func Monitor_invoice_state(r_hash []byte) {
// SubscribeSingleInvoice // SubscribeSingleInvoice
// get node parameters from environment variables // get node parameters from environment variables
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT")) ln_port, err := strconv.Atoi(db.Get_setting("LN_PORT"))
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
return return
} }
connection := getGrpcConn( connection := getGrpcConn(
db_get_setting("LN_HOST"), db.Get_setting("LN_HOST"),
ln_port, ln_port,
db_get_setting("LN_TLS_FILE"), db.Get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE")) db.Get_setting("LN_MACAROON_FILE"))
i_client := invoicesrpc.NewInvoicesClient(connection) i_client := invoicesrpc.NewInvoicesClient(connection)
@ -156,14 +159,14 @@ func monitor_invoice_state(r_hash []byte) {
"invoice_state": invoice_state, "invoice_state": invoice_state,
}).Info("invoice state updated") }).Info("invoice state updated")
db_update_receipt_state(hex.EncodeToString(r_hash), invoice_state) db.Update_receipt_state(hex.EncodeToString(r_hash), invoice_state)
} }
connection.Close() connection.Close()
// send email // send email
card_id, err := db_get_card_id_for_r_hash(hex.EncodeToString(r_hash)) card_id, err := db.Get_card_id_for_r_hash(hex.EncodeToString(r_hash))
if err != nil { if err != nil {
log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err) log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err)
return return
@ -171,52 +174,52 @@ func monitor_invoice_state(r_hash []byte) {
log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash), "card_id": card_id}).Debug("card found") log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash), "card_id": card_id}).Debug("card found")
c, err := db_get_card_from_card_id(card_id) c, err := db.Get_card_from_card_id(card_id)
if err != nil { if err != nil {
log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err) log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err)
return return
} }
if c.email_enable != "Y" { if c.Email_enable != "Y" {
log.Debug("email is not enabled for the card") log.Debug("email is not enabled for the card")
return return
} }
go send_balance_email(c.email_address, card_id) go email.Send_balance_email(c.Email_address, card_id)
return return
} }
// https://api.lightning.community/?shell#sendpaymentv2 // https://api.lightning.community/?shell#sendpaymentv2
func pay_invoice(card_payment_id int, invoice string) { func Pay_invoice(card_payment_id int, invoice string) {
// SendPaymentV2 // SendPaymentV2
// get node parameters from environment variables // get node parameters from environment variables
ln_port, err := strconv.Atoi(db_get_setting("LN_PORT")) ln_port, err := strconv.Atoi(db.Get_setting("LN_PORT"))
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return return
} }
connection := getGrpcConn( connection := getGrpcConn(
db_get_setting("LN_HOST"), db.Get_setting("LN_HOST"),
ln_port, ln_port,
db_get_setting("LN_TLS_FILE"), db.Get_setting("LN_TLS_FILE"),
db_get_setting("LN_MACAROON_FILE")) db.Get_setting("LN_MACAROON_FILE"))
r_client := routerrpc.NewRouterClient(connection) r_client := routerrpc.NewRouterClient(connection)
fee_limit_sat_str := db_get_setting("FEE_LIMIT_SAT") fee_limit_sat_str := db.Get_setting("FEE_LIMIT_SAT")
fee_limit_sat, err := strconv.ParseInt(fee_limit_sat_str, 10, 64) fee_limit_sat, err := strconv.ParseInt(fee_limit_sat_str, 10, 64)
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return return
} }
fee_limit_percent_str := db_get_setting("FEE_LIMIT_PERCENT") fee_limit_percent_str := db.Get_setting("FEE_LIMIT_PERCENT")
fee_limit_percent, err := strconv.ParseFloat(fee_limit_percent_str, 64) fee_limit_percent, err := strconv.ParseFloat(fee_limit_percent_str, 64)
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
@ -260,7 +263,7 @@ func pay_invoice(card_payment_id int, invoice string) {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Info("payment failure reason : ", failure_reason) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Info("payment failure reason : ", failure_reason)
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Info("payment status : ", payment_status) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Info("payment status : ", payment_status)
err = db_update_payment_status(card_payment_id, payment_status, failure_reason) err = db.Update_payment_status(card_payment_id, payment_status, failure_reason)
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return return
@ -271,7 +274,7 @@ func pay_invoice(card_payment_id int, invoice string) {
// send email // send email
card_id, err := db_get_card_id_for_card_payment_id(card_payment_id) card_id, err := db.Get_card_id_for_card_payment_id(card_payment_id)
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return return
@ -279,18 +282,18 @@ func pay_invoice(card_payment_id int, invoice string) {
log.WithFields(log.Fields{"card_payment_id": card_payment_id, "card_id": card_id}).Debug("card found") log.WithFields(log.Fields{"card_payment_id": card_payment_id, "card_id": card_id}).Debug("card found")
c, err := db_get_card_from_card_id(card_id) c, err := db.Get_card_from_card_id(card_id)
if err != nil { if err != nil {
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err) log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
return return
} }
if c.email_enable != "Y" { if c.Email_enable != "Y" {
log.Debug("email is not enabled for the card") log.Debug("email is not enabled for the card")
return return
} }
go send_balance_email(c.email_address, card_id) go email.Send_balance_email(c.Email_address, card_id)
return return
} }

12
lndhub/lndhub.go Normal file
View file

@ -0,0 +1,12 @@
package lndhub
import (
log "github.com/sirupsen/logrus"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/email"
)
func Pay_invoice(card_payment_id int, invoice string) {
}

View file

@ -1,4 +1,4 @@
package main package lnurlp
import ( import (
"encoding/hex" "encoding/hex"
@ -6,10 +6,13 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"strconv" "strconv"
"github.com/boltcard/boltcard/lnd"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/resp_err"
) )
func lnurlp_callback(w http.ResponseWriter, r *http.Request) { func Callback(w http.ResponseWriter, r *http.Request) {
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" { if db.Get_setting("FUNCTION_LNURLP") != "ENABLE" {
log.Debug("LNURLp function is not enabled") log.Debug("LNURLp function is not enabled")
return return
} }
@ -17,10 +20,10 @@ func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
name := mux.Vars(r)["name"] name := mux.Vars(r)["name"]
amount := r.URL.Query().Get("amount") amount := r.URL.Query().Get("amount")
card_id, err := db_get_card_id_for_name(name) card_id, err := db.Get_card_id_for_name(name)
if err != nil { if err != nil {
log.Info("card name not found") log.Info("card name not found")
write_error(w) resp_err.Write(w)
return return
} }
@ -33,38 +36,38 @@ func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
"req.Host": r.Host, "req.Host": r.Host,
}).Info("lnurlp_callback") }).Info("lnurlp_callback")
domain := db_get_setting("HOST_DOMAIN") domain := db.Get_setting("HOST_DOMAIN")
if r.Host != domain { if r.Host != domain {
log.Warn("wrong host domain") log.Warn("wrong host domain")
write_error(w) resp_err.Write(w)
return return
} }
amount_msat, err := strconv.ParseInt(amount, 10, 64) amount_msat, err := strconv.ParseInt(amount, 10, 64)
if err != nil { if err != nil {
log.Warn("amount is not a valid integer") log.Warn("amount is not a valid integer")
write_error(w) resp_err.Write(w)
return return
} }
amount_sat := amount_msat / 1000 amount_sat := amount_msat / 1000
metadata := "[[\"text/identifier\",\"" + name + "@" + domain + "\"],[\"text/plain\",\"bolt card deposit\"]]" metadata := "[[\"text/identifier\",\"" + name + "@" + domain + "\"],[\"text/plain\",\"bolt card deposit\"]]"
pr, r_hash, err := add_invoice(amount_sat, metadata) pr, r_hash, err := lnd.Add_invoice(amount_sat, metadata)
if err != nil { if err != nil {
log.Warn("could not add_invoice") log.Warn("could not add_invoice")
write_error(w) resp_err.Write(w)
return return
} }
err = db_insert_receipt(card_id, pr, hex.EncodeToString(r_hash), amount_msat) err = db.Insert_receipt(card_id, pr, hex.EncodeToString(r_hash), amount_msat)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
write_error(w) resp_err.Write(w)
return return
} }
go monitor_invoice_state(r_hash) go lnd.Monitor_invoice_state(r_hash)
log.Debug("sending 'status OK' response") log.Debug("sending 'status OK' response")

View file

@ -1,13 +1,15 @@
package main package lnurlp
import ( import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/resp_err"
) )
func lnurlp_response(w http.ResponseWriter, r *http.Request) { func Response(w http.ResponseWriter, r *http.Request) {
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" { if db.Get_setting("FUNCTION_LNURLP") != "ENABLE" {
log.Debug("LNURLp function is not enabled") log.Debug("LNURLp function is not enabled")
return return
} }
@ -23,25 +25,25 @@ func lnurlp_response(w http.ResponseWriter, r *http.Request) {
// look up domain setting (HOST_DOMAIN) // look up domain setting (HOST_DOMAIN)
domain := db_get_setting("HOST_DOMAIN") domain := db.Get_setting("HOST_DOMAIN")
if r.Host != domain { if r.Host != domain {
log.Warn("wrong host domain") log.Warn("wrong host domain")
write_error(w) resp_err.Write(w)
return return
} }
// look up name in database (table cards, field card_name) // look up name in database (table cards, field card_name)
card_count, err := db_get_card_count_for_name_lnurlp(name) card_count, err := db.Get_card_count_for_name_lnurlp(name)
if err != nil { if err != nil {
log.Warn("could not get card count for name") log.Warn("could not get card count for name")
write_error(w) resp_err.Write(w)
return return
} }
if card_count != 1 { if card_count != 1 {
log.Info("not one enabled card with that name") log.Info("not one enabled card with that name")
write_error(w) resp_err.Write(w)
return return
} }

231
lnurlw/lnurlw_callback.go Normal file
View file

@ -0,0 +1,231 @@
package lnurlw
import (
decodepay "github.com/fiatjaf/ln-decodepay"
log "github.com/sirupsen/logrus"
"net/http"
"bytes"
"io"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/lnd"
"github.com/boltcard/boltcard/resp_err"
)
func lndhub_payment(w http.ResponseWriter, p *db.Payment) {
//get setting for LNDHUB_URL
lndhub_url := "https://" + db.Get_setting("LNDHUB_URL")
//get lndhub login details from database
c, err := db.Get_card_from_card_id(p.Card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
//lndhub.auth API call
//the login JSON is held in the Card_name field
body := []byte(c.Card_name)
r, err := http.NewRequest("POST", lndhub_url + "/auth", bytes.NewBuffer(body))
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
r.Header.Add("Access-Control-Allow-Origin", "*")
r.Header.Add("Content-Type", "application/json")
client := &http.Client{}
res, err := client.Do(r)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
defer res.Body.Close()
b, err2 := io.ReadAll(res.Body)
if err2 != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
log.Info(string(b))
// fmt.Println(string(b))
//lndhub.payinvoice API call
}
func lnd_payment(w http.ResponseWriter, bolt11 decodepay.Bolt11, p *db.Payment, param_pr string) {
// check amount limits
invoice_sats := int(bolt11.MSatoshi / 1000)
day_total_sats, err := db.Get_card_totals(p.Card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
c, err := db.Get_card_from_card_id(p.Card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
if invoice_sats > c.Tx_limit_sats {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats)
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("tx_limit_sats: ", c.Tx_limit_sats)
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over tx_limit_sats!")
resp_err.Write(w)
return
}
if day_total_sats+invoice_sats > c.Day_limit_sats {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("invoice_sats: ", invoice_sats)
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_total_sats: ", day_total_sats)
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("day_limit_sats: ", c.Day_limit_sats)
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("over day_limit_sats!")
resp_err.Write(w)
return
}
// check the card balance if marked as 'must stay above zero' (default)
// i.e. cards.allow_negative_balance == 'N'
if c.Allow_negative_balance != "Y" {
card_total, err := db.Get_card_total_sats(p.Card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
if card_total-invoice_sats < 0 {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn("not enough balance")
resp_err.Write(w)
return
}
}
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("paying invoice")
// update paid_flag so we only attempt payment once
err = db.Update_payment_paid(p.Card_payment_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
// https://github.com/fiatjaf/lnurl-rfc/blob/luds/03.md
//
// LN SERVICE sends a {"status": "OK"} or
// {"status": "ERROR", "reason": "error details..."}
// JSON response and then attempts to pay the invoices asynchronously.
go lnd.Pay_invoice(p.Card_payment_id, param_pr)
log.Debug("sending 'status OK' response")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"OK"}`)
w.Write(jsonData)
}
func Callback(w http.ResponseWriter, req *http.Request) {
env_host_domain := db.Get_setting("HOST_DOMAIN")
if req.Host != env_host_domain {
log.Warn("wrong host domain")
resp_err.Write(w)
return
}
url := req.URL.RequestURI()
log.WithFields(log.Fields{"url": url}).Debug("cb request")
// check k1 value
params_k1, ok := req.URL.Query()["k1"]
if !ok || len(params_k1[0]) < 1 {
log.WithFields(log.Fields{"url": url}).Debug("k1 not found")
resp_err.Write(w)
return
}
param_k1 := params_k1[0]
p, err := db.Get_payment_k1(param_k1)
if err != nil {
log.WithFields(log.Fields{"url": url, "k1": param_k1}).Warn(err)
resp_err.Write(w)
return
}
// check that payment has not been made
if p.Paid_flag != "N" {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("payment already made")
resp_err.Write(w)
return
}
// check if lnurlw_request has timed out
lnurlw_timeout, err := db.Check_lnurlw_timeout(p.Card_payment_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
if lnurlw_timeout == true {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("lnurlw request has timed out")
resp_err.Write(w)
return
}
params_pr, ok := req.URL.Query()["pr"]
if !ok || len(params_pr[0]) < 1 {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn("pr field not found")
resp_err.Write(w)
return
}
param_pr := params_pr[0]
bolt11, _ := decodepay.Decodepay(param_pr)
// record the lightning invoice
err = db.Update_payment_invoice(p.Card_payment_id, param_pr, bolt11.MSatoshi)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Warn(err)
resp_err.Write(w)
return
}
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Debug("checking payment rules")
// check if we are only sending funds to a defined test node
testnode := db.Get_setting("LN_TESTNODE")
if testnode != "" && bolt11.Payee != testnode {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("rejected as not the defined test node")
resp_err.Write(w)
return
}
//check if we are using LND or LNDHUB for payment
lndhub := db.Get_setting("FUNCTION_LNDHUB")
if lndhub == "ENABLE" {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("initiating lndhub payment")
lndhub_payment(w, p)
} else {
log.WithFields(log.Fields{"card_payment_id": p.Card_payment_id}).Info("initiating lnd payment")
lnd_payment(w, bolt11, p, param_pr)
}
}

View file

@ -1,4 +1,4 @@
package main package lnurlw
import ( import (
"encoding/hex" "encoding/hex"
@ -9,9 +9,12 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/crypto"
"github.com/boltcard/boltcard/resp_err"
) )
type Response struct { type ResponseData struct {
Tag string `json:"tag"` Tag string `json:"tag"`
Callback string `json:"callback"` Callback string `json:"callback"`
LnurlwK1 string `json:"k1"` LnurlwK1 string `json:"k1"`
@ -60,7 +63,7 @@ func check_cmac(uid []byte, ctr []byte, k2_cmac_key []byte, cmac []byte) (bool,
sv2[14] = ctr[1] sv2[14] = ctr[1]
sv2[15] = ctr[2] sv2[15] = ctr[2]
cmac_verified, err := crypto_aes_cmac(k2_cmac_key, sv2, cmac) cmac_verified, err := crypto.Aes_cmac(k2_cmac_key, sv2, cmac)
if err != nil { if err != nil {
return false, err return false, err
@ -74,10 +77,10 @@ func setup_card_record(uid string, ctr uint32, uid_bin []byte, ctr_bin []byte, c
// find the card record by matching the cmac // find the card record by matching the cmac
// get possible card records from the database // get possible card records from the database
cards, err := db_get_cards_blank_uid() cards, err := db.Get_cards_blank_uid()
if err != nil { if err != nil {
return errors.New("db_get_cards_blank_uid errored") return errors.New("db.Get_cards_blank_uid errored")
} }
// check card records for a matching cmac // check card records for a matching cmac
@ -85,7 +88,7 @@ func setup_card_record(uid string, ctr uint32, uid_bin []byte, ctr_bin []byte, c
for i, card := range cards { for i, card := range cards {
// check the cmac // check the cmac
k2_cmac_key, err := hex.DecodeString(card.k2_cmac_key) k2_cmac_key, err := hex.DecodeString(card.K2_cmac_key)
if err != nil { if err != nil {
return errors.New("card.k2_cmac_key decode failed") return errors.New("card.k2_cmac_key decode failed")
@ -100,12 +103,12 @@ func setup_card_record(uid string, ctr uint32, uid_bin []byte, ctr_bin []byte, c
if cmac_valid == true { if cmac_valid == true {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"i": i, "i": i,
"card.card_id": card.card_id, "card.card_id": card.Card_id,
"card.k2_cmac_key": card.k2_cmac_key, "card.k2_cmac_key": card.K2_cmac_key,
}).Info("cmac match found") }).Info("cmac match found")
// store the uid and ctr in the card record // store the uid and ctr in the card record
err := db_update_card_uid_ctr(card.card_id, uid, ctr) err := db.Update_card_uid_ctr(card.Card_id, uid, ctr)
if err != nil { if err != nil {
return err return err
@ -150,7 +153,7 @@ func parse_request(req *http.Request) (int, error) {
// decrypt p with aes_decrypt_key // decrypt p with aes_decrypt_key
aes_decrypt_key := db_get_setting("AES_DECRYPT_KEY") aes_decrypt_key := db.Get_setting("AES_DECRYPT_KEY")
key_sdm_file_read, err := hex.DecodeString(aes_decrypt_key) key_sdm_file_read, err := hex.DecodeString(aes_decrypt_key)
@ -158,7 +161,7 @@ func parse_request(req *http.Request) (int, error) {
return 0, err return 0, err
} }
dec_p, err := crypto_aes_decrypt(key_sdm_file_read, ba_p) dec_p, err := crypto.Aes_decrypt(key_sdm_file_read, ba_p)
if err != nil { if err != nil {
return 0, err return 0, err
@ -179,7 +182,7 @@ func parse_request(req *http.Request) (int, error) {
log.WithFields(log.Fields{"uid": uid_str, "ctr": ctr_int}).Info("decrypted card data") log.WithFields(log.Fields{"uid": uid_str, "ctr": ctr_int}).Info("decrypted card data")
card_count, err := db_get_card_count_for_uid(uid_str) card_count, err := db.Get_card_count_for_uid(uid_str)
if err != nil { if err != nil {
return 0, errors.New("could not get card count for uid") return 0, errors.New("could not get card count for uid")
@ -197,7 +200,7 @@ func parse_request(req *http.Request) (int, error) {
// get card record from database for UID // get card record from database for UID
c, err := db_get_card_from_uid(uid_str) c, err := db.Get_card_from_uid(uid_str)
if err != nil { if err != nil {
return 0, errors.New("card not found for uid") return 0, errors.New("card not found for uid")
@ -205,13 +208,13 @@ func parse_request(req *http.Request) (int, error) {
// check if card is enabled // check if card is enabled
if c.lnurlw_enable != "Y" { if c.Lnurlw_enable != "Y" {
return 0, errors.New("card lnurlw enable is not set to Y") return 0, errors.New("card lnurlw enable is not set to Y")
} }
// check cmac // check cmac
k2_cmac_key, err := hex.DecodeString(c.k2_cmac_key) k2_cmac_key, err := hex.DecodeString(c.K2_cmac_key)
if err != nil { if err != nil {
return 0, err return 0, err
@ -229,7 +232,7 @@ func parse_request(req *http.Request) (int, error) {
// check and update last_counter_value // check and update last_counter_value
counter_ok, err := db_check_and_update_counter(c.card_id, ctr_int) counter_ok, err := db.Check_and_update_counter(c.Card_id, ctr_int)
if err != nil { if err != nil {
return 0, err return 0, err
@ -239,17 +242,17 @@ func parse_request(req *http.Request) (int, error) {
return 0, errors.New("counter not increasing") return 0, errors.New("counter not increasing")
} }
log.WithFields(log.Fields{"card_id": c.card_id, "counter": ctr_int}).Info("validated") log.WithFields(log.Fields{"card_id": c.Card_id, "counter": ctr_int}).Info("validated")
return c.card_id, nil return c.Card_id, nil
} }
func lnurlw_response(w http.ResponseWriter, req *http.Request) { func Response(w http.ResponseWriter, req *http.Request) {
env_host_domain := db_get_setting("HOST_DOMAIN") env_host_domain := db.Get_setting("HOST_DOMAIN")
if req.Host != env_host_domain { if req.Host != env_host_domain {
log.Warn("wrong host domain") log.Warn("wrong host domain")
write_error(w) resp_err.Write(w)
return return
} }
@ -257,25 +260,25 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
log.Debug(err.Error()) log.Debug(err.Error())
write_error(w) resp_err.Write(w)
return return
} }
lnurlw_k1, err := create_k1() lnurlw_k1, err := crypto.Create_k1()
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
write_error(w) resp_err.Write(w)
return return
} }
// store k1 in database and include in response // store k1 in database and include in response
err = db_insert_payment(card_id, lnurlw_k1) err = db.Insert_payment(card_id, lnurlw_k1)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
write_error(w) resp_err.Write(w)
return return
} }
@ -286,25 +289,25 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
lnurlw_cb_url = "https://" + req.Host + "/cb" lnurlw_cb_url = "https://" + req.Host + "/cb"
} }
min_withdraw_sats_str := db_get_setting("MIN_WITHDRAW_SATS") min_withdraw_sats_str := db.Get_setting("MIN_WITHDRAW_SATS")
min_withdraw_sats, err := strconv.Atoi(min_withdraw_sats_str) min_withdraw_sats, err := strconv.Atoi(min_withdraw_sats_str)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
write_error(w) resp_err.Write(w)
return return
} }
max_withdraw_sats_str := db_get_setting("MAX_WITHDRAW_SATS") max_withdraw_sats_str := db.Get_setting("MAX_WITHDRAW_SATS")
max_withdraw_sats, err := strconv.Atoi(max_withdraw_sats_str) max_withdraw_sats, err := strconv.Atoi(max_withdraw_sats_str)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
write_error(w) resp_err.Write(w)
return return
} }
response := Response{} response := ResponseData{}
response.Tag = "withdrawRequest" response.Tag = "withdrawRequest"
response.Callback = lnurlw_cb_url response.Callback = lnurlw_cb_url
response.LnurlwK1 = lnurlw_k1 response.LnurlwK1 = lnurlw_k1
@ -316,7 +319,7 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
write_error(w) resp_err.Write(w)
return return
} }

View file

@ -1,164 +0,0 @@
package main
import (
decodepay "github.com/fiatjaf/ln-decodepay"
log "github.com/sirupsen/logrus"
"net/http"
)
func lnurlw_callback(w http.ResponseWriter, req *http.Request) {
env_host_domain := db_get_setting("HOST_DOMAIN")
if req.Host != env_host_domain {
log.Warn("wrong host domain")
write_error(w)
return
}
url := req.URL.RequestURI()
log.WithFields(log.Fields{"url": url}).Debug("cb request")
// check k1 value
params_k1, ok := req.URL.Query()["k1"]
if !ok || len(params_k1[0]) < 1 {
log.WithFields(log.Fields{"url": url}).Debug("k1 not found")
write_error(w)
return
}
param_k1 := params_k1[0]
p, err := db_get_payment_k1(param_k1)
if err != nil {
log.WithFields(log.Fields{"url": url, "k1": param_k1}).Warn(err)
write_error(w)
return
}
// check that payment has not been made
if p.paid_flag != "N" {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("payment already made")
write_error(w)
return
}
// check if lnurlw_request has timed out
lnurlw_timeout, err := db_check_lnurlw_timeout(p.card_payment_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
if lnurlw_timeout == true {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("lnurlw request has timed out")
write_error(w)
return
}
params_pr, ok := req.URL.Query()["pr"]
if !ok || len(params_pr[0]) < 1 {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn("pr field not found")
write_error(w)
return
}
param_pr := params_pr[0]
bolt11, _ := decodepay.Decodepay(param_pr)
// record the lightning invoice
err = db_update_payment_invoice(p.card_payment_id, param_pr, bolt11.MSatoshi)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Debug("checking payment rules")
// check if we are only sending funds to a defined test node
testnode := db_get_setting("LN_TESTNODE")
if testnode != "" && bolt11.Payee != testnode {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("rejected as not the defined test node")
write_error(w)
return
}
// check amount limits
invoice_sats := int(bolt11.MSatoshi / 1000)
day_total_sats, err := db_get_card_totals(p.card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
c, err := db_get_card_from_card_id(p.card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
if invoice_sats > c.tx_limit_sats {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("invoice_sats: ", invoice_sats)
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("tx_limit_sats: ", c.tx_limit_sats)
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("over tx_limit_sats!")
write_error(w)
return
}
if day_total_sats+invoice_sats > c.day_limit_sats {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("invoice_sats: ", invoice_sats)
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("day_total_sats: ", day_total_sats)
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("day_limit_sats: ", c.day_limit_sats)
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("over day_limit_sats!")
write_error(w)
return
}
// check the card balance if marked as 'must stay above zero' (default)
// i.e. cards.allow_negative_balance == 'N'
if c.allow_negative_balance != "Y" {
card_total, err := db_get_card_total_sats(p.card_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
if card_total-invoice_sats < 0 {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn("not enough balance")
write_error(w)
return
}
}
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Info("paying invoice")
// update paid_flag so we only attempt payment once
err = db_update_payment_paid(p.card_payment_id)
if err != nil {
log.WithFields(log.Fields{"card_payment_id": p.card_payment_id}).Warn(err)
write_error(w)
return
}
// https://github.com/fiatjaf/lnurl-rfc/blob/luds/03.md
//
// LN SERVICE sends a {"status": "OK"} or
// {"status": "ERROR", "reason": "error details..."}
// JSON response and then attempts to pay the invoices asynchronously.
go pay_invoice(p.card_payment_id, param_pr)
log.Debug("sending 'status OK' response")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"OK"}`)
w.Write(jsonData)
}

29
main.go
View file

@ -5,24 +5,15 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"time" "time"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/lnurlw"
"github.com/boltcard/boltcard/lnurlp"
) )
func write_error(w http.ResponseWriter) { var router = mux.NewRouter()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"ERROR","reason":"bad request"}`)
w.Write(jsonData)
}
func write_error_message(w http.ResponseWriter, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"ERROR","reason":"` + message + `"}`)
w.Write(jsonData)
}
func main() { func main() {
log_level := db_get_setting("LOG_LEVEL") log_level := db.Get_setting("LOG_LEVEL")
switch log_level { switch log_level {
case "DEBUG": case "DEBUG":
@ -49,11 +40,11 @@ func main() {
// createboltcard // createboltcard
external_router.Path("/new").Methods("GET").HandlerFunc(new_card_request) external_router.Path("/new").Methods("GET").HandlerFunc(new_card_request)
// lnurlw for pos // lnurlw for pos
external_router.Path("/ln").Methods("GET").HandlerFunc(lnurlw_response) external_router.Path("/ln").Methods("GET").HandlerFunc(lnurlw.Response)
external_router.Path("/cb").Methods("GET").HandlerFunc(lnurlw_callback) external_router.Path("/cb").Methods("GET").HandlerFunc(lnurlw.Callback)
// lnurlp for lightning address // lnurlp for lightning address
external_router.Path("/.well-known/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp_response) external_router.Path("/.well-known/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp.Response)
external_router.Path("/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp_callback) external_router.Path("/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp.Callback)
// internal API // internal API
// this has no authentication and is not to be exposed publicly // this has no authentication and is not to be exposed publicly
@ -63,7 +54,7 @@ func main() {
internal_router.Path("/createboltcard").Methods("GET").HandlerFunc(createboltcard) internal_router.Path("/createboltcard").Methods("GET").HandlerFunc(createboltcard)
internal_router.Path("/wipeboltcard").Methods("GET").HandlerFunc(wipeboltcard) internal_router.Path("/wipeboltcard").Methods("GET").HandlerFunc(wipeboltcard)
port := db_get_setting("HOST_PORT") port := db.Get_setting("HOST_PORT")
if port == "" { if port == "" {
port = "9000" port = "9000"
} }

View file

@ -5,6 +5,8 @@ import (
"encoding/json" "encoding/json"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/resp_err"
) )
/** /**
@ -47,7 +49,7 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
params_a, ok := req.URL.Query()["a"] params_a, ok := req.URL.Query()["a"]
if !ok || len(params_a[0]) < 1 { if !ok || len(params_a[0]) < 1 {
log.Debug("a not found") log.Debug("a not found")
write_error(w) resp_err.Write(w)
return return
} }
@ -55,33 +57,33 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
lnurlw_base := "lnurlw://" + req.Host + "/ln" lnurlw_base := "lnurlw://" + req.Host + "/ln"
c, err := db_get_new_card(a) c, err := db.Get_new_card(a)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
log.Debug(err) log.Debug(err)
write_error_message(w, "one time code was used or card was wiped or card does not exist") resp_err.Write_message(w, "one time code was used or card was wiped or card does not exist")
return return
} }
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
write_error(w) resp_err.Write(w)
return return
} }
k1_decrypt_key := db_get_setting("AES_DECRYPT_KEY") k1_decrypt_key := db.Get_setting("AES_DECRYPT_KEY")
response := NewCardResponse{} response := NewCardResponse{}
response.PROTOCOL_NAME = "create_bolt_card_response" response.PROTOCOL_NAME = "create_bolt_card_response"
response.PROTOCOL_VERSION = 2 response.PROTOCOL_VERSION = 2
response.CARD_NAME = c.card_name response.CARD_NAME = c.Card_name
response.LNURLW_BASE = lnurlw_base response.LNURLW_BASE = lnurlw_base
response.K0 = c.k0_auth_key response.K0 = c.K0_auth_key
response.K1 = k1_decrypt_key response.K1 = k1_decrypt_key
response.K2 = c.k2_cmac_key response.K2 = c.K2_cmac_key
response.K3 = c.k3 response.K3 = c.K3
response.K4 = c.k4 response.K4 = c.K4
response.UID_PRIVACY = c.uid_privacy response.UID_PRIVACY = c.Uid_privacy
log.SetFormatter(&log.JSONFormatter{ log.SetFormatter(&log.JSONFormatter{
DisableHTMLEscape: true, DisableHTMLEscape: true,
@ -89,7 +91,7 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
jsonData, err := json.Marshal(response) jsonData, err := json.Marshal(response)
if err != nil { if err != nil {
log.Warn(err) log.Warn(err)
write_error(w) resp_err.Write(w)
return return
} }

19
resp_err/resp_err.go Normal file
View file

@ -0,0 +1,19 @@
package resp_err
import (
"net/http"
)
func Write(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"ERROR","reason":"bad request"}`)
w.Write(jsonData)
}
func Write_message(w http.ResponseWriter, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
jsonData := []byte(`{"status":"ERROR","reason":"` + message + `"}`)
w.Write(jsonData)
}

View file

@ -12,10 +12,10 @@ echo Continue? "(y or n)"
read x read x
if [ "$x" = "y" ]; then if [ "$x" = "y" ]; then
psql postgres -f create_db_init.sql psql postgres -f sql/create_db_init.sql
psql postgres -f create_db.sql psql postgres -f sql/create_db.sql
psql postgres -f create_db_user.sql psql postgres -f sql/create_db_user.sql
psql postgres -f settings.sql psql postgres -f sql/settings.sql
echo Database created echo Database created
else else
echo No action echo No action

View file

@ -21,3 +21,5 @@ INSERT INTO settings (name, value) VALUES ('AWS_SES_ID', '');
INSERT INTO settings (name, value) VALUES ('AWS_SES_SECRET', ''); INSERT INTO settings (name, value) VALUES ('AWS_SES_SECRET', '');
INSERT INTO settings (name, value) VALUES ('AWS_SES_EMAIL_FROM', ''); INSERT INTO settings (name, value) VALUES ('AWS_SES_EMAIL_FROM', '');
INSERT INTO settings (name, value) VALUES ('EMAIL_MAX_TXS', ''); INSERT INTO settings (name, value) VALUES ('EMAIL_MAX_TXS', '');
INSERT INTO settings (name, value) VALUES ('FUNCTION_LNDHUB', '');
INSERT INTO settings (name, value) VALUES ('LNDHUB_URL', '');

View file

@ -4,23 +4,15 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"strconv" "strconv"
"github.com/boltcard/boltcard/db"
"github.com/boltcard/boltcard/resp_err"
) )
type card_wipe_info struct {
id int
k0 string
k1 string
k2 string
k3 string
k4 string
uid string
}
func wipeboltcard(w http.ResponseWriter, r *http.Request) { func wipeboltcard(w http.ResponseWriter, r *http.Request) {
if db_get_setting("FUNCTION_INTERNAL_API") != "ENABLE" { if db.Get_setting("FUNCTION_INTERNAL_API") != "ENABLE" {
msg := "wipeboltcard: internal API function is not enabled" msg := "wipeboltcard: internal API function is not enabled"
log.Debug(msg) log.Debug(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
@ -31,24 +23,24 @@ func wipeboltcard(w http.ResponseWriter, r *http.Request) {
if card_name == "" { if card_name == "" {
msg := "wipeboltcard: the card name must be set" msg := "wipeboltcard: the card name must be set"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
// check if card_name exists // check if card_name exists
card_count, err := db_get_card_name_count(card_name) card_count, err := db.Get_card_name_count(card_name)
if card_count == 0 { if card_count == 0 {
msg := "the card name does not exist in the database" msg := "the card name does not exist in the database"
log.Warn(msg) log.Warn(msg)
write_error_message(w, msg) resp_err.Write_message(w, msg)
return return
} }
// set the card as wiped and disabled, get the keys // set the card as wiped and disabled, get the keys
card_wipe_info_values, err := db_wipe_card(card_name) card_wipe_info_values, err := db.Wipe_card(card_name)
if err != nil { if err != nil {
log.Warn(err.Error()) log.Warn(err.Error())
return return
@ -63,13 +55,13 @@ func wipeboltcard(w http.ResponseWriter, r *http.Request) {
jsonData := `{"status":"OK",` + jsonData := `{"status":"OK",` +
`"action": "wipe",` + `"action": "wipe",` +
`"id": ` + strconv.Itoa(card_wipe_info_values.id) + `,` + `"id": ` + strconv.Itoa(card_wipe_info_values.Id) + `,` +
`"k0": "` + card_wipe_info_values.k0 + `",` + `"k0": "` + card_wipe_info_values.K0 + `",` +
`"k1": "` + card_wipe_info_values.k1 + `",` + `"k1": "` + card_wipe_info_values.K1 + `",` +
`"k2": "` + card_wipe_info_values.k2 + `",` + `"k2": "` + card_wipe_info_values.K2 + `",` +
`"k3": "` + card_wipe_info_values.k3 + `",` + `"k3": "` + card_wipe_info_values.K3 + `",` +
`"k4": "` + card_wipe_info_values.k4 + `",` + `"k4": "` + card_wipe_info_values.K4 + `",` +
`"uid": "` + card_wipe_info_values.uid + `",` + `"uid": "` + card_wipe_info_values.Uid + `",` +
`"version": 1}` `"version": 1}`
// log the response // log the response