boxcart.dev

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.

Tip

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:

HookParametersWhen It Fires
boxcart_order_created$order_idImmediately after a new order is placed
boxcart_order_status_changed$order_id, $old_status, $new_statusWhen an order's status changes (e.g. Pending to Processing)
boxcart_payment_status_changed$order_id, $old_status, $new_statusWhen the payment status updates (e.g. Pending to Paid)
boxcart_order_modified$order_id, $changes, $user_idWhen a customer modifies an existing order

Here is a basic example that logs new orders to an external system whenever an order is created:

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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:

PHP
/**
 * 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:

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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.

PHP
/**
 * 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:

  1. Payment methods are registered and configured in BoxCart → Settings → Payments.
  2. During checkout, the selected payment method is passed to BoxCart_Checkout which creates the order and delegates payment processing.
  3. Payment status is tracked separately from order status, using the boxcart_payment_status_changed action to notify listeners of changes.
  4. The BoxCart_Payment class provides constants and helpers for working with payment statuses: not_required, pending, awaiting_payment, processing, paid, failed, refunded, and cancelled.

A custom payment integration typically involves:

Warning

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

Warning

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:

PHP
/**
 * 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 );
    }
}