Extending BoxCart
BoxCart exposes a comprehensive set of WordPress hooks and filters that let you integrate with external systems, customise checkout flows, modify emails, and tailor the shopping experience without editing core plugin files.
Overview
BoxCart is designed to be extended via standard WordPress actions and filters. Rather than modifying plugin files directly, you can hook into key events throughout the order lifecycle, customise the checkout form, adjust email notifications, filter available collection slots, and connect to any external system.
This page covers the most common integration patterns with working code examples. For a complete list of every available hook and filter, see the Hooks & Filters reference.
All code examples on this page should be placed in your theme's functions.php file or, ideally, in a custom plugin. Never edit BoxCart's core files directly.
Custom Integrations
BoxCart fires actions at key moments in the order lifecycle, making it straightforward to sync data with external systems such as CRMs, accounting packages, stock management tools, and notification services.
The most commonly used action hooks for integrations are:
| Hook | Parameters | When It Fires |
|---|---|---|
boxcart_order_created | $order_id | Immediately after a new order is placed |
boxcart_order_status_changed | $order_id, $old_status, $new_status | When an order's status changes (e.g. Pending to Processing) |
boxcart_payment_status_changed | $order_id, $old_status, $new_status | When the payment status updates (e.g. Pending to Paid) |
boxcart_order_modified | $order_id, $changes, $user_id | When a customer modifies an existing order |
Here is a basic example that logs new orders to an external system whenever an order is created:
/**
* Send new BoxCart orders to an external CRM or ERP system.
*/
add_action( 'boxcart_order_created', 'my_sync_order_to_crm' );
function my_sync_order_to_crm( $order_id ) {
// Load the order using the BoxCart order model
$order = new BoxCart_Order( $order_id );
$payload = array(
'order_id' => $order_id,
'customer' => $order->get_customer_name(),
'email' => $order->get_customer_email(),
'total' => $order->get_total(),
'status' => $order->get_status(),
'created_at' => current_time( 'mysql' ),
);
wp_remote_post( 'https://api.example.com/orders', array(
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . MY_CRM_API_KEY,
),
'body' => wp_json_encode( $payload ),
'timeout' => 15,
) );
}
Modifying Email Templates
BoxCart sends nine different email types throughout the order lifecycle, from order confirmation to status updates. You can modify recipients, subjects, and email body content using filters.
Adding or Changing Email Recipients
Use the boxcart_email_recipient filter to add additional recipients or change who receives a particular email. This is useful for sending copies to warehouse staff, accountants, or other team members.
/**
* Add a warehouse manager to all order notification emails.
*/
add_filter( 'boxcart_email_recipient', 'my_add_warehouse_recipient', 10, 2 );
function my_add_warehouse_recipient( $to, $order ) {
// Append the warehouse email as a CC-style comma-separated recipient
$to .= ', warehouse@example.com';
return $to;
}
Modifying Email Content
Use the boxcart_email_content filter to modify the HTML body of any email before it is sent. You can append custom content, inject tracking links, or add promotional messages.
/**
* Append a custom message to order confirmation emails.
*/
add_filter( 'boxcart_email_content', 'my_add_email_footer', 10, 3 );
function my_add_email_footer( $content, $template_type, $order ) {
if ( $template_type === 'order_confirmation' ) {
$content .= '<p style="margin-top:20px;color:#666;">';
$content .= 'Remember to bring a reusable bag for your collection!';
$content .= '</p>';
}
return $content;
}
Changing Email Subjects
The boxcart_email_subject filter lets you customise the subject line for any email type:
/**
* Prefix all BoxCart email subjects with the business name.
*/
add_filter( 'boxcart_email_subject', 'my_prefix_email_subject', 10, 3 );
function my_prefix_email_subject( $subject, $template_type, $order ) {
$settings = BoxCart_Helpers::get_settings();
$name = $settings['business_name'] ?? 'Our Shop';
return '[' . $name . '] ' . $subject;
}
Adding Custom Checkout Fields
BoxCart provides a boxcart_checkout_fields filter that lets you inject additional fields into the checkout form. This is useful for collecting extra information such as marketing attribution, dietary requirements, or special requests.
The following example adds a "How did you hear about us?" dropdown to the checkout:
/**
* Add a "How did you hear about us?" field to the checkout form.
*/
add_filter( 'boxcart_checkout_fields', 'my_add_referral_field' );
function my_add_referral_field( $fields ) {
$fields['referral_source'] = array(
'type' => 'select',
'label' => 'How did you hear about us?',
'options' => array(
'' => '-- Please select --',
'google' => 'Google Search',
'social_media' => 'Social Media',
'friend' => 'Friend or Family',
'flyer' => 'Flyer or Leaflet',
'other' => 'Other',
),
'required' => false,
);
return $fields;
}
/**
* Save the custom field value when the order is created.
*/
add_action( 'boxcart_order_created', 'my_save_referral_source' );
function my_save_referral_source( $order_id ) {
if ( ! empty( $_POST['referral_source'] ) ) {
update_post_meta(
$order_id,
'_referral_source',
sanitize_text_field( $_POST['referral_source'] )
);
}
}
Filtering Available Slots
The boxcart_available_slots filter lets you modify which collection slots are presented to customers. This is useful for restricting slots based on custom business logic, such as limiting same-day orders, reserving slots for members, or blocking slots during busy periods.
/**
* Remove weekend morning slots (before 10 AM) for non-logged-in users.
*/
add_filter( 'boxcart_available_slots', 'my_filter_weekend_slots', 10, 2 );
function my_filter_weekend_slots( $slots, $location_id ) {
// Only restrict slots for guests
if ( is_user_logged_in() ) {
return $slots;
}
return array_filter( $slots, function( $slot ) {
$day_of_week = date( 'N', strtotime( $slot['date'] ) );
$start_hour = (int) date( 'G', strtotime( $slot['time_start'] ) );
// 6 = Saturday, 7 = Sunday
if ( in_array( $day_of_week, array( 6, 7 ), true ) && $start_hour < 10 ) {
return false;
}
return true;
} );
}
Modifying Product Display
BoxCart provides filters and actions that let you alter how products are displayed on the frontend. You can modify prices, add custom badges, or inject content before and after the product grid.
Filtering Product Prices
Use the boxcart_product_price filter to modify the displayed price for a product. This could be used for member discounts, bulk pricing, or promotional offers.
/**
* Apply a 10% discount for logged-in customers.
*/
add_filter( 'boxcart_product_price', 'my_member_discount', 10, 2 );
function my_member_discount( $price, $product_id ) {
if ( is_user_logged_in() ) {
$price = $price * 0.9; // 10% off
}
return $price;
}
Adding Content Around the Product Grid
Use the boxcart_before_products_display and boxcart_after_products_display actions to inject custom HTML before or after the product grid.
/**
* Show a seasonal banner above the products grid.
*/
add_action( 'boxcart_before_products_display', 'my_seasonal_banner' );
function my_seasonal_banner() {
$month = (int) date( 'n' );
if ( $month === 12 ) {
echo '<div class="my-seasonal-banner">';
echo '<p>Order early for Christmas — last collection date is 23rd December.</p>';
echo '</div>';
}
}
/**
* Add a custom message below the product grid.
*/
add_action( 'boxcart_after_products_display', 'my_products_footer' );
function my_products_footer() {
echo '<p class="my-products-note">';
echo 'All prices include VAT. Prices are subject to seasonal availability.';
echo '</p>';
}
External System Integration
BoxCart's hook system makes it straightforward to integrate with a wide range of external platforms. Below are practical patterns for the most common integration scenarios.
Accounting Software
Hook into boxcart_order_created and boxcart_payment_status_changed to create invoices and record payments in your accounting system.
/**
* Create a draft invoice in accounting software when an order is placed.
*/
add_action( 'boxcart_order_created', 'my_create_invoice', 10, 1 );
function my_create_invoice( $order_id ) {
$order = new BoxCart_Order( $order_id );
$invoice_data = array(
'reference' => $order->get_order_number(),
'customer' => $order->get_customer_email(),
'total' => $order->get_total(),
'line_items' => $order->get_items(),
'date' => current_time( 'Y-m-d' ),
);
wp_remote_post( 'https://api.accounting-app.com/invoices', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( $invoice_data ),
) );
}
/**
* Mark the invoice as paid when payment is confirmed.
*/
add_action( 'boxcart_payment_status_changed', 'my_mark_invoice_paid', 10, 3 );
function my_mark_invoice_paid( $order_id, $old_status, $new_status ) {
if ( $new_status !== 'paid' ) {
return;
}
$order = new BoxCart_Order( $order_id );
wp_remote_post( 'https://api.accounting-app.com/invoices/mark-paid', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'reference' => $order->get_order_number(),
) ),
) );
}
Inventory Management
Hook into boxcart_order_created to sync ordered items with an external inventory or stock management system.
/**
* Deduct stock in an external inventory system when an order is placed.
*/
add_action( 'boxcart_order_created', 'my_sync_inventory', 20, 1 );
function my_sync_inventory( $order_id ) {
$order = new BoxCart_Order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
wp_remote_post( 'https://api.inventory-app.com/stock/deduct', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'sku' => get_post_meta( $item->product_id, '_boxcart_sku', true ),
'quantity' => $item->quantity,
'order_id' => $order_id,
) ),
) );
}
}
CRM Systems
Hook into boxcart_customer_registered and boxcart_order_created to keep your CRM in sync with new customers and their orders.
/**
* Add new customers to the CRM when they register.
*/
add_action( 'boxcart_customer_registered', 'my_crm_add_contact', 10, 2 );
function my_crm_add_contact( $user_id, $data ) {
$user = get_userdata( $user_id );
wp_remote_post( 'https://api.my-crm.com/contacts', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'email' => $user->user_email,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'phone' => get_user_meta( $user_id, 'boxcart_phone', true ),
'source' => 'boxcart',
) ),
) );
}
Notification Services
Hook into boxcart_order_status_changed to send notifications via SMS, Slack, or any other messaging service when order statuses change.
/**
* Send a Slack notification when an order moves to "Ready for Collection".
*/
add_action( 'boxcart_order_status_changed', 'my_slack_ready_alert', 10, 3 );
function my_slack_ready_alert( $order_id, $old_status, $new_status ) {
if ( $new_status !== 'ready' ) {
return;
}
$order = new BoxCart_Order( $order_id );
$message = sprintf(
'Order #%s is ready for collection. Customer: %s',
$order->get_order_number(),
$order->get_customer_name()
);
wp_remote_post( 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array( 'text' => $message ) ),
) );
}
Creating a Custom Payment Method
BoxCart ships with three built-in payment methods: Stripe (card payments via Payment Elements), Cash on Collection, and Bank Transfer. If you need to integrate an additional payment gateway, you can hook into BoxCart's checkout and payment flow.
The payment architecture works as follows:
- Payment methods are registered and configured in BoxCart → Settings → Payments.
- During checkout, the selected payment method is passed to
BoxCart_Checkoutwhich creates the order and delegates payment processing. - Payment status is tracked separately from order status, using the
boxcart_payment_status_changedaction to notify listeners of changes. - The
BoxCart_Paymentclass provides constants and helpers for working with payment statuses:not_required,pending,awaiting_payment,processing,paid,failed,refunded, andcancelled.
A custom payment integration typically involves:
- Hooking into
boxcart_before_checkoutto inject your payment gateway's frontend elements - Using
boxcart_checkout_validationto validate payment data before the order is created - Listening to
boxcart_order_createdto initiate payment processing with your gateway - Updating the order's payment status via
BoxCart_Ordermethods once the gateway confirms the transaction
Custom payment method integration is advanced usage and requires a solid understanding of both the BoxCart checkout flow and your payment gateway's API. Always test thoroughly in a staging environment before deploying to production. Incorrect payment handling can result in orders being created without payment or customers being charged incorrectly.
Best Practices
Never modify BoxCart's core plugin files directly. Any changes you make will be lost when the plugin is updated. Always use hooks, filters, a child theme's functions.php, or a custom plugin to extend functionality.
Follow these guidelines when extending BoxCart to ensure your customisations are reliable and maintainable:
- Use a custom plugin or child theme — Place all your BoxCart extensions in a standalone plugin or your child theme's
functions.php. This keeps customisations separate from both the parent theme and the BoxCart plugin, surviving updates to either. - Check class and function existence — Before calling BoxCart classes or functions, always verify they exist. This prevents fatal errors if BoxCart is deactivated.
- Use appropriate hook priorities — The default priority for WordPress hooks is
10. Use a higher number (e.g.20) if your callback depends on BoxCart having already processed data, or a lower number (e.g.5) if you need to run before BoxCart. - Test in staging first — Always test your extensions on a staging or development site before deploying to production. This is especially important for hooks that affect orders, payments, and emails.
- Handle errors gracefully — Wrap external API calls in try/catch blocks or check response codes. A failed API call should never prevent an order from being placed.
- Use WordPress coding standards — Follow the WordPress Coding Standards for consistency and readability.
/**
* Always check that BoxCart classes exist before using them.
* This prevents fatal errors if BoxCart is deactivated.
*/
add_action( 'boxcart_order_created', 'my_safe_integration', 10, 1 );
function my_safe_integration( $order_id ) {
// Guard: ensure BoxCart is active and the class exists
if ( ! class_exists( 'BoxCart_Order' ) ) {
return;
}
$order = new BoxCart_Order( $order_id );
// Wrap external calls so failures don't break the checkout
$response = wp_remote_post( 'https://api.example.com/orders', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array( 'order_id' => $order_id ) ),
'timeout' => 10,
) );
if ( is_wp_error( $response ) ) {
error_log( 'BoxCart integration error: ' . $response->get_error_message() );
return;
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code !== 200 ) {
error_log( 'BoxCart integration returned HTTP ' . $code );
}
}