patx/projectpay
remove hardcoded logo;
Commit f77eab7 · patx · 2026-06-28T17:00:30-04:00
Comments
No comments yet.
Diff
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e90f443
--- /dev/null
+++ b/README.md
@@ -0,0 +1,175 @@
+# 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
+
+1. Create and activate a virtual environment:
+
+ ```bash
+ python3 -m venv .venv
+ source .venv/bin/activate
+ ```
+
+2. Install dependencies:
+
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+3. Start MongoDB locally, or point the app at a hosted MongoDB database.
+
+4. Create a `.env` file:
+
+ ```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
+ ```
+
+5. Run the app:
+
+ ```bash
+ python3 app.py
+ ```
+
+6. Open `http://127.0.0.1:8000/login` and sign in with `ADMIN_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
+
+1. Create a Product in Stripe and copy its product ID into `STRIPE_PRODUCT_ID`.
+2. Add a webhook endpoint that points to:
+
+ ```text
+ https://your-domain.example/webhook/stripe
+ ```
+
+3. Subscribe the webhook endpoint to the `checkout.session.completed` event.
+4. Copy the webhook signing secret into `STRIPE_WEBHOOK_SECRET`.
+5. Set `STRIPE_SECRET_KEY` to your Stripe secret key.
+
+ProjectPay records payments from 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 the signed webhook is received and processed.
+
+For local webhook testing, use the Stripe CLI:
+
+```bash
+stripe listen --forward-to localhost:8000/webhook/stripe
+```
+
+Then copy the `whsec_...` value from the Stripe CLI output into `STRIPE_WEBHOOK_SECRET`.
+
+## Usage
+
+1. Sign in at `/login`.
+2. Create a project with a customer name, optional email, line items, and scope or terms.
+3. Open the project detail page and copy the generated payment link.
+4. Send the payment link to the customer.
+5. The customer opens the public project page and clicks `Make a Payment`.
+6. Stripe Checkout collects the payment.
+7. Stripe sends a webhook back to ProjectPay, and the payment is recorded.
+
+## Deployment
+
+The included `Procfile` runs the app with Uvicorn:
+
+```text
+web: uvicorn app:app --host 0.0.0.0 --port $PORT
+```
+
+For production, set at least:
+
+- `ADMIN_PASSWORD`
+- `ADMIN_COOKIE_SECRET`
+- `APP_BASE_URL`
+- `MONGODB_URI`
+- `MONGODB_DB`
+- `STRIPE_SECRET_KEY`
+- `STRIPE_WEBHOOK_SECRET`
+- `STRIPE_PRODUCT_ID`
+
+Use HTTPS in production so Stripe webhooks and customer payment redirects work reliably.
+
+## Customization
+
+- Set `PUBLIC_LOGO_URL` and `PUBLIC_LOGO_ALT` to brand the public payment page.
+- Update styles in `templates/base.html`.
+- The current default database name is `invoice_maker`; set `MONGODB_DB=projectpay` for 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:
+
+- `projects`
+- `payments`
+- `checkout_sessions`
+- `webhook_events`
+- `counters`
+
+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
+
+Add a license before publishing the repository. If you want broad open-source reuse, common choices are MIT or Apache-2.0.
diff --git a/app.py b/app.py
index 284e362..ab7b37d 100644
--- a/app.py
+++ b/app.py
@@ -127,6 +127,8 @@ class InvoiceApp(App):
or "invoice-maker-dev"
)
self.app_base_url = os.getenv("APP_BASE_URL", "").rstrip("/")
+ self.public_logo_url = os.getenv("PUBLIC_LOGO_URL", "").strip()
+ self.public_logo_alt = os.getenv("PUBLIC_LOGO_ALT", "ProjectPay Logo").strip()
self.currency = os.getenv("STRIPE_CURRENCY", "usd").upper()
self.stripe_secret_key = os.getenv("STRIPE_SECRET_KEY", "")
self.stripe_webhook_secret = os.getenv("STRIPE_WEBHOOK_SECRET", "")
@@ -631,6 +633,8 @@ class InvoiceApp(App):
async def _render(self, template: str, **kwargs: Any) -> str:
kwargs.setdefault("admin", self._is_admin())
kwargs.setdefault("currency", self.currency)
+ kwargs.setdefault("public_logo_url", self.public_logo_url)
+ kwargs.setdefault("public_logo_alt", self.public_logo_alt)
return await self._render_template(template, **kwargs)
async def index(self) -> Any:
diff --git a/templates/base.html b/templates/base.html
index 35b4ba4..75b7cb9 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -54,6 +54,15 @@
}
.topbar-inner, .header-row { justify-content: space-between; }
.brand { font-weight: 750; text-decoration: none; }
+ .public-logo {
+ margin: 0 0 18px;
+ text-align: center;
+ }
+ .public-logo img {
+ display: inline-block;
+ max-width: min(350px, 100%);
+ height: auto;
+ }
.title-line {
display: flex;
align-items: center;
diff --git a/templates/public_project.html b/templates/public_project.html
index fc2be53..464790f 100644
--- a/templates/public_project.html
+++ b/templates/public_project.html
@@ -2,9 +2,11 @@
{% block title %}{{ project.project_number }}{% endblock %}
{% block page_class %}{{ 'has-sticky-pay' if project.balance_cents > 0 else '' }}{% endblock %}
{% block content %}
- <center>
- <img src="https://i.postimg.cc/sDdprXgH/ea51ebc1-6a86-4b80-9a94-e8c30dd94c3a-removebg-preview.png" alt="After Dark Labs Logo" style="max-width:350px;">
- </center>
+ {% if public_logo_url %}
+ <div class="public-logo">
+ <img src="{{ public_logo_url }}" alt="{{ public_logo_alt }}">
+ </div>
+ {% endif %}
<div class="header-row">
<div>
<div class="title-line">