Episodes

Resources

Download Source Code

Summary

# Terminal
rails g scaffold product name 'price:decimal{8,2}' quantity:integer
rails g migration add_fields_to_users stripe_id:string vbuck:integer

brew install ngrok
ngrok http 3000

# views/layouts/application.html.erb
<%= tag :meta, name: 'stripe-public-key', content: ENV['STRIPE_PUBLIC_KEY'] %>
<%= javascript_include_tag 'https://js.stripe.com/v3/', 'data-turbolinks-track': 'reload' %>

# config/environments/development.rb
config.hosts = nil

# config/initializers/stripe.rb
Stripe.api_key = ENV['STRIPE_API_KEY']
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET']

StripeEvent.configure do |config|
  config.subscribe 'checkout.session.completed' do |event|
    PaymentReceived.call(event)
  end
end

# views/products/index.html.erb
<%= link_to 'Buy Now', purchases_path(product_id: product.id), method: :post, remote: true %>

# routes.rb
  resource :purchases, only: :create
  mount StripeEvent::Engine, at: '/stripe/webhook'

# purchases_controller.rb
class PurchasesController < ApplicationController
  before_action :authenticate_user!

  def create
    session = Stripe::Checkout::Session.create(stripe_parameters)
    @session_id = session.id
  end

  private

  def stripe_parameters
    {}.tap do |json|
      json[:payment_method_types] = ['card']
      json[:customer] = current_user.stripe_id if current_user.stripe_id
      json[:client_reference_id] = current_user.id unless current_user.stripe_id
      json[:customer_email] = current_user.email unless current_user.stripe_id
      json[:allow_promotion_codes] = true
      json[:line_items] = line_items
      json[:success_url] = 'http://localhost:3000/'
      json[:cancel_url] = 'http://localhost:3000/'
    end
  end

  def line_items
    [].tap do |array|
      products.each do |product|
        array << {
          quantity: 1,
          name: product.name,
          amount: (product.price * 100).to_i,
          currency: 'USD',
          description: product.name
        }
      end
    end
  end

  def products
    Product.where(id: params[:product_id])
  end
end

# views/purchases/create.js.erb
stripe_publishable_key = document.querySelector('meta[name="stripe-public-key"]').getAttribute('content')
stripe = Stripe(stripe_publishable_key)
stripe.redirectToCheckout({
  sessionId: "<%= @session_id %>"
})

# models/payment_received.rb
class PaymentReceived
  def self.call(event)
    new(event).call
  end

  attr_reader :event
  def initialize(event)
    @event = event
  end

  def call
    return unless user
    user.update(stripe_id: object.customer) unless user.stripe_id
    complete_purchase
  end

  private

  def user
    @user ||= if object.client_reference_id
      User.find_by(id: object.client_reference_id)
    else
      User.find_by(stripe_id: object.customer)
    end
  end

  def object
    @object ||= event.data.object
  end

  def complete_purchase
    line_items.each do |item|
      product = Product.find_by(name: item.description)
      total = product.quantity * item.quantity
      user.update(vbuck: user.vbuck.to_i + total)
    end
  end

  def line_items
    Stripe::Checkout::Session.list_line_items(object.id).data
  end
end