Install the Stripe Ruby gem, configure your secret key, and make API calls from a Rails application. This guide walks through setting up the gem, creating charges, and handling webhooks so you can accept payments in any Ruby or Rails project within minutes.
Accepting Payments in Ruby on Rails with the Stripe Gem
The official Stripe Ruby gem gives you full access to the Stripe API from any Ruby application. In a Rails project you can create charges, manage customers, and listen for webhook events with just a few lines of code. This tutorial covers gem installation, API key configuration, creating a PaymentIntent, and verifying webhook signatures.
Prerequisites
- A Stripe account with access to your secret key (sk_test_...)
- Ruby 3.0 or later installed locally
- A Rails 7+ application (or plain Ruby project)
- Bundler for dependency management
Step-by-step guide
Install the Stripe gem
Install the Stripe gem
Add the Stripe gem to your Gemfile and run bundle install. This pulls in the official Stripe client library that wraps every Stripe API endpoint.
1# Gemfile2gem 'stripe', '~> 10.0'34# Then run:5# bundle installExpected result: The stripe gem is installed and available in your Rails application.
Configure your API key
Configure your API key
Set your Stripe secret key in an initializer. Never hard-code the key — use an environment variable. The sk_test_ key is for test mode; switch to sk_live_ for production.
1# config/initializers/stripe.rb2Stripe.api_key = ENV.fetch('STRIPE_SECRET_KEY')3Stripe.api_version = '2024-12-18'Expected result: Stripe is configured and ready to make API calls when the Rails app boots.
Create a PaymentIntent from a controller
Create a PaymentIntent from a controller
Add a controller action that creates a PaymentIntent. The amount is in cents — 2000 means $20.00. Return the client_secret to your frontend so Stripe.js can confirm the payment.
1# app/controllers/payments_controller.rb2class PaymentsController < ApplicationController3 skip_before_action :verify_authenticity_token, only: [:create]45 def create6 intent = Stripe::PaymentIntent.create(7 amount: params[:amount].to_i, # amount in cents8 currency: 'usd',9 automatic_payment_methods: { enabled: true }10 )11 render json: { client_secret: intent.client_secret }12 rescue Stripe::StripeError => e13 render json: { error: e.message }, status: 42214 end15endExpected result: POST /payments returns a JSON object with a client_secret string that your frontend uses to confirm the payment.
Handle webhooks
Handle webhooks
Create a webhook endpoint to receive events like payment_intent.succeeded. Always verify the webhook signature to ensure the event came from Stripe.
1# app/controllers/webhooks_controller.rb2class WebhooksController < ApplicationController3 skip_before_action :verify_authenticity_token45 def stripe6 payload = request.body.read7 sig_header = request.env['HTTP_STRIPE_SIGNATURE']8 endpoint_secret = ENV.fetch('STRIPE_WEBHOOK_SECRET')910 event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret)1112 case event.type13 when 'payment_intent.succeeded'14 handle_successful_payment(event.data.object)15 end1617 head :ok18 rescue Stripe::SignatureVerificationError19 head :bad_request20 end2122 private2324 def handle_successful_payment(payment_intent)25 Rails.logger.info("Payment succeeded: #{payment_intent.id}")26 end27endExpected result: Stripe sends POST requests to your webhook URL and your app processes them securely.
Test with a test card
Test with a test card
Use Stripe's test card number to simulate a successful payment. Make sure your Stripe Dashboard is in test mode and you are using sk_test_ keys.
1# Test card details:2# Number: 4242 4242 4242 42423# Expiry: any future date (e.g. 12/34)4# CVC: any 3 digits (e.g. 123)5# ZIP: any 5 digits (e.g. 10001)Expected result: The payment succeeds in test mode, a PaymentIntent with status 'succeeded' appears in your Stripe Dashboard under Test Data.
Complete working example
1# app/controllers/payments_controller.rb2# Full PaymentIntent controller for Rails + Stripe3#4# Routes (add to config/routes.rb):5# post '/payments', to: 'payments#create'6# post '/webhooks/stripe', to: 'webhooks#stripe'78class PaymentsController < ApplicationController9 skip_before_action :verify_authenticity_token, only: [:create]1011 # POST /payments12 # Body: { "amount": 2000, "currency": "usd" }13 def create14 intent = Stripe::PaymentIntent.create(15 amount: params[:amount].to_i,16 currency: params[:currency] || 'usd',17 automatic_payment_methods: { enabled: true },18 metadata: { order_id: params[:order_id] }19 )2021 render json: {22 client_secret: intent.client_secret,23 payment_intent_id: intent.id24 }25 rescue Stripe::CardError => e26 render json: { error: e.message }, status: 40227 rescue Stripe::InvalidRequestError => e28 render json: { error: e.message }, status: 40029 rescue Stripe::StripeError => e30 render json: { error: 'Payment processing failed. Please try again.' }, status: 50031 end3233 # GET /payments/:id/status34 def status35 intent = Stripe::PaymentIntent.retrieve(params[:id])36 render json: {37 status: intent.status,38 amount: intent.amount,39 currency: intent.currency40 }41 rescue Stripe::StripeError => e42 render json: { error: e.message }, status: 40443 end44endCommon mistakes when using Stripe API with Ruby
Why it's a problem: Hard-coding the Stripe secret key in source code
How to avoid: Always use ENV.fetch('STRIPE_SECRET_KEY') and store the key in Rails credentials or a .env file.
Why it's a problem: Passing the amount in dollars instead of cents
How to avoid: Stripe expects amounts in the smallest currency unit. For USD, 2000 = $20.00.
Why it's a problem: Not verifying webhook signatures
How to avoid: Always use Stripe::Webhook.construct_event with your endpoint secret to prevent forged events.
Why it's a problem: Using live keys in development
How to avoid: Use sk_test_ and pk_test_ keys during development. Only switch to live keys in production.
Best practices
- Pin the Stripe gem to a major version (e.g., '~> 10.0') to avoid breaking changes
- Set Stripe.api_version explicitly so your app is not affected by Stripe API updates
- Store all Stripe keys in environment variables, never in code or config files committed to Git
- Use idempotency keys for create operations to prevent duplicate charges during retries
- Log webhook event IDs to detect and skip duplicate deliveries
- Wrap Stripe calls in begin/rescue blocks and handle each error type separately
- Test with multiple test card numbers to cover success, decline, and authentication scenarios
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a Rails 7 app and need to accept payments with Stripe. Show me how to install the stripe gem, configure the API key from an environment variable, create a PaymentIntent in a controller, and handle the payment_intent.succeeded webhook with signature verification.
Create a Rails controller that accepts POST /payments with an amount parameter, creates a Stripe PaymentIntent, and returns the client_secret as JSON. Also add a webhook endpoint at POST /webhooks/stripe that verifies the signature and handles payment_intent.succeeded events.
Frequently asked questions
Which Ruby versions does the Stripe gem support?
The Stripe Ruby gem supports Ruby 2.7 and later. For the best experience and security patches, use Ruby 3.1 or later.
Can I use the Stripe gem outside of Rails?
Yes. The stripe gem works in any Ruby project — Sinatra, plain Ruby scripts, or background jobs. Just require 'stripe' and set your API key.
How do I handle Stripe errors in Ruby?
Rescue Stripe::StripeError for a catch-all, or rescue specific subclasses like Stripe::CardError, Stripe::RateLimitError, and Stripe::InvalidRequestError for more granular handling.
Is it safe to expose the client_secret to the frontend?
Yes. The client_secret is designed to be passed to the frontend. It can only confirm the specific PaymentIntent it belongs to and cannot be used to create new charges.
How do I test webhooks locally?
Use the Stripe CLI: run 'stripe listen --forward-to localhost:3000/webhooks/stripe' to forward events to your local Rails server.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation