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¶
- Log into dashboard.stripe.com
- Go to Developers → API keys
- Copy the following:
- Publishable key — starts with
pk_live_...(orpk_test_...for test mode) - Secret key — starts with
sk_live_...(orsk_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)¶
[!NOTE] If
STRIPE_WEBHOOK_SECRETis 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.
- Go to Product catalogue → Add product
- Create one product per plan:
- Professional Plan
- Premium Plan
- For each product, set:
- Name: matches your plan display name
- 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:
- Inside the product, click Add price
- Set:
- Currency: select the currency (e.g. GBP, AED, EUR, USD)
- Price: the amount for that currency
- Billing period: Monthly
- Click Save — Stripe gives you a Price ID like
price_1AbCdEfGhIjKlMnO - 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.
Step 5 — Link Price IDs in Django Admin¶
- Log into Django Admin → Subscriptions → Subscription Plans
- Open Professional Plan
- Click the Payment Gateway section (collapsed by default)
- 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
- Also fill in the Regional Pricing section with the display prices:
- Click Save
- 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.
- Django Admin → Subscriptions → Payment Providers
- If no
striperecord exists, click Add: - Name:
stripe - Display name:
Card (Stripe) - Is active: ✅ checked
- Config:
{}(leave empty — keys come from environment variables) - 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¶
- Go to
/register→ select Professional plan - Verify the plan card shows your region's currency (auto-detected from browser locale)
- Complete registration → you're redirected to Stripe Checkout
- Use a test card above to complete payment
- Verify redirect back to
/register/success - Check Django Admin → Company Subscription is now
active
Step 8 — Setting Up Webhooks (Optional, Recommended for Production)¶
Webhooks allow Stripe to notify your server when payments succeed or fail — useful for handling edge cases like payment retries.
- Go to Developers → Webhooks → Add endpoint
- Endpoint URL:
https://your-domain.com/api/stripe-webhook/ - Events to listen to:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deletedinvoice.payment_succeededinvoice.payment_failed- After creating, click Reveal on the Signing secret — copy
whsec_... - Add to
backend/.env: - Restart the backend server
[!NOTE] Without webhooks, subscription activation relies entirely on the Stripe Checkout
success_urlredirect. 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 |