Liberapay is a recurrent donations platform. We help you fund the creators and projects you appreciate.
Note: This webapp is not self-hostable.
Want to chat? Join us on Gitter.
Alternatively you can post a message in our GitHub salon.
You can help translate Liberapay via Weblate. Current status:
If you have questions about translating Liberapay, you can ask them in the salon.
Liberapay was originally forked from Gratipay and inherited its web micro-framework Pando (né Aspen), which is based on filesystem routing and simplates. Don't worry, it's quite simple. For example to make Liberapay return a Hello $user, your id is $userid
message for requests to the URL /$user/hello
, you only need to create the file www/%username/hello.spt
with this inside:
from liberapay.utils import get_participant
[---]
participant = get_participant(state)
[---] text/html
{{ _("Hello {0}, your id is {1}", request.path['username'], participant.id) }}
As illustrated by the last line our default template engine is Jinja.
The _
function attempts to translate the message into the user's language and escapes the variables properly (it knows that it's generating a message for an HTML page).
The python code inside simplates is only for request-specific logic, common backend code is in the liberapay/
directory.
Make sure you have the following dependencies installed first:
- python ≥ 3.12
- including the C headers of python and libffi, which are packaged separately in many Linux distributions
- postgresql 16 (see the official download & install docs)
- make
Then run:
make env
Now you need to give yourself superuser postgres powers (if it hasn't been done already), and create two databases:
su postgres -c "createuser --superuser $(whoami)"
createdb liberapay
createdb liberapay_tests
If you need a deeper understanding take a look at the Database Roles and Managing Databases sections of PostgreSQL's documentation.
Then you can set up the DB:
make schema
Environment variables are used for configuration, the default values are in
defaults.env
and tests/test.env
. You can override them in
local.env
and tests/local.env
respectively.
Once you've installed everything and set up the database, you can run the app:
make run
It should now be accessible at http://localhost:8339/.
There are no users provided by default. You can create accounts as you would on the real website, and if you want you can also create a bunch of fake users (but they're not great):
make data
To grant admin permissions to an account, modify the database like so:
psql liberapay -c "update participants set privileges = 1 where username = 'account-username'"
To run a local payday open http://localhost:8339/admin/payday and click the "Run payday" button. You can add OVERRIDE_PAYDAY_CHECKS=yes
in the local.env
file to disable the safety checks that prevent running payday at the wrong time.
The python code interacts with the database by sending raw SQL queries through the postgres.py library.
The official PostgreSQL documentation is your friend when dealing with SQL, especially the sections "The SQL Language" and "SQL Commands".
The DB schema is in sql/schema.sql
, but don't modify that file directly,
instead put the changes in sql/branch.sql
. During deployment that script will
be run on the production DB and the changes will be merged into sql/schema.sql
.
That process is semi-automated by release.sh
.
For our styles we use SASS and Bootstrap 3. Stylesheets are in the style/
directory and our JavaScript code is in js/
. Our policy for both is to include as little as possible of them: the website should be almost entirely usable without JS, and our CSS should leverage Bootstrap as much as possible instead of containing lots of custom rules that would become a burden to maintain.
We compile Bootstrap ourselves from the SASS source in the style/bootstrap/
directory. We do that to be able to easily customize it by changing values in
style/variables.scss
. Modifying the files in style/bootstrap/
is probably
a bad idea.
For user interface icons we use Bootstrap Icons. An icon can be included in a page by calling the icon
macro from templates/macros/icons.html
, e.g. {{ icon('liberapay') }}
. The icons are stored in the www/assets/icons.svg
file. To add a new icon in that file, the root <svg>
element of the icon being added must be turned into a <symbol>
element, preserving only its viewBox
attribute and adding an id
attribute.
If you don't find any icon in Bootstrap Icons that fits your use case, you can try to search online catalogs like Flaticon, Icons8, Pictogrammers, SVG Repo and The Noun Project. For brand icons, Simple Icons is a good resource.
The easiest way to run the test suite is:
make test
This recreates the test DB's schema and runs all the tests. To speed things up you can also use the following commands:
make pytest
only runs the python tests without recreating the test DBmake pytest-re
only runs the tests that failed previously
Some of our tests include interactions with external services. In order to speed up those tests we record the requests and responses automatically using vcr. The records are in the tests/py/fixtures
directory, one per test class.
If you add or modify interactions with external services, then the tests will fail, because VCR will not find the new or modified request in the records, and will refuse to record the new request by default (see Record Modes for more information). When that happens you can either add VCR=new_episodes
to your test command (e.g. make pytest VCR=new_episodes
) or delete the obsolete fixture files (e.g. rm tests/py/fixtures/TestPayinsStripe.yml
).
If you're testing an API which uses idempotency keys (for example Stripe's API), then some requests will fail if they're no longer exactly identical. In that case, increase the value of the test class' offset
attribute so that different idempotency keys will be used.
PostgreSQL is designed to prevent data loss, so it does a lot of synchronous disk writes by default. To reduce the number of those blocking writes, our recreate-schema.sh
script automatically switches the synchronous_commit
option to off
for the test database, however this doesn't completely disable syncing. If your PostgreSQL instance only contains data that you can afford to lose, then you can speed things up further by setting fsync
to off
, wal_level
to minimal
and max_wal_senders
to 0
in the server's configuration file (postgresql.conf
).
Liberapay currently supports two payment processors: Stripe and PayPal.
You can forward Stripe's callbacks to your local Liberapay instance by running make stripe-bridge
. The stripe-cli program has to be installed for this to work.
All new dependencies need to be audited to check that they don't contain malicious code or security vulnerabilities.
We use pip's Hash-Checking Mode to protect ourselves from dependency tampering. Thus, when adding or upgrading a dependency the new hashes need to be computed and put in the requirements file. For that you can use hashin:
pip install hashin
hashin package==x.y -r requirements_base.txt
If for some reason you need to rehash all requirements, run make rehash-requirements
.
To upgrade all the dependencies in the requirements file, run hashin -u -r requirements_base.txt
. You may have to run extra hashin
commands if new subdependencies are missing.
The testing dependencies in requirements_tests.txt
don't follow these rules because they're not installed in production. It's up to you to isolate your development environment from the rest of your system in order to protect it from possible vulnerabilities in the testing dependencies.
When writing code that handles personal information, keep in mind the principles enshrined in the GDPR.
Note: Liberapay cannot be self-hosted, this section is only meant to document how we deploy new versions.
Liberapay is currently hosted on AWS (Ireland).
To deploy the app simply run release.sh
, it'll guide you through it. Of course you need to be given access first.
CC0 Public Domain Dedication (See this discussion for details.)