223539bebb
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
245 lines
9.3 KiB
Markdown
245 lines
9.3 KiB
Markdown
# Billing Setup Guide
|
|
|
|
This guide covers how to configure Stripe billing and local user sign-up in DocuElevate.
|
|
|
|
## Table of Contents
|
|
|
|
- [Local User Sign-up](#local-user-sign-up)
|
|
- [Stripe Billing Integration](#stripe-billing-integration)
|
|
- [Prerequisites](#prerequisites)
|
|
- [Configuration](#configuration)
|
|
- [Stripe Setup Wizard (recommended)](#stripe-setup-wizard-recommended)
|
|
- [Setting Up Plans Manually](#setting-up-plans-manually)
|
|
- [Webhook Configuration](#webhook-configuration)
|
|
- [Billing Flows](#billing-flows)
|
|
- [Compliance Notes](#compliance-notes)
|
|
|
|
---
|
|
|
|
## Local User Sign-up
|
|
|
|
By default, user accounts are created by an administrator. To allow users to self-register with an email address and password, set `ALLOW_LOCAL_SIGNUP=true`.
|
|
|
|
> **Note:** SMTP is **optional** for local sign-up. When SMTP is configured, new accounts require email verification before they can log in. Without SMTP, accounts are activated immediately upon registration — useful for self-hosted deployments without email infrastructure.
|
|
|
|
### Configuration
|
|
|
|
```bash
|
|
ALLOW_LOCAL_SIGNUP=true
|
|
|
|
# SMTP (optional — enables email verification and password reset)
|
|
EMAIL_HOST=smtp.example.com
|
|
EMAIL_PORT=587
|
|
EMAIL_USERNAME=noreply@example.com
|
|
EMAIL_PASSWORD=yourpassword
|
|
EMAIL_USE_TLS=true
|
|
EMAIL_SENDER=DocuElevate <noreply@example.com>
|
|
```
|
|
|
|
### Sign-up Flow
|
|
|
|
**With SMTP configured (recommended):**
|
|
1. User visits `/signup` and fills out the registration form.
|
|
2. DocuElevate sends a verification email with a 24-hour token link.
|
|
3. User clicks the link — their account is activated and they are signed in.
|
|
4. First-time users are redirected to the onboarding wizard.
|
|
|
|
**Without SMTP:**
|
|
1. User visits `/signup` and fills out the registration form.
|
|
2. Account is activated immediately — no email verification required.
|
|
3. User is redirected to the login page to sign in straight away.
|
|
|
|
### Admin-Created Accounts
|
|
|
|
Administrators can create local user accounts directly from the **Admin → User Management** page without requiring self-registration. Admin-created accounts are immediately active regardless of SMTP configuration.
|
|
|
|
### Password Reset Flow
|
|
|
|
1. User clicks "Forgot password?" on the login page.
|
|
2. User enters their email address.
|
|
3. DocuElevate sends a password reset email with a 24-hour token link.
|
|
4. User clicks the link, enters a new password, and is redirected to sign in.
|
|
|
|
### Security
|
|
|
|
- Passwords are hashed with bcrypt (12 rounds).
|
|
- Verification and reset tokens are 256-bit URL-safe random strings.
|
|
- All tokens expire after 24 hours.
|
|
- Sign-up and login endpoints return generic error messages to prevent user enumeration.
|
|
|
|
---
|
|
|
|
## Stripe Billing Integration
|
|
|
|
DocuElevate integrates with [Stripe](https://stripe.com) to handle subscription payments. Stripe acts as a data processor under a Data Processing Agreement (DPA) and is SOC 2 Type II certified.
|
|
|
|
### Prerequisites
|
|
|
|
- A Stripe account (sign up at [stripe.com](https://stripe.com))
|
|
- A publicly reachable webhook endpoint (or use [Stripe CLI](https://stripe.com/docs/stripe-cli) for local testing)
|
|
|
|
### Configuration
|
|
|
|
```bash
|
|
STRIPE_SECRET_KEY=sk_live_... # Your Stripe secret key
|
|
STRIPE_PUBLISHABLE_KEY=pk_live_... # Your Stripe publishable key (for frontend)
|
|
STRIPE_WEBHOOK_SECRET=whsec_... # Webhook signing secret
|
|
STRIPE_SUCCESS_URL=https://app.example.com/api/billing/success # Optional override
|
|
STRIPE_CANCEL_URL=https://app.example.com/pricing # Optional override
|
|
```
|
|
|
|
> **Security:** Never commit your Stripe secret key. Store it in your environment or secrets manager.
|
|
|
|
### Stripe Setup Wizard (recommended)
|
|
|
|
DocuElevate includes a built-in **Stripe Setup Wizard** at `/admin/stripe-wizard` that guides you through the complete setup in three steps:
|
|
|
|
1. **Verify API Keys** — checks that your Stripe secret key is configured and the connection to Stripe is working.
|
|
2. **Sync Plans to Stripe** — automatically creates Stripe **Products** and **Prices** for every paid plan defined in DocuElevate, then stores the resulting `price_id` values back in the database. Free plans are skipped; plans that already have a price ID are left unchanged.
|
|
3. **Configure Webhook** — shows the exact webhook endpoint URL to register in the Stripe Dashboard and which events to subscribe to.
|
|
|
|
You can also reach the wizard from the **Admin → Plans** page via the **Stripe Setup** button.
|
|
|
|
#### Auto-sync API
|
|
|
|
The sync step is also available as an API endpoint for automation:
|
|
|
|
```bash
|
|
curl -X POST https://your-app.example.com/api/billing/stripe/sync-plans \
|
|
-H "Cookie: <admin-session-cookie>"
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"results": [
|
|
{
|
|
"plan_id": "starter",
|
|
"name": "Starter",
|
|
"status": "created",
|
|
"stripe_price_id_monthly": "price_1OtAbc...",
|
|
"stripe_price_id_yearly": "price_1OtDef..."
|
|
},
|
|
{
|
|
"plan_id": "free",
|
|
"name": "Free",
|
|
"status": "skipped_free"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Possible `status` values:
|
|
|
|
| Status | Meaning |
|
|
|--------|---------|
|
|
| `created` | Stripe product and price(s) were created and saved |
|
|
| `already_synced` | Plan already had a price ID — no changes made |
|
|
| `skipped_free` | Free plan (price is $0) — no Stripe price needed |
|
|
| `error` | Stripe API call failed — `detail` contains the error message |
|
|
|
|
#### Stripe connection status API
|
|
|
|
```bash
|
|
curl https://your-app.example.com/api/billing/stripe/status
|
|
```
|
|
|
|
Returns connection health, API mode (test/live), webhook secret status, and per-plan sync status.
|
|
|
|
### Setting Up Plans Manually
|
|
|
|
If you prefer to enter price IDs yourself rather than using the wizard:
|
|
|
|
1. Create **Products** and **Prices** in the [Stripe Dashboard](https://dashboard.stripe.com/products).
|
|
2. Go to **Admin → Plans** in DocuElevate and click the **Edit** (pencil) button for a paid plan.
|
|
3. Scroll to the **Stripe Integration** section in the plan editor.
|
|
4. Enter the **Stripe Price ID (monthly)** (e.g. `price_1OtAbc...`).
|
|
5. Optionally enter the **Stripe Price ID (yearly)** for annual billing.
|
|
6. Save the plan.
|
|
|
|
> **Stable link:** DocuElevate stores the Stripe `customer_id` in the `UserProfile.stripe_customer_id` column and matches it on every webhook event. This is the stable link between Stripe billing profiles and DocuElevate accounts. It is set automatically when a user completes their first checkout.
|
|
|
|
### Webhook Configuration
|
|
|
|
Stripe webhooks allow DocuElevate to sync subscription status in real time.
|
|
|
|
#### Stripe Dashboard setup
|
|
|
|
1. Go to **Developers → Webhooks** in the Stripe Dashboard.
|
|
2. Click **Add endpoint**.
|
|
3. Set the endpoint URL to: `https://your-app-domain.com/api/billing/webhook`
|
|
(The Stripe Setup Wizard shows the exact URL for your deployment.)
|
|
4. Select the following events:
|
|
- `checkout.session.completed`
|
|
- `customer.subscription.updated`
|
|
- `customer.subscription.deleted`
|
|
- `invoice.payment_failed`
|
|
5. Copy the **Signing secret** and set `STRIPE_WEBHOOK_SECRET` in your environment.
|
|
|
|
#### Local testing with Stripe CLI
|
|
|
|
```bash
|
|
# Install Stripe CLI and log in
|
|
stripe login
|
|
|
|
# Forward webhooks to your local server
|
|
stripe listen --forward-to http://localhost:8000/api/billing/webhook
|
|
|
|
# Trigger test events
|
|
stripe trigger checkout.session.completed
|
|
stripe trigger customer.subscription.updated
|
|
stripe trigger customer.subscription.deleted
|
|
```
|
|
|
|
### Billing Flows
|
|
|
|
#### Subscribe to a plan
|
|
|
|
1. User visits `/pricing`.
|
|
2. User clicks the **CTA button** on a paid plan.
|
|
3. DocuElevate calls `POST /api/billing/create-checkout-session`.
|
|
4. User is redirected to Stripe Checkout.
|
|
5. After payment, Stripe fires `checkout.session.completed`.
|
|
6. DocuElevate webhook handler activates the subscription tier and stores the Stripe `customer_id`.
|
|
7. User is redirected to `/api/billing/success`.
|
|
|
|
#### Manage or cancel subscription
|
|
|
|
1. User visits their account settings.
|
|
2. DocuElevate calls `POST /api/billing/create-portal-session`.
|
|
3. User is redirected to the Stripe Customer Portal.
|
|
4. User can update payment method, upgrade, downgrade, or cancel.
|
|
5. Stripe fires `customer.subscription.updated` or `customer.subscription.deleted`.
|
|
6. DocuElevate webhook handler syncs the change.
|
|
|
|
#### Cancellation
|
|
|
|
When a subscription is cancelled, Stripe fires `customer.subscription.deleted` and DocuElevate automatically downgrades the user to the free tier.
|
|
|
|
---
|
|
|
|
## Compliance Notes
|
|
|
|
| Topic | Details |
|
|
|-------|---------|
|
|
| **GDPR** | Stripe acts as a data processor. A Data Processing Agreement (DPA) is available in the Stripe Dashboard. Stripe supports EU data residency. |
|
|
| **SOC 2** | Stripe is SOC 2 Type II certified. |
|
|
| **EU VAT** | Configure [Stripe Tax](https://stripe.com/tax) in the Stripe Dashboard for automatic VAT collection. |
|
|
| **PCI DSS** | Card data is handled entirely by Stripe. DocuElevate never sees or stores card details. |
|
|
|
|
---
|
|
|
|
## Environment Variable Reference
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `ALLOW_LOCAL_SIGNUP` | bool | `false` | Allow users to self-register with email/password |
|
|
| `STRIPE_SECRET_KEY` | string | — | Stripe API secret key |
|
|
| `STRIPE_PUBLISHABLE_KEY` | string | — | Stripe API publishable key |
|
|
| `STRIPE_WEBHOOK_SECRET` | string | — | Webhook signing secret from Stripe Dashboard |
|
|
| `STRIPE_SUCCESS_URL` | string | — | Override redirect URL after successful checkout |
|
|
| `STRIPE_CANCEL_URL` | string | — | Override redirect URL when checkout is cancelled |
|
|
|
|
See [ConfigurationGuide.md](./ConfigurationGuide.md) for the full environment variable reference.
|