/stripe-guides

How to redirect after successful payment with Stripe Checkout?

Learn how to redirect users after successful payment with Stripe Checkout, including setup, session creation, success handling, webhooks, and security best practices.

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.

Book a free consultation

How to redirect after successful payment with Stripe Checkout?

How to Redirect After Successful Payment with Stripe Checkout

 

Step 1: Set Up Stripe Checkout

 

First, you need to set up Stripe Checkout in your application. Make sure you have the Stripe library installed in your project.

For Node.js applications:

npm install stripe

For PHP applications:

composer require stripe/stripe-php

 

Step 2: Create a Checkout Session

 

When creating a Checkout Session, you need to specify the success and cancel URLs. These are the URLs where customers will be redirected after they complete or cancel the payment.

For Node.js with Express:

const stripe = require('stripe')('your_stripe_secret\_key');
const express = require('express');
const app = express();

app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line\_items: [
        {
          price\_data: {
            currency: 'usd',
            product\_data: {
              name: 'Your Product',
            },
            unit\_amount: 2000, // $20.00 in cents
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
      cancel\_url: 'https://yourwebsite.com/cancel',
    });

    res.json({ id: session.id });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

For PHP:

 ['card'],
  'line\_items' => [[
    'price\_data' => [
      'currency' => 'usd',
      'product\_data' => [
        'name' => 'Your Product',
      ],
      'unit\_amount' => 2000,
    ],
    'quantity' => 1,
  ]],
  'mode' => 'payment',
  'success_url' => 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
  'cancel\_url' => 'https://yourwebsite.com/cancel',
]);

echo json_encode(['id' => $checkout_session->id]);
?>

 

Step 3: Redirect the Customer to the Checkout Page

 

After creating the session, redirect the customer to the Stripe Checkout page using the session ID.

For client-side JavaScript with Stripe.js:

// Add Stripe.js to your HTML



 

Step 4: Handle the Success Redirect

 

Create a success page that will handle the redirect after a successful payment. This page will receive the session ID in the URL query parameters.

For Node.js with Express:

app.get('/success', async (req, res) => {
  const { session\_id } = req.query;
  
  try {
    // Retrieve the session to get payment details
    const session = await stripe.checkout.sessions.retrieve(session\_id);
    
    // Verify payment was successful
    if (session.payment\_status === 'paid') {
      // Payment was successful, update your database or perform other actions
      
      // Render success page or redirect
      res.render('success', { 
        paymentId: session.payment\_intent,
        customerEmail: session.customer\_details.email
      });
    } else {
      // Payment was not successful
      res.redirect('/payment-failed');
    }
  } catch (error) {
    console.error('Error retrieving session:', error);
    res.redirect('/error');
  }
});

For PHP:

payment\_status === 'paid') {
      // Payment was successful, update your database or perform other actions
      
      // Display success page
      echo "Thank you for your payment!";
      echo "Payment ID: " . $session->payment\_intent;
      echo "Customer Email: " . $session->customer\_details->email;
    } else {
      // Payment was not successful
      header('Location: /payment-failed.php');
      exit;
    }
  } catch (\Exception $e) {
    echo "Error: " . $e->getMessage();
    header('Location: /error.php');
    exit;
  }
} else {
  header('Location: /');
  exit;
}
?>

 

Step 5: Implement Webhook for Payment Verification (Recommended)

 

It's important to implement a webhook to verify payments server-side. This ensures that you capture all payment events, even if the user closes the browser before being redirected.

For Node.js with Express:

const bodyParser = require('body-parser');

// Use the raw text body parser for webhook signatures
app.post('/webhook', bodyParser.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  const endpointSecret = 'your_webhook_signing\_secret';
  
  let event;
  
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.log(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  
  // Handle the checkout.session.completed event
  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;
    
    // Fulfill the order
    if (session.payment\_status === 'paid') {
      fulfillOrder(session);
    }
  }
  
  res.status(200).send();
});

function fulfillOrder(session) {
  // Update your database
  // Send an email to the customer
  console.log(`Order fulfilled: ${session.id}`);
}

For PHP:

type == 'checkout.session.completed') {
  $session = $event->data->object;
  
  // Fulfill the order
  if ($session->payment\_status === 'paid') {
    fulfillOrder($session);
  }
}

http_response_code(200);

function fulfillOrder($session) {
  // Update your database
  // Send an email to the customer
  error\_log("Order fulfilled: " . $session->id);
}
?>

 

Step 6: Add Security Measures

 

To enhance security, add measures to verify that the payment is legitimate before providing access to purchased content or services.

