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

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. If you enable delayed payment methods, also subscribe to `checkout.session.async_payment_succeeded`.
4. Copy the webhook signing secret into `STRIPE_WEBHOOK_SECRET`.
5. Set `STRIPE_SECRET_KEY` to 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:

```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

ProjectPay is released under the MIT License.