ShoppingCart plugin provides checkout functionality, which can be integrated into your online store and configured according to your business logic.
- Define your step
- Create model and configure associations
- Prepare data for templates
- Create templates
- Create a handler
The plugin provides a cart, in which logged in users can add any number and any types of products. Cart provides an opportunity to:
- change the amount of products
- remove products from the order
- apply a discount code
- empty cart
By default checkout consists of 5 steps.
:address
– specify billing & shipping:delivery
– choose delivery method:payment
– add credit card:confirm
– view all previous information and confirm it:complete
– success message and final order information
[:address, :delivery, :payment]
– are not required, so you can disable one/all of them, can change their order. You also can specify your own steps.
[:confirm, :complete]
– are required and can not be removed.
Orders can have 5 predefined states. You can use aasm-transactions (listed in third column) to change order state in your code.
State | Description | Method |
---|---|---|
:in_progress |
Created by customer but not confirmed yet (default state) | – |
:processing |
Confirmed by customer and accepted in processing. You'll got this state after customer completed checkout. | :completed |
:in_delivery |
Sent to customer | :sent_to_client |
:delivered |
Delivered to the customer (final state) | :delivered |
:canceled |
Canceled by the manager (final state) | :canceled |
For now you can not change these states. Wait for this feature in future versions.
- User model – must be configurated with Devise.
- Product models – must have
title
andprice
colums. - Database – PostgreSQL
Add this line to your application's Gemfile and run the bundle command to install it.
gem 'shopping-cart'
Run the built-in generator with the command:
$ rails g shopping_cart:install
This will do all the necessary actions for you. At runtime, you will be asked the following questions:
-
User model (leave blank for 'User'):
– It's a Customer model in your main application. Note: If you type nothing, plugin will try to interact 'User'-model. -
Checkout steps (leave blank for confirmation only):
– Presence and order of checkout steps. Specify your own or listed above. Note: If you type nothing, you'll got only:confirm
and:complete
steps, which will be created automatically anyway. -
Cart path (leave blank for '/cart'):
– Route in main application, where shopping cart will be available. Note: If you type nothing shopping cart will be available on '/cart' path. -
Do you want to run ShoppingCart migrations now?
– Runs all just copied plugin migrations. Note: If you prefere to do this later, you can run$ bin/rails db:migrate SCOPE=shopping_cart
any time.
You can add any data as usual in console. However plugin provides some useful generators to save your time.
Delivery types
$ rails g shopping_cart:add_delivery 'Funny express' 5
In this example a new delivery type will be added to database with title Funny express and price $5.00
Coupon codes
$ rails g shopping_cart:add_coupon 'i want discount' 20
In this example a new coupon will be added to database with code i want discount, which gives 20% discount.
"Add to cart" form on product page
Just create a usual form tag with shopping_cart.orders_path
and method: :post
. You must send the following:
:quantity
of product:productable_id
– an id of your product:predictable_type
– your product's Class name
Here is an example:
<%= form_tag shopping_cart.orders_path, method: :post do %>
<%= number_field_tag :quantity, 1, min: '1' %>
<%= hidden_field_tag :productable_id, @product.id %>
<%= hidden_field_tag :productable_type, @product.class.to_s %>
<%= submit_tag 'Add to cart' %>
<% end %>
"Cart" button
Path for your link: shopping_cart.root_path
"Checkout" button
Path for your link: shopping_cart.checkout_index_path
. If user have no order with state :in_progress
, he will be redirected to empty cart.
Under the hood ShoppingCart
manipulates Rectify's Commands and Query Objects inside Wicked-based controller structure. It means that you do not need to create new controller for your step. Here is what you have to do:
- Define your step
- Create model and configure associations
- Prepare data for templates (optional)
- Create templates
- Create a handler
Lets imagine that we have different packing options in our store. So our custom step will be called :packing
.
Note: there are conventions about step name all over the plugin, so be careful with giving names to models, templates and so on...
There are two ways to do it:
- Specify step name during the
shopping_cart:install
runtime (see above) - Type custom step name in
config/initializers/shopping_cart.rb
file, inconfig.checkout_steps
.
Note: you'll need to restart application after changing initializer.
Create your model
According to our example with packing options, we need a model with title and price. Let's do it.
rails g model Packing title price:float
Note: ShoppingCart is smart enough. It will try to find a
price
in your model. And if it succeeds, then that value will be added to the order total. Therefore, if you do not want this, come up with another name. On the other hand, if the price should be further processed, create aprice
method, which will return the desired value.
Add relation to Order model
You've got an awesome generator for this task. Just run
rails g shopping_cart:add_order_relation belongs_to packing
and that will add belongs_to
association to ShoppingCart::Order
, create appropriate migrations and run them. If you need another relation type feel free to specify it here.
Create a query object
ShoppingCart
uses Rectify's Query Objects to prepare data for checkout steps. It will try to create new StepNameForCheckout
and :call
it automatically. The answer will be saved to @checkout_data
variable, which will be available in templates.
Note: If query object does not exist you'll get
false
inside@checkout_data
. So if you don't need anything for your custom step, just do nothing.
According to our example we need to create a PackingForCheckout
class:
module ShoppingCart
class PackingForCheckout < Rectify::Query
def query
Packing.all
end
end
end
Additional arguments
This query object will be created with current @order
as an argument. So you can define initializer and catch it if you need.
File location
You can place this file anywhere in you 'app' folder, inside of shopping_cart
namespace. However recommended path is: app/queries/shopping_cart/packing_for_checkout.rb
.
You'll need templates for two 'spots':
- Step page
- Confirmation/Completed pages.
Step page
It's important to remember that at that point you are inside a form for current order. And default plugin's templates build with Bootstrap. So two things are required:
- Place all your code inside
= render 'shopping_cart/checkout/checkout_form' do |f|
- Width of this template must be 8 bootstrap columns (because of 'order summary' sidebar nearby).
Also you have access to:
@order
with current order@checkout_data
with the result of calling query object- form helper
f
In our example we are gonna to list all available packing options. Also we want to mark an option, which is already associated with current order.
= render 'shopping_cart/checkout/checkout_form' do |f|
.col-md-8
.well
%h3 Packing options
%ul.list-group
- @checkout_data.all.each_with_index do |packing, i|
%li.fields.form-group.list-group-item
= f.radio_button :packing_id, packing.id,
checked: (packing.title == @order.try(:packing).try(:title))
= f.label :packing_id,
"#{packing.title} +#{number_to_currency packing.price}"
This file must be named by step name and placed into shopping_cart
namespace. In our case the path will be:
app/views/shopping_cart/checkout/packing.html.haml
.
Confirmation & Completed pages
Both :confirm
and :complete
steps contain information the user has previously selected. So, you need to specify how your information should appear. It will be the same for both pages.
It's required to place all code here inside div
with id named by your step. You can use a helper method edit_step_link
with name of your step as an argument if you want to allow user go back to your step. And of course, @order
variable is available here.
Here goes our example:
#packing_block
%h4
Packing opitons
%small= edit_step_link :packing
%p
= @order.packing.title
= number_to_currency @order.packing.price
The name of this file has the same requirements as in previous case, except that it must have a _confirmation_
prefix. So in our case it will be:
app/views/shopping_cart/checkout/_confirmation_packing.html.haml
.
Completely custom templates
As you can see in example above, you are using the existing plugin templates. They are incredibly beautiful, but can not quite perfect balance with the design of your application. If you want to replace them, just create a new templates and save them in the main application according to the structure below.
└─ views
├─ shopping_cart
│ └─ checkout
│ ├─ packing.html.haml
│ ├─ payment.html.haml
│ ├─ confirm.html.haml
│ ├─ complete.html.haml
│ └─ ...
└─ layouts
└─ shopping_cart
├─ checkout.html.haml
└─ ...
Note: Keep in mind that for each checkout step you need a template with the exact same name.
Each step's brains are encapsulated in a separate Rectify's Command. ShoppingCart
will try to send a :call
to a command named StepNameCheckoutStep
with @order
and params
as an arguments.
So, let's define our command:
module ShoppingCart
class PackingCheckoutStep < Rectify::Command
def initialize(order, params)
@order = order
@params = params
end
def call
@order.update_attributes packing_params
end
private
def packing_params
@params.require(:order).permit(:packing_id)
end
end
end
Feel free to add any additional logic and validations here.
You can place this file anywhere in you 'app' folder, inside of shopping_cart
namespace. However recommended path is: app/commands/shopping_cart/packing_checkout_step.rb
.
The gem is available as open source under the terms of the MIT License.