commit 58a55b8ed9fdd1d4b2ba949226d6d854cf058539
parent 9b4a7e71df64ce5b2fb2ba1b8c5c71fdea990d43
Author: ingolevin <34458804+ingolevin@users.noreply.github.com>
Date: Fri, 19 Nov 2021 11:53:02 +0100
Add ability to change base currency (#24)
* add extra currency provider
* WIP: currency conversion working
* Global replacement of variable names
- From: dollar_amount|value To: fiat_amount|value
- Note. The database.db has to be recreated.
If there existed a database.db prior to that,
this will be a breaking change
* Use rate_float instead of rate from coindesk response json
* Add generic get_price function + variable renaming
* Dynymically replace currency symbol in donate.html based on config
* Moving base_currency upwards in file
* Fix centering
* Fix spacing in config file
* Refactor payment_provider constants + reformat
- Move payment_provider constants into a separate function/disctionary get_payment_provider_constants()
- Added basic unittests: test_get_currency_provider_constants, test_get_price
- Apply PEP8 Autoformatting to price_feed.py
* Remove tests: Add these great tests to another PR (ive got some in progress too)
* remove currency docs arg for create_payment (again oops)
Co-authored-by: Ingo Levin <ingo.levin@absolute-bi.com>
Co-authored-by: nickfarrow <nicholas.w.farrow@gmail.com>
Diffstat:
6 files changed, 47 insertions(+), 23 deletions(-)
diff --git a/config.py b/config.py
@@ -67,6 +67,10 @@ connection_attempts = 3
# Generic redirect url after payment
redirect = "https://github.com/nickfarrow/satsale"
+# Currency and exchange rate provider
+base_currency = "USD"
+currency_provider = "COINGECKO" # Supported: COINDESK | COINGECKO
+
# Lightning Address e.g. name@you.satsale.domain (think this requires https url)
lightning_address = None
lightning_address_comment = None # Defaults to: "Thank you for your support <3"
@@ -80,3 +84,4 @@ liquid_address = None
# DO NOT CHANGE THIS TO TRUE UNLESS YOU WANT ALL PAYMENTS TO AUTOMATICALLY
# BE CONSIDERED AS PAID.
free_mode = False
+
diff --git a/gateways/woo_webhook.py b/gateways/woo_webhook.py
@@ -11,7 +11,7 @@ def hook(satsale_secret, invoice, order_id):
# Calculate a secret that is required to send back to the
# woocommerce gateway, proving we did not modify id nor amount.
- secret_seed = str(int(100 * float(invoice["dollar_value"]))).encode("utf-8")
+ secret_seed = str(int(100 * float(invoice["fiat_value"]))).encode("utf-8")
print("Secret seed: {}".format(secret_seed))
secret = hmac.new(key, secret_seed, hashlib.sha256).hexdigest()
diff --git a/payments/database.py b/payments/database.py
@@ -5,7 +5,7 @@ def create_database(name="database.db"):
with sqlite3.connect("database.db") as conn:
print("Creating new database.db...")
conn.execute(
- "CREATE TABLE payments (uuid TEXT, dollar_value DECIMAL, btc_value DECIMAL, method TEXT, address TEXT, time DECIMAL, webhook TEXT, rhash TEXT)"
+ "CREATE TABLE payments (uuid TEXT, fiat_value DECIMAL, btc_value DECIMAL, method TEXT, address TEXT, time DECIMAL, webhook TEXT, rhash TEXT)"
)
return
@@ -14,10 +14,10 @@ def write_to_database(invoice, name="database.db"):
with sqlite3.connect("database.db") as conn:
cur = conn.cursor()
cur.execute(
- "INSERT INTO payments (uuid,dollar_value,btc_value,method,address,time,webhook,rhash) VALUES (?,?,?,?,?,?,?,?)",
+ "INSERT INTO payments (uuid,fiat_value,btc_value,method,address,time,webhook,rhash) VALUES (?,?,?,?,?,?,?,?)",
(
invoice["uuid"],
- invoice["dollar_value"],
+ invoice["fiat_value"],
invoice["btc_value"],
invoice["method"],
invoice["address"],
diff --git a/payments/price_feed.py b/payments/price_feed.py
@@ -3,14 +3,31 @@ import requests
import config
-def get_price(currency):
- price_feed = "https://api.coindesk.com/v1/bpi/currentprice.json"
- r = requests.get(price_feed)
+def get_currency_provider(currency, currency_provider):
+ # Define some currency_provider-specific settings
+ if currency_provider == "COINDESK":
+ return {
+ "price_feed": "https://api.coindesk.com/v1/bpi/currentprice.json",
+ "result_root": "bpi",
+ "value_attribute": "rate_float",
+ "ticker": currency.upper(),
+ }
+ else:
+ return {
+ "price_feed": "https://api.coingecko.com/api/v3/exchange_rates",
+ "result_root": "rates",
+ "value_attribute": "value",
+ "ticker": currency.lower(),
+ }
+
+def get_price(currency, currency_provider=config.currency_provider):
+ provider = get_currency_provider(currency, currency_provider)
for i in range(config.connection_attempts):
try:
+ r = requests.get(provider["price_feed"])
price_data = r.json()
- prices = price_data["bpi"]
+ prices = price_data[provider["result_root"]]
break
except Exception as e:
@@ -23,7 +40,7 @@ def get_price(currency):
raise ("Failed to reach {}.".format(price_feed))
try:
- price = prices[currency]["rate"].replace(",", "")
+ price = prices[provider["ticker"]][provider["value_attribute"]]
return price
except:
@@ -31,17 +48,17 @@ def get_price(currency):
return None
-def get_btc_value(dollar_value, currency):
+def get_btc_value(base_amount, currency):
price = get_price(currency)
- if price is not None:
+ if price is not None:
try:
- float_value = float(dollar_value) / float(price)
+ float_value = float(base_amount) / float(price)
if not isinstance(float_value, float):
- raise Exception("Dollar value should be a float.")
+ raise Exception("Fiat value should be a float.")
except Exception as e:
print(e)
return float_value
- raise Exception("Failed to get dollar value.")
+ raise Exception("Failed to get fiat value.")
diff --git a/satsale.py b/satsale.py
@@ -47,8 +47,10 @@ if not os.path.exists("database.db"):
# This is currently a donation page that submits to /pay
@app.route("/")
def index():
+ params = dict(request.args)
+ params["currency"] = config.base_currency
headers = {"Content-Type": "text/html"}
- return make_response(render_template("donate.html"), 200, headers)
+ 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=
@@ -79,7 +81,7 @@ invoice_model = api.model(
"invoice",
{
"uuid": fields.String(),
- "dollar_value": fields.Float(),
+ "fiat_value": fields.Float(),
"btc_value": fields.Float(),
"method": fields.String(),
"address": fields.String(),
@@ -102,7 +104,7 @@ status_model = api.model(
@api.doc(
params={
- "amount": "An amount in USD.",
+ "amount": "An amount in `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.",
}
@@ -112,9 +114,9 @@ class create_payment(Resource):
@api.response(400, "Invalid payment method")
def get(self):
"Create Payment"
- """Initiate a new payment with an `amount` in USD."""
- dollar_amount = request.args.get("amount")
- currency = "USD"
+ """Initiate a new payment with an `amount` in `config.base_currecy`."""
+ base_amount = request.args.get("amount")
+ currency = config.base_currency
label = "" # request.args.get('label')
payment_method = request.args.get("method")
if payment_method is None:
@@ -134,8 +136,8 @@ class create_payment(Resource):
invoice = {
"uuid": str(uuid.uuid4().hex),
- "dollar_value": dollar_amount,
- "btc_value": round(get_btc_value(dollar_amount, currency), 8),
+ "fiat_value": base_amount,
+ "btc_value": round(get_btc_value(base_amount, currency), 8),
"method": payment_method,
"time": time.time(),
"webhook": webhook,
diff --git a/templates/donate.html b/templates/donate.html
@@ -49,7 +49,7 @@
<center>
<form id="pay" action='/pay' style="margin:0;padding0;">
<h2 style="margin:0;padding0;">Amount:
- <input id="amountenter" style="display:inline" size="4" type="float" name="amount" id="amount" placeholder="USD" required>
+ <input id="amountenter" style="display:inline" size="4" type="float" name="amount" id="amount" placeholder="{{ params.currency }}" required>
</h2>
<br>
<input class="button button1" style="width:40%" type="submit" value="Donate">