Skip to main content

ProPickup — Force Delivery Date & Time Before Checkout

This ProPickup validation script prevents checkout until the customer selects a valid delivery date when using Local Deliver

Overview

Some Shopify themes and accelerated checkout methods can allow customers to proceed to checkout before selecting a required delivery date or delivery time. This can create fulfillment issues, failed deliveries, scheduling conflicts, and manual customer support work.

This ProPickup validation script prevents checkout until the customer selects a valid delivery date when using Local Delivery.


Why Force a Delivery Date?

Enforcing a delivery date helps merchants:

  • Prevent incomplete delivery orders

  • Reduce failed deliveries

  • Eliminate manual follow-up emails/calls

  • Improve fulfillment accuracy

  • Ensure route planning is possible

  • Maintain same-day or scheduled delivery rules

  • Prevent customers from bypassing delivery widgets

  • Ensure Shopify checkout contains required delivery metadata

Without validation, customers may:

  • Click checkout before the widget fully loads

  • Skip date selection accidentally

  • Use Shop Pay / accelerated checkout too quickly

  • Submit orders with missing delivery scheduling information


Common Use Cases

This validation is especially useful for:

Business Type

Why It Matters

Florists

Delivery timing is critical

Grocery stores

Fresh inventory scheduling

Bakeries

Production planning

Meal prep services

Route batching

Cannabis delivery

Compliance scheduling

Furniture delivery

Delivery windows required

Gift shops

Holiday/date-sensitive orders


What This Script Does

The script:

  1. Detects when the customer selected Delivery

  2. Checks whether the hidden ProPickup Delivery-Date attribute contains a value

  3. Blocks checkout if no date exists

  4. Displays an error message

  5. Automatically scrolls the customer back to the ProPickup widget


Validation Script

<script>
/**
* ---------------------------------------------------------
* ProPickup — Force Delivery Date + Postal/ZIP Validation
* ---------------------------------------------------------
*
* Prevents checkout unless Local Delivery customers enter:
* 1. Postal/ZIP code
* 2. Delivery date
*
* Required Elements:
* - attributes[Checkout-Method]
* - attributes[Delivery-Date]
* - input[name="sca-filter-location-by-postal-code"]
*
* ---------------------------------------------------------
*/

