patx/projectpay
A Heroku ready, invoice maker powered by Stripe and MicroPie with an extremely clean interface.
$ 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 APP_ENV=development ADMIN_PASSWORD=change-me MONGODB_URI=mongodb://localhost:27017 MONGODB_DB=projectpay APP_BASE_URL=http://127.0.0.1:8000 PUBLIC_LOGO_URL= PUBLIC_DARK_LOGO_URL= PUBLIC_LOGO_ALT=ProjectPay Logo PUBLIC_APP_ICON_URL=https://gitman.io/patx/afterdarklabs/raw/docs/favicon.png
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 |
|---|---|---|---|
APP_ENV |
Required | unset | Set to development locally and production for deployed environments. Startup fails if this is missing or unrecognized. |
ADMIN_PASSWORD |
Required outside development | admin in development only |
Password for the admin dashboard. Production startup fails if this is missing or set to admin. |
APP_BASE_URL |
Required outside development | Derived from request headers in development only | 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_DARK_LOGO_URL |
Optional | PUBLIC_LOGO_URL |
Dark-mode logo image URL shown on public project payment pages. When empty, the normal logo is used. |
PUBLIC_LOGO_ALT |
Optional | ProjectPay Logo |
Alt text for the public payment page logo. |
PUBLIC_APP_ICON_URL |
Optional | https://gitman.io/patx/afterdarklabs/raw/docs/favicon.png |
PNG icon URL used by the web app manifest. |
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. |
LOGIN_RATE_LIMIT_ATTEMPTS |
Optional | 5 |
Failed login attempts allowed per client IP during the rate-limit window. Set to 0 to disable. |
LOGIN_RATE_LIMIT_WINDOW_SECONDS |
Optional | 900 |
Login rate-limit window in seconds. |
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. The Heroku Procfile binds Uvicorn to 0.0.0.0 separately. |
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:
APP_ENV=productionADMIN_PASSWORDAPP_BASE_URLMONGODB_URIMONGODB_DBSTRIPE_SECRET_KEYSTRIPE_WEBHOOK_SECRETSTRIPE_PRODUCT_ID
Use HTTPS in production so Stripe webhooks, customer payment redirects, and MicroPie's Secure session cookie work reliably. If a local browser does not retain the secure session cookie over plain HTTP, use HTTPS locally or a secure tunnel.
Heroku
The included Procfile already binds Uvicorn to 0.0.0.0 and Heroku's dynamic $PORT. For Heroku, set config vars explicitly:
heroku config:set APP_ENV=production
heroku config:set ADMIN_PASSWORD='replace-with-a-strong-password'
heroku config:set APP_BASE_URL='https://your-app.herokuapp.com'
heroku config:set PUBLIC_APP_ICON_URL='https://gitman.io/patx/afterdarklabs/raw/docs/favicon.png'
heroku config:set MONGODB_URI='mongodb+srv://...'
heroku config:set MONGODB_DB=projectpay
heroku config:set STRIPE_SECRET_KEY='sk_live_...'
heroku config:set STRIPE_WEBHOOK_SECRET='whsec_...'
heroku config:set STRIPE_PRODUCT_ID='prod_...'
On Heroku dynos, login rate limiting uses the final X-Forwarded-For value added by Heroku's router instead of the router's internal connection address.
Customization
- Set
PUBLIC_LOGO_URL,PUBLIC_DARK_LOGO_URL, andPUBLIC_LOGO_ALTto brand the public payment page. - Set
PUBLIC_APP_ICON_URLto change the icon used by/manifest.json. - 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_eventscounterssessionslogin_attempts
Indexes are created automatically when the app first touches the database.
Security Notes
- Set
APP_ENVexplicitly; startup fails if it is missing or misspelled. - Do not deploy with
APP_ENV=development. - Do not deploy with the default
ADMIN_PASSWORD=admin. - Admin sessions are server-side and stored in MongoDB behind MicroPie's opaque
session_idcookie. - Browser POST forms use CSRF tokens stored in the server-side session.
- Login attempts are rate-limited by client IP.
- Project search treats search text as a literal string before sending it to MongoDB regex queries.
- Stripe webhook storage keeps minimized event summaries instead of full raw event payloads.
- 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.