Feedback Laraform is now in maintenance mode. Read more on our GitHub.

Shopify Checkout Clone

Shopify-like checkout form made from scratch in 400 lines of code


            
Download
ShopifyCheckoutForm.php ShopifyCheckoutForm.vue
                
<?php

namespace App\Forms;

class ShopifyCheckoutForm extends \Laraform
{
  // Setting theme
  public $theme = 'shopify';

  // Assigning Vue component
  public $component = 'shopify-checkout-form';

  // Defining form steps
  public $wizard = [

    // 1st step - 'Customer information'
    'customer_information' => [
      'label' => 'Customer information',
      'elements' => ['contact_information', 'shipping_address'],
      'labels' => [
        'previous' => ' ',
        'next' => 'Continue to shipping method'
      ]
    ],

    // 2nd step - 'Shipping method'
    'shipping_method' => [
      'label' => 'Shipping method',
      'elements' => ['shipping_summary', 'shipping_method'],
      'labels' => [
        'previous' => '< Return to customer information',
        'next' => 'Continue to payment method'
      ]
    ],

    // 3rd step - 'Payment method'
    'payment_method' => [
      'label' => 'Payment method',
      'elements' => ['payment_summary', 'payment_method', 'billing_address', 'remember_me', 'terms'],
      'labels' => [
        'previous' => '< Return to shipping method',
        'finish' => 'Complete order'
      ]
    ],
  ];