(function () {
const ERROR_ID = 'propickup-delivery-validation-error';

function getCartForm() {
return document.querySelector('form[action*="/cart"]');
}

function getAttribute(name) {
return document.querySelector(`input[name="attributes[${name}]"]`);
}

function getPostalCodeInput() {
return document.querySelector(
'input[name="sca-filter-location-by-postal-code"]'
);
}

function getErrorMount() {
return (
document.querySelector('.sca-filter-location-by-postal-code-container') ||
getCartForm() ||
document.body
);
}

function showMainError(message) {
let error = document.getElementById(ERROR_ID);
const mount = getErrorMount();

if (!error) {
error = document.createElement('div');
error.id = ERROR_ID;
error.setAttribute('role', 'alert');
error.style.color = '#b00020';
error.style.background = '#fff3f5';
error.style.border = '1px solid #b00020';
error.style.borderRadius = '6px';
error.style.padding = '10px 12px';
error.style.margin = '12px 0';
error.style.fontSize = '14px';
error.style.fontWeight = '600';

mount.prepend(error);
}

error.textContent = message;
}

function clearMainError() {
const error = document.getElementById(ERROR_ID);
if (error) error.remove();
}

function showZipError(input) {
const helper = document.querySelector('.scasp-zipcode-required');

if (helper) {
helper.style.display = 'block';
helper.textContent = 'Please enter your postal/ZIP code before checkout.';
}

if (input) {
input.style.border = '1px solid #b00020';

if (input.scrollIntoView) {
input.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}

setTimeout(function () {
input.focus();
}, 250);
}
}

function clearZipError(input) {
const helper = document.querySelector('.scasp-zipcode-required');

if (helper) {
helper.style.display = 'none';
}

if (input) {
input.style.border = '';
}
}

function isCheckoutAction(target) {
return !!target.closest(
'button[name="checkout"], input[name="checkout"], [type="submit"][name="checkout"], a[href*="/checkout"], button[onclick*="checkout"], [data-testid*="Checkout"], [class*="checkout"]'
);
}

function blockCheckout(e) {
e.preventDefault();
e.stopPropagation();

if (typeof e.stopImmediatePropagation === 'function') {
e.stopImmediatePropagation();
}

return false;
}

function validate(e) {
const checkoutMethod = getAttribute('Checkout-Method');
const deliveryDate = getAttribute('Delivery-Date');
const postalCodeInput = getPostalCodeInput();

const checkoutMethodValue = checkoutMethod && checkoutMethod.value
? checkoutMethod.value.trim().toLowerCase()
: '';

const isDelivery =
checkoutMethodValue === 'delivery' ||
checkoutMethodValue === 'local delivery' ||
checkoutMethodValue === 'local_delivery';

/**
* If Checkout-Method is missing, we still validate the ZIP field
* because some themes/apps load the attribute after the checkout button.
*/
const shouldValidateDelivery = isDelivery || !checkoutMethodValue;

if (!shouldValidateDelivery) {
clearMainError();
clearZipError(postalCodeInput);
return true;
}

const hasPostalCode =
postalCodeInput &&
postalCodeInput.value &&
postalCodeInput.value.trim() !== '';

const hasDeliveryDate =
deliveryDate &&
deliveryDate.value &&
deliveryDate.value.trim() !== '';

if (!hasPostalCode) {
blockCheckout(e);
showMainError('Please enter your postal/ZIP code before checkout.');
showZipError(postalCodeInput);
return false;
}

clearZipError(postalCodeInput);

if (!hasDeliveryDate) {
blockCheckout(e);
showMainError('Please select a delivery date before checkout.');

const widget = document.querySelector(
'#sca-storepickup-container, .sca-storepickup-container, #date-delivery, [data-propickup-widget]'
);

if (widget && widget.scrollIntoView) {
widget.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}

return false;
}

clearMainError();
return true;
}

document.addEventListener(
'click',
function (e) {
if (isCheckoutAction(e.target)) {
return validate(e);
}
},
true
);

document.addEventListener(
'submit',
function (e) {
const form = e.target;

if (
form &&
(
form.matches('form[action*="/cart"]') ||
form.querySelector('input[name="checkout"]') ||
form.querySelector('button[name="checkout"]')
)
) {
return validate(e);
}
},
true
);
})();
</script>

Installation

Option 1 — Shopify Theme (Recommended)

Add the script to:

theme.liquid

Place it:

  • Near the bottom of the <body>

  • After the ProPickup widget/app block loads


Option 2 — Cart Template

Add directly inside:

  • cart.liquid

  • main-cart-footer.liquid

  • cart drawer templates


How It Works

The script checks these hidden cart attributes generated by ProPickup:

Attribute

Purpose

attributes[Checkout-Method]

pickup or delivery

attributes[Delivery-Date]

selected delivery date

If:

Checkout-Method = delivery

AND

Delivery-Date = empty

Checkout is blocked.


Optional Enhancements

Require Delivery Time Too

You can also validate:

attributes[Delivery-Time]

Example:

const deliveryTime = getAttribute(form, 'Delivery-Time');

const hasDeliveryTime =
deliveryTime &&
deliveryTime.value &&
deliveryTime.value.trim() !== '';

Then require both date AND time before checkout.


Recommended When Using

This validation is strongly recommended when merchants use:

  • Same-day delivery

  • Delivery cutoff times

  • Time slot scheduling

  • Driver route optimization

  • Delivery fees by date/time

  • Perishable products

  • Holiday scheduling

  • Multi-location delivery routing


Troubleshooting

Validation Not Triggering

Check:

  • ProPickup widget loads before script runs

  • Hidden cart attributes exist

  • Theme uses standard Shopify cart form

  • Checkout buttons are inside the cart form


Error Message Appears But Checkout Continues

Some themes use AJAX or dynamic checkout methods.

Try:

  • Moving script lower in the page

  • Disabling accelerated checkout buttons

  • Binding validation to custom checkout buttons


Best Practice Recommendation

For delivery-based stores, requiring:

  • Delivery date

  • Delivery time

  • Delivery location

before checkout dramatically reduces:

  • support tickets

  • fulfillment mistakes

  • failed delivery attempts

  • refund requests

This creates a cleaner operational workflow and improves customer experience.

Did this answer your question?