commit 1be9494e18e25afe05871c1716816454e2869c37
parent 56521ed08941cbcbc59180eff4148cf765ba5c59
Author: Kristaps Kaupe <kristaps@blogiem.lv>
Date: Thu, 14 Jul 2022 10:12:58 +0300
Multiple currency support
Co-authored-by: Eddie Pease <eddiepease@tutanota.com>
Diffstat:
8 files changed, 51 insertions(+), 15 deletions(-)
diff --git a/config.py b/config.py
@@ -79,10 +79,23 @@ for method_name in config["payment_methods"]:
raise KeyError("Mising {}: config {}".format(method_name, "xpub"))
else:
- Exception("Unknown payment method: {}".format(method_name))
+ raise Exception("Unknown payment method: {}".format(method_name))
payment_methods.append(method_config)
+supported_currencies = get_opt("supported_currencies", ["USD"])
+if "BTC" in supported_currencies:
+ supported_currencies.append("sats")
+
+base_currency = get_opt("base_currency", "USD")
+if base_currency not in supported_currencies:
+ raise Exception("base_currency must be one of supported_currencies")
+
+currency_provider = get_opt("currency_provider", "COINGECKO")
+if currency_provider not in ["COINDESK","COINGECKO"]:
+ raise Exception("Unsupported currency price feed provider: {}".format(
+ currency_provider))
+
host = get_opt("host", "127.0.0.1")
api_key_path = get_opt("api_key_path", "SatSale_API_key")
tunnel_host = get_opt("tunnel_host", None)
@@ -95,8 +108,6 @@ payment_timeout = get_opt("payment_timeout", 60 * 60)
required_confirmations = get_opt("required_confirmations", 2)
connection_attempts = get_opt("connection_attempts", 3)
redirect = get_opt("redirect", "https://github.com/nickfarrow/satsale")
-base_currency = get_opt("base_currency", "USD")
-currency_provider = get_opt("currency_provider", "COINGECKO")
bitcoin_rate_multiplier = get_opt("bitcoin_rate_multiplier", 1.00)
allowed_underpay_amount = get_opt("allowed_underpay_amount", 0.00000001)
liquid_address = get_opt("liquid_address", None)
diff --git a/config.toml b/config.toml
@@ -91,8 +91,9 @@ loglevel = "DEBUG"
redirect = "https://github.com/nickfarrow/satsale"
# Currency and exchange rate provider
-base_currency = "USD"
-currency_provider = "COINGECKO" # Supported: COINDESK | COINGECKO
+supported_currencies = ["BTC", "USD"] # Currencies to display on donation dropdown
+base_currency = "USD" # Selection default for that dropdown
+currency_provider = "COINGECKO" # Supported: COINDESK | COINGECKO
# Multiplier added on top of BTC / fiat exchange rate. Allows to give discount or add extra commission.
# Values above 1.00 gives discount, values below add extra comission.
diff --git a/payments/database.py b/payments/database.py
@@ -49,7 +49,13 @@ def migrate_database(name="database.db"):
conn.execute("CREATE TABLE addresses (n INTEGER, address TEXT, xpub TEXT)")
_set_database_schema_version(2)
- #if schema_version < 2:
+ if schema_version < 3:
+ _log_migrate_database(2, 3, "Adding fiat currency column to payments table")
+ with sqlite3.connect(name) as conn:
+ conn.execute("ALTER TABLE payments ADD fiat_currency TEXT")
+ _set_database_schema_version(3)
+
+ #if schema_version < 4:
# do next migration
new_version = _get_database_schema_version(name)
@@ -65,9 +71,10 @@ def write_to_database(invoice, name="database.db"):
with sqlite3.connect(name) as conn:
cur = conn.cursor()
cur.execute(
- "INSERT INTO payments (uuid,fiat_value,btc_value,method,address,time,webhook,rhash) VALUES (?,?,?,?,?,?,?,?)",
+ "INSERT INTO payments (uuid,fiat_currency,fiat_value,btc_value,method,address,time,webhook,rhash) VALUES (?,?,?,?,?,?,?,?,?)",
(
invoice["uuid"],
+ invoice["fiat_currency"],
invoice["fiat_value"],
invoice["btc_value"],
invoice["method"],
diff --git a/payments/price_feed.py b/payments/price_feed.py
@@ -56,6 +56,11 @@ def get_price(currency, currency_provider=config.currency_provider, bitcoin_rate
def get_btc_value(base_amount, currency):
+ if currency == "BTC":
+ return float(base_amount)
+ elif currency == "sats":
+ return float(base_amount) / 10**8
+
price = get_price(currency)
if price is not None:
diff --git a/satsale.py b/satsale.py
@@ -57,13 +57,14 @@ database.migrate_database()
@app.route("/")
def index():
params = dict(request.args)
- params["currency"] = config.base_currency
+ params["supported_currencies"] = config.supported_currencies
+ params["base_currency"] = config.base_currency
params["node_info"] = config.node_info
headers = {"Content-Type": "text/html"}
return make_response(render_template("donate.html", params=params), 200, headers)
-# /pay is the main page for initiating a payment, takes a GET request with ?amount=
+# /pay is the main page for initiating a payment, takes a GET request with ?amount=[x]¤cy=[x]
@app.route("/pay")
def pay():
params = dict(request.args)
@@ -92,6 +93,7 @@ invoice_model = api.model(
"invoice",
{
"uuid": fields.String(),
+ "fiat_currency": fields.String(),
"fiat_value": fields.Float(),
"btc_value": fields.Float(),
"method": fields.String(),
@@ -115,7 +117,8 @@ status_model = api.model(
@api.doc(
params={
- "amount": "An amount in `config.base_currency`.",
+ "amount": "An amount.",
+ "currency": "(Opional) Currency units of the amount (defaults to `config.base_currency`).",
"method": "(Optional) Specify a payment method: `bitcoind` for onchain, `lnd` for lightning).",
"w_url": "(Optional) Specify a webhook url to call after successful payment. Currently only supports WooCommerce plugin.",
}
@@ -127,9 +130,11 @@ class create_payment(Resource):
@api.response(522, "Error fetching address from node")
def get(self):
"Create Payment"
- """Initiate a new payment with an `amount` in `config.base_currecy`."""
+ """Initiate a new payment with an `amount` in specified currency."""
base_amount = request.args.get("amount")
- currency = config.base_currency
+ currency = request.args.get("currency")
+ if currency is None:
+ currency = config.base_currency
payment_method = request.args.get("method")
if payment_method is None:
payment_method = enabled_payment_methods[0]
@@ -160,6 +165,7 @@ class create_payment(Resource):
invoice = {
"uuid": str(uuid.uuid4().hex),
+ "fiat_currency": currency,
"fiat_value": base_amount,
"btc_value": btc_amount_format(btc_value),
"method": payment_method,
diff --git a/static/satsale.js b/static/satsale.js
@@ -2,7 +2,7 @@
function payment(payment_data) {
$('document').ready(function(){
var payment_uuid;
- var invoiceData = {amount: payment_data.amount, method: payment_data.method};
+ var invoiceData = {amount: payment_data.amount, currency: payment_data.currency, method: payment_data.method};
// If a webhook URL is provided (woocommerce)
if (payment_data.w_url) {
diff --git a/static/style.css b/static/style.css
@@ -101,11 +101,12 @@ h1 {
transform: scale(3);
}
-#amountenter {
+.donate_input {
background-color: black;
color: white;
font-size: 16px;
border-radius:5px;
+ display: inline;
}
#qrImage {
diff --git a/templates/donate.html b/templates/donate.html
@@ -47,7 +47,12 @@
<form id="pay" action='/pay'>
<div style="display:block;text-align: center;">
<h2 style="margin:0;">Amount:
- <input id="amountenter" style="display:inline" size="4" type="float" name="amount" id="amount" placeholder="{{ params.currency }}" required>
+ <input class="donate_input" size="4" type="float" name="amount" placeholder="0" required>
+ <select class="donate_input" name="currency" required>
+ {% for currency_option in params.supported_currencies %}
+ <option value="{{currency_option}}"{% if currency_option == params.base_currency %} selected{% endif %}>{{currency_option}}</option>
+ {% endfor %}
+ </select>
</h2>
<br>
<input class="button button1" style="width:40%" type="submit" value="Donate">