Lovable + Stripe Security Blueprint

Share

To secure a Lovable + Stripe stack, you need to: (1) keep Secret Keys server-side only, (2) verify webhook signatures on every event, (3) use Stripe Checkout or Elements for PCI compliance, and (4) create payment intents on the server. This blueprint covers key management, webhook security, and payment flow best practices.

Setup Time1-2 hours

TL;DR

Stripe integration requires careful key management and webhook verification. Key rules: never expose the Secret Key (sk_*) in client code, always verify webhook signatures, use Stripe Checkout or Elements for PCI compliance, and create payment intents on the server only. Test with Stripe's test mode before going live.

Stripe Key Management

Key TypePrefixWhere to Use
Publishable Keypk_test_ / pk_live_Client-side (safe to expose)
Secret Keysk_test_ / sk_live_Server-side only (NEVER expose)
Webhook Secretwhsec_Server-side only

Part 1: Stripe Environment Variables

Environment configuration
# Client-side (safe)
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxx

# Server-side only (NEVER in client code)
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx

Critical: If the Secret Key (sk_*) is ever exposed, rotate it immediately in the Stripe dashboard. An exposed key allows full access to your Stripe account.

Part 2: Stripe Webhook Verification

Verify webhook signatures
import Stripe from 'stripe';

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

export async function handleWebhook(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature');

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature!,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    console.error('Webhook signature verification failed');
    return new Response('Invalid signature', { status: 400 });
  }

  // Handle the verified event
  switch (event.type) {
    case 'checkout.session.completed':
      // Fulfill the purchase
      break;
    case 'payment_intent.succeeded':
      // Handle successful payment
      break;
  }

  return new Response('OK', { status: 200 });
}

Part 3: Stripe Payment Flow

Create checkout session (server-side)
// This must run on the server, not the client
export async function createCheckoutSession(userId: string, priceId: string) {
  const session = await stripe.checkout.sessions.create({
    customer_email: userEmail,  // Or use customer ID
    line_items: [{ price: priceId, quantity: 1 }],
    mode: 'subscription',
    success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.APP_URL}/cancel`,
    metadata: { userId },  // For webhook processing
  });

  return session.url;
}

Security Checklist

Pre-Launch Checklist for Lovable + Stripe

Secret Key only in server-side code

Webhook signature verification implemented

Using Stripe Checkout or Elements (PCI compliance)

Payment intents created server-side only

Test mode used for development

Live keys in production environment only

Webhook endpoint secured (HTTPS)

Idempotency keys used for retries

Is the publishable key safe to expose?

Yes, the publishable key (pk_*) is designed for client-side use. It can only be used to create tokens or confirm payments, not to read data or make charges.

Why verify webhook signatures?

Without verification, attackers could send fake webhook events to your endpoint, potentially granting free access or disrupting your service. Always verify the signature.

Alternative Stack Options

Consider these related blueprints for different stack combinations:

Integrating Stripe with Lovable?

Scan for key exposure and webhook issues.

Start Free Scan
Security Blueprints

Lovable + Stripe Security Blueprint