> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cocartapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Extending Cart: Creating a Callback

> Need to extend the cart to apply something unique?

<Note>
  This comprehensive guide covers both **CoCart v4 (stable)** and **CoCart v5+ (pre-release)** callback systems. Choose the version that matches your CoCart installation.
</Note>

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

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    * **Base class**: `CoCart_Cart_Extension_Callback`
    * **Registration**: `cocart_register_extension_callback` action hook
    * **Totals calculation**: `$this->recalculate_totals()`
    * **Status**: Production ready
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    * **Base class**: `CoCart_REST_Callback`
    * **Registration**: `cocart_rest_callbacks` filter
    * **Totals calculation**: `$controller->calculate_totals()`
    * **Status**: Testing/development
    * **Performance**: Improved and streamlined
  </Tab>
</Tabs>

## 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:

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    ```php v4 Basic Callback Structure theme={"system"}
    <?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...
    }
    ```
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    ```php v5+ Basic Callback Structure theme={"system"}
    <?php
    /**
     * Loyalty Points Callback for CoCart v5+
     */
    if ( ! defined( 'ABSPATH' ) ) {
        exit;
    }

    class Loyalty_Points_CoCart_Callback extends CoCart_REST_Callback {

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

        // Callback method goes here...
    }
    ```
  </Tab>
</Tabs>

<Note>
  The `$name` property is crucial - it's used as the `namespace` parameter when calling your callback via the API.
</Note>

### Step 2: Input Validation

The validation logic remains the same across both versions:

```php Input Validation theme={"system"}
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:

```php Custom Logic Implementation theme={"system"}
// 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:

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    ```php v4 Finalize Callback theme={"system"}
    // 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;
    ```
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    ```php v5+ Finalize Callback theme={"system"}
    // After your custom logic...

    if ( $cart_updated ) {
        // v5+ uses controller method directly
        $controller->calculate_totals( $request, true );

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

    return true;
    ```
  </Tab>
</Tabs>

## Registering Your Callback

The registration method differs between CoCart versions:

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

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

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    ```php v4 Registration Code theme={"system"}
    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() );
    }
    ```
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    ```php v5+ Registration Code theme={"system"}
    add_filter( 'cocart_rest_callbacks', 'register_loyalty_points_callback_v5' );

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

        // Add callback to the array (simpler than v4 method)
        $callbacks[] = new Loyalty_Points_CoCart_Callback();

        return $callbacks;
    }
    ```
  </Tab>
</Tabs>

### Migration from v4 to v5+

If you're upgrading from CoCart v4, here's what changes:

<Tabs>
  <Tab title="v4 Registration (Old)">
    ```php v4 Method theme={"system"}
    add_action( 'cocart_register_extension_callback', 'register_callback' );

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

  <Tab title="v5+ Registration (New)">
    ```php v5+ Method theme={"system"}
    add_filter( 'cocart_rest_callbacks', 'register_callback' );

    function register_callback( $callbacks ) {
        include_once( dirname( __FILE__) . '/callback.php' );
        $callbacks[] = new My_Callback();
        return $callbacks;
    }
    ```
  </Tab>
</Tabs>

### Multiple Callbacks Registration

You can register multiple callbacks in one function:

```php Multiple Callbacks theme={"system"}
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

```bash API Usage (Both Versions) theme={"system"}
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

```javascript Frontend Integration (Both Versions) theme={"system"}
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

<Warning>
  v5+ callbacks are **not** backward compatible with v4. You must update your registration code when upgrading.
</Warning>

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

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    * **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
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    * **Use filter registration** - `cocart_rest_callbacks`
    * **Update base class inheritance** - Use `CoCart_REST_Callback`
    * **Use controller totals method** - Call `$controller->calculate_totals()`
    * **Don't mix registration methods** - Don't use both v4 and v5+ methods
    * **Test thoroughly after upgrade** - Verify all callbacks work as expected
  </Tab>
</Tabs>

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

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    * **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`
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    * **Callbacks not loading**: Check that you're using the `cocart_rest_callbacks` filter
    * **Wrong base class**: Ensure you're extending `CoCart_REST_Callback`
    * **Totals not calculating**: Use `$controller->calculate_totals()` instead of `$this->recalculate_totals()`
    * **Mixed versions**: Don't use both v4 and v5+ registration methods
  </Tab>
</Tabs>

### Debug Mode

Enable debug mode to see detailed error messages:

```php Debug Mode theme={"system"}
// Add to wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
```

### Debug Callback Registration

<Tabs>
  <Tab title="CoCart v4 (Stable)">
    ```php Debug v4 Callback Registration theme={"system"}
    add_action( 'cocart_register_extension_callback', function( $callback ) {
        error_log( 'CoCart v4 callback system initialized' );
    });
    ```
  </Tab>

  <Tab title="CoCart v5+ (Pre-release)">
    ```php Debug v5+ Callback Registration theme={"system"}
    add_filter( 'cocart_rest_callbacks', function( $callbacks ) {
        error_log( 'Registered callbacks: ' . print_r( $callbacks, true ) );
        return $callbacks;
    });
    ```
  </Tab>
</Tabs>

## Version Compatibility Summary

| Feature                 | CoCart v4 (Stable)                   | CoCart v5+ (Pre-release)          |
| ----------------------- | ------------------------------------ | --------------------------------- |
| **Base Class**          | `CoCart_Cart_Extension_Callback`     | `CoCart_REST_Callback`            |
| **Registration**        | Action hook                          | Filter                            |
| **Hook Name**           | `cocart_register_extension_callback` | `cocart_rest_callbacks`           |
| **Registration Method** | `$callback->register()`              | `$callbacks[] = new Class()`      |
| **Totals Calculation**  | `$this->recalculate_totals()`        | `$controller->calculate_totals()` |
| **Performance**         | Standard                             | Improved                          |
| **API Usage**           | Same                                 | Same                              |
| **Backward Compatible** | Yes (with older versions)            | No (requires migration)           |
| **Production Ready**    | ✅ Yes                                | ⚠️ Testing only                   |

Choose the version that matches your CoCart installation and development needs.