// In your success route handler, add additional verification
app.get('/success', async (req, res) => {
  const { session\_id } = req.query;
  
  // Verify that the session\_id exists
  if (!session\_id) {
    return res.redirect('/error');
  }
  
  try {
    const session = await stripe.checkout.sessions.retrieve(session\_id);
    
    // Verify that the session belongs to the current user (if applicable)
    // This could involve checking against a user ID stored in the session metadata
    if (session.metadata.userId !== req.user.id) {
      return res.redirect('/unauthorized');
    }
    
    // Verify payment was successful
    if (session.payment\_status === 'paid') {
      // Additional verification: Check if this order has already been processed
      const orderExists = await checkOrderExists(session.id);
      
      if (orderExists) {
        // Order already processed, just show success page
        return res.render('success', { paymentId: session.payment\_intent });
      }
      
      // Process the order and save to database
      await processOrder(session);
      
      // Render success page
      res.render('success', { 
        paymentId: session.payment\_intent,
        customerEmail: session.customer\_details.email
      });
    } else {
      res.redirect('/payment-failed');
    }
  } catch (error) {
    console.error('Error verifying payment:', error);
    res.redirect('/error');
  }
});

 

Step 7: Testing the Integration

 

Before going live, test your integration using Stripe's test mode.

  1. Use Stripe's test card numbers (e.g., 4242 4242 4242 4242)
  2. Test both successful and failed payments
  3. Test webhook events using the Stripe CLI
// Install Stripe CLI and run the following command to forward webhook events
stripe listen --forward-to http://localhost:3000/webhook

 

Step 8: Advanced: Custom Success Page with Dynamic Content

 

Create a more personalized success page by dynamically loading content based on the payment details.

app.get('/success', async (req, res) => {
  const { session\_id } = req.query;
  
  try {
    // Retrieve the session
    const session = await stripe.checkout.sessions.retrieve(session\_id, {
      expand: ['line_items', 'customer', 'payment_intent']
    });
    
    if (session.payment\_status === 'paid') {
      // Get purchased items details
      const purchasedItems = session.line\_items.data;
      
      // Get customer information
      const customer = session.customer;
      
      // Get payment details
      const paymentIntent = session.payment\_intent;
      
      // Generate download links, access codes, or other resources
      const resources = await generateResources(purchasedItems);
      
      // Send confirmation email
      await sendConfirmationEmail(customer.email, purchasedItems, paymentIntent.id);
      
      // Render success page with all details
      res.render('detailed-success', {
        customer: customer,
        paymentId: paymentIntent.id,
        amount: paymentIntent.amount / 100, // Convert cents to dollars
        items: purchasedItems,
        resources: resources,
        date: new Date(paymentIntent.created \* 1000).toLocaleDateString()
      });
    } else {
      res.redirect('/payment-pending');
    }
  } catch (error) {
    console.error('Error processing success page:', error);
    res.redirect('/error');
  }
});

// Helper functions
async function generateResources(items) {
  // Generate download links, access codes, etc.
  // Based on purchased items
  return items.map(item => ({
    id: item.id,
    name: item.description,
    downloadLink: `https://yoursite.com/downloads/${item.id}`,
    accessCode: generateUniqueCode()
  }));
}

function generateUniqueCode() {
  return Math.random().toString(36).substring(2, 15) + 
         Math.random().toString(36).substring(2, 15);
}

async function sendConfirmationEmail(email, items, paymentId) {
  // Implement email sending logic
  console.log(`Sending confirmation email to ${email} for payment ${paymentId}`);
}

 

Step 9: Handling Failed Payments

 

Create a page to handle failed payments, providing clear instructions for customers.

app.get('/payment-failed', (req, res) => {
  res.render('payment-failed', {
    supportEmail: '[email protected]',
    supportPhone: '+1-800-123-4567',
    tryAgainUrl: '/checkout'
  });
});

 

Step 10: Session Recovery for Abandoned Checkouts

 

Implement session recovery for customers who abandon the checkout process.

// Store session ID in your database when creating the checkout session
app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line\_items: [
        {
          price\_data: {
            currency: 'usd',
            product\_data: {
              name: 'Your Product',
            },
            unit\_amount: 2000,
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: 'https://yourwebsite.com/success?session_id={CHECKOUT_SESSION_ID}',
      cancel\_url: 'https://yourwebsite.com/cancel',
      // Add metadata for tracking
      metadata: {
        userId: req.user.id,
        cartId: req.body.cartId
      }
    });

    // Store session in database
    await saveCheckoutSession(session.id, req.user.id, req.body.cartId);

    res.json({ id: session.id });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Function to save checkout session
async function saveCheckoutSession(sessionId, userId, cartId) {
  // Implementation depends on your database
  console.log(`Saving session ${sessionId} for user ${userId} with cart ${cartId}`);
}

// Endpoint to recover abandoned sessions
app.get('/recover-checkout', async (req, res) => {
  try {
    // Get user's latest abandoned session
    const abandonedSession = await getAbandonedSession(req.user.id);
    
    if (abandonedSession) {
      // Check if session is still valid
      const session = await stripe.checkout.sessions.retrieve(abandonedSession.sessionId);
      
      if (session.status === 'open' && !session.expired) {
        // Redirect to existing checkout
        return res.redirect(session.url);
      }
    }
    
    // If no valid session exists, create a new one
    res.redirect('/checkout');
  } catch (error) {
    console.error('Error recovering session:', error);
    res.redirect('/checkout');
  }
});

async function getAbandonedSession(userId) {
  // Implementation depends on your database
  console.log(`Getting abandoned session for user ${userId}`);
  // Return null if no abandoned session exists
  return null;
}

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!

Book a Free Consultation

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