diff --git a/README.md b/README.md
index eccf0d7..d2107b7 100644
--- a/README.md
+++ b/README.md
@@ -22,25 +22,37 @@ source venv/bin/activate
pip3 install requirements.txt
```
-Create a .env file with :
+Create a settings.py file with :
```
-PORT=8000
-DEBUG=True
-PROJECT_NAME=YunoHost
-DOMAIN=http://localhost:8000
-STATIC_DIR=assets
-SECRET_CSRF_KEY=TO_CHANGE
+ENV = 'development'
+PORT = 8000
+DOMAIN = 'http://localhost:8000'
+SECRET_KEY = '712AZPOC87HXD5SQSb12rd'
+SECRET_CSRF_KEY = '712AZPOC87HXD5SQSb12'
+LANGUAGES = ['en', 'fr']
+BABEL_TRANSLATION_DIRECTORIES = 'locales'
+
+# Customization
+CUSTOM = {}
+CUSTOM['name'] = 'YunoHost'
+CUSTOM['contact_url'] = 'mailto:donate-6521@yunohost.org'
+CUSTOM['logo'] = 'https://yunohost.org/user/images/logo.png'
+CUSTOM['favicon'] = 'https://yunohost.org/user/themes/yunohost-docs/images/favicon.png'
+CUSTOM['currencies'] = [
+ ('EUR', '€'),
+ ('USD', '$')
+]
# Stripe keys
-STRIPE_PUBLISHABLE_KEY=pk_test_gOgGjacs9YfvDJY03BRZ576O
-STRIPE_SECRET_KEY=TO_REPLACE_BY_THE_GOOD_VALUE
+CUSTOM['stripe_publishable_key'] = 'pk_test_gOgGjacs9YfvDJY03BRZ576O'
+STRIPE_SECRET_KEY = 'sk_test_'
# Stripe subscription data
-ONE_TIME_EUR_DONATION=price_1IKuPVE7vOmTpJBiYMq7ztLH
-RECURING_EUR_DONATION=price_1IKumjE7vOmTpJBikyqS2NqD
-
-ONE_TIME_USD_DONATION=price_1IKuQfE7vOmTpJBi0A3nRGCJ
-RECURING_USD_DONATION=price_1IKumAE7vOmTpJBiO4CEfa3Q
+DONATION={'one_time':{}, 'recuring': {}}
+DONATION['one_time']['EUR'] = 'price_1IKuPVE7vOmTpJBiYMq7ztLH'
+DONATION['one_time']['USD'] = 'price_1IKuQfE7vOmTpJBi0A3nRGCJ'
+DONATION['recuring']['EUR'] = 'price_1IKumjE7vOmTpJBikyqS2NqD'
+DONATION['recuring']['USD'] = 'price_1IKumAE7vOmTpJBiO4CEfa3Q'
```
```
diff --git a/assets/canceled.html b/assets/canceled.html
index 4a6ebfb..4befef3 100644
--- a/assets/canceled.html
+++ b/assets/canceled.html
@@ -4,9 +4,9 @@
-
Donate to YunoHost
+ {{ _('Donate to %(name)s', name=name) }}
-
+
@@ -14,10 +14,10 @@
-
-
Your payment was canceled
+
+
{{ _('Your payment was canceled') }}
-
+
diff --git a/assets/favicon.ico b/assets/favicon.ico
deleted file mode 100644
index 49ffcee..0000000
Binary files a/assets/favicon.ico and /dev/null differ
diff --git a/assets/index.html b/assets/index.html
index ec9b9af..655e4b6 100644
--- a/assets/index.html
+++ b/assets/index.html
@@ -4,9 +4,9 @@
- Donate to YunoHost
+ {{ _('Donate to %(name)s', name=name) }}
-
+
@@ -16,22 +16,25 @@
-
-
I want to give to YunoHost
+
+
{{ _('I want to give to %(name)s', name=name) }}
+
+
-
-
-
+
+
{{ _('If you want to stop a monthly donation contact us') }}
diff --git a/assets/index.js b/assets/index.js
index c97a962..710bef1 100644
--- a/assets/index.js
+++ b/assets/index.js
@@ -1,24 +1,12 @@
-// Fetch your Stripe publishable key to initialize Stripe.js
-// In practice, you might just hard code the publishable API
-// key here.
-fetch('/config')
- .then(function (result) {
- return result.json();
- })
- .then(function (json) {
- window.config = json;
- window.stripe = Stripe(config.publicKey);
- });
+window.stripe = Stripe(document.getElementById('public_key').value);
// When the form is submitted...
var submitBtn = document.querySelector('#submit');
submitBtn.addEventListener('click', function (evt) {
- var inputEl = document.getElementById('quantity');
- var quantity = parseInt(inputEl.value);
- inputEl = document.getElementById('currency');
- var currency = inputEl.value;
- inputEl = document.getElementById('frequency');
- var frequency = inputEl.value;
+ var quantity = parseInt(document.getElementById('quantity').value);
+ var currency = document.getElementById('currency').value;
+ var frequency = document.getElementById('frequency').value;
+ var csrf = document.getElementById('csrf').value;
// Create the checkout session.
fetch('/create-checkout-session', {
@@ -27,7 +15,7 @@ submitBtn.addEventListener('click', function (evt) {
'Content-Type': 'application/json',
},
body: JSON.stringify({
- user_csrf: window.config.csrf,
+ user_csrf: csrf,
quantity: quantity,
currency: currency,
frequency: frequency
diff --git a/assets/logo.png b/assets/logo.png
deleted file mode 100644
index ff113c9..0000000
Binary files a/assets/logo.png and /dev/null differ
diff --git a/assets/success.html b/assets/success.html
index d865b26..d698d9c 100644
--- a/assets/success.html
+++ b/assets/success.html
@@ -4,9 +4,9 @@
- Donate to YunoHost
+ {{ _('Donate to %(name)s', name=name) }}
-
+
@@ -14,10 +14,10 @@
-
-
Thanks for your donation 🙂
+
+
{{ _('Thanks for your donation 🙂') }}
-
+
diff --git a/babel.cfg b/babel.cfg
new file mode 100644
index 0000000..0d3d70b
--- /dev/null
+++ b/babel.cfg
@@ -0,0 +1,3 @@
+[python: server.py]
+[jinja2: assets/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_
diff --git a/locales/en/LC_MESSAGES/messages.po b/locales/en/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..fa9e4e0
--- /dev/null
+++ b/locales/en/LC_MESSAGES/messages.po
@@ -0,0 +1,54 @@
+# English translations for PROJECT.
+# Copyright (C) 2021 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR , 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2021-02-18 23:32+0100\n"
+"PO-Revision-Date: 2021-02-18 23:33+0100\n"
+"Last-Translator: FULL NAME \n"
+"Language: en\n"
+"Language-Team: en \n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.9.0\n"
+
+#: assets/canceled.html:7 assets/index.html:7 assets/success.html:7
+#, python-format
+msgid "Donate to %(name)s"
+msgstr ""
+
+#: assets/canceled.html:18
+msgid "Your payment was canceled"
+msgstr ""
+
+#: assets/canceled.html:20 assets/success.html:20
+msgid "Go back to the donate form"
+msgstr ""
+
+#: assets/index.html:20
+#, python-format
+msgid "I want to give to %(name)s"
+msgstr ""
+
+#: assets/index.html:31
+msgid "/ month"
+msgstr ""
+
+#: assets/index.html:32
+msgid "one time"
+msgstr ""
+
+#: assets/index.html:36
+msgid "Donate"
+msgstr ""
+
+#: assets/success.html:18
+msgid "Thanks for your donation 🙂"
+msgstr ""
+
diff --git a/locales/fr/LC_MESSAGES/messages.po b/locales/fr/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..b316e6c
--- /dev/null
+++ b/locales/fr/LC_MESSAGES/messages.po
@@ -0,0 +1,54 @@
+# French translations for PROJECT.
+# Copyright (C) 2021 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR , 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2021-02-18 23:32+0100\n"
+"PO-Revision-Date: 2021-02-18 23:33+0100\n"
+"Last-Translator: FULL NAME \n"
+"Language: fr\n"
+"Language-Team: fr \n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.9.0\n"
+
+#: assets/canceled.html:7 assets/index.html:7 assets/success.html:7
+#, python-format
+msgid "Donate to %(name)s"
+msgstr "Donner à %(name)s"
+
+#: assets/canceled.html:18
+msgid "Your payment was canceled"
+msgstr "Votre paiement a été annulé"
+
+#: assets/canceled.html:20 assets/success.html:20
+msgid "Go back to the donate form"
+msgstr "Retourner au formulaire de don"
+
+#: assets/index.html:20
+#, python-format
+msgid "I want to give to %(name)s"
+msgstr "Je veux donner à %(name)s"
+
+#: assets/index.html:31
+msgid "/ month"
+msgstr "/ mois"
+
+#: assets/index.html:32
+msgid "one time"
+msgstr "une fois"
+
+#: assets/index.html:36
+msgid "Donate"
+msgstr "Donner"
+
+#: assets/success.html:18
+msgid "Thanks for your donation 🙂"
+msgstr "Merci pour votre don 🙂"
+
diff --git a/requirements.txt b/requirements.txt
index 00bc321..1956d56 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,3 +12,4 @@ stripe==2.47.0
toml==0.9.6
urllib3==1.25.3
flask-simple-csrf
+flask-babel
diff --git a/server.py b/server.py
index 85497c2..eeb8920 100644
--- a/server.py
+++ b/server.py
@@ -13,25 +13,21 @@ import random
import string
from flask import Flask, render_template, jsonify, request, send_from_directory, session
+from flask_babel import Babel, _
from flask_simple_csrf import CSRF
-from dotenv import load_dotenv, find_dotenv
-# Setup Stripe python client library.
-load_dotenv(find_dotenv())
-
-stripe.api_key = os.getenv('STRIPE_SECRET_KEY')
-stripe.api_version = os.getenv('STRIPE_API_VERSION')
-
static_dir = str(os.path.abspath(os.path.join(
- __file__, "..", os.getenv("STATIC_DIR"))))
+ __file__, "..", 'assets')))
app = Flask(__name__, static_folder=static_dir,
static_url_path="", template_folder=static_dir)
-app.secret_key = os.getenv('SECRET_KEY')
+app.config.from_pyfile('settings.py')
+stripe.api_key = app.config['STRIPE_SECRET_KEY']
CSRF = CSRF(config={
- 'SECRET_CSRF_KEY':os.getenv('SECRET_CSRF_KEY')
+ 'SECRET_CSRF_KEY': app.config['SECRET_CSRF_KEY']
})
app = CSRF.init_app(app)
+babel = Babel(app)
@app.before_request
def before_request():
@@ -39,43 +35,52 @@ def before_request():
session['USER_CSRF'] = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(64))
session['CSRF_TOKEN'] = CSRF.create(session['USER_CSRF'])
+@babel.localeselector
+def get_locale():
+ return 'fr' #request.accept_languages.best_match(app.config['LANGUAGES'])
+
@app.route('/', methods=['GET'])
def get_index():
- return render_template('index.html')
+ return render_template('index.html', **app.config['CUSTOM'],
+ csrf=session['USER_CSRF'])
-@app.route('/config', methods=['GET'])
-def get_publishable_key():
- return jsonify({
- 'publicKey': os.getenv('STRIPE_PUBLISHABLE_KEY'),
- 'name': os.getenv('PROJECT_NAME'),
- 'csrf': session['USER_CSRF'],
- })
+@app.route('/success', methods=['GET'])
+def get_success():
+ return render_template('success.html', **app.config['CUSTOM'])
+
+
+@app.route('/canceled', methods=['GET'])
+def get_canceled():
+ return render_template('canceled.html', **app.config['CUSTOM'])
+
@app.route('/create-checkout-session', methods=['POST'])
def create_checkout_session():
data = json.loads(request.data)
- domain_url = os.getenv('DOMAIN')
+ domain_url = app.config['DOMAIN']
try:
+ donation = app.config['DONATION']
+ currencies = [iso for iso, symbol in app.config['CUSTOM']['currencies']]
if CSRF.verify(data['user_csrf'], session['CSRF_TOKEN']) is False or \
- data['frequency'] not in ['RECURING', 'ONE_TIME'] or \
- data['currency'] not in ['EUR', 'USD'] or \
+ data['frequency'] not in ['recuring', 'one_time'] or \
+ data['currency'] not in currencies or \
int(data['quantity']) <= 0:
return jsonify(error="Bad value"), 400
# Create new Checkout Session for the order
- price = f"{data['frequency']}_{data['currency']}_DONATION"
- mode = "payment" if data['frequency'] == 'ONE_TIME' else "subscription"
+ price = donation[data['frequency']][data['currency']]
+ mode = "payment" if data['frequency'] == 'one_time' else "subscription"
checkout_session = stripe.checkout.Session.create(
success_url=domain_url +
- "/success.html?session_id={CHECKOUT_SESSION_ID}",
- cancel_url=domain_url + "/canceled.html",
+ "/success?session_id={CHECKOUT_SESSION_ID}",
+ cancel_url=domain_url + "/canceled",
payment_method_types= ["card"],
mode=mode,
line_items=[
{
- "price": os.getenv(price),
+ "price": price,
"quantity": data['quantity']
}
]
@@ -87,4 +92,4 @@ def create_checkout_session():
if __name__ == '__main__':
- app.run(port=os.getenv('PORT'), debug=os.getenv('DEBUG'))
+ app.run(port=app.config['PORT'], debug=app.debug)