Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to use Stripe API with Ruby

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.

What you'll learn

  • How to install and configure the Stripe Ruby gem
  • How to create a PaymentIntent from a Rails controller
  • How to handle Stripe webhooks in Rails
  • How to test payments with Stripe test cards
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner5 min read15 minutesRuby 3.0+, Rails 7+, Stripe API 2024-12+March 2026RapidDev Engineering Team
TL;DR

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

1

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.

typescript
1# Gemfile
2gem 'stripe', '~> 10.0'
3
4# Then run:
5# bundle install

Expected result: The stripe gem is installed and available in your Rails application.

2

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.

typescript
1# config/initializers/stripe.rb
2Stripe.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.

3

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.

typescript
1# app/controllers/payments_controller.rb
2class PaymentsController < ApplicationController
3 skip_before_action :verify_authenticity_token, only: [:create]
4
5 def create
6 intent = Stripe::PaymentIntent.create(
7 amount: params[:amount].to_i, # amount in cents
8 currency: 'usd',
9 automatic_payment_methods: { enabled: true }
10 )
11 render json: { client_secret: intent.client_secret }
12 rescue Stripe::StripeError => e
13 render json: { error: e.message }, status: 422
14 end
15end

Expected result: POST /payments returns a JSON object with a client_secret string that your frontend uses to confirm the payment.

4

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.

typescript
1# app/controllers/webhooks_controller.rb
2class WebhooksController < ApplicationController
3 skip_before_action :verify_authenticity_token
4
5 def stripe
6 payload = request.body.read
7 sig_header = request.env['HTTP_STRIPE_SIGNATURE']
8 endpoint_secret = ENV.fetch('STRIPE_WEBHOOK_SECRET')
9
10 event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret)
11
12 case event.type
13 when 'payment_intent.succeeded'
14 handle_successful_payment(event.data.object)
15 end
16
17 head :ok
18 rescue Stripe::SignatureVerificationError
19 head :bad_request
20 end
21
22 private
23
24 def handle_successful_payment(payment_intent)
25 Rails.logger.info("Payment succeeded: #{payment_intent.id}")
26 end
27end

Expected result: Stripe sends POST requests to your webhook URL and your app processes them securely.

5

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.

typescript
1# Test card details:
2# Number: 4242 4242 4242 4242
3# 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

app/controllers/payments_controller.rb
1# app/controllers/payments_controller.rb
2# Full PaymentIntent controller for Rails + Stripe
3#
4# Routes (add to config/routes.rb):
5# post '/payments', to: 'payments#create'
6# post '/webhooks/stripe', to: 'webhooks#stripe'
7
8class PaymentsController < ApplicationController
9 skip_before_action :verify_authenticity_token, only: [:create]
10
11 # POST /payments
12 # Body: { "amount": 2000, "currency": "usd" }
13 def create
14 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 )
20
21 render json: {
22 client_secret: intent.client_secret,
23 payment_intent_id: intent.id
24 }
25 rescue Stripe::CardError => e
26 render json: { error: e.message }, status: 402
27 rescue Stripe::InvalidRequestError => e
28 render json: { error: e.message }, status: 400
29 rescue Stripe::StripeError => e
30 render json: { error: 'Payment processing failed. Please try again.' }, status: 500
31 end
32
33 # GET /payments/:id/status
34 def status
35 intent = Stripe::PaymentIntent.retrieve(params[:id])
36 render json: {
37 status: intent.status,
38 amount: intent.amount,
39 currency: intent.currency
40 }
41 rescue Stripe::StripeError => e
42 render json: { error: e.message }, status: 404
43 end
44end

Common 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.

ChatGPT Prompt

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.

Stripe Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.