Skip to main content
This comprehensive guide covers both CoCart v4 (stable) and CoCart v5+ (pre-release) callback systems. Choose the version that matches your CoCart installation.
Cart callbacks allow you to extend CoCart’s functionality by creating custom cart update operations. This tutorial shows you how to create, register, and use custom callbacks for both CoCart versions.

Version Differences Overview

  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
  • Base class: CoCart_Cart_Extension_Callback
  • Registration: cocart_register_extension_callback action hook
  • Totals calculation: $this->recalculate_totals()
  • Status: Production ready

What are Cart Callbacks?

Cart callbacks are custom PHP classes that execute when the cart is updated via the /wp-json/cocart/v2/cart/update endpoint. They allow you to:
  • Apply discounts or loyalty points
  • Update cart metadata
  • Integrate with third-party services
  • Implement custom cart validation rules
  • Add special promotions or fees

Creating a Custom Callback

Let’s create a loyalty points callback that works with both CoCart versions:

Step 1: Basic Class Structure

The base class differs between versions:
  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
v4 Basic Callback Structure
<?php
/**
 * Loyalty Points Callback for CoCart v4
 */
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Loyalty_Points_CoCart_Callback extends CoCart_Cart_Extension_Callback {

    /**
     * Callback name - this identifies your callback
     */
    protected $name = 'apply-loyalty-points';

    // Callback method goes here...
}
The $name property is crucial - it’s used as the namespace parameter when calling your callback via the API.

Step 2: Input Validation

The validation logic remains the same across both versions:
Input Validation
public function callback( $request, $controller ) {
    try {
        // Safety check: Ensure cart has items
        if ( $controller->is_completely_empty() ) {
            throw new CoCart_Data_Exception(
                'cocart_cart_empty',
                __( 'Cart is empty. Please add items to cart first.', 'cart-rest-api-for-woocommerce' ),
                404
            );
        }

        // Extract custom data from request
        $data = isset( $request['data'] ) ? $request['data'] : array();

        // Validate required parameters
        if ( empty( $data['points'] ) || ! is_numeric( $data['points'] ) ) {
            throw new CoCart_Data_Exception(
                'cocart_invalid_points',
                __( 'Please provide a valid number of points to redeem.', 'cart-rest-api-for-woocommerce' ),
                400
            );
        }

        // Continue with callback logic...
    } catch ( CoCart_Data_Exception $e ) {
        return CoCart_Response::get_error_response(
            $e->getErrorCode(),
            $e->getMessage(),
            $e->getCode(),
            $e->getAdditionalData()
        );
    }
}

Step 3: Custom Logic Implementation

The business logic implementation is identical across both versions:
Custom Logic Implementation
// Inside the callback method, after validation...

$points_to_redeem = intval( $data['points'] );
$current_user = wp_get_current_user();
$cart_updated = false;

if ( $current_user->exists() ) {
    // Get user's available points
    $available_points = get_user_meta( $current_user->ID, 'loyalty_points', true );
    $available_points = intval( $available_points );

    // Check if user has enough points
    if ( $available_points >= $points_to_redeem ) {
        // Calculate discount (1 point = $0.10)
        $discount_amount = $points_to_redeem * 0.10;

        // Apply discount to cart
        WC()->cart->add_fee(
            sprintf( __( 'Loyalty Points Discount (%d points)', 'your-textdomain' ), $points_to_redeem ),
            -$discount_amount
        );

        // Deduct points from user account
        $new_points_balance = $available_points - $points_to_redeem;
        update_user_meta( $current_user->ID, 'loyalty_points', $new_points_balance );

        // Store redeemed points in session for reference
        WC()->session->set( 'redeemed_loyalty_points', $points_to_redeem );

        $cart_updated = true;

        wc_add_notice(
            sprintf(
                __( 'Applied %d loyalty points for $%.2f discount. Remaining balance: %d points.', 'your-textdomain' ),
                $points_to_redeem,
                $discount_amount,
                $new_points_balance
            ),
            'success'
        );
    } else {
        throw new CoCart_Data_Exception(
            'cocart_insufficient_points',
            sprintf(
                __( 'Insufficient points. You have %d points available.', 'your-textdomain' ),
                $available_points
            ),
            400
        );
    }
} else {
    throw new CoCart_Data_Exception(
        'cocart_authentication_required',
        __( 'Please log in to redeem loyalty points.', 'your-textdomain' ),
        401
    );
}

Step 4: Totals Calculation

The totals calculation method differs between versions:
  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
v4 Finalize Callback
// After your custom logic...

if ( $cart_updated ) {
    // v4 uses class method
    $this->recalculate_totals( $request, $controller );

    // Add success notice only if no errors occurred
    if ( 0 === wc_notice_count( 'error' ) ) {
        // Custom success message already added above
    }
}

return true;

Registering Your Callback

The registration method differs between CoCart versions:
If you registered a callback before with version 4 or lower of CoCart, you will need to register them again for v5+. We simplified the callback system to perform better in version 5.

Registration Steps

  1. Save your callback class to a PHP file (e.g., loyalty-points-callback.php)
  2. Include the file in your theme or plugin
  3. Register using the appropriate method for your CoCart version
  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
v4 Registration Code
add_action( 'cocart_register_extension_callback', 'register_loyalty_points_callback_v4' );

