patx/projectpay
Get paid for freelance projects with Stripe and no-nonsense!
$ git clone https://gitman.io/git/patx/projectpay
ProjectPay
ProjectPay is a small web app for creating project payment pages and collecting payments through Stripe Checkout. It gives an admin user a private project dashboard, creates shareable payment links for customers, and records successful Stripe payments against each project.
Features
- Password-protected admin area
- Create, edit, search, and delete projects
- Line-item project pricing with scope and terms text
- Public, tokenized project payment pages
- Stripe Checkout payment flow
- Stripe webhook handling for successful payments
- Payment history and automatic paid/open project status updates
- MongoDB-backed storage
Tech Stack
- Python ASGI app built with
micropie - MongoDB via
pymongo - Stripe Checkout and webhooks via
stripe - Uvicorn for local and production serving
Requirements
- Python 3.10 or newer recommended
- MongoDB
- Stripe account, if you want live payment collection
Local Setup
- Create and activate a virtual environment:
bash
python3 -m venv .venv
source .venv/bin/activate
- Install dependencies:
bash
pip install -r requirements.txt
-
Start MongoDB locally, or point the app at a hosted MongoDB database.
-
Create a
.envfile:
```bash ADMIN_PASSWORD=change-me ADMIN_COOKIE_SECRET=replace-with-a-long-random-secret MONGODB_URI=mongodb://localhost:27017 MONGODB_DB=projectpay APP_BASE_URL=http://127.0.0.1:8000 PUBLIC_LOGO_URL= PUBLIC_LOGO_ALT=ProjectPay Logo
STRIPE_SECRET_KEY= STRIPE_WEBHOOK_SECRET= STRIPE_PRODUCT_ID= STRIPE_CURRENCY=usd STRIPE_MINIMUM_AMOUNT_CENTS=100 ```
- Run the app:
bash
python3 app.py
- Open
http://127.0.0.1:8000/loginand sign in withADMIN_PASSWORD.
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
ADMIN_PASSWORD |
Recommended | admin |
Password for the admin dashboard. Change this before deploying. |
ADMIN_COOKIE_SECRET |
Recommended | Falls back to APP_SECRET, then ADMIN_PASSWORD |
Secret used to sign the admin cookie. Use a long random value in production. |
APP_SECRET |
Optional | unset | Fallback cookie secret if ADMIN_COOKIE_SECRET is not set. |
APP_BASE_URL |
Recommended in production | Derived from request headers | Public base URL used to generate customer payment links and Stripe return URLs. |
PUBLIC_LOGO_URL |
Optional | unset | Logo image URL shown on public project payment pages. No logo is shown when this is empty. |
PUBLIC_LOGO_ALT |
Optional | ProjectPay Logo |
Alt text for the public payment page logo. |
MONGODB_URI |
Optional | mongodb://localhost:27017 |
MongoDB connection string. |
MONGODB_DB |
Optional | invoice_maker |
MongoDB database name. Use projectpay or another explicit name for new installs. |
MONGODB_SERVER_SELECTION_TIMEOUT_MS |
Optional | 5000 |
MongoDB connection timeout in milliseconds. |
STRIPE_SECRET_KEY |
Required for payments | unset | Stripe secret API key. |
STRIPE_WEBHOOK_SECRET |
Required for webhook processing | unset | Signing secret for the Stripe webhook endpoint. |
STRIPE_PRODUCT_ID |
Required for payments | unset | Stripe Product ID used when creating custom-amount prices. |
STRIPE_CURRENCY |
Optional | usd |
Currency for Stripe Checkout and display formatting. |
STRIPE_MINIMUM_AMOUNT_CENTS |
Optional | 100 |
Minimum customer payment amount in cents. |
HOST |
Optional | 127.0.0.1 |
Host used by python3 app.py. |
PORT |
Optional | 8000 |
Port used by python3 app.py and the included Procfile. |
Stripe Setup
- Create a Product in Stripe and copy its product ID into
STRIPE_PRODUCT_ID. - Add a webhook endpoint that points to:
text
https://your-domain.example/webhook/stripe
- Subscribe the webhook endpoint to the
checkout.session.completedevent. If you enable delayed payment methods, also subscribe tocheckout.session.async_payment_succeeded. - Copy the webhook signing secret into
STRIPE_WEBHOOK_SECRET. - Set
STRIPE_SECRET_KEYto your Stripe secret key.
ProjectPay records payments from signed Stripe webhooks. The customer return page may show a thank-you message immediately after Checkout redirects back, but the project balance is updated only after a successful payment webhook is received and processed.
For local webhook testing, use the Stripe CLI:
stripe listen --forward-to localhost:8000/webhook/stripe
Then copy the whsec_... value from the Stripe CLI output into STRIPE_WEBHOOK_SECRET.
Usage
- Sign in at
/login. - Create a project with a customer name, optional email, line items, and scope or terms.
- Open the project detail page and copy the generated payment link.
- Send the payment link to the customer.
- The customer opens the public project page and clicks
Make a Payment. - Stripe Checkout collects the payment.
- Stripe sends a webhook back to ProjectPay, and the payment is recorded.
Deployment
The included Procfile runs the app with Uvicorn:
web: uvicorn app:app --host 0.0.0.0 --port $PORT
For production, set at least:
ADMIN_PASSWORDADMIN_COOKIE_SECRETAPP_BASE_URLMONGODB_URIMONGODB_DBSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETSTRIPE_PRODUCT_ID
Use HTTPS in production so Stripe webhooks and customer payment redirects work reliably.
Customization
- Set
PUBLIC_LOGO_URLandPUBLIC_LOGO_ALTto brand the public payment page. - Update styles in
templates/base.html. - The current default database name is
invoice_maker; setMONGODB_DB=projectpayfor a fresh ProjectPay deployment. - Currency formatting is optimized for USD. Non-USD currencies display as
CURRENCY amount.
Data Model
ProjectPay creates and manages these MongoDB collections:
projectspaymentscheckout_sessionswebhook_eventscounters
Indexes are created automatically when the app first touches the database.
Security Notes
- Do not deploy with the default
ADMIN_PASSWORD=admin. - Use a long random
ADMIN_COOKIE_SECRET. - Keep Stripe and MongoDB credentials out of source control.
- The public project URL uses a random share token, but anyone with the link can view the project payment page.
License
ProjectPay is released under the MIT License.