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 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.

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;
}

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.

PHP
/**
 * 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. pendingprocessing, processingready). Use it to drive fulfilment integrations or send custom notifications tied to specific transitions.

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

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

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 );
    }
}