function register_loyalty_points_callback_v4( $callback ) {
    // Load your callback class file
    include_once( dirname( __FILE__) . '/callbacks/loyalty-points-callback.php' );

    // Register the callback instance using action hook
    $callback->register( new Loyalty_Points_CoCart_Callback() );
}

Migration from v4 to v5+

If you’re upgrading from CoCart v4, here’s what changes:
  • v4 Registration (Old)
  • v5+ Registration (New)
v4 Method
add_action( 'cocart_register_extension_callback', 'register_callback' );

function register_callback( $callback ) {
    include_once( dirname( __FILE__) . '/callback.php' );
    $callback->register( new My_Callback() );
}

Multiple Callbacks Registration

You can register multiple callbacks in one function:
Multiple Callbacks
add_filter( 'cocart_rest_callbacks', 'register_all_my_callbacks' );

function register_all_my_callbacks( $callbacks ) {
    // Include all callback files
    include_once( dirname( __FILE__) . '/callbacks/loyalty-points-callback.php' );
    include_once( dirname( __FILE__) . '/callbacks/gift-card-callback.php' );
    include_once( dirname( __FILE__) . '/callbacks/shipping-insurance-callback.php' );

    // Register all callbacks
    $callbacks[] = new Loyalty_Points_CoCart_Callback();
    $callbacks[] = new Gift_Card_CoCart_Callback();
    $callbacks[] = new Shipping_Insurance_CoCart_Callback();

    return $callbacks;
}

Using Your Callback

The API usage remains identical across both versions - only the backend registration method differs.

API Request Structure

API Usage (Both Versions)
curl --location --request POST 'https://example-store.com/wp-json/cocart/v2/cart/update' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_TOKEN' \
--data '{
    "namespace": "apply-loyalty-points",
    "data": {
        "points": 50
    }
}'

JavaScript Usage

Frontend Integration (Both Versions)
async function redeemLoyaltyPoints(points) {
    try {
        const response = await fetch('/wp-json/cocart/v2/cart/update', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${userToken}`
            },
            body: JSON.stringify({
                namespace: 'apply-loyalty-points',
                data: {
                    points: points
                }
            })
        });

        const result = await response.json();

        if (response.ok) {
            console.log('Points redeemed successfully:', result);
            updateCartDisplay(result);
        } else {
            console.error('Failed to redeem points:', result.message);
            showErrorMessage(result.message);
        }
    } catch (error) {
        console.error('Network error:', error);
    }
}

// Usage remains the same
redeemLoyaltyPoints(25);

v5+ Performance Improvements

The new callback system offers several advantages:

Improved Performance

  • Reduced overhead: Filter-based registration is more efficient
  • Better memory usage: Callbacks are loaded only when needed
  • Faster initialization: Streamlined callback discovery

Enhanced Developer Experience

  • Simpler registration: No need for complex callback objects
  • Better debugging: Clearer callback flow
  • Easier testing: Direct instantiation for unit tests

Backward Compatibility Considerations

v5+ callbacks are not backward compatible with v4. You must update your registration code when upgrading.
What breaks:
  • Registration method (action hook vs filter)
  • Base class name change
  • Totals calculation method
What stays the same:
  • API endpoint usage
  • Callback $name property
  • Error handling patterns
  • Frontend integration code

Best Practices

General Best Practices (Both Versions)

  1. Always validate input data - Never trust user input
  2. Use proper error handling - Throw CoCart_Data_Exception for consistent error responses
  3. Check authentication when needed - Verify user permissions for sensitive operations
  4. Provide clear feedback - Use WooCommerce notices to inform users of what happened
  5. Test thoroughly - Test all success and error scenarios

Version-Specific Best Practices

  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
  • Use action hook registration - cocart_register_extension_callback
  • Extend correct base class - CoCart_Cart_Extension_Callback
  • Use class totals method - Call $this->recalculate_totals()
  • Follow established patterns - Production-tested approach

Troubleshooting

Common Issues (Both Versions)

  1. Callback not found: Check that your namespace matches the $name property
  2. Class not loaded: Ensure your include path is correct
  3. Authentication errors: Verify user login status for user-specific callbacks

Version-Specific Issues

  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
  • Registration not working: Check cocart_register_extension_callback action
  • Totals not updating: Ensure you’re calling $this->recalculate_totals()
  • Base class error: Verify extending CoCart_Cart_Extension_Callback

Debug Mode

Enable debug mode to see detailed error messages:
Debug Mode
// Add to wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );

Debug Callback Registration

  • CoCart v4 (Stable)
  • CoCart v5+ (Pre-release)
Debug v4 Callback Registration
add_action( 'cocart_register_extension_callback', function( $callback ) {
    error_log( 'CoCart v4 callback system initialized' );
});

Version Compatibility Summary

FeatureCoCart v4 (Stable)CoCart v5+ (Pre-release)
Base ClassCoCart_Cart_Extension_CallbackCoCart_REST_Callback
RegistrationAction hookFilter
Hook Namecocart_register_extension_callbackcocart_rest_callbacks
Registration Method$callback->register()$callbacks[] = new Class()
Totals Calculation$this->recalculate_totals()$controller->calculate_totals()
PerformanceStandardImproved
API UsageSameSame
Backward CompatibleYes (with older versions)No (requires migration)
Production Ready✅ Yes⚠️ Testing only
Choose the version that matches your CoCart installation and development needs.
I