- Hero mockup: enhanced 3D perspective and shadow - Testimonials: illustrated SVG avatars - Growth pricing card: visual prominence (scale, gradient, badge) - Most Popular badge: repositioned to avoid overlapping heading - Nav: added Log In link next to Start Free Trial - Fixed btn-primary text colour on anchor tags (white on blue) - Fixed cursor: default on all non-interactive elements - Disabled user-select on non-form content to prevent text caret
7.4 KiB
TenderRadar Stripe Payment Integration
This document describes the Stripe payment integration for TenderRadar, including setup instructions and API endpoints.
Overview
TenderRadar now supports three paid subscription tiers via Stripe:
- Starter: £39/month
- Growth: £99/month
- Pro: £249/month
All plans include a 14-day free trial.
Database Schema
A new subscriptions table has been added to track user subscription status:
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
user_id INTEGER UNIQUE REFERENCES users(id) ON DELETE CASCADE,
stripe_customer_id VARCHAR(255) UNIQUE NOT NULL,
stripe_subscription_id VARCHAR(255),
plan VARCHAR(50) NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'active',
trial_start TIMESTAMP,
trial_end TIMESTAMP,
current_period_start TIMESTAMP,
current_period_end TIMESTAMP,
cancel_at_period_end BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Fields:
user_id: Reference to the user accountstripe_customer_id: Stripe customer ID for this userstripe_subscription_id: Active Stripe subscription IDplan: Current plan tier (starter, growth, pro)status: Subscription status (active, trialing, past_due, cancelled)trial_start/end: Trial period datescurrent_period_start/end: Current billing period datescancel_at_period_end: Whether subscription is scheduled for cancellation
Environment Variables
Add the following to .env:
# Stripe API Keys (from Stripe Dashboard)
STRIPE_SECRET_KEY=sk_test_placeholder
STRIPE_PUBLISHABLE_KEY=pk_test_placeholder
STRIPE_WEBHOOK_SECRET=whsec_placeholder
# Stripe Price IDs (created in Stripe Dashboard)
STRIPE_PRICE_STARTER=price_starter_placeholder
STRIPE_PRICE_GROWTH=price_growth_placeholder
STRIPE_PRICE_PRO=price_pro_placeholder
Peter: Update these placeholder values with your actual Stripe keys.
Setup Instructions
1. Create Stripe Account and Get API Keys
- Sign up at https://dashboard.stripe.com
- Navigate to Developers → API Keys
- Copy your Secret Key (starts with
sk_test_orsk_live_) - Copy your Publishable Key (starts with
pk_test_orpk_live_) - Update
.envwith these keys
2. Create Webhook Endpoint
- In Stripe Dashboard, go to Developers → Webhooks
- Click "Add an endpoint"
- Endpoint URL:
https://your-domain.com/api/billing/webhook - Select events to listen for:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_failed
- Copy the Signing Secret (starts with
whsec_) - Update
.envwithSTRIPE_WEBHOOK_SECRET
3. Create Stripe Price Objects
In Stripe Dashboard, go to Products → Create Product:
Starter Plan
- Name: "TenderRadar Starter"
- Price: £39.00 GBP / month
- Recurring: Monthly
- Copy the Price ID (starts with
price_) →STRIPE_PRICE_STARTER
Growth Plan
- Name: "TenderRadar Growth"
- Price: £99.00 GBP / month
- Recurring: Monthly
- Copy the Price ID →
STRIPE_PRICE_GROWTH
Pro Plan
- Name: "TenderRadar Pro"
- Price: £249.00 GBP / month
- Recurring: Monthly
- Copy the Price ID →
STRIPE_PRICE_PRO
Update .env with all three Price IDs.
4. Initialize Database
If this is a fresh setup, run:
node init-db.js
This will create the subscriptions table and indexes.
5. Restart Server
npm start
API Endpoints
POST /api/billing/checkout
Creates a Stripe Checkout session for a selected plan.
Request:
{
"plan": "starter|growth|pro",
"successUrl": "https://app.example.com/success",
"cancelUrl": "https://app.example.com/cancel"
}
Response:
{
"sessionId": "cs_test_...",
"url": "https://checkout.stripe.com/pay/..."
}
Usage:
- Call this endpoint with the desired plan
- Redirect user to the returned
url - User completes payment on Stripe Checkout
- Stripe redirects to
successUrl
POST /api/billing/webhook
Handles incoming Stripe webhook events. This endpoint is automatically called by Stripe.
Handled Events:
checkout.session.completed- Creates subscription record when user completes checkoutcustomer.subscription.updated- Updates subscription status in databasecustomer.subscription.deleted- Marks subscription as cancelledinvoice.payment_failed- Logs failed payment (can trigger alerts)
GET /api/billing/subscription
Retrieves the current subscription status for the authenticated user.
Response (with active subscription):
{
"subscription": {
"id": 1,
"user_id": 42,
"stripe_customer_id": "cus_...",
"stripe_subscription_id": "sub_...",
"plan": "growth",
"status": "active",
"trial_start": "2026-02-14T12:00:00Z",
"trial_end": "2026-02-28T12:00:00Z",
"current_period_start": "2026-03-14T12:00:00Z",
"current_period_end": "2026-04-14T12:00:00Z",
"cancel_at_period_end": false,
"created_at": "2026-02-14T12:00:00Z",
"updated_at": "2026-02-14T12:00:00Z"
}
}
Response (no subscription):
{
"subscription": null,
"message": "No active subscription. User is on free tier."
}
POST /api/billing/portal
Creates a Stripe Customer Portal session for managing subscriptions (upgrade, downgrade, cancel).
Request:
{
"returnUrl": "https://app.example.com/billing"
}
Response:
{
"url": "https://billing.stripe.com/session/..."
}
Middleware
attachSubscription(pool)
Automatically attaches subscription info to req.subscription for all authenticated requests. Place after verifyToken middleware.
requireActiveSubscription
Middleware to restrict endpoints to users with active subscriptions. Use for premium features:
app.get('/api/premium-feature', verifyToken, requireActiveSubscription, (req, res) => {
// This endpoint now requires active subscription
});
Implementation Notes
- Stripe Checkout is used for PCI compliance (no sensitive payment data handled by TenderRadar)
- 14-day trial is automatically applied to all subscriptions via checkout session config
- Webhook validation ensures events are authentic before processing
- Subscription metadata includes
user_idandplanfor easy lookup - Raw body parsing is configured for the webhook endpoint to verify signatures
- Plan mapping converts plan names to Stripe Price IDs in
stripe-billing.js
Testing Webhook Locally
For local development, use Stripe CLI:
# Install Stripe CLI: https://stripe.com/docs/stripe-cli
stripe login
stripe listen --forward-to localhost:3456/api/billing/webhook
This outputs a webhook signing secret — update .env with this value for testing.
File Structure
├── server.js # Main Express app with billing routes
├── stripe-billing.js # Stripe integration functions
├── subscription-middleware.js # Middleware for subscription checks
├── init-db.js # Database setup (includes subscriptions table)
├── .env # Configuration (update with Stripe keys)
└── STRIPE_SETUP.md # This file
Support
For questions about Stripe integration, consult:
- Stripe API Docs: https://stripe.com/docs/api
- Stripe Webhooks: https://stripe.com/docs/webhooks
- Stripe Checkout: https://stripe.com/docs/payments/checkout