  // Defining form elements
  public function schema() {
    return [

      // 'Contact information' (email)
      'contact_information' => [
        'type' => 'group',
        'label' => 'Contact information',
        'schema' => [
          'email' => [
            'type' => 'text',
            'placeholder' => 'Email',
            'floating' => 'Email',
            'rules' => 'required|email',
          ],
          'updates' => [
            'type' => 'checkbox',
            'text' => 'Keep me up to date on news and exclusive offers',
          ],
        ]
      ],

      // 'Shipping address' (name, company, address fields, phone)
      'shipping_address' => [
        'type' => 'group',
        'label' => 'Shipping address',
        'schema' => [
          'firstname' => [
            'type' => 'text',
            'placeholder' => 'First name (optional)',
            'floating' => 'First name (optional)',
            'columns' => 6
          ],
          'lastname' => [
            'type' => 'text',
            'placeholder' => 'Last name',
            'floating' => 'Last name',
            'rules' => 'required',
            'columns' => 6
          ],
          'company' => [
            'type' => 'text',
            'floating' => 'Company (optional)',
            'placeholder' => 'Company (optional)',
          ],
          'address' => [
            'type' => 'text',
            'floating' => 'Address',
            'placeholder' => 'Address',
            'rules' => 'required'
          ],
          'address2' => [
            'type' => 'text',
            'floating' => 'Apartment, suite, etc. (optional)',
            'placeholder' => 'Apartment, suite, etc. (optional)',
          ],
          'city' => [
            'type' => 'text',
            'floating' => 'City',
            'placeholder' => 'City',
            'rules' => 'required'
          ],
          'country' => [
            'type' => 'select',
            'floating' => 'Country',
            'placeholder' => 'Country',
            'rules' => 'required',
            'items' => [
              'de' => 'Germany',
                'us' => 'United States',
            ],
            'columns' => 8,
          ],
          'state' => [
            'type' => 'select',
            'floating' => 'State',
            'placeholder' => 'State',
            'columns' => 4,
            'items' => self::$states,
            'rules' => 'required',
            'conditions' => [
              ['shipping_address.country', 'us']
            ],
          ],
          'zip_code' => [
            'type' => 'text',
            'floating' => 'ZIP Code',
            'placeholder' => 'ZIP Code',
            'rules' => 'required',
            'columns' => 4
          ],
          'phone' => [
            'type' => 'text',
            'floating' => 'Phone',
            'placeholder' => 'Phone',
            'rules' => 'required',
          ],
        ]
      ],

      // Shipping summary block with custom SummaryElement imported at app.js
      'shipping_summary' => [
        'type' => 'static',
        'component' => 'SummaryElement',
        'items' => ['contact', 'ship_to']
      ],

      // 'Shipping method' - using custom template for checkboxes
      'shipping_method' => [
        'type' => 'radiogroup',
        'label' => 'Shipping method',
        'before' => '<span class="group-description">Please Note - Orders will be ship the next business day. Please add one shipping day to all estimates.</span>',
        'rules' => 'required',
      ],

      // Payment summary block with custom SummaryElement imported at app.js      
      'payment_summary' => [
        'type' => 'static',
        'component' => 'SummaryElement',
        'items' => ['contact', 'ship_to', 'method']
      ],

      // 'Payment method' block
      'payment_method' => [
        'type' => 'group',
        'label' => 'Payment method',
        'class' => 'payment-method',
        'before' => '<span class="group-description">All transactions are secure and encrypted.</span>',
        'schema' => [

          // 'Credit card' option
          'credit_card' => [
            'type' => 'radio',
            'text' => 'Credit card',
            'fieldName' =>  'payment_method',
            'after' => '<span class="card-logos"></span>',
            'rules' => [
              [
                'required' => ['paypal', null]
              ]
            ],
            'messages' => [
              'required' => 'One payment method must be chosen.'
            ]
          ],

          // Credit card details (if selected)
          'credit_card_details' => [
            'type' => 'group',
            'class' => 'credit-card-details',
            'conditions' => [
              ['payment_method.credit_card', 1]
            ],
            'schema' => [
              'card_number' => [
                'type' => 'text',
                'floating' => 'Card number',
                'placeholder' => 'Card number (do not provide actual card number)',
              ],
              'cardholder_name' => [
                'type' => 'text',
                'floating' => 'Cardholder name',
                'placeholder' => 'Cardholder name',
                'columns' => 6
              ],
              'expiration' => [
                'type' => 'text',
                'floating' => 'MM / YY',
                'placeholder' => 'MM / YY',
                'columns' => 3
              ],
              'cvv' => [
                'type' => 'text',
                'floating' => 'CVV',
                'placeholder' => 'CVV',
                'columns' => 3
              ],
            ]
          ],

          // 'Paypal' option
          'paypal' => [
            'type' => 'radio',
            'text' => ' ',
            'class' => 'paypal-payment',
            'fieldName' => 'payment_method',
            'after' => '<span class="card-logos only-3"></span>',
            'rules' => [
              [
                'required' => ['credit_card', null]
              ]
            ],
            'messages' => [
              'required' => 'One payment method must be chosen.'
            ]
          ],

          // Paypal info window (if selected)
          'paypal_payment' => [
            'type' => 'static',
            'component' => 'PaymentElement',
            'text' => 'After clicking "Complete order", you will be redirected to PayPal to complete your purchase securely.',
            'conditions' => [
              ['payment_method.paypal', 1]
            ],
          ],
        ],
      ],

      // 'Billing address' block
      'billing_address' => [
        'type' => 'object',
        'label' => 'Billing address',
        'class' => 'billing-address',
        'schema' => [
          'same' => [
            'type' => 'radio',
            'text' => 'Same as shipping address',
            'fieldName' => 'billing_address',
            'rules' => [
              [
                'required' => ['billing_address.different', null]
              ]
            ],
            'messages' => [
              'required' => 'One billing address option must be chosen.'
            ]
          ],
          'different' => [
            'type' => 'radio',
            'text' => 'Use a different billing address',
            'fieldName' => 'billing_address',
            'rules' => [
              [
                'required' => ['billing_address.same', null]
              ]
            ],
            'messages' => [
              'required' => 'One billing address option must be chosen.'
            ]
          ],

          // Billing info block (if 'use different is selected')
          'billing_info' => [
            'conditions' => [
              ['billing_address.different', 1]
            ],
            'type' => 'group',
            'class' => 'billing-address-fields',
            'schema' => [
              'firstname' => [
                'type' => 'text',
                'placeholder' => 'First name (optional)',
                'floating' => 'First name (optional)',
                'columns' => 6,
              ],
              'lastname' => [
                'type' => 'text',
                'placeholder' => 'Last name',
                'floating' => 'Last name',
                'columns' => 6,
                'rules' => 'required'
              ],
              'company' => [
                'type' => 'text',
                'placeholder' => 'Company (optional)',
                'floating' => 'Company (optional)',
              ],
              'address' => [
                'type' => 'text',
                'placeholder' => 'Address',
                'floating' => 'Address',
                'rules' => 'required'
              ],
              'address2' => [
                'type' => 'text',
                'placeholder' => 'Apartment, suite, etc. (optional)',
                'floating' => 'Apartment, suite, etc. (optional)',
              ],
              'city' => [
                'type' => 'text',
                'placeholder' => 'City',
                'floating' => 'City',
                'rules' => 'required'
              ],
              'country' => [
                'type' => 'select',
                'floating' => 'Country',
                'placeholder' => 'Country',
                'items' => [
                  'de' => 'Germany',
                  'us' => 'United States',
                ],
                'columns' => 8,
                'rules' => 'required',
              ],
              'state' => [
                'type' => 'select',
                'floating' => 'State',
                'placeholder' => 'State',
                'columns' => 4,
                'conditions' => [
                  ['billing_address.billing_info.country', 'us']
                ],
                'items' => self::$states,
                'rules' => 'required'
              ],
              'zip_code' => [
                'type' => 'text',
                'floating' => 'ZIP Code',
                'placeholder' => 'ZIP Code',
                'rules' => 'required',
                'columns' => 4
              ],
            ]
          ]
        ],
      ],

      // 'Remember me' block
      'remember_me' => [
        'type' => 'group',
        'label' => 'Remember me',
        'class' => 'remember-me',
        'schema' => [

          // 'Remember me' checkbox
          'remember' => [
            'type' => 'checkbox',
            'text' => 'Save my information for faster checkout',
          ],

          // 'Mobile phone number' input (if 'Remember me' is checked)
          'mobile_phone_number' => [
            'type' => 'text',
            'class' => 'remember-me-number',
            'floating' => 'Mobile phone number',
            'placeholder' => 'Mobile phone number',
            'addon' => [
              'before' => '<span class="phone-icon"></span>'
            ],
            'conditions' => [
              ['remember_me.remember', true]
            ],
            'columns' => [
              'field' => 6
            ],
            'rules' => 'required'
          ],

          // Additional description for 'Mobile phone number' (if 'Remember me' is checked)
          'phone_description' => [
            'type' => 'static',
            'class' => 'phone-description',
            'content' => '<span class="field-description">Next time you check out here or other stores powered by Shopify, Shopify will send you an authorization code by SMS to securely purchase with Shopify Pay.</span>',
            'conditions' => [
              ['remember_me.remember', true]
            ]
          ]
        ]
      ],

      // 'Accept terms' checkbox (if 'Remember me' is checked)
      'terms' => [
        'type' => 'static',
        'content' => '<span class="accept-terms">By continuing, you agree to Shopify Pay’s <a href="#">Privacy Policy</a> and <a href="#">Terms of Service</a>.</span>',
        'conditions' => [
          ['remember_me.remember', true]
        ],
      ]
    ];
  }

