ShopifyCheckoutForm.vue
app.js
checkout.blade.php
<script>
// Custom Elements (Vue Single File Components)
import SummaryElement from './../elements/SummaryElement'
import PaymentElement from './../elements/PaymentElement'
export default {
mixins: [Laraform],
data: () => ({
// Defining form steps
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
schema: {
// 'Contact information' (email)
contact_information: {
type: 'group',
label: 'Contact information',
schema: {
email: {
type: 'text',
placeholder: 'Email',
label: false,
floating: 'Email',
rules: 'required|email:debounce=300',
},
updates: {
type: 'checkbox',
label: false,
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,
// Using custom event to update column width when country changes
onChange() {
this.updateColumns(this.value == 'us' ? 5 : 8)
this.form$.el$('shipping_address.zip_code').updateColumns(this.value == 'us' ? 3 : 4)
}
},
state: {
type: 'select',
floating: 'State',
placeholder: 'State',
columns: 4,
items: states,
rules: 'required',
// Should only appear if the country is USA
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',
// Using some fancy text mask
mask: ['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
},
}
},
// Shipping summary block with custom SummaryElement imported at top
shipping_summary: {
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>',
items: shippingUS,
rules: 'required',
slots: {
radio: Vue.extend({
inject: ['theme'],
props: ['el$', 'item', 'value'],
template: `
<div :class="theme.classes.radioContainer">
<label
:class="el$.theme.classes.radioLabel"
:for="value"
>
<input
type="radio"
v-model="el$.model"
:value="value"
:class="el$.theme.classes.radio"
:name="el$.fieldName"
:id="value"
@change="el$.handleChange"
/>
<span class="info">
<span class="left">
<span class="carrier">{{ item.carrier }}</span>
<span class="delivery">{{ item.delivery_date }}</span>
</span>
<span class="price">{{ item.price }}</span>
</span>
</label>
</div>`
})
},
mounted() {
this.$nextTick(() => {
var country = this.form$.el$('shipping_address.country')
country.on('change', () => {
this.items = country.value == 'us' ? shippingUS : shippingEU
})
})
}
},
// Payment summary block with custom SummaryElement imported at top
payment_summary: {
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: ['payment_method.paypal', null]
}
],
messages: {
required: 'One payment method must be choosen.'
}
},
// Credit card details (if selected)
credit_card_details: {
type: 'group',
class: 'credit-card-details',
// Only appears if credit card is selected
conditions: [
['payment_method.credit_card', 1]
],
// Defines its child elements for credit card info
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: ['payment_method.credit_card', null]
}
],
messages: {
required: 'One payment method must be choosen.'
}
},
// Paypal info window (if selected)
paypal_payment: {
component: PaymentElement,
text: 'After clicking "Complete order", you will be redirected to PayPal to complete your purchase securely.',
// Only appears if 'credit card' is selected
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 choosen.'
}
},
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 choosen.'
}
},
// Billing info block (if 'use different is selected')
billing_info: {
type: 'group',
class: 'billing-address-fields',
// Only appears if 'different' is selected
conditions: [
['billing_address.different', 1]
],
// Defines its child elements for billing info
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',
// Using custom event to update column width when country changes
onChange() {
this.updateColumns(this.value == 'us' ? 5 : 8)
this.form$.el$('billing_address.billing_info.zip_code').updateColumns(this.value == 'us' ? 3 : 4)
}
},
state: {
type: 'select',
floating: 'State',
placeholder: 'State',
columns: 4,
items: states,
rules: 'required',
// Should only appear if the country is USA
conditions: [
['billing_address.billing_info.country', 'us']
],
},
zip_code: {
type: 'text',
floating: 'ZIP Code',
placeholder: 'ZIP Code',
rules: 'required',
columns: 4
},
phone: {
type: 'text',
placeholder: 'Phone',
floating: 'Phone',
rules: 'required'
},
}
}
},
},
// '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',
mask: ['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/],
addon: {
before: '<span class="phone-icon"></span>'
},
columns: {
field: 6
},
rules: 'required',
// Only appears if 'Remember me' is checked)
conditions: [
['remember_me.remember', true]
],
},
// 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>',
// Only appears if 'Remember me' is checked)
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>',
// Only appears if 'Remember me' is checked)
conditions: [
['remember_me.remember', true]
],
}
},
// Selected theme
theme: 'shopify',
}),
methods: {
send() {
alert('Your order has been processed. Thank you')
this.reset()
}
}
}
const 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'
}
const shippingUS = {
'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'
},
}
const shippingEU = {
'eu': {
carrier: 'Free EU Shipping',
delivery_date: '2-3 business days',
price: 'FREE'
}
}
</script>
import Laraform from '@laraform/laraform'
import ShopifyCheckoutForm from './path/to/ShopifyCheckoutForm.vue'
import shopifyTheme from './path/to/shopify-theme'
Laraform.theme('shopify', shopifyTheme)
Vue.use(Laraform)
const app = Vue({
el: '#app',
components: {
ShopifyCheckoutForm,
}
})
<html>
<head>
<!-- ... -->
</head>
<body>
<div id="app">
<shopify-checkout-form></shopify-checkout-form>
</div>
</body>
</html>