A rails store with shopping cart, stripe checkout, and social oauth. Confirmation emails also. The goal is to easily combine this bit with other snippets.
rails new <app name> --database=postgresql
Using the custom multiple database postgres docker image:
docker run -id --name rails-db -e POSTGRES_MULTIPLE_DATABASES="railyard-dev","railyard-test","railyard-prod" -e POSTGRES_USER=<database user> -e POSTGRES_PASSWORD=<password> -p 5434:5432 db:latest
Needs:
Initial setup:
#/.env
PUBLISHABLE_KEY=
SECRET_KEY=
#Gemfile
gem 'devise'
gem 'omniauth-github'
gem 'omniauth-twitter'
gem 'dotenv'
gem 'activerecord-session_store'
gem 'mailgun-ruby'
gem 'stripe'
gem 'jquery-rails'
#/config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: 5
timeout: 5000
username: worker
password: S0itg0es1
host: 127.0.0.1
port: 5434
development:
<<: *default
database: railyard-dev
test:
<<: *default
database: railyard-test
production:
<<: *default
database: railyard-prod
rails g controller charges
#/config/initializers/stripe.rb
Rails.configuration.stripe = {
:publishable_key => ENV['PUBLISHABLE_KEY'],
:secret_key => ENV['SECRET_KEY']
}
Stripe.api_key = Rails.configuration.stripe[:secret_key]
Controllers:
class ChargesController < ApplicationController
before_action :authenticate_user!
def new
end
def create
# Amount in cents
@amount = params[:amount]
# puts "creating a carge"
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken],
# :metadata => {'name' => params[:nameField], 'address' => params[:addressField], 'username' => current_user.email }
)
puts "creating charge"
puts params
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => @amount,
:description => "a test",
:currency => 'usd',
:metadata => {'order_id' => '6735'}
)
if charge["paid"] == true
#Save customer to the db
# redirect_to root_path
end
rescue Stripe::CardError => e
flash[:error] = e.message
puts "ERRROR ERROR"
# redirect_to new_charge_path
end
end
Views:
<%= form_tag charges_path do %>
<article>
<% if flash[:error].present? %>
<div id="error_explanation">
<p><%= flash[:error] %></p>
</div>
<% end %>
<label class="amount">
<span>
<strong>Price:</strong>
$<%= @product.price %>
<button id="purchaseButton">Purchase</button>
</span>
</label>
</article>
<article>
<%= hidden_field_tag(:stripeToken) %>
</article>
<script src="https://checkout.stripe.com/checkout.js"></script>
<script>
var handler = StripeCheckout.configure({
key: "<%= Rails.configuration.stripe[:publishable_key] %>",
image: 'https://stripe.com/assets/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// $('input#stripeToken').val(token.id);
// $('form').submit();
console.log(JSON.stringify(token));
var tokenInput = $("<input type=hidden name=stripeToken />").val(token.id);
var emailInput = $("<input type=hidden name=stripeEmail />").val(token.email);
var amount = $("<input type=hidden name=amount />").val(parseFloat(<%= @product.price * 100 %>));
// // document.getElementById("chargeForm").submit();
$("form").append(tokenInput).append(emailInput).append(amount).submit();
// You can access the token ID with `token.id`.
// Get the token ID to your server-side code for use.
}
});
// console.log();
document.getElementById('purchaseButton').addEventListener('click', function(e) {
e.preventDefault();
// Open Checkout with further options:
handler.open({
name: "<%= @product.name %>",
description: "<%= @product.description %>",
amount: parseFloat(<%= @product.price * 100 %>)
// zipCode: true,
// billingAddress: true
});
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
<% end %>
Migrations:
rails g model Cart
class CreateCartItems < ActiveRecord::Migration[5.2]
def change
create_table :cart_items do |t|
t.integer :quantity
t.integer :cart_id
t.product :integer
t.timestamps
end
end
end
Models:
#carts
class Cart < ApplicationRecord
has_one :user
has_many :cart_items
def add_item(product_id)
puts "adding item"
self.cart_items.create(:product => product_id, :quantity => 1)
# if product
# puts "we have a product"
# puts product[:description]
# # increase the quantity of product in cart
# # product.quantity + 1
# # @cart = Cart.find(session[:cart_id])
# self.cart_items.create(:product => product)
# save
# else
# # product does not exist in cart
# # @cart.cart_items.create(product)
# end
save
end
end
#cart_items
class CartItem < ApplicationRecord
belongs_to :cart, {:optional => true}
end
Controllers:
class ApplicationController < ActionController::Base
def current_cart
puts "getting card_id"
puts session[:cart_id]
if session[:cart_id]
@current_cart ||= Cart.where(id: session[:cart_id])
end
if session[:cart_id].nil?
@current_cart = Cart.create!
session[:cart_id] = @current_cart.id
end
@current_cart
end
end
class CartsController < ApplicationController
before_action :set_cart, only: [:show, :edit, :update, :destroy]
# GET /carts
# GET /carts.json
def index
if session[:cart_id]
@carts = current_cart
redirect_to '/carts/' + session[:cart_id].to_s
else
@carts = Cart.all
end
end
def add_to_cart
puts "add to cart"
if session[:cart_id]
@cart = Cart.find(session[:cart_id])
puts "cart exists"
@cart.add_item(params[:product_id])
else
puts "cart doesnt exist"
@cart = Cart.new()
respond_to do |format|
if @cart.save
puts "create cart"
puts @cart.id
session[:cart_id] = @cart.id
@cart.add_item(params[:product_id])
format.html { redirect_to @cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: @cart }
else
format.html { render :new }
format.json { render json: @cart.errors, status: :unprocessable_entity }
end
end
end
end
# GET /carts/1
# GET /carts/1.json
def show
if @cart.cart_items
# if @cart.cart_items.product
@totals = Hash.new(0)
@output = Hash.new(0)
productArr = @cart.cart_items.pluck(:product)
searchArr = productArr.uniq
productArr.each do |v|
@totals[v] += 1
end
@products = Product.where(id: searchArr)
@products.each do |product|
num = @totals[product.id]
@output = {
:product => product,
:quantity => num
}
end
puts @output
# end
end
end
# # POST /carts
# # POST /carts.json
def create
@cart = Cart.new(cart_params)
respond_to do |format|
if @cart.save
puts "create cart"
session[:cart_id] = @cart.id
format.html { redirect_to @cart, notice: 'Cart was successfully created.' }
format.json { render :show, status: :created, location: @cart }
else
format.html { render :new }
format.json { render json: @cart.errors, status: :unprocessable_entity }
end
end
end
end