add max txs setting for email, add setting to stop negative card balance
This commit is contained in:
parent
94267c47cf
commit
9c5149e373
4 changed files with 99 additions and 62 deletions
|
|
@ -25,6 +25,7 @@ CREATE TABLE cards (
|
|||
one_time_code CHAR(32) NOT NULL DEFAULT '',
|
||||
one_time_code_expiry TIMESTAMPTZ DEFAULT NOW() + INTERVAL '1 DAY',
|
||||
one_time_code_used CHAR(1) NOT NULL DEFAULT 'Y',
|
||||
allow_negative_balance CHAR(1) NOT NULL DEFAULT 'N',
|
||||
PRIMARY KEY(card_id)
|
||||
);
|
||||
|
||||
|
|
|
|||
21
database.go
21
database.go
|
|
@ -27,6 +27,7 @@ type card struct {
|
|||
email_enable string
|
||||
one_time_code string
|
||||
card_name string
|
||||
allow_negative_balance string
|
||||
}
|
||||
|
||||
type payment struct {
|
||||
|
|
@ -65,7 +66,7 @@ func db_open() (*sql.DB, error) {
|
|||
|
||||
func db_get_new_card(one_time_code string) (*card, error) {
|
||||
|
||||
c :=card{}
|
||||
c := card{}
|
||||
|
||||
db, err := db_open()
|
||||
if err != nil {
|
||||
|
|
@ -314,8 +315,8 @@ func db_get_card_from_card_id(card_id int) (*card, error) {
|
|||
sqlStatement := `SELECT card_id, k2_cmac_key, uid, ` +
|
||||
`last_counter_value, lnurlw_request_timeout_sec, ` +
|
||||
`lnurlw_enable, tx_limit_sats, day_limit_sats, ` +
|
||||
`email_enable, email_address, card_name ` +
|
||||
`FROM cards WHERE card_id=$1;`
|
||||
`email_enable, email_address, card_name, ` +
|
||||
`allow_negative_balance FROM cards WHERE card_id=$1;`
|
||||
row := db.QueryRow(sqlStatement, card_id)
|
||||
err = row.Scan(
|
||||
&c.card_id,
|
||||
|
|
@ -328,7 +329,8 @@ func db_get_card_from_card_id(card_id int) (*card, error) {
|
|||
&c.day_limit_sats,
|
||||
&c.email_enable,
|
||||
&c.email_address,
|
||||
&c.card_name)
|
||||
&c.card_name,
|
||||
&c.allow_negative_balance)
|
||||
if err != nil {
|
||||
return &c, err
|
||||
}
|
||||
|
|
@ -595,7 +597,7 @@ func db_get_card_totals(card_id int) (int, error) {
|
|||
return day_total_sats, nil
|
||||
}
|
||||
|
||||
func db_get_card_txs(card_id int) ([]transaction, error) {
|
||||
func db_get_card_txs(card_id int, max_txs int) ([]transaction, error) {
|
||||
// open the database
|
||||
|
||||
db, err := db_open()
|
||||
|
|
@ -608,19 +610,19 @@ func db_get_card_txs(card_id int) ([]transaction, error) {
|
|||
|
||||
// query the database
|
||||
|
||||
//TODO: LIMIT 10 + COUNT
|
||||
sqlStatement := `SELECT card_id, ` +
|
||||
`card_payments.card_payment_id AS tx_id, 'payment' AS tx_type, ` +
|
||||
`amount_msats as tx_amount_msats, ` +
|
||||
`TO_CHAR(payment_status_time, 'DD/MM/YYYY HH:MI:SS') AS tx_time ` +
|
||||
`FROM card_payments WHERE card_id = $1 AND payment_status != 'FAILED' ` +
|
||||
`AND payment_status != '' ` +
|
||||
`AND amount_msats != 0 UNION SELECT card_id, card_receipts.card_receipt_id AS tx_id, ` +
|
||||
`'receipt' AS tx_type, amount_msats as tx_amount_msats, ` +
|
||||
`TO_CHAR(receipt_status_time, 'DD/MM/YYYY HH:MI:SS') AS tx_time ` +
|
||||
`FROM card_receipts WHERE card_id = $1 ` +
|
||||
`AND receipt_status = 'SETTLED' ORDER BY tx_time DESC`
|
||||
`AND receipt_status = 'SETTLED' ORDER BY tx_time DESC LIMIT $2`
|
||||
|
||||
rows, err := db.Query(sqlStatement, card_id)
|
||||
rows, err := db.Query(sqlStatement, card_id, max_txs)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -652,7 +654,7 @@ func db_get_card_txs(card_id int) ([]transaction, error) {
|
|||
return transactions, nil
|
||||
}
|
||||
|
||||
func db_get_card_total(card_id int) (int, error) {
|
||||
func db_get_card_total_sats(card_id int) (int, error) {
|
||||
|
||||
db, err := db_open()
|
||||
if err != nil {
|
||||
|
|
@ -665,6 +667,7 @@ func db_get_card_total(card_id int) (int, error) {
|
|||
`card_payments.card_payment_id AS tx_id, 'payment' AS tx_type, ` +
|
||||
`-amount_msats as tx_amount_msats, payment_status_time AS tx_time ` +
|
||||
`FROM card_payments WHERE card_id = $1 AND payment_status != 'FAILED' ` +
|
||||
`AND payment_status != '' ` +
|
||||
`AND amount_msats != 0 UNION SELECT card_id, card_receipts.card_receipt_id AS tx_id, ` +
|
||||
`'receipt' AS tx_type, amount_msats as tx_amount_msats, ` +
|
||||
`receipt_status_time AS tx_time FROM card_receipts WHERE card_id = $1 ` +
|
||||
|
|
|
|||
25
email.go
25
email.go
|
|
@ -20,13 +20,19 @@ func send_balance_email(recipient_email string, card_id int) {
|
|||
return
|
||||
}
|
||||
|
||||
card_total_sats, err := db_get_card_total(card_id)
|
||||
card_total_sats, err := db_get_card_total_sats(card_id)
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
txs, err := db_get_card_txs(card_id)
|
||||
email_max_txs, err := strconv.Atoi(os.Getenv("EMAIL_MAX_TXS"))
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
txs, err := db_get_card_txs(card_id, email_max_txs+1)
|
||||
if err != nil {
|
||||
log.Warn(err.Error())
|
||||
return
|
||||
|
|
@ -46,17 +52,26 @@ func send_balance_email(recipient_email string, card_id int) {
|
|||
html_body_sb.WriteString("<h3>transactions</h3><table><tr><th>date</th><th>action</th><th>amount</th>")
|
||||
text_body_sb.WriteString("transactions\n\n")
|
||||
|
||||
for _, tx := range txs {
|
||||
for i, tx := range txs {
|
||||
|
||||
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 style='text-align:right'>" + strconv.Itoa(tx.tx_amount_msats/1000) + "</td>" +
|
||||
"</tr>")
|
||||
} else {
|
||||
html_body_sb.WriteString(
|
||||
"<tr>" +
|
||||
"<td style='text-align:center'> ... </td>" +
|
||||
"<td style='text-align:center'> ... </td>" +
|
||||
"<td style='text-align:center'> ... </td>" +
|
||||
"</tr>")
|
||||
}
|
||||
|
||||
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>")
|
||||
|
|
|
|||
|
|
@ -120,6 +120,24 @@ func lnurlw_callback(w http.ResponseWriter, req *http.Request) {
|
|||
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
|
||||
|
|
@ -138,7 +156,7 @@ func lnurlw_callback(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
go pay_invoice(p.card_payment_id, param_pr)
|
||||
|
||||
log.Debug("sending 'status OK' response");
|
||||
log.Debug("sending 'status OK' response")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue