Skip to content

The checkout process

Valentin Ballestrino edited this page Feb 1, 2016 · 6 revisions

Stall comes with a default checkout process and the ability to customize it entirely by different means.

Using the default checkout

The checkout consists in 4 simple steps :

  • Informations : The customer fills in shipping and billing informations
  • Shipping method : The customer chooses its shipping method
  • Payment method : The customer chooses its payment method
  • Payment : The customer sees a cart recap and proceeds to payment

The only things needed here are :

  • Shipping methods : Shipping methods are created in the database and associated to shipping calculator
  • Payment methods : Payment methods are created in the database and associated to payment gateways

You can find more informations on creating and using shipping calculators and payment methods in the wiki.

Default checkout customization

You can override the templates used by each step by overriding the following templates :

app/views/checkout/steps/_informations.html.haml
app/views/checkout/steps/_shipping_method.html.haml
app/views/checkout/steps/_payment_method.html.haml
app/views/checkout/steps/_payment.html.haml

Customizing the checkout workflow

The checkout workflow is handled by what Stall calls "Wizards". A default wizard is generated with the install generator and can be found in your app at : lib/default_checkout_wizard.rb.

Wizards manages the general workflow checkout. By default, you'll see that your default wizard looks like the following :

class DefaultCheckoutWizard < Stall::Checkout::Wizard
  steps :informations, :shipping_method, :payment_method, :payment
end

The .steps macro defines the general workflow, while the Stall::Checkout::Wizard parent class ensures that those steps flow during the checkout.

If you need to reorder or delete steps, you can do it by editing this steps list.

Creating new checkout steps

Often, you'll need to create steps yourself to adapt to the shop's desired checkout workflow.

Here, we'll imagine a new step called authentication where we'll want to authenticate the customer before he fills his informations.

We'll start by generating a step :

rails g stall:checkout:step authentication

which will create the lib/authentication_checkout_step.rb file with the #prepare and #process methods, and a view template at app/views/checkout/steps/_authentication.html.haml with a default form inside.

You now need to implement the methods to handle cart preparation and params processing for the current step, and the form or other control you want in your template.

Now just add this step to your default checkout wizard in lib/default_checkout_wizard.rb :

class DefaultCheckoutWizard < Stall::Checkout::Wizard
  steps :authentication, :informations, :shipping_method, :payment_method, :payment
end

Accessing controller data from the steps

When in the checkout process, the steps have access to the following controller-bound variables : params, session, request and flash.

The steps are not controllers, but often you need to access some specific data which is request-bound, or pass user flash messages to describe error reasons or success messages.

Validating data in the step

Often, you'll want to avoid adding to much validations in the cart or its associated models.
Validations are often contextual to the current step and should be written for each step.

You can add such step-specific validations to the cart by using the validations macro in your step :

class MyStep < Stall::Checkout::Step
  validations do
    validates :customer, presence: true
  end
end

If you need to validate nested models, you can use the .nested method inside the validations block. Note that this works for single and collection relations :

class MyStep < Stall::Checkout::Step
  validations do
    nested :customer do
      validates :email, presence: true
    end

    nested :addresses do
      validates :city, presence: true
    end
  end
end

You can also use custom validations by adding a validation method to : the validations block, the step class or the cart object itself.
Note that the preferred way is inside the step class, in a private method, which is more readable.

class MyStep < Stall::Checkout::Step
  validations do
    validate :customer_is_authorized
  end
  
  private

  def date_is_future
    unless check_customer_authorization_here
      cart.errors(:customer, 'not authorized to place orders') 
    end
  end
end

Customizing existing steps code

You can override existing steps code by subclassing them in your app, Stall will find your step instead of the lib's one. For instance, now that you have an authentication step, you may want to retrieve data from your authenticated user in your informations step.

Create a lib/informations_checkout_step.rb file and override the #prepare and #process methods :

class InformationsCheckoutStep < Stall::Checkout::InformationsCheckoutStep
  def prepare
    cart.customer = current_user.customer
    super
  end
end

Injecting dependencies into steps

In addition to the default controller data injected into the step, you may want to inject other specific informations, like, for instance, the current_user. You can do this by using the config.steps_initialization hook in your config/initializers/stall.rb initializer :

Stall.configure do |config|
  config.steps_initialization do |step|
    # Add the `current_user` in all steps
    step.inject(:current_user, current_user)
  end
end