/stripe-guides

How to use Stripe API with Ruby?

Learn how to integrate Stripe API with Ruby: setup, install the Stripe gem, handle payments, subscriptions, webhooks, error handling, and go live securely.

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 use Stripe API with Ruby?

How to use Stripe API with Ruby

 

Step 1: Set Up Your Ruby Environment

 

First, ensure you have Ruby installed on your system. You can check your Ruby version with:

ruby -v

If Ruby is not installed, download and install it from the official Ruby website or use a version manager like RVM or rbenv.

 

Step 2: Install the Stripe Ruby Gem

 

Add the Stripe gem to your project by adding it to your Gemfile:

gem 'stripe', '~> 8.0'

Then run:

bundle install

Alternatively, you can install the gem directly:

gem install stripe

 

Step 3: Sign Up for a Stripe Account and Get API Keys

 

  1. Go to https://stripe.com and sign up for an account
  2. Navigate to the Developers section in your Stripe dashboard
  3. Find your API keys (you'll see both test and live keys)
  4. For development, use the test keys which start with 'sk_test_' (secret key) and 'pk_test_' (publishable key)

 

Step 4: Configure the Stripe Ruby Library

 

Initialize the Stripe client with your secret API key:

require 'stripe'
Stripe.api_key = 'sk_test\_YourSecretKeyHere'

# Optional configuration
Stripe.api\_version = '2023-08-16' # Specify API version

For Rails applications, create an initializer file at config/initializers/stripe.rb:

# config/initializers/stripe.rb
Rails.configuration.stripe = {
  publishable_key: ENV['STRIPE_PUBLISHABLE\_KEY'],
  secret_key:      ENV['STRIPE_SECRET\_KEY']
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]

 

Step 5: Create a Customer

 

Create a Stripe customer object that you can associate with payments:

customer = Stripe::Customer.create({
  email: '[email protected]',
  name: 'John Doe',
  description: 'Customer created through Ruby API',
  metadata: {
    order\_id: '6735'
  }
})

puts "Created customer with ID: #{customer.id}"

 

Step 6: Add a Payment Method to a Customer

 

First, create a payment method token on the client-side using Stripe.js. Then, attach it to your customer:

# Assuming you have a payment method token from the client-side
payment_method_id = 'pm\_123456789'

payment\_method = Stripe::PaymentMethod.attach(
  payment_method_id,
  { customer: customer.id }
)

# Set as the default payment method
Stripe::Customer.update(
  customer.id,
  { invoice_settings: { default_payment_method: payment_method.id } }
)

 

Step 7: Create a One-time Charge

 

Process a one-time payment using the Payment Intents API:

payment\_intent = Stripe::PaymentIntent.create({
  amount: 2000, # Amount in cents
  currency: 'usd',
  customer: customer.id,
  payment_method: payment_method.id,
  description: 'Payment for order #6735',
  confirm: true, # Confirm the payment immediately
  metadata: {
    order\_id: '6735'
  }
})

puts "Payment status: #{payment\_intent.status}"

 

Step 8: Create a Subscription

 

Create a subscription for recurring payments:

# First, let's create a product if it doesn't exist
product = Stripe::Product.create({
  name: 'Premium Plan',
  description: 'Monthly subscription for premium features'
})

# Create a price for the product
price = Stripe::Price.create({
  product: product.id,
  unit\_amount: 1999, # $19.99 in cents
  currency: 'usd',
  recurring: {
    interval: 'month'
  }
})

# Create the subscription
subscription = Stripe::Subscription.create({
  customer: customer.id,
  items: [
    { price: price.id }
  ],
  payment_behavior: 'default_incomplete', # Create an invoice and attempt to pay
  expand: ['latest_invoice.payment_intent'] # Get the payment intent details
})

puts "Subscription status: #{subscription.status}"
puts "Payment intent client secret: #{subscription.latest_invoice.payment_intent.client\_secret}"

 

Step 9: Handle Webhooks

 

Set up a webhook endpoint to receive and process Stripe events:

require 'sinatra'
require 'stripe'
require 'json'

# For Rails, you would create a controller instead

post '/stripe/webhook' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE\_SIGNATURE']
  endpoint_secret = 'whsec_your_webhook_signing\_secret'

  begin
    event = Stripe::Webhook.construct\_event(
      payload, sig_header, endpoint_secret
    )
  rescue JSON::ParserError => e
    # Invalid payload
    status 400
    return
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature
    status 400
    return
  end

  # Handle the event
  case event.type
  when 'payment\_intent.succeeded'
    payment\_intent = event.data.object
    puts "PaymentIntent was successful: #{payment\_intent.id}"
    # Update your database, fulfill an order, etc.
    
  when 'payment_intent.payment_failed'
    payment\_intent = event.data.object
    puts "Payment failed: #{payment_intent.id}, #{payment_intent.last_payment_error&.message}"
    # Notify the customer that payment failed
    
  when 'invoice.payment\_succeeded'
    invoice = event.data.object
    puts "Invoice paid: #{invoice.id}"
    # Provision access to your service
    
  when 'customer.subscription.created'
    subscription = event.data.object
    puts "Subscription created: #{subscription.id}"
    # Set up the customer's subscription
    
  when 'customer.subscription.deleted'
    subscription = event.data.object
    puts "Subscription canceled: #{subscription.id}"
    # Remove the customer's access
    
  else
    puts "Unhandled event type: #{event.type}"
  end

  status 200
end

 

Step 10: List and Retrieve Objects

 

Query and retrieve objects from Stripe:

# List customers
customers = Stripe::Customer.list({
  limit: 10,
  email: '[email protected]'
})

customers.each do |customer|
  puts "Customer: #{customer.id}, #{customer.email}"
end

# Retrieve a specific customer
begin
  customer = Stripe::Customer.retrieve('cus\_123456789')
  puts "Found customer: #{customer.name}"
rescue Stripe::InvalidRequestError => e
  puts "Error retrieving customer: #{e.message}"
end

# List recent payments
payments = Stripe::PaymentIntent.list({
  limit: 10
})

payments.each do |payment|
  puts "Payment: #{payment.id}, Amount: #{payment.amount}, Status: #{payment.status}"
end

 

Step 11: Update and Delete Objects

 

Update and delete Stripe objects:

# Update a customer
Stripe::Customer.update(
  'cus\_123456789',
  {
    email: '[email protected]',
    metadata: { updated_at: Time.now.to_i }
  }
)

# Cancel a subscription
Stripe::Subscription.update(
  'sub\_123456789',
  { cancel_at_period\_end: true }
)

# Delete a customer (only works if there are no charges or other objects linked to this customer)
Stripe::Customer.delete('cus\_123456789')

 

Step 12: Error Handling and Best Practices

 

Implement proper error handling for your Stripe integration:

begin
  # Attempt to create a charge
  charge = Stripe::PaymentIntent.create({
    amount: 2000,
    currency: 'usd',
    customer: 'cus\_nonexistent',
    payment_method: 'pm_nonexistent',
    confirm: true
  })
rescue Stripe::CardError => e
  # Since it's a decline, Stripe::CardError will be caught
  puts "Card error: #{e.error.message}"
  puts "Decline code: #{e.error.decline_code}" if e.error.decline_code
rescue Stripe::RateLimitError => e
  # Too many requests made to the API too quickly
  puts "Rate limit error: #{e.message}"
rescue Stripe::InvalidRequestError => e
  # Invalid parameters were supplied to Stripe's API
  puts "Invalid request error: #{e.message}"
rescue Stripe::AuthenticationError => e
  # Authentication with Stripe's API failed
  puts "Authentication error: #{e.message}"
rescue Stripe::APIConnectionError => e
  # Network communication with Stripe failed
  puts "API connection error: #{e.message}"
rescue Stripe::StripeError => e
  # Generic Stripe error
  puts "Stripe error: #{e.message}"
rescue => e
  # Something else happened, completely unrelated to Stripe
  puts "Other error: #{e.message}"
end

 

Step 13: Creating a Simple Checkout Flow

 

Implement a simple checkout flow using Stripe Checkout:

# Server-side code to create a checkout session
session = Stripe::Checkout::Session.create({
  payment_method_types: ['card'],
  line\_items: [{
    price\_data: {
      currency: 'usd',
      product\_data: {
        name: 'T-shirt',
        description: 'Comfortable cotton t-shirt',
        images: ['https://example.com/t-shirt.png'],
      },
      unit\_amount: 2000,
    },
    quantity: 1,
  }],
  mode: 'payment',
  success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
  cancel\_url: 'https://example.com/cancel',
})

# Redirect to the URL returned in the response
puts "Redirect to: #{session.url}"

 

Step 14: Working with PaymentIntents for Dynamic Payments

 

Implement a more flexible payment flow using Payment Intents:

# Create a PaymentIntent without confirming it
payment\_intent = Stripe::PaymentIntent.create({
  amount: 1000,
  currency: 'usd',
  payment_method_types: ['card'],
  description: 'Software development services',
  metadata: {
    order\_id: '6735'
  }
})

# The client\_secret is used on the client side to confirm the payment
client_secret = payment_intent.client\_secret

puts "Client Secret: #{client\_secret}"

# Later, you can retrieve and confirm the payment intent
retrieved_intent = Stripe::PaymentIntent.retrieve(payment_intent.id)
confirmed\_intent = Stripe::PaymentIntent.confirm(
  payment\_intent.id,
  {
    payment_method: 'pm_card\_visa', # Provided by the client
    return\_url: 'https://example.com/return'
  }
)

puts "Payment status: #{confirmed\_intent.status}"

 

Step 15: Testing

 

Test your Stripe integration using test cards and webhooks:

# Test card numbers
# 4242 4242 4242 4242 - Successful payment
# 4000 0000 0000 0002 - Card declined (generic)
# 4000 0000 0000 9995 - Insufficient funds

# Create a test payment
payment\_intent = Stripe::PaymentIntent.create({
  amount: 2000,
  currency: 'usd',
  payment_method_data: {
    type: 'card',
    card: {
      number: '4242424242424242',
      exp\_month: 12,
      exp\_year: 2025,
      cvc: '123'
    }
  },
  confirm: true
})

puts "Test payment status: #{payment\_intent.status}"

# To test webhooks locally, use the Stripe CLI:
# stripe listen --forward-to localhost:4567/stripe/webhook

 

Step 16: Going Live

 

Prepare your integration for production:

# Switch from test to live keys
if Rails.env.production?
  Stripe.api_key = ENV['STRIPE_LIVE_SECRET_KEY']
else
  Stripe.api_key = ENV['STRIPE_TEST_SECRET_KEY']
end

# Implement proper logging
begin
  charge = Stripe::PaymentIntent.create({
    amount: 2000,
    currency: 'usd',
    payment_method: 'pm_123456789',
    confirm: true
  })
  
  # Log successful transaction
  Rails.logger.info("Payment succeeded: #{charge.id}, Amount: #{charge.amount}")
rescue Stripe::StripeError => e
  # Log error details
  Rails.logger.error("Stripe error: #{e.message}")
  
  # Also consider sending alerts for critical failures
  if e.is\_a?(Stripe::CardError)
    # Handle gracefully - expected error
  else
    # Unexpected error - might need immediate attention
    ErrorNotifier.notify(e)
  end
  
  raise e
end

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