/how-to-build-replit

How to Build a Subscription system with Replit

Learn how to build a subscription system with Replit step by step. Create recurring payments and manage subscribers easily for your app or website.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

How to Build a Subscription system with Replit

To build a subscription system on Replit, you’ll use a backend (like Express in Node.js) to handle payment logic, connect it with a payment provider such as Stripe, and store user subscription data in a database (Replit DB or external DB like Supabase). The workflow is simple: the frontend calls your backend when someone clicks “Subscribe”, the backend creates a checkout session using Stripe, and once payment succeeds, Stripe calls your backend webhook to confirm the subscription. This system runs fine inside Replit if you handle secrets properly and understand Replit’s always-on limits.

 

Step 1: Create a new Node.js Repl

 

Select the “Node.js” template. This will give you a index.js starter file. Your main backend code will go here. Replit automatically runs index.js when you click Run.

 

npm install express stripe body-parser

 

This installs Express (for HTTP handling), Stripe (for subscription billing), and Body-parser (for request parsing).

 

Step 2: Set up your environment secrets

 

In Replit’s left sidebar, click the 🔒 Secrets icon. Add:

  • STRIPE_SECRET_KEY — from your Stripe Dashboard (Developers → API keys).
  • STRIPE_WEBHOOK_SECRET — from your Stripe Dashboard when you create a webhook endpoint.

Never hardcode these directly in your code; Replit Secrets keep them safe even if your code is public.

 

Step 3: Basic Express app

 

Inside index.js, set up a small Express server. This handles routing (URLs) for checkout and webhook events.

 

import express from "express";
import Stripe from "stripe";
import bodyParser from "body-parser";

const app = express();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); // Reads your Replit secret

app.use(bodyParser.json());

// Route for homepage or frontend
app.get("/", (req, res) => {
  res.send("Welcome! You can POST to /create-checkout-session to subscribe.");
});

// Route to start checkout session
app.post("/create-checkout-session", async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      mode: "subscription",
      payment_method_types: ["card"],
      line_items: [
        {
          price: "price_1234567890", // Replace with your Stripe Price ID
          quantity: 1
        },
      ],
      success_url: `${req.headers.origin}/success`,
      cancel_url: `${req.headers.origin}/cancel`,
    });
    res.json({ url: session.url });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "server_error" });
  }
});

 

Place this code directly in index.js. Replace the price\_1234567890 with an actual price ID from your Stripe dashboard (Products → Prices).

 

Step 4: Handling Stripe Webhooks

 

You’ll need a webhook route to confirm successful subscriptions. Stripe will send a POST request when subscription events happen. You’ll store or update this data in your database.

 

// Raw body parser required for Stripe signature validation
import { buffer } from "micro"; 

app.post("/webhook", bodyParser.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["stripe-signature"];
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    console.error("Webhook signature failed.", err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === "checkout.session.completed") {
    const session = event.data.object;
    // Save subscription data (user id, stripeCustomerId, etc.)
    console.log("Subscription successful for:", session.customer_email);
  }

  res.sendStatus(200);
});

 