  public function after() {
    // Process data here

    return $this->data;
  }

  public static $states = [
    'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California', 'CO' => 'Colorado',
    'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District Of Columbia', 'FL' => 'Florida', 'GA' => 'Georgia',
    'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky',
    'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota',
    'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire',
    'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota',
    'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island', 'SC' => 'South Carolina',
    'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont', 'VA' => 'Virginia',
    'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin', 'WY' => 'Wyoming'
  ];
}
              
                
<script>
  import ShippingMethodRadioSlot from './../slots/ShippingMethodRadioSlot'

  export default {
    mixins: [Laraform],
    data() {
      return {
        // Extending schema with frontend features
        schema: {
          shipping_method: {
            slots: {
              // Adding custom radio template for shipping method
              radio: ShippingMethodRadioSlot,
            },
          },
          shipping_address: {
            schema: {
              phone: {
                // Defining mask for `phone` element
                mask: ['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
              }
            }
          },
          remember_me: {
            schema: {
              mobile_phone_number: {
                // Defining mask for `mobile_phone_number` element
                mask: ['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
              }
            }
          },
        },
      }
    },

    // Update columns and shipping options on country change
    watch: {
      'data.country': {
        handler(country) {
          this.el$('shipping_method').items = this.shippingOptions[country || 'us']
          this.el$('shipping_address.country').updateColumns(country == 'us' ? 5 : 8)
          this.el$('shipping_address.zip_code').updateColumns(country == 'us' ? 3 : 4)
        },
      },
      'data.billing_address.country': {
        handler(country) {
          this.el$('billing_address.billing_info.country').updateColumns(country == 'us' ? 5 : 8)
          this.el$('billing_address.billing_info.zip_code').updateColumns(country == 'us' ? 3 : 4)
        },
      },
    },

    computed: {
      shippingOptions(){
        return {
          us: {
            usps: {
              carrier: 'USPS Priority Mail Express',
              delivery_date: '1 business days',
              price: '$66.46'
            },
            fedex: {
              carrier: 'FedEx Home Delivery',
              delivery_date: '1 to 5 business days',
              price: '$66.98'
            },
            ups: {
              carrier: 'UPS Second Day Air',
              delivery_date: '2 business days',
              price: '$120.82'
            },
          },
          de: {
            eu: {
              carrier: 'Free EU Shipping',
              delivery_date: '2-3 business days',
              price: 'FREE'
            }
          }
        }
      }
    },

    mounted() {
      // Handle form success event
      this.on('response', (response) => {
        alert('Order sent!')
        
        console.log(response)
      })
    }
  }
</script>
              
  • Form wizard which is used to break form fields into steps
  • Nested elements for creating element groups and fields next to each other
  • Validation with Laravel compatible validators
  • Searchable select field for countries and states
  • Input mask for phone number
  • Floating labels for each field
  • Custom elements to create summaries
  • Custom template for radio groups for shipping and payment
  • Custom theme made for Shopify design
  • Conditional values for shipping method based on selected country
  • Conditional fields to show states, credit card details, billing address fields and account creation options

Login Form

Simple login form with one single class

Download
LoginForm.php LoginForm.vue
                
<?php

namespace App\Forms;

use Auth;
use Illuminate\Support\Arr;

class LoginForm extends \Laraform
{
  // Assigning Vue component
  public $component = "login-form";

  // Adding class
  public $class = "login-form";

  public function schema() {
    return [
      "title" => [
        "type" => "static",
        "content" => "<h5>Log in to your account</h5>"
      ],
      "email" => [
        "type" => "text",
        "placeholder" => "Email address",
        "floating" => "Email address",
        "rules" => "email"
      ],
      "password" => [
        "type" => "password",
        "placeholder" => "Password",
        "floating" => "Password"
      ],
      "remember" => [
        "type" => "toggle",
        "text" => "Remember me"
      ]
    ];
  }

  public function buttons() {
    return [[
      "label" => "Login",
      "class" => "btn-primary w-100"
    ]];
  }

  public function after () {
    if (Auth::attempt(Arr::only($this->data, ['email', 'password']), $this->data["remember"])) {
      return $this->success("You are authenticated!");
    }
    
    return $this->fail("Authentication failed");
  }
}
              
                
<script>
  export default {
    mixins: [Laraform],
    created() {
      this.on('response', (response) => {
        alert(response.messages[0])
      })
    }
  }
</script>
              
  • Form schema defined in the backend
  • Backend hook to process form after validation
  • Validation with Laravel compatible validators
  • Frontend event for watching response
  • Built in toggle element for Remember me

Elements

Check out 90+ small code snippets for different Laraform elements

Get a License

Get one-time fee license with no project limit