HomeBlogWeb Development
Web Development

How to Integrate Stripe into Your Web App

Checkout, webhooks, and subscriptions — plain HTTP examples that work with any backend language or framework.

JS

Jovi Studio

9 min read · Web Development

When Stripe is the right gateway

Stripe's documentation, subscription infrastructure, and webhook reliability are ahead of every other gateway. If your users are in the US, UK, or Europe — or you're building a B2B SaaS where complex billing matters (trials, proration, usage-based pricing) — Stripe is the right foundation. Just don't expect it to handle Nigerian or Ghanaian cards reliably. It doesn't, and it's not trying to. For African markets, Paystack or Flutterwave belong in the same conversation.

How Stripe Checkout works

Your server creates a Checkout Session via the Stripe API — Stripe returns a URL for the hosted checkout page. You redirect the user there. They pay. Stripe sends them back to your success URL. You verify via webhook before fulfilling. The session handles everything in between: card validation, 3D Secure, Apple Pay, Google Pay, local payment methods by region.

1. Get your API keys

From the Stripe dashboard under Developers > API keys. The secret key belongs on the server only:

bash
# Test keys — safe for development
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_... # From Stripe dashboard > Webhooks

2. Create a Checkout Session (server-side)

Plain HTTP — works in any language. The Stripe SDK is convenient but optional:

javascript
async function createStripeCheckoutSession({ priceId, customerEmail }) {
  const response = await fetch('https://api.stripe.com/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      mode: 'payment',
      'line_items[0][price]': priceId,
      'line_items[0][quantity]': '1',
      success_url: `${process.env.APP_URL}/payment/success`,
      cancel_url: `${process.env.APP_URL}/pricing`,
      customer_email: customerEmail,
    }),
  });

  const session = await response.json();
  if (!session.url) throw new Error('Stripe session creation failed');

  // Redirect user to session.url
  return session.url;
}

3. Verify webhooks — this is not optional

A user can hit your success URL without paying. Never fulfil an order based on the redirect alone. Stripe signs every webhook event — verify the signature before acting on it:

javascript
import Stripe from 'stripe';

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

async function handleStripeWebhook(rawBody, signatureHeader) {
  const event = stripe.webhooks.constructEvent(
    rawBody,
    signatureHeader,
    process.env.STRIPE_WEBHOOK_SECRET
  );

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    // Fulfil using session.id as idempotency key
  }

  return { received: true };
}

4. Subscriptions with Stripe Checkout

Switch mode from 'payment' to 'subscription' and pass a recurring price ID — Stripe handles the rest:

javascript
// Same function as above, just change mode
async function createStripeSubscriptionSession({ priceId, customerEmail }) {
  const response = await fetch('https://api.stripe.com/v1/checkout/sessions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      mode: 'subscription', // Changed from 'payment'
      'line_items[0][price]': priceId, // Must be a recurring price
      'line_items[0][quantity]': '1',
      success_url: `${process.env.APP_URL}/dashboard`,
      cancel_url: `${process.env.APP_URL}/pricing`,
      customer_email: customerEmail,
    }),
  });

  const session = await response.json();
  return session.url;

  // Listen for: customer.subscription.created, invoice.payment_failed
}

Test card numbers

Use these with any future expiry and any 3-digit CVV:

  • 4242 4242 4242 4242 — payment succeeds
  • 4000 0000 0000 9995 — card declined
  • 4000 0025 0000 3155 — requires 3D Secure authentication
  • 4000 0000 0000 0341 — attaches successfully but payment fails
  • Run stripe listen --forward-to localhost:3000/your-webhook-path for local webhook testing

Common questions

Need payments that work for your market — not just the US?

We build payment integrations that fit where your users actually are — Stripe for global, Paystack for Nigeria, Flutterwave for the wider continent. Tell us your stack and your markets.