Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
9.3 KiB
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
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
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):
- User visits
/signupand fills out the registration form. - DocuElevate sends a verification email with a 24-hour token link.
- User clicks the link — their account is activated and they are signed in.
- First-time users are redirected to the onboarding wizard.
Without SMTP:
- User visits
/signupand fills out the registration form. - Account is activated immediately — no email verification required.
- 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
- User clicks "Forgot password?" on the login page.
- User enters their email address.
- DocuElevate sends a password reset email with a 24-hour token link.
- 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 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)
- A publicly reachable webhook endpoint (or use Stripe CLI for local testing)
Configuration
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:
- Verify API Keys — checks that your Stripe secret key is configured and the connection to Stripe is working.
- Sync Plans to Stripe — automatically creates Stripe Products and Prices for every paid plan defined in DocuElevate, then stores the resulting
price_idvalues back in the database. Free plans are skipped; plans that already have a price ID are left unchanged. - 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:
curl -X POST https://your-app.example.com/api/billing/stripe/sync-plans \
-H "Cookie: <admin-session-cookie>"
Response:
{
"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
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:
- Create Products and Prices in the Stripe Dashboard.
- Go to Admin → Plans in DocuElevate and click the Edit (pencil) button for a paid plan.
- Scroll to the Stripe Integration section in the plan editor.
- Enter the Stripe Price ID (monthly) (e.g.
price_1OtAbc...). - Optionally enter the Stripe Price ID (yearly) for annual billing.
- Save the plan.
Stable link: DocuElevate stores the Stripe
customer_idin theUserProfile.stripe_customer_idcolumn 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
- Go to Developers → Webhooks in the Stripe Dashboard.
- Click Add endpoint.
- Set the endpoint URL to:
https://your-app-domain.com/api/billing/webhook(The Stripe Setup Wizard shows the exact URL for your deployment.) - Select the following events:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_failed
- Copy the Signing secret and set
STRIPE_WEBHOOK_SECRETin your environment.
Local testing with Stripe CLI
# 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
- User visits
/pricing. - User clicks the CTA button on a paid plan.
- DocuElevate calls
POST /api/billing/create-checkout-session. - User is redirected to Stripe Checkout.
- After payment, Stripe fires
checkout.session.completed. - DocuElevate webhook handler activates the subscription tier and stores the Stripe
customer_id. - User is redirected to
/api/billing/success.
Manage or cancel subscription
- User visits their account settings.
- DocuElevate calls
POST /api/billing/create-portal-session. - User is redirected to the Stripe Customer Portal.
- User can update payment method, upgrade, downgrade, or cancel.
- Stripe fires
customer.subscription.updatedorcustomer.subscription.deleted. - 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 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 for the full environment variable reference.