merge
This commit is contained in:
commit
f3949b1b59
23 changed files with 586 additions and 969 deletions
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"github.com/boltcard/boltcard/db"
|
||||
"github.com/boltcard/boltcard/resp_err"
|
||||
)
|
||||
|
||||
func random_hex() string {
|
||||
|
|
@ -21,10 +23,10 @@ func random_hex() string {
|
|||
}
|
||||
|
||||
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"
|
||||
log.Debug(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
msg := "createboltcard: tx_max is not a valid integer"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +44,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
msg := "createboltcard: day_max is not a valid integer"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
msg := "createboltcard: enable is not a valid boolean"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +64,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
msg := "createboltcard: uid_privacy is not a valid boolean"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -71,13 +73,13 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
msg := "createboltcard: allow_neg_bal is not a valid boolean"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Warn(err.Error())
|
||||
return
|
||||
|
|
@ -86,7 +88,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if card_count > 0 {
|
||||
msg := "createboltcard: the card name already exists in the database"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// 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,
|
||||
uid_privacy_flag, allow_neg_bal_flag)
|
||||
if err != nil {
|
||||
|
|
@ -117,7 +119,7 @@ func createboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// return the URI + one_time_code
|
||||
|
||||
hostdomain := db_get_setting("HOST_DOMAIN")
|
||||
hostdomain := db.Get_setting("HOST_DOMAIN")
|
||||
url := ""
|
||||
if strings.HasSuffix(hostdomain, ".onion") {
|
||||
url = "http://" + hostdomain + "/new?a=" + one_time_code
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/aead/cmac"
|
||||
)
|
||||
|
||||
func create_k1() (string, error) {
|
||||
func Create_k1() (string, error) {
|
||||
|
||||
// 16 bytes = 128 bits
|
||||
b := make([]byte, 16)
|
||||
|
|
@ -24,7 +24,7 @@ func create_k1() (string, error) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
|
@ -8,45 +8,55 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
type card struct {
|
||||
card_id int
|
||||
card_guid string
|
||||
k0_auth_key string
|
||||
k1_decrypt_key string
|
||||
k2_cmac_key string
|
||||
k3 string
|
||||
k4 string
|
||||
db_uid string
|
||||
last_counter_value uint32
|
||||
lnurlw_request_timeout_sec int
|
||||
lnurlw_enable string
|
||||
tx_limit_sats int
|
||||
day_limit_sats int
|
||||
lnurlp_enable string
|
||||
email_address string
|
||||
email_enable string
|
||||
uid_privacy string
|
||||
one_time_code string
|
||||
card_name string
|
||||
allow_negative_balance string
|
||||
type Card struct {
|
||||
Card_id int
|
||||
Card_guid string
|
||||
K0_auth_key string
|
||||
K1_decrypt_key string
|
||||
K2_cmac_key string
|
||||
K3 string
|
||||
K4 string
|
||||
Db_uid string
|
||||
Last_counter_value uint32
|
||||
Lnurlw_request_timeout_sec int
|
||||
Lnurlw_enable string
|
||||
Tx_limit_sats int
|
||||
Day_limit_sats int
|
||||
Lnurlp_enable string
|
||||
Email_address string
|
||||
Email_enable string
|
||||
Uid_privacy string
|
||||
One_time_code string
|
||||
Card_name string
|
||||
Allow_negative_balance string
|
||||
}
|
||||
|
||||
type payment struct {
|
||||
card_payment_id int
|
||||
card_id int
|
||||
lnurlw_k1 string
|
||||
paid_flag string
|
||||
type Payment struct {
|
||||
Card_payment_id int
|
||||
Card_id int
|
||||
Lnurlw_k1 string
|
||||
Paid_flag string
|
||||
}
|
||||
|
||||
type transaction struct {
|
||||
card_id int
|
||||
tx_id int
|
||||
tx_type string
|
||||
tx_amount_msats int
|
||||
tx_time string
|
||||
type Transaction struct {
|
||||
Card_id int
|
||||
Tx_id int
|
||||
Tx_type string
|
||||
Tx_amount_msats int
|
||||
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
|
||||
|
||||
|
|
@ -65,11 +75,11 @@ func db_open() (*sql.DB, error) {
|
|||
return db, nil
|
||||
}
|
||||
|
||||
func db_get_setting(setting_name string) string {
|
||||
func Get_setting(setting_name string) string {
|
||||
|
||||
setting_value := ""
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
|
@ -86,11 +96,11 @@ func db_get_setting(setting_name string) string {
|
|||
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 {
|
||||
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';`
|
||||
row := db.QueryRow(sqlStatement, one_time_code)
|
||||
err = row.Scan(
|
||||
&c.k0_auth_key,
|
||||
&c.k2_cmac_key,
|
||||
&c.k3,
|
||||
&c.k4,
|
||||
&c.card_name,
|
||||
&c.uid_privacy)
|
||||
&c.K0_auth_key,
|
||||
&c.K2_cmac_key,
|
||||
&c.K3,
|
||||
&c.K4,
|
||||
&c.Card_name,
|
||||
&c.Uid_privacy)
|
||||
if err != nil {
|
||||
return &c, err
|
||||
}
|
||||
|
|
@ -120,11 +130,11 @@ func db_get_new_card(one_time_code string) (*card, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -141,11 +151,11 @@ func db_get_card_count_for_uid(uid string) (int, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -162,11 +172,11 @@ func db_get_card_count_for_name_lnurlp(name string) (int, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -183,10 +193,10 @@ func db_get_card_id_for_name(name string) (int, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -223,11 +233,11 @@ func db_get_card_id_for_r_hash(r_hash string) (int, error) {
|
|||
return card_id, nil
|
||||
}
|
||||
|
||||
func db_get_cards_blank_uid() ([]card, error) {
|
||||
func Get_cards_blank_uid() ([]Card, error) {
|
||||
|
||||
// open the database
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -249,12 +259,12 @@ func db_get_cards_blank_uid() ([]card, error) {
|
|||
|
||||
// prepare the results
|
||||
|
||||
var cards []card
|
||||
var cards []Card
|
||||
|
||||
// Loop through rows, using Scan to assign column data to struct fields.
|
||||
for rows.Next() {
|
||||
var c card
|
||||
err := rows.Scan(&c.card_id, &c.k2_cmac_key)
|
||||
var c Card
|
||||
err := rows.Scan(&c.Card_id, &c.K2_cmac_key)
|
||||
|
||||
if err != nil {
|
||||
return cards, err
|
||||
|
|
@ -271,8 +281,8 @@ func db_get_cards_blank_uid() ([]card, error) {
|
|||
return cards, nil
|
||||
}
|
||||
|
||||
func db_update_card_uid_ctr(card_id int, uid string, ctr uint32) error {
|
||||
db, err := db_open()
|
||||
func Update_card_uid_ctr(card_id int, uid string, ctr uint32) error {
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -294,11 +304,11 @@ func db_update_card_uid_ctr(card_id int, uid string, ctr uint32) error {
|
|||
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 {
|
||||
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';`
|
||||
row := db.QueryRow(sqlStatement, card_uid)
|
||||
err = row.Scan(
|
||||
&c.card_id,
|
||||
&c.k2_cmac_key,
|
||||
&c.db_uid,
|
||||
&c.last_counter_value,
|
||||
&c.lnurlw_request_timeout_sec,
|
||||
&c.lnurlw_enable,
|
||||
&c.tx_limit_sats,
|
||||
&c.day_limit_sats)
|
||||
&c.Card_id,
|
||||
&c.K2_cmac_key,
|
||||
&c.Db_uid,
|
||||
&c.Last_counter_value,
|
||||
&c.Lnurlw_request_timeout_sec,
|
||||
&c.Lnurlw_enable,
|
||||
&c.Tx_limit_sats,
|
||||
&c.Day_limit_sats)
|
||||
if err != nil {
|
||||
return &c, err
|
||||
}
|
||||
|
|
@ -325,11 +335,11 @@ func db_get_card_from_uid(card_uid string) (*card, error) {
|
|||
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 {
|
||||
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;`
|
||||
row := db.QueryRow(sqlStatement, card_id)
|
||||
err = row.Scan(
|
||||
&c.card_id,
|
||||
&c.k2_cmac_key,
|
||||
&c.db_uid,
|
||||
&c.last_counter_value,
|
||||
&c.lnurlw_request_timeout_sec,
|
||||
&c.lnurlw_enable,
|
||||
&c.tx_limit_sats,
|
||||
&c.day_limit_sats,
|
||||
&c.email_enable,
|
||||
&c.email_address,
|
||||
&c.card_name,
|
||||
&c.allow_negative_balance)
|
||||
&c.Card_id,
|
||||
&c.K2_cmac_key,
|
||||
&c.Db_uid,
|
||||
&c.Last_counter_value,
|
||||
&c.Lnurlw_request_timeout_sec,
|
||||
&c.Lnurlw_enable,
|
||||
&c.Tx_limit_sats,
|
||||
&c.Day_limit_sats,
|
||||
&c.Email_enable,
|
||||
&c.Email_address,
|
||||
&c.Card_name,
|
||||
&c.Allow_negative_balance)
|
||||
if err != nil {
|
||||
return &c, err
|
||||
}
|
||||
|
|
@ -361,9 +371,9 @@ func db_get_card_from_card_id(card_id int) (*card, error) {
|
|||
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 {
|
||||
return true, err
|
||||
}
|
||||
|
|
@ -383,9 +393,9 @@ func db_check_lnurlw_timeout(card_payment_id int) (bool, error) {
|
|||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -436,13 +446,13 @@ func db_insert_payment(card_id int, lnurlw_k1 string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func db_insert_receipt(
|
||||
func Insert_receipt(
|
||||
card_id int,
|
||||
ln_invoice string,
|
||||
r_hash_hex string,
|
||||
amount_msat int64) error {
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -468,8 +478,8 @@ func db_insert_receipt(
|
|||
return nil
|
||||
}
|
||||
|
||||
func db_update_receipt_state(r_hash_hex string, invoice_state string) error {
|
||||
db, err := db_open()
|
||||
func Update_receipt_state(r_hash_hex string, invoice_state string) error {
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -493,10 +503,10 @@ func db_update_receipt_state(r_hash_hex string, invoice_state string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func db_get_payment_k1(lnurlw_k1 string) (*payment, error) {
|
||||
p := payment{}
|
||||
func Get_payment_k1(lnurlw_k1 string) (*Payment, error) {
|
||||
p := Payment{}
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return &p, err
|
||||
}
|
||||
|
|
@ -506,9 +516,9 @@ func db_get_payment_k1(lnurlw_k1 string) (*payment, error) {
|
|||
` FROM card_payments WHERE lnurlw_k1=$1;`
|
||||
row := db.QueryRow(sqlStatement, lnurlw_k1)
|
||||
err = row.Scan(
|
||||
&p.card_payment_id,
|
||||
&p.card_id,
|
||||
&p.paid_flag)
|
||||
&p.Card_payment_id,
|
||||
&p.Card_id,
|
||||
&p.Paid_flag)
|
||||
if err != nil {
|
||||
return &p, err
|
||||
}
|
||||
|
|
@ -516,9 +526,9 @@ func db_get_payment_k1(lnurlw_k1 string) (*payment, error) {
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -540,9 +550,9 @@ func db_update_payment_invoice(card_payment_id int, ln_invoice string, amount_ms
|
|||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -564,9 +574,9 @@ func db_update_payment_paid(card_payment_id int) error {
|
|||
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 {
|
||||
return err
|
||||
|
|
@ -596,9 +606,9 @@ func db_update_payment_status(card_payment_id int, payment_status string, failur
|
|||
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 {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -620,10 +630,10 @@ func db_get_card_totals(card_id int) (int, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -655,12 +665,12 @@ func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) {
|
|||
|
||||
// prepare the results
|
||||
|
||||
var transactions []transaction
|
||||
var transactions []Transaction
|
||||
|
||||
// Loop through rows, using Scan to assign column data to struct fields.
|
||||
for rows.Next() {
|
||||
var t transaction
|
||||
err := rows.Scan(&t.card_id, &t.tx_id, &t.tx_type, &t.tx_amount_msats, &t.tx_time)
|
||||
var t Transaction
|
||||
err := rows.Scan(&t.Card_id, &t.Tx_id, &t.Tx_type, &t.Tx_amount_msats, &t.Tx_time)
|
||||
|
||||
if err != nil {
|
||||
return transactions, err
|
||||
|
|
@ -677,9 +687,9 @@ func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) {
|
|||
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 {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -707,11 +717,11 @@ func db_get_card_total_sats(card_id int) (int, error) {
|
|||
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
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
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"
|
||||
}
|
||||
|
||||
db, err := db_open()
|
||||
db, err := open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -777,11 +787,11 @@ func db_insert_card(one_time_code string, k0_auth_key string, k2_cmac_key string
|
|||
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 {
|
||||
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;`
|
||||
row := db.QueryRow(sqlStatement, card_name)
|
||||
err = row.Scan(
|
||||
&card_wipe_info.id,
|
||||
&card_wipe_info.uid,
|
||||
&card_wipe_info.k0,
|
||||
&card_wipe_info.k2,
|
||||
&card_wipe_info.k3,
|
||||
&card_wipe_info.k4)
|
||||
&card_wipe_info.Id,
|
||||
&card_wipe_info.Uid,
|
||||
&card_wipe_info.K0,
|
||||
&card_wipe_info.K2,
|
||||
&card_wipe_info.K3,
|
||||
&card_wipe_info.K4)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package email
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
|
@ -9,35 +9,36 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"strconv"
|
||||
"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 {
|
||||
log.Warn(err.Error())
|
||||
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 {
|
||||
log.Warn(err.Error())
|
||||
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 {
|
||||
log.Warn(err.Error())
|
||||
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 {
|
||||
log.Warn(err.Error())
|
||||
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
|
||||
|
||||
|
|
@ -56,9 +57,9 @@ func send_balance_email(recipient_email string, card_id int) {
|
|||
if i < email_max_txs {
|
||||
html_body_sb.WriteString(
|
||||
"<tr>" +
|
||||
"<td>" + tx.tx_time + "</td>" +
|
||||
"<td>" + tx.tx_type + "</td>" +
|
||||
"<td style='text-align:right'>" + strconv.Itoa(tx.tx_amount_msats/1000) + "</td>" +
|
||||
"<td>" + tx.Tx_time + "</td>" +
|
||||
"<td>" + tx.Tx_type + "</td>" +
|
||||
"<td style='text-align:right'>" + strconv.Itoa(tx.Tx_amount_msats/1000) + "</td>" +
|
||||
"</tr>")
|
||||
} else {
|
||||
html_body_sb.WriteString(
|
||||
|
|
@ -69,8 +70,8 @@ func send_balance_email(recipient_email string, card_id int) {
|
|||
"</tr>")
|
||||
}
|
||||
|
||||
text_body_sb.WriteString(tx.tx_type +
|
||||
" " + strconv.Itoa(tx.tx_amount_msats/1000))
|
||||
text_body_sb.WriteString(tx.Tx_type +
|
||||
" " + strconv.Itoa(tx.Tx_amount_msats/1000))
|
||||
}
|
||||
|
||||
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()
|
||||
text_body := text_body_sb.String()
|
||||
|
||||
send_email(recipient_email,
|
||||
Send_email(recipient_email,
|
||||
subject,
|
||||
html_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
|
||||
|
||||
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_secret := db_get_setting("AWS_SES_SECRET")
|
||||
sender := db_get_setting("AWS_SES_EMAIL_FROM")
|
||||
aws_ses_id := db.Get_setting("AWS_SES_ID")
|
||||
aws_ses_secret := db.Get_setting("AWS_SES_SECRET")
|
||||
sender := db.Get_setting("AWS_SES_EMAIL_FROM")
|
||||
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
Region: aws.String("us-east-1"),
|
||||
21
go.mod
21
go.mod
|
|
@ -16,9 +16,10 @@ 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/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/btcsuite/btcd v0.23.4 // 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/chaincfg/chainhash v1.0.1 // 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/wallet/txauthor v1.3.2 // 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/dcrec/secp256k1/v4 v4.1.0 // 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/dvyukov/go-fuzz v0.0.0-20220726122315-1d375ef9f9f6 // 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/jrick/logrotate v1.0.0 // 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/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/neutrino v0.14.2 // 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/tlv v1.0.3 // 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/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/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/nbd-wtf/ln-decodepay v1.11.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.14.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/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // 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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // 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/raft/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/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/otlptrace 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/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/proto/otlp v0.19.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/time v0.2.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/protobuf v1.28.1 // indirect
|
||||
gopkg.in/errgo.v1 v1.0.1 // indirect
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lnd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -20,6 +20,9 @@ import (
|
|||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"gopkg.in/macaroon.v2"
|
||||
|
||||
"github.com/boltcard/boltcard/db"
|
||||
"github.com/boltcard/boltcard/email"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
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))
|
||||
|
||||
connection := getGrpcConn(
|
||||
db_get_setting("LN_HOST"),
|
||||
db.Get_setting("LN_HOST"),
|
||||
ln_port,
|
||||
db_get_setting("LN_TLS_FILE"),
|
||||
db_get_setting("LN_MACAROON_FILE"))
|
||||
db.Get_setting("LN_TLS_FILE"),
|
||||
db.Get_setting("LN_MACAROON_FILE"))
|
||||
|
||||
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
|
||||
|
||||
func monitor_invoice_state(r_hash []byte) {
|
||||
func Monitor_invoice_state(r_hash []byte) {
|
||||
|
||||
// SubscribeSingleInvoice
|
||||
|
||||
// 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 {
|
||||
log.Warn(err)
|
||||
return
|
||||
}
|
||||
|
||||
connection := getGrpcConn(
|
||||
db_get_setting("LN_HOST"),
|
||||
db.Get_setting("LN_HOST"),
|
||||
ln_port,
|
||||
db_get_setting("LN_TLS_FILE"),
|
||||
db_get_setting("LN_MACAROON_FILE"))
|
||||
db.Get_setting("LN_TLS_FILE"),
|
||||
db.Get_setting("LN_MACAROON_FILE"))
|
||||
|
||||
i_client := invoicesrpc.NewInvoicesClient(connection)
|
||||
|
||||
|
|
@ -156,14 +159,14 @@ func monitor_invoice_state(r_hash []byte) {
|
|||
"invoice_state": invoice_state,
|
||||
}).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()
|
||||
|
||||
// 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 {
|
||||
log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err)
|
||||
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")
|
||||
|
||||
c, err := db_get_card_from_card_id(card_id)
|
||||
c, err := db.Get_card_from_card_id(card_id)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"r_hash": hex.EncodeToString(r_hash)}).Warn(err)
|
||||
return
|
||||
}
|
||||
|
||||
if c.email_enable != "Y" {
|
||||
if c.Email_enable != "Y" {
|
||||
log.Debug("email is not enabled for the card")
|
||||
return
|
||||
}
|
||||
|
||||
go send_balance_email(c.email_address, card_id)
|
||||
go email.Send_balance_email(c.Email_address, card_id)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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 {
|
||||
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
|
||||
return
|
||||
}
|
||||
|
||||
connection := getGrpcConn(
|
||||
db_get_setting("LN_HOST"),
|
||||
db.Get_setting("LN_HOST"),
|
||||
ln_port,
|
||||
db_get_setting("LN_TLS_FILE"),
|
||||
db_get_setting("LN_MACAROON_FILE"))
|
||||
db.Get_setting("LN_TLS_FILE"),
|
||||
db.Get_setting("LN_MACAROON_FILE"))
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
|
||||
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)
|
||||
if err != nil {
|
||||
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 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 {
|
||||
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
|
||||
return
|
||||
|
|
@ -271,7 +274,7 @@ func pay_invoice(card_payment_id int, invoice string) {
|
|||
|
||||
// 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 {
|
||||
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
|
||||
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")
|
||||
|
||||
c, err := db_get_card_from_card_id(card_id)
|
||||
c, err := db.Get_card_from_card_id(card_id)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"card_payment_id": card_payment_id}).Warn(err)
|
||||
return
|
||||
}
|
||||
|
||||
if c.email_enable != "Y" {
|
||||
if c.Email_enable != "Y" {
|
||||
log.Debug("email is not enabled for the card")
|
||||
return
|
||||
}
|
||||
|
||||
go send_balance_email(c.email_address, card_id)
|
||||
go email.Send_balance_email(c.Email_address, card_id)
|
||||
|
||||
return
|
||||
}
|
||||
12
lndhub/lndhub.go
Normal file
12
lndhub/lndhub.go
Normal 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) {
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lnurlp
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
|
@ -6,10 +6,13 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"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) {
|
||||
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" {
|
||||
func Callback(w http.ResponseWriter, r *http.Request) {
|
||||
if db.Get_setting("FUNCTION_LNURLP") != "ENABLE" {
|
||||
log.Debug("LNURLp function is not enabled")
|
||||
return
|
||||
}
|
||||
|
|
@ -17,10 +20,10 @@ func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
|
|||
name := mux.Vars(r)["name"]
|
||||
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 {
|
||||
log.Info("card name not found")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -33,38 +36,38 @@ func lnurlp_callback(w http.ResponseWriter, r *http.Request) {
|
|||
"req.Host": r.Host,
|
||||
}).Info("lnurlp_callback")
|
||||
|
||||
domain := db_get_setting("HOST_DOMAIN")
|
||||
domain := db.Get_setting("HOST_DOMAIN")
|
||||
if r.Host != domain {
|
||||
log.Warn("wrong host domain")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
amount_msat, err := strconv.ParseInt(amount, 10, 64)
|
||||
if err != nil {
|
||||
log.Warn("amount is not a valid integer")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
amount_sat := amount_msat / 1000
|
||||
|
||||
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 {
|
||||
log.Warn("could not add_invoice")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
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 {
|
||||
log.Warn(err)
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
go monitor_invoice_state(r_hash)
|
||||
go lnd.Monitor_invoice_state(r_hash)
|
||||
|
||||
log.Debug("sending 'status OK' response")
|
||||
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
package main
|
||||
package lnurlp
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"github.com/boltcard/boltcard/db"
|
||||
"github.com/boltcard/boltcard/resp_err"
|
||||
)
|
||||
|
||||
func lnurlp_response(w http.ResponseWriter, r *http.Request) {
|
||||
if db_get_setting("FUNCTION_LNURLP") != "ENABLE" {
|
||||
func Response(w http.ResponseWriter, r *http.Request) {
|
||||
if db.Get_setting("FUNCTION_LNURLP") != "ENABLE" {
|
||||
log.Debug("LNURLp function is not enabled")
|
||||
return
|
||||
}
|
||||
|
|
@ -23,25 +25,25 @@ func lnurlp_response(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// look up domain setting (HOST_DOMAIN)
|
||||
|
||||
domain := db_get_setting("HOST_DOMAIN")
|
||||
domain := db.Get_setting("HOST_DOMAIN")
|
||||
if r.Host != domain {
|
||||
log.Warn("wrong host domain")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Warn("could not get card count for name")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
if card_count != 1 {
|
||||
log.Info("not one enabled card with that name")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
231
lnurlw/lnurlw_callback.go
Normal file
231
lnurlw/lnurlw_callback.go
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lnurlw
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
|
@ -9,9 +9,12 @@ import (
|
|||
"os"
|
||||
"strconv"
|
||||
"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"`
|
||||
Callback string `json:"callback"`
|
||||
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[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 {
|
||||
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
|
||||
// get possible card records from the database
|
||||
|
||||
cards, err := db_get_cards_blank_uid()
|
||||
cards, err := db.Get_cards_blank_uid()
|
||||
|
||||
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
|
||||
|
|
@ -85,7 +88,7 @@ func setup_card_record(uid string, ctr uint32, uid_bin []byte, ctr_bin []byte, c
|
|||
for i, card := range cards {
|
||||
// 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 {
|
||||
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 {
|
||||
log.WithFields(log.Fields{
|
||||
"i": i,
|
||||
"card.card_id": card.card_id,
|
||||
"card.k2_cmac_key": card.k2_cmac_key,
|
||||
"card.card_id": card.Card_id,
|
||||
"card.k2_cmac_key": card.K2_cmac_key,
|
||||
}).Info("cmac match found")
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
|
|
@ -150,7 +153,7 @@ func parse_request(req *http.Request) (int, error) {
|
|||
|
||||
// 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)
|
||||
|
||||
|
|
@ -158,7 +161,7 @@ func parse_request(req *http.Request) (int, error) {
|
|||
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 {
|
||||
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")
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
c, err := db_get_card_from_uid(uid_str)
|
||||
c, err := db.Get_card_from_uid(uid_str)
|
||||
|
||||
if err != nil {
|
||||
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
|
||||
|
||||
if c.lnurlw_enable != "Y" {
|
||||
if c.Lnurlw_enable != "Y" {
|
||||
return 0, errors.New("card lnurlw enable is not set to Y")
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return 0, err
|
||||
|
|
@ -229,7 +232,7 @@ func parse_request(req *http.Request) (int, error) {
|
|||
|
||||
// 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 {
|
||||
return 0, err
|
||||
|
|
@ -239,17 +242,17 @@ func parse_request(req *http.Request) (int, error) {
|
|||
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 {
|
||||
log.Warn("wrong host domain")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -257,25 +260,25 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
if err != nil {
|
||||
log.Debug(err.Error())
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
lnurlw_k1, err := create_k1()
|
||||
lnurlw_k1, err := crypto.Create_k1()
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Warn(err.Error())
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -286,25 +289,25 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
|
|||
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)
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
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)
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
response := ResponseData{}
|
||||
response.Tag = "withdrawRequest"
|
||||
response.Callback = lnurlw_cb_url
|
||||
response.LnurlwK1 = lnurlw_k1
|
||||
|
|
@ -316,7 +319,7 @@ func lnurlw_response(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -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
29
main.go
|
|
@ -5,24 +5,15 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"time"
|
||||
"github.com/boltcard/boltcard/db"
|
||||
"github.com/boltcard/boltcard/lnurlw"
|
||||
"github.com/boltcard/boltcard/lnurlp"
|
||||
)
|
||||
|
||||
func write_error(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_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)
|
||||
}
|
||||
var router = mux.NewRouter()
|
||||
|
||||
func main() {
|
||||
log_level := db_get_setting("LOG_LEVEL")
|
||||
log_level := db.Get_setting("LOG_LEVEL")
|
||||
|
||||
switch log_level {
|
||||
case "DEBUG":
|
||||
|
|
@ -49,11 +40,11 @@ func main() {
|
|||
// createboltcard
|
||||
external_router.Path("/new").Methods("GET").HandlerFunc(new_card_request)
|
||||
// lnurlw for pos
|
||||
external_router.Path("/ln").Methods("GET").HandlerFunc(lnurlw_response)
|
||||
external_router.Path("/cb").Methods("GET").HandlerFunc(lnurlw_callback)
|
||||
external_router.Path("/ln").Methods("GET").HandlerFunc(lnurlw.Response)
|
||||
external_router.Path("/cb").Methods("GET").HandlerFunc(lnurlw.Callback)
|
||||
// lnurlp for lightning address
|
||||
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("/.well-known/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp.Response)
|
||||
external_router.Path("/lnurlp/{name}").Methods("GET").HandlerFunc(lnurlp.Callback)
|
||||
|
||||
// internal API
|
||||
// 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("/wipeboltcard").Methods("GET").HandlerFunc(wipeboltcard)
|
||||
|
||||
port := db_get_setting("HOST_PORT")
|
||||
port := db.Get_setting("HOST_PORT")
|
||||
if port == "" {
|
||||
port = "9000"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import (
|
|||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"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"]
|
||||
if !ok || len(params_a[0]) < 1 {
|
||||
log.Debug("a not found")
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -55,33 +57,33 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
lnurlw_base := "lnurlw://" + req.Host + "/ln"
|
||||
|
||||
c, err := db_get_new_card(a)
|
||||
c, err := db.Get_new_card(a)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
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
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
k1_decrypt_key := db_get_setting("AES_DECRYPT_KEY")
|
||||
k1_decrypt_key := db.Get_setting("AES_DECRYPT_KEY")
|
||||
|
||||
response := NewCardResponse{}
|
||||
response.PROTOCOL_NAME = "create_bolt_card_response"
|
||||
response.PROTOCOL_VERSION = 2
|
||||
response.CARD_NAME = c.card_name
|
||||
response.CARD_NAME = c.Card_name
|
||||
response.LNURLW_BASE = lnurlw_base
|
||||
response.K0 = c.k0_auth_key
|
||||
response.K0 = c.K0_auth_key
|
||||
response.K1 = k1_decrypt_key
|
||||
response.K2 = c.k2_cmac_key
|
||||
response.K3 = c.k3
|
||||
response.K4 = c.k4
|
||||
response.UID_PRIVACY = c.uid_privacy
|
||||
response.K2 = c.K2_cmac_key
|
||||
response.K3 = c.K3
|
||||
response.K4 = c.K4
|
||||
response.UID_PRIVACY = c.Uid_privacy
|
||||
|
||||
log.SetFormatter(&log.JSONFormatter{
|
||||
DisableHTMLEscape: true,
|
||||
|
|
@ -89,7 +91,7 @@ func new_card_request(w http.ResponseWriter, req *http.Request) {
|
|||
jsonData, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
log.Warn(err)
|
||||
write_error(w)
|
||||
resp_err.Write(w)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
19
resp_err/resp_err.go
Normal file
19
resp_err/resp_err.go
Normal 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)
|
||||
}
|
||||
|
|
@ -12,10 +12,10 @@ echo Continue? "(y or n)"
|
|||
read x
|
||||
|
||||
if [ "$x" = "y" ]; then
|
||||
psql postgres -f create_db_init.sql
|
||||
psql postgres -f create_db.sql
|
||||
psql postgres -f create_db_user.sql
|
||||
psql postgres -f settings.sql
|
||||
psql postgres -f sql/create_db_init.sql
|
||||
psql postgres -f sql/create_db.sql
|
||||
psql postgres -f sql/create_db_user.sql
|
||||
psql postgres -f sql/settings.sql
|
||||
echo Database created
|
||||
else
|
||||
echo No action
|
||||
|
|
|
|||
|
|
@ -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_EMAIL_FROM', '');
|
||||
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', '');
|
||||
|
|
@ -4,23 +4,15 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"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) {
|
||||
if db_get_setting("FUNCTION_INTERNAL_API") != "ENABLE" {
|
||||
if db.Get_setting("FUNCTION_INTERNAL_API") != "ENABLE" {
|
||||
msg := "wipeboltcard: internal API function is not enabled"
|
||||
log.Debug(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -31,24 +23,24 @@ func wipeboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
if card_name == "" {
|
||||
msg := "wipeboltcard: the card name must be set"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
msg := "the card name does not exist in the database"
|
||||
log.Warn(msg)
|
||||
write_error_message(w, msg)
|
||||
resp_err.Write_message(w, msg)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Warn(err.Error())
|
||||
return
|
||||
|
|
@ -63,13 +55,13 @@ func wipeboltcard(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
jsonData := `{"status":"OK",` +
|
||||
`"action": "wipe",` +
|
||||
`"id": ` + strconv.Itoa(card_wipe_info_values.id) + `,` +
|
||||
`"k0": "` + card_wipe_info_values.k0 + `",` +
|
||||
`"k1": "` + card_wipe_info_values.k1 + `",` +
|
||||
`"k2": "` + card_wipe_info_values.k2 + `",` +
|
||||
`"k3": "` + card_wipe_info_values.k3 + `",` +
|
||||
`"k4": "` + card_wipe_info_values.k4 + `",` +
|
||||
`"uid": "` + card_wipe_info_values.uid + `",` +
|
||||
`"id": ` + strconv.Itoa(card_wipe_info_values.Id) + `,` +
|
||||
`"k0": "` + card_wipe_info_values.K0 + `",` +
|
||||
`"k1": "` + card_wipe_info_values.K1 + `",` +
|
||||
`"k2": "` + card_wipe_info_values.K2 + `",` +
|
||||
`"k3": "` + card_wipe_info_values.K3 + `",` +
|
||||
`"k4": "` + card_wipe_info_values.K4 + `",` +
|
||||
`"uid": "` + card_wipe_info_values.Uid + `",` +
|
||||
`"version": 1}`
|
||||
|
||||
// log the response
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue