Skip to content
BlackFig

Replit Stripe payments: from sandbox to live without breaking checkout

AntPublished Updated 4 min read

Checked against Replit's documentation on 10 November 2026.

Quick answer

Replit gives you two ways to add Stripe: the Replit Stripe connector, which starts in a sandbox and goes live through the Replit Integrated Payments app, or your own keys in Secrets. Use one method per environment, test with Stripe's test cards, and only mark an order paid when a verified payment event arrives.

On this page
  1. Symptom
  2. Why it happens
  3. The fix
  4. How to prevent it
  5. When to get help

Symptom

Checkout works in the preview with a test card. Then one of these:

  • A customer pays, Stripe shows the payment, but the order or booking in your app still says unpaid.
  • Webhook events fail signature checks in production.
  • Agent keeps asking you to paste keys or add a webhook, even though Stripe is already connected.
  • You finished business verification in Stripe, but Replit still shows the step as open.

Why it happens

Two methods that don't mix

Replit supports two setups, and its docs are firm that you use one per environment:

The two ways to connect Stripe on Replit
Replit Stripe connectorYour own Stripe keys
PlansCore and Pro, not StarterYou manage it yourself
Where it livesIntegrations, then StripeSecrets
TestingA Stripe sandbox Agent sets upYour test keys and the Stripe CLI
Going liveInstall the Replit Integrated Payments app from PublishLive keys in Production app secrets
Payment eventsAgent wires up the integrationYou register and verify your own webhook endpoint
Products and pricesSandbox changes sync to your app's databaseCreate them again in live mode: Stripe objects don't cross between sandbox and live

Combining them in one environment causes connection conflicts, even when Agent suggests adding a webhook. You can use different methods in each environment, for example the connector's sandbox while building and your own live keys in production.

The sandbox is a separate account

Sandbox payments never touch real money. Replit's docs warn the sandbox integration is not production-ready. Publishing checks for a live connection and stops if there isn't one.

With the connector, product and price changes you make in the sandbox dashboard sync into your app's database. You can see them in the Database tool under My Data by switching the schema selector to Stripe. That is handy for checking what Agent built against, and a good reminder that the payment state your app shows is a copy. The source of truth is Stripe.

One more boundary: if you are building a mobile app and need in-app subscriptions, Replit points you to RevenueCat rather than Stripe.

The success page is not proof of payment

If your app marks an order paid when the customer lands on the success page, anyone who closes the tab early leaves a paid order marked unpaid. Anyone who finds the success URL gets the reverse. Replit's own troubleshooting covers the first case: connect the payment success event to the order status in the database.

The fix

  1. Pick one method per environment. Open Integrations, then Stripe, and open Secrets. If the connector is active and you also have STRIPE_SECRET_KEY in the same environment, remove one. To remove the connector, go to Integrations, then Stripe, then Manage, and disconnect the sandbox and live accounts.

  2. Test properly in the sandbox. Open the preview in a new tab and pay with 4242 4242 4242 4242, any future expiry and any CVC. Then pay with the decline card 4000 0000 0000 0002 and confirm nothing gets marked paid. Finally, pay and close the tab before the success page loads. The order should still turn paid.

  3. Going live with the connector. Select Publish, then Install Stripe app. When Stripe asks for an account, choose your live account, not a sandbox. If the flow picks the sandbox version of the app, choose the Live Mode version yourself. Keep the Stripe window open until it finishes. If your account isn't activated, Stripe runs business verification first. Back in Replit, select Got It to clear the step, then publish.

  4. Going live with your own keys. Under Publishing, Adjust settings, Production app secrets, set STRIPE_SECRET_KEY to a live key starting sk_live_ or rk_live_ (the name must match exactly) and STRIPE_PUBLISHABLE_KEY to your live publishable key. Register a separate live webhook endpoint in Stripe at your published URL plus the webhook path. Choose the Snapshot payload and the checkout.session.completed event, because the handler below reads the full session from the event. Set STRIPE_WEBHOOK_SECRET to that endpoint's signing secret, not the one stripe listen printed, then republish. On a first publish, get the URL first, then finish the endpoint and republish before taking payments.

  5. Mark orders paid from the webhook. On the own-keys path, the handler must read the raw body, so register it before express.json():

// server/stripeWebhook.ts
import express from "express";
import Stripe from "stripe";
import { eq } from "drizzle-orm";
import { db } from "./db";
import { orders } from "@shared/schema";
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export const stripeWebhook = express.Router();
 
stripeWebhook.post(
  "/api/stripe/webhook",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event: Stripe.Event;
    try {
      event = stripe.webhooks.constructEvent(
        req.body,
        req.headers["stripe-signature"] as string,
        process.env.STRIPE_WEBHOOK_SECRET!
      );
    } catch (err) {
      console.error("Stripe signature check failed:", (err as Error).message);
      return res.sendStatus(400);
    }
 
    if (event.type === "checkout.session.completed") {
      const session = event.data.object;
      const orderId = session.metadata?.orderId; // set when you create the session
      if (orderId && session.payment_status === "paid") {
        await db
          .update(orders)
          .set({ status: "paid", stripeSessionId: session.id })
          .where(eq(orders.id, orderId));
      }
    }
    res.json({ received: true });
  }
);
// server/index.ts: order matters
app.use(stripeWebhook);   // raw body for signature checks
app.use(express.json());  // everything else

The update is safe to run twice, which matters because Stripe retries deliveries. On the connector path, ask Agent to show you the exact code that changes an order's status after payment, then run the close-the-tab test from step 2 against it.

  1. Make one real payment. Replit's own guide suggests a low-cost real purchase with your own card before you share the app. Refund it afterwards.

How to prevent it

  • Write the payment method into replit.md, for example "Stripe uses the Replit connector in development and production. Never add STRIPE_SECRET_KEY." Agent reads that file in every session.
  • On the own-keys path, create products and prices in the live account yourself. Stripe's docs say sandbox objects can't be used in live mode.
  • Keep the three test payments from step 2 as a release check, and run them after every change near checkout.

When to get help

Payments are where a small bug costs real money and real trust. The prototype to product guide covers the rest of going live, and adding Stripe to an AI-built app goes deeper on subscriptions, refunds and failed payments. Payment problems often arrive with sign-in problems, so see Replit auth not working too.

Run the free vibe-code health check first. If the app needs more than a payment fix, the Rescue Sprint covers payments, auth and the rest in one go.

Sources

The launch checklist for founders, as a PDF

About 40 checks across legal, payments, domain and email, analytics, backups and support.

You get the PDF now and two short follow-ups over the next week. Reply “stop” and the rest never arrive.

Questions founders ask

Can I use the Replit Stripe connector on the free plan?

No. Replit's docs say the Stripe connector is available on paid plans (Core and Pro), and Agent prompts you to upgrade when it proposes it. Your own keys in Secrets are the alternative.

Do I need to paste live Stripe keys into Replit?

Not with the connector. Installing the Replit Integrated Payments app from the Publish pane connects your live account. With your own keys, you set the live values yourself under Production app secrets.

Why does publishing say I need to connect a live Stripe account?

The sandbox only takes test payments. Either install the Stripe app from the Publish pane, or, if production uses your own keys, store a live key starting sk_live_ or rk_live_ under the exact name STRIPE_SECRET_KEY.

Ant

Product designer who builds MVPs, based in Nottingham. 10+ years designing digital products, and the only person at BlackFig. More about me