How to Add Stripe Embedded Checkout to Rails 8
Stripe's embedded checkout allows you to accept payments directly within your Rails app, eliminating the need to redirect users to external pages. This guide will help you to get it working with Rails 8 and Hotwire.
What You'll Build
A page that renders Stripe's embedded checkout form directly in your Rails app. That's it. No database, no user management, no webhooks - just the core integration.
Prerequisites
- Rails 8 app with Hotwire
- Stripe account (test mode or sandbox)
Step 1: Set up Dependencies
Add the following directly to your Gemfile and run bundle install:
Ruby Gems: Stripe
gem 'stripe', '~> 15.3' gem 'dotenv-rails', '~> 3.1', '>= 3.1.8'
Add the JavaScript SDK:
NPM npm install @stripe/stripe-js yarn yarn add @stripe/stripe-js
Step 2: Configure Stripe
Add the following to your .env file
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key_here
STRIPE_SECRET_KEY=sk_test_your_secret_key_here
STRIPE_PRICE_ID=price_your_product_price_id_here
Create the initializer:
# config/initializers/stripe.rb
Stripe.api_key = ENV["STRIPE_SECRET_KEY"]
Step 3: Create the Checkout Service
This service class handles the Stripe API integration with clean, focused responsibility
# app/services/checkout_service.rb class CheckoutService def initialize(return_url:) @return_url = return_url end def create_checkout_session Stripe::Checkout::Session.create({ line_items: [{ price: ENV["STRIPE_PRICE_ID"], quantity: 1 }], mode: "payment", payment_method_types: ["card"], return_url: @return_url, ui_mode: "embedded" }) end end
Step 4: Create the Stimulus Controller & Register it
Run the following command in your terminal to generate the controller and register it in the app/javascript/controllers/index.js
file
./bin/rails generate stimulus stripeCheckout
// app/javascript/controllers/stripe_checkout_controller.js import { Controller } from "@hotwired/stimulus" import { loadStripe } from '@stripe/stripe-js' export default class extends Controller { static values = { publishableKey: String, clientSecret: String } async connect() { const stripe = await loadStripe(this.publishableKeyValue) const checkout = await stripe.initEmbeddedCheckout({ clientSecret: this.clientSecretValue }); checkout.mount(this.element); } }
This Stimulus controller handles the frontend payment interface with minimal code:
Configuration: The controller expects two data attributes from the Rails view:
- publishableKey - Your Stripe public key for client-side operations
- clientSecret - The checkout session secret from your Rails controller
The connect() method runs automatically when the controller connects to a DOM element and:
- Loads the Stripe JavaScript library using your publishable key
- Initializes Stripe's embedded checkout component with the client secret
- Mounts the complete checkout form directly into the HTML element
Step 5: Update the Desired Controller
This Rails controller handles the core functionality of a Stripe checkout flow in just two actions:
The show action initiates the checkout process by:
- Creating a new CheckoutService instance with a return URL for post-payment redirects
- Generating a Stripe checkout session through the service
- Extracting the client_secret from the session to pass to the frontend for payment processing
The return action handles the post-payment flow by:
- Processing successful payments (this is where you'd typically update order status, send confirmation emails, etc.)
- Redirecting users back to the homepage with a success message
# app/controllers/checkout_controller.rb class CheckoutController < ApplicationController def show checkout_service = CheckoutService.new(return_url: return_checkout_url) checkout_session = checkout_service.create_checkout_session @client_secret = checkout_session.client_secret end def return # Handle successful payment redirect_to root_path, notice: "Payment successful!" end end
Step 6: Set up Routes
# config/routes.rb Rails.application.routes.draw do root "checkout#show" resource :checkout, only: [:show] do get :return, on: :collection end end
Step 7: Create the View
<!-- app/views/checkout/show.html.erb --> <div class="container mx-auto px-4 py-8"> <h1 class="text-2xl font-bold mb-6">Complete Your Purchase</h1> <div class="max-w-md mx-auto"> <div data-controller="stripe-checkout" data-stripe-checkout-publishable-key-value="<%= ENV['STRIPE_PUBLISHABLE_KEY'] %>" data-stripe-checkout-client-secret-value="<%= @client_secret %>"> <!-- Stripe embedded checkout loads here --> </div> </div> </div>
Simple Stripe Checkout View
This minimal Rails view creates a clean checkout page with just a few lines of HTML:
The Structure:
- Uses Tailwind CSS for styling with a centered container layout
- Displays a "Complete Your Purchase" heading
- Contains a div that connects to the Stimulus controller
The Magic Happens in the data-controller div:
- data-controller="stripe-checkout" connects to your Stimulus controller
- data-stripe-checkout-publishable-key-value passes your Stripe public key from environment variables
- data-stripe-checkout-client-secret-value passes the checkout session secret from your Rails controller
The Result: When the page loads, the Stimulus controller automatically transforms the empty div into a complete Stripe checkout form with card fields, payment buttons, and error handling.
Pro Tip: You can extract this into a partial for reusability:
Testing
- Start your server:
./bin/dev
- Visit
http://localhost:3000
- Test with card:
4242 4242 4242 4242
Don't Forget - Additional Setup Required
This guide gets Stripe embedded checkout working, but for a production application, you'll need:
Required for Production
- Webhooks - Handle payment events (
checkout.session.completed
,payment_intent.succeeded
) - Database models - Store orders, payments, and customer data
- User authentication - Track who made purchases
- Error handling - Handle Stripe API failures gracefully
That's it! You now have Stripe embedded checkout working in Rails 8. The checkout form renders directly in your app without any redirects. At Echobind, we have experience working with Stripe and can help you integrate it in a new or exisiting application.