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 seven 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;
}
Reacting to New Orders
The boxcart_order_created action fires immediately after a new order is saved during checkout, and boxcart_after_checkout fires once the checkout flow has finished. Use either hook to trigger external notifications, sync orders to a third-party system, or run post-checkout housekeeping.
/**
* Send new orders to an external fulfilment service.
*/
add_action( 'boxcart_order_created', 'my_forward_order_to_fulfilment' );
function my_forward_order_to_fulfilment( $order_id ) {
$order = new BoxCart_Order( $order_id );
wp_remote_post( 'https://fulfilment.example.com/api/orders', array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( array(
'order_id' => $order_id,
'total' => $order->get_total(),
'items' => $order->get_items(),
) ),
) );
}
/**
* Clear a custom cache once checkout is complete.
*/
add_action( 'boxcart_after_checkout', 'my_post_checkout_cleanup' );
function my_post_checkout_cleanup( $order_id ) {
delete_transient( 'my_featured_products_cache' );
}
Reacting to Order Status Changes
The boxcart_order_status_changed action fires every time an order transitions between statuses (e.g. pending → processing, processing → ready). Use it to drive fulfilment integrations or send custom notifications tied to specific transitions.
/**
* Send an SMS when an order is marked "ready" for collection.
*/
add_action( 'boxcart_order_status_changed', 'my_sms_on_ready', 10, 3 );
function my_sms_on_ready( $order_id, $old_status, $new_status ) {
if ( $new_status !== 'ready' ) {
return;
}
$order = new BoxCart_Order( $order_id );
my_sms_gateway_send(
$order->get_customer_phone(),
sprintf( 'Order #%d is ready for collection.', $order_id )
);
}
Validating Items Before They Hit the Basket
The boxcart_before_add_to_basket action fires just before an item is added. The boxcart_customer_registered action fires after a new customer account is created. The example below uses both — logging add-to-basket attempts and welcoming new customers to a mailing list.
/**
* Log every add-to-basket attempt for analytics.
*/
add_action( 'boxcart_before_add_to_basket', 'my_log_add_to_basket', 10, 3 );
function my_log_add_to_basket( $product_id, $quantity, $quantity_type_id ) {
error_log( sprintf(
'[BoxCart] user=%d added product=%d qty=%s',
get_current_user_id(),
$product_id,
$quantity
) );
}
/**
* Subscribe new customers to the newsletter.
*/
add_action( 'boxcart_customer_registered', 'my_subscribe_new_customer', 10, 2 );
function my_subscribe_new_customer( $user_id, $data ) {
$user = get_userdata( $user_id );
my_newsletter_api_subscribe( $user->user_email, array(
'first_name' => $data['first_name'] ?? '',
'last_name' => $data['last_name'] ?? '',
) );
}
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 );
}
}