Documentation Index Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Order Validation Functions
Order validation functions run at the final step of checkout to verify orders meet your requirements. Use them to enforce purchase limits, validate addresses, check inventory, or implement compliance rules.
How It Works
Function Manifest
{
"handle" : "my-validation-app" ,
"name" : "Order Validator" ,
"version" : "1.0.0" ,
"functions" : {
"order_validation" : {
"handle" : "order-rules" ,
"name" : "Order Validation Rules" ,
"config" : {
"maxItemsPerOrder" : 50 ,
"maxQuantityPerSku" : 10 ,
"blockedCountries" : [ "XX" , "YY" ]
}
}
}
}
interface OrderValidationInput {
order : {
lineItems : LineItem [];
subtotal : number ;
total : number ;
shippingTotal : number ;
taxTotal : number ;
discountTotal : number ;
currency : string ;
discountCodes : string [];
note : string ;
attributes : Record < string , string >;
};
shippingAddress : Address ;
billingAddress : Address ;
customer : {
id : string ;
email : string ;
tags : string [];
ordersCount : number ;
totalSpent : number ;
} | null ;
paymentMethod : {
type : string ; // "credit_card", "paypal", etc.
gateway : string ;
};
shop : {
id : string ;
name : string ;
currency : string ;
};
}
interface LineItem {
variantId : string ;
productId : string ;
title : string ;
quantity : number ;
price : number ;
sku : string ;
productType : string ;
vendor : string ;
tags : string [];
properties : Record < string , string >;
requiresShipping : boolean ;
}
interface Address {
firstName : string ;
lastName : string ;
address1 : string ;
address2 : string ;
city : string ;
province : string ;
provinceCode : string ;
country : string ;
countryCode : string ;
zip : string ;
phone : string ;
company : string ;
}
Output Schema
interface OrderValidationOutput {
valid : boolean ;
errors ?: ValidationError [];
}
interface ValidationError {
code : string ; // Machine-readable code
message : string ; // Human-readable message shown to customer
field ?: string ; // Optional: field that caused error
}
Response Examples
Valid Order
Invalid Order
{
"valid" : false ,
"errors" : [
{
"code" : "QUANTITY_LIMIT_EXCEEDED" ,
"message" : "Maximum 10 units per product. Please reduce the quantity of 'Premium Widget' from 15 to 10." ,
"field" : "lineItems"
},
{
"code" : "RESTRICTED_DESTINATION" ,
"message" : "Sorry, we cannot ship to PO Box addresses." ,
"field" : "shippingAddress"
}
]
}
Example Implementations
Quantity Limits
function validateOrder ( input , config ) {
const errors = [];
// Per-SKU quantity limit
const maxPerSku = config . maxQuantityPerSku || 10 ;
for ( const item of input . order . lineItems ) {
if ( item . quantity > maxPerSku ) {
errors . push ({
code: 'QUANTITY_LIMIT_EXCEEDED' ,
message: `Maximum ${ maxPerSku } units allowed for " ${ item . title } ". Please reduce quantity from ${ item . quantity } to ${ maxPerSku } .` ,
field: 'lineItems'
});
}
}
// Total items limit
const totalItems = input . order . lineItems . reduce (
( sum , item ) => sum + item . quantity , 0
);
const maxItems = config . maxItemsPerOrder || 50 ;
if ( totalItems > maxItems ) {
errors . push ({
code: 'ORDER_TOO_LARGE' ,
message: `Orders are limited to ${ maxItems } items. Your cart has ${ totalItems } items.` ,
field: 'lineItems'
});
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Geographic Restrictions
function validateOrder ( input , config ) {
const errors = [];
const { shippingAddress , order } = input ;
// Blocked countries
const blockedCountries = config . blockedCountries || [];
if ( blockedCountries . includes ( shippingAddress . countryCode )) {
errors . push ({
code: 'RESTRICTED_COUNTRY' ,
message: `Sorry, we cannot ship to ${ shippingAddress . country } .` ,
field: 'shippingAddress.country'
});
}
// No PO Box for certain items
const requiresPhysicalAddress = order . lineItems . some (
item => item . tags . includes ( 'no-po-box' )
);
if ( requiresPhysicalAddress ) {
const isPOBox = / \b ( P \. ? O \. ? \s * BOX | POST \s * OFFICE ) \b / i . test (
shippingAddress . address1 + ' ' + shippingAddress . address2
);
if ( isPOBox ) {
errors . push ({
code: 'PO_BOX_NOT_ALLOWED' ,
message: 'Some items in your order cannot be shipped to PO Box addresses. Please enter a physical address.' ,
field: 'shippingAddress.address1'
});
}
}
// State restrictions (e.g., for alcohol)
const hasAlcohol = order . lineItems . some (
item => item . productType === 'alcohol'
);
const dryStates = [ 'UT' , 'MS' ]; // Example: restricted states
if ( hasAlcohol && dryStates . includes ( shippingAddress . provinceCode )) {
errors . push ({
code: 'PRODUCT_RESTRICTED_IN_STATE' ,
message: `Alcohol cannot be shipped to ${ shippingAddress . province } . Please remove alcohol items or use a different shipping address.` ,
field: 'lineItems'
});
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Customer Verification
function validateOrder ( input , config ) {
const errors = [];
const { customer , order } = input ;
// Require account for high-value orders
if ( order . total > 50000 && ! customer ) { // $500+
errors . push ({
code: 'ACCOUNT_REQUIRED' ,
message: 'Orders over $500 require an account. Please log in or create an account to continue.' ,
field: 'customer'
});
}
// Block flagged customers
if ( customer ?. tags . includes ( 'blocked' )) {
errors . push ({
code: 'CUSTOMER_BLOCKED' ,
message: 'Your account has been restricted. Please contact support.' ,
field: 'customer'
});
}
// Limit for new customers (fraud prevention)
if ( customer && customer . ordersCount === 0 && order . total > 30000 ) {
errors . push ({
code: 'NEW_CUSTOMER_LIMIT' ,
message: 'First orders are limited to $300. Place a smaller order first.' ,
field: 'order.total'
});
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Product Combination Rules
function validateOrder ( input , config ) {
const errors = [];
const { order } = input ;
// Check for incompatible products
const productTypes = order . lineItems . map ( item => item . productType );
const hasFlammable = productTypes . includes ( 'flammable' );
const hasOxidizer = productTypes . includes ( 'oxidizer' );
if ( hasFlammable && hasOxidizer ) {
errors . push ({
code: 'INCOMPATIBLE_PRODUCTS' ,
message: 'Flammable and oxidizer products cannot be shipped together. Please place separate orders.' ,
field: 'lineItems'
});
}
// Check required accessories
const mainProducts = order . lineItems . filter (
item => item . tags . includes ( 'requires-accessory' )
);
const accessories = order . lineItems . filter (
item => item . tags . includes ( 'accessory' )
);
for ( const product of mainProducts ) {
const requiredAccessory = product . properties ?. required_accessory ;
const hasRequired = accessories . some (
acc => acc . sku === requiredAccessory
);
if ( ! hasRequired ) {
errors . push ({
code: 'MISSING_ACCESSORY' ,
message: `" ${ product . title } " requires an accessory. Please add the required item to your cart.` ,
field: 'lineItems'
});
}
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Payment Method Validation
function validateOrder ( input , config ) {
const errors = [];
const { paymentMethod , order , customer } = input ;
// COD limits
if ( paymentMethod . type === 'cod' ) {
// Max COD amount
if ( order . total > 10000 ) { // $100
errors . push ({
code: 'COD_LIMIT_EXCEEDED' ,
message: 'Cash on Delivery is limited to orders under $100. Please choose a different payment method.' ,
field: 'paymentMethod'
});
}
// Require verified customer for COD
if ( ! customer || ! customer . tags . includes ( 'verified' )) {
errors . push ({
code: 'COD_VERIFICATION_REQUIRED' ,
message: 'Cash on Delivery requires a verified account. Please verify your phone number or use a different payment method.' ,
field: 'customer'
});
}
}
// Subscription requires saved payment
const hasSubscription = order . lineItems . some (
item => item . properties ?. subscription_id
);
if ( hasSubscription && paymentMethod . type !== 'credit_card' ) {
errors . push ({
code: 'SUBSCRIPTION_PAYMENT_REQUIRED' ,
message: 'Subscriptions require a credit card for recurring billing.' ,
field: 'paymentMethod'
});
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Compliance Validation
function validateOrder ( input , config ) {
const errors = [];
const { order , customer , shippingAddress } = input ;
// Age-restricted products
const ageRestrictedItems = order . lineItems . filter (
item => item . tags . includes ( 'age-restricted' )
);
if ( ageRestrictedItems . length > 0 ) {
// Check for age verification
if ( ! customer ?. tags . includes ( 'age-verified' )) {
errors . push ({
code: 'AGE_VERIFICATION_REQUIRED' ,
message: 'Your order contains age-restricted items. Please verify your age before purchasing.' ,
field: 'customer'
});
}
}
// Export compliance
const controlledItems = order . lineItems . filter (
item => item . tags . includes ( 'export-controlled' )
);
if ( controlledItems . length > 0 ) {
const domesticCountries = [ 'US' ];
if ( ! domesticCountries . includes ( shippingAddress . countryCode )) {
errors . push ({
code: 'EXPORT_RESTRICTED' ,
message: 'Some items in your order cannot be exported. Please remove restricted items or ship to a domestic address.' ,
field: 'lineItems'
});
}
}
// Reseller check
if ( order . total > 200000 ) { // $2000+
const hasResellerAgreement = customer ?. tags . includes ( 'reseller-approved' );
if ( ! hasResellerAgreement ) {
errors . push ({
code: 'RESELLER_AGREEMENT_REQUIRED' ,
message: 'Bulk orders over $2000 require a reseller agreement. Please contact our sales team.' ,
field: 'order.total'
});
}
}
return {
valid: errors . length === 0 ,
errors: errors . length > 0 ? errors : undefined
};
}
Error Display
When validation fails, errors appear at checkout:
Unable to complete your order
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ Maximum 10 units allowed for "Premium Widget".
Please reduce quantity from 15 to 10.
⚠ Some items in your order cannot be shipped
to PO Box addresses. Please enter a physical
address.
[Update Cart] [Change Address]
Multiple Functions
If multiple apps have order validation functions, they all run. An order is blocked if any function returns valid: false:
Error Codes
Use clear, specific error codes:
Code Meaning QUANTITY_LIMIT_EXCEEDEDItem quantity too high ORDER_TOO_LARGETotal items exceed limit RESTRICTED_COUNTRYCannot ship to country PO_BOX_NOT_ALLOWEDPhysical address required CUSTOMER_BLOCKEDAccount restricted AGE_VERIFICATION_REQUIREDAge check needed PAYMENT_METHOD_INVALIDWrong payment for order type MISSING_ACCESSORYRequired product not in cart
Best Practices
Be specific in error messages
Tell customers exactly what’s wrong and how to fix it. “Maximum 10 units for Widget” is better than “Quantity invalid”.
Validate early when possible
Some validations can happen in cart transforms or checkout UI, giving earlier feedback.
If you check quantity limits in cart transform, don’t duplicate in order validation.
Guest checkouts won’t have customer data. Handle null customer gracefully.
Test with various addresses, payment methods, and product combinations.
Track why orders fail validation to identify patterns or bugs.
Order validation must complete within:
Timeout : 2 seconds
Memory : 128 MB
If your function times out or errors, the order will be blocked with a generic error message. Ensure fast, reliable execution.
See Also