You can temporarily test webhooks locally using a tool like stripe listen CLI, but on Replit you can just use your public URL (visible as soon as you click Run). Copy that URL into your Stripe Dashboard Webhooks endpoint (e.g., https://your-repl-name.your-username.repl.co/webhook).

 

Step 5: Optional — Save subscriptions

 

If you just want to track who subscribed, you can use Replit DB. Create a new file db.js containing:

 

import Database from "@replit/database";
const db = new Database();
export default db;

 

Then in index.js inside your webhook after a successful payment:

 

import db from "./db.js";

// inside if (event.type === "checkout.session.completed")
await db.set(session.customer_email, {
  stripeCustomerId: session.customer,
  status: "active",
});

 

This will store the subscriber information in Replit DB. You can later check it with:

 

const data = await db.getAll();
console.log(data);

 

Step 6: Frontend Integration

 

If you have a frontend (React, HTML, etc.), your “Subscribe” button should call the backend endpoint you made earlier (/create-checkout-session) and redirect users to Stripe checkout.

 

// Example frontend fetch
async function subscribe() {
  const res = await fetch("/create-checkout-session", { method: "POST" });
  const data = await res.json();
  window.location.href = data.url; // Redirect to Stripe
}

 

Step 7: Deployment considerations

 

  • Replit Always-on: Projects sleep after inactivity unless you upgrade to Replit Deployments or high-tier plans. For production subscriptions, use an active server plan so webhooks don’t fail.
  • Secrets: Manage Stripe keys only in Replit secrets.
  • Database: Replit DB works for simple cases, but for professional apps, connect external DB like Supabase, MongoDB Atlas, or PostgreSQL.

 

This setup gives you a **fully functional subscription flow** on Replit — from a subscribe button on your frontend, to payment processing in your backend, to webhook handling and data persistence. It’s lightweight, reliable for development, and a solid approach before moving to a more robust hosting environment.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us

How to Handle Stripe Webhooks for Subscription Updates in Replit




How to Create a Stripe-Powered Subscription Checkout in Replit




How to Cancel a User’s Subscription in Replit




Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Best Practices for Building a Subscription system with Replit

A reliable subscription system on Replit usually combines a secure backend (Node/Express), a payment provider (like Stripe), and a simple frontend for users to sign up and manage subscriptions. The best practice is not to store payment or subscription logic directly in the client-side code — instead, keep it in your backend, hide all sensitive keys in Replit Secrets, and handle communication between your frontend and Stripe safely through your backend routes.

 

Basic Setup Outline

 

Let’s imagine you have a regular Node.js + Express setup in Replit (which most full-stack Repls use). You’ll handle subscriptions in these steps:

  • Step 1: Create an Express backend server (if you don’t already have one) in index.js.
  • Step 2: Set up Stripe server-side logic in a new file stripe.js.
  • Step 3: Secure your Stripe secret key using Replit Secrets, not in your code.
  • Step 4: Create API routes (like /create-checkout-session) that your frontend can call to start a subscription checkout.
  • Step 5: Handle Stripe webhooks to track when a subscription is created, canceled, or renewed.

 

1. Add Stripe Secret to Replit Secrets

 

On the left sidebar, click the “🔒 Secrets (Environment Variables)” icon and add your key pair:

  • Key: STRIPE_SECRET_KEY
  • Value: your secret starting with sk_live_... (from your Stripe dashboard)

Replit will automatically make this available as process.env.STRIPE_SECRET_KEY in your code. This ensures you never accidentally publish sensitive credentials in a public Repl.

 

2. Install Dependencies

 

npm install express stripe body-parser

 

3. Create or Edit index.js

 

This is your Replit server entry file. It will serve both your frontend and backend routes.

// index.js
import express from "express"
import bodyParser from "body-parser"
import { createCheckoutSession } from "./stripe.js"

const app = express()
app.use(bodyParser.json())

// Simple health route (optional)
app.get("/", (req, res) => {
  res.send("Server is running and ready for subscriptions!")
})

// Route to start subscription checkout
app.post("/create-checkout-session", async (req, res) => {
  try {
    const session = await createCheckoutSession(req.body.priceId)
    res.json({ url: session.url })
  } catch (err) {
    console.error(err)
    res.status(500).json({ error: "Something went wrong" })
  }
})

// Replit automatically provides the port
app.listen(3000, () => console.log("Server running on port 3000"))

 

4. Create a new file stripe.js

 

This file will handle all your Stripe logic separately to keep your main server file cleaner.

// stripe.js
import Stripe from "stripe"

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

// Create a new checkout session for a subscription
export async function createCheckoutSession(priceId) {
  // priceId is the Stripe Price object ID for your plan, e.g. "price_12345"
  const session = await stripe.checkout.sessions.create({
    mode: "subscription",
    payment_method_types: ["card"],
    line_items: [
      { price: priceId, quantity: 1 }
    ],
    success_url: "https://your-repl-name.username.repl.co/success",
    cancel_url: "https://your-repl-name.username.repl.co/cancel"
  })
  return session
}

 

5. Frontend Integration (React or Plain JS)

 

On your frontend (for example, inside src/App.js or script.js), you’ll call this API to start the checkout flow. This part can differ depending on your stack, but the idea is the same.

// Example: src/App.js (React)
async function handleSubscribe() {
  const priceId = "price_12345" // Replace with your real Stripe price ID
  const res = await fetch("/create-checkout-session", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ priceId })
  })
  const data = await res.json()
  window.location.href = data.url // redirect to Stripe checkout
}

 

6. Handling Stripe Webhooks (Recommended for Real Deployments)

 

Webhooks allow Stripe to notify your app about subscription events (like when a payment succeeds or a user cancels). You’ll create a route like /webhook in index.js.

// In index.js (add after other routes)
app.post("/webhook", bodyParser.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["stripe-signature"]
  let event

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET)
  } catch (err) {
    console.error("Webhook signature failed:", err)
    return res.sendStatus(400)
  }

  // Handle successful subscription creation, etc.
  if (event.type === "customer.subscription.created") {
    console.log("New subscription created:", event.data.object.id)
  }

  res.json({ received: true })
})

Remember: add STRIPE_WEBHOOK_SECRET to Replit Secrets.

 

7. Database (Optional but Useful)

 

For storing user status after checkout (like “premium” flag), use a simple database such as SQLite (comes with Replit), or connect to an external one like Supabase. Keep your DB connection logic in a separate file db.js and update it from your webhook route when Stripe confirms a subscription.

 

Practical Tips for Using Replit

 

  • Never hard-code keys or URLs. Always use Replit Secrets or dynamic URLs using process.env.REPL_SLUG and process.env.REPL_OWNER.
  • Keep your session URLs public only for testing. Use Stripe’s test mode first (keys starting with sk\_test).
  • Prevent your Repl from sleeping during active development by keeping the tab open — paid Replit plans can keep it always on for production testing.
  • Use the built-in “Multipayer” button for pair debugging when something doesn’t work — subscription flow debugging is easier shared live.

 

In short: The winning Replit pattern is — Stripe logic isolated in stripe.js, backend in index.js, keys hidden in Secrets, frontend only triggers API calls, and database or webhook keeps track of subscription state. That’s the clean and real way to build a subscription system on Replit that actually works reliably and safely.

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022