Skip to content

Stripe Payment Gateway — Admin Setup Guide

Applies to: MERP25 · Subscriptions Module
Last updated: March 2026
Stripe account type: UK-registered (European) — 2-key setup (Publishable Key + Secret Key)


Overview

This application uses Stripe Checkout to handle subscription payments. Regional pricing is supported natively — you configure a separate Stripe Price object per currency in the Stripe Dashboard, then link the Price IDs in Django Admin.

The integration requires only 2 keys from your Stripe account:

Key Used by Where to set
STRIPE_SECRET_KEY Backend (API calls, checkout sessions) .env
STRIPE_PUBLIC_KEY Frontend (future direct card integrations) Frontend .env
STRIPE_WEBHOOK_SECRET (optional) Backend webhook verification .env (add later)

Step 1 — Get Your Stripe API Keys

  1. Log into dashboard.stripe.com
  2. Go to Developers → API keys
  3. Copy the following:
  4. Publishable key — starts with pk_live_... (or pk_test_... for test mode)
  5. Secret key — starts with sk_live_... (or sk_test_... for test mode)

[!IMPORTANT] Use Test mode keys (sk_test_... / pk_test_...) during development and staging. Switch to Live mode keys only after going live.


Step 2 — Configure Environment Variables

Backend (backend/.env)

# Stripe Integration
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_PUBLIC_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxx

# Optional — only needed if you configure a Webhook Endpoint in the Stripe Dashboard
# STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx

Frontend (frontend/.env)

VITE_STRIPE_PUBLIC_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxx

[!NOTE] If STRIPE_WEBHOOK_SECRET is not set, the backend logs a warning and skips signature verification on incoming webhooks. This is safe for initial launch when you have no webhook endpoint configured yet.


Step 3 — Create Products in Stripe Dashboard

Each subscription plan needs a Product in Stripe.

  1. Go to Product catalogue → Add product
  2. Create one product per plan:
  3. Professional Plan
  4. Premium Plan
  5. For each product, set:
  6. Name: matches your plan display name
  7. Billing: Recurring / Monthly

The Free plan does not need a Stripe product.


Step 4 — Create Prices Per Currency

This is the key step for regional pricing. Each plan needs a separate Price object per currency.

For each plan (Professional, Premium), create prices for every currency you want to support:

  1. Inside the product, click Add price
  2. Set:
  3. Currency: select the currency (e.g. GBP, AED, EUR, USD)
  4. Price: the amount for that currency
  5. Billing period: Monthly
  6. Click Save — Stripe gives you a Price ID like price_1AbCdEfGhIjKlMnO
  7. Repeat for each currency

Example prices for Professional Plan:

Currency Amount Stripe Price ID (example)
USD $49.00/mo price_usd_professional_xxx
GBP £39.00/mo price_gbp_professional_xxx
AED AED 179.00/mo price_aed_professional_xxx
EUR €45.00/mo price_eur_professional_xxx

[!TIP] You can find all Price IDs in Product catalogue → [plan name] → Pricing section.


  1. Log into Django Admin → Subscriptions → Subscription Plans
  2. Open Professional Plan
  3. Click the Payment Gateway section (collapsed by default)
  4. In the "Stripe Price IDs (by Currency)" field, enter one price per line:
USD: price_1AbCdEfGhIjKlMnO
GBP: price_1XxXxXxXxXxXxXxX
AED: price_1YyYyYyYyYyYyYyY
EUR: price_1ZzZzZzZzZzZzZzZ
  1. Also fill in the Regional Pricing section with the display prices:
AED: 179.00
GBP: 39.00
EUR: 45.00
  1. Click Save
  2. Repeat for Premium Plan

[!IMPORTANT] The "Stripe price id" field (legacy, just above the new section) is the USD fallback. Set it to your USD price ID too. It is used when no matching currency is found in the new per-currency map.


Step 6 — Activate a Payment Provider Record (First Time Only)

The application uses a PaymentProvider database record to enable Stripe.

  1. Django Admin → Subscriptions → Payment Providers
  2. If no stripe record exists, click Add:
  3. Name: stripe
  4. Display name: Card (Stripe)
  5. Is active: ✅ checked
  6. Config: {} (leave empty — keys come from environment variables)
  7. Save

Step 7 — Test the Checkout Flow

Test Cards (Stripe Test Mode)

Scenario Card Number Expiry CVC
Successful payment 4242 4242 4242 4242 Any future date Any 3 digits
Payment declined 4000 0000 0000 0002 Any future date Any 3 digits
3D Secure required 4000 0025 0000 3155 Any future date Any 3 digits

Checkout Test Flow

  1. Go to /register → select Professional plan
  2. Verify the plan card shows your region's currency (auto-detected from browser locale)
  3. Complete registration → you're redirected to Stripe Checkout
  4. Use a test card above to complete payment
  5. Verify redirect back to /register/success
  6. Check Django Admin → Company Subscription is now active

Webhooks allow Stripe to notify your server when payments succeed or fail — useful for handling edge cases like payment retries.

  1. Go to Developers → Webhooks → Add endpoint
  2. Endpoint URL: https://your-domain.com/api/stripe-webhook/
  3. Events to listen to:
  4. checkout.session.completed
  5. customer.subscription.created
  6. customer.subscription.updated
  7. customer.subscription.deleted
  8. invoice.payment_succeeded
  9. invoice.payment_failed
  10. After creating, click Reveal on the Signing secret — copy whsec_...
  11. Add to backend/.env:
    STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
    
  12. Restart the backend server

[!NOTE] Without webhooks, subscription activation relies entirely on the Stripe Checkout success_url redirect. This works for most cases. Webhooks add resilience for failed payment retries and cancellations.


Currency Auto-Detection Logic

The frontend detects the user's currency from their browser locale (navigator.language):

Browser Locale Currency Shown
ar-AE, en-AE AED (UAE Dirham)
en-GB GBP (British Pound)
de-DE, fr-FR, nl-NL, es-ES, it-IT, pt-PT, etc. EUR (Euro)
All others USD (US Dollar)

Users can also manually switch currency using the currency picker on the pricing page. This overrides the auto-detection for that session.

[!CAUTION] Stripe uses the currency defined on the Price object — not what the frontend displays. If a user switches to AED but you haven't created an AED Price in Stripe, checkout will fall back to the USD price. Always create Stripe Price objects for every currency you want to support.


Troubleshooting

Issue Likely Cause Fix
"No Stripe price ID configured" error Plan has no Stripe Price ID for selected currency Add the Price ID in Django Admin (Step 5)
Checkout redirects to cancel URL Stripe test payment declined Use test card 4242 4242 4242 4242
Subscription stays in pending after payment Webhook not configured Set up webhook (Step 8) or verify success_url redirect is working
STRIPE_WEBHOOK_SECRET warning in logs No webhook secret in .env Expected — safe to ignore until you add a webhook endpoint
Wrong currency shown in pricing Browser locale not in the mapping User can switch manually with the currency picker