• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Setting Up the Custom REST API Endpoint

To manage B2B pricing grids securely, we’ll leverage WordPress’s REST API. This involves creating a custom endpoint that can be accessed programmatically. We’ll define a new route within the `register_rest_route` function, specifying the namespace, route, and the callback function that will handle requests.

For this example, let’s assume our plugin is named ‘b2b-pricing’ and our endpoint will be `/b2b-pricing/v1/pricing-grid`. The callback function will be responsible for fetching or updating pricing data based on the request parameters.

Registering the REST API Route

This code snippet should be placed within your plugin’s main PHP file or an included file that’s loaded on the admin side.

add_action( 'rest_api_init', function () {
    register_rest_route( 'b2b-pricing/v1', '/pricing-grid', array(
        'methods'  => WP_REST_Server::READABLE, // Or WP_REST_Server::EDITABLE for POST/PUT
        'callback' => 'b2b_pricing_get_grid_callback',
        'permission_callback' => 'b2b_pricing_permissions_check',
    ) );
} );

The Callback Function

The `b2b_pricing_get_grid_callback` function will contain the core logic for retrieving pricing data. For simplicity, we’ll simulate fetching data from a custom database table or options. In a real-world scenario, you’d replace this with your actual data retrieval mechanism.

function b2b_pricing_get_grid_callback( WP_REST_Request $request ) {
    // In a real application, fetch pricing data from your custom table or options.
    // Example: $pricing_data = get_option( 'b2b_pricing_grid_data' );
    $pricing_data = array(
        'product_a' => array(
            'tier_1' => 10.00,
            'tier_2' => 9.50,
            'tier_3' => 9.00,
        ),
        'product_b' => array(
            'tier_1' => 25.00,
            'tier_2' => 23.75,
            'tier_3' => 22.50,
        ),
    );

    // You might want to filter data based on user roles or other criteria here.
    // For now, we return all data.

    return new WP_REST_Response( $pricing_data, 200 );
}

Implementing Role-Based Access Control

Security is paramount. We need to ensure that only authorized users can access and manipulate pricing data. WordPress’s role and capability system is the ideal mechanism for this. We’ll implement a `permission_callback` for our REST API endpoint.

The Permission Callback Function

The `b2b_pricing_permissions_check` function will be executed before the main callback. It receives the `WP_REST_Request` object and should return `true` if the user has permission, or a `WP_Error` object otherwise.

function b2b_pricing_permissions_check( WP_REST_Request $request ) {
    // Define the capability required to access this endpoint.
    // For example, 'manage_options' is typically reserved for administrators.
    // You might create a custom capability like 'manage_b2b_pricing'.
    $required_capability = 'manage_b2b_pricing';

    // Check if the current user has the required capability.
    if ( ! current_user_can( $required_capability ) ) {
        return new WP_Error( 'rest_forbidden', esc_html__( 'You do not have permission to access this resource.', 'b2b-pricing' ), array( 'status' => rest_authorization_required_code() ) );
    }

    // If the user has the capability, allow access.
    return true;
}

Defining Custom Capabilities

To implement granular control, it’s best practice to define custom capabilities. This prevents conflicts with other plugins and makes your permission logic clearer. Add this to your plugin’s activation hook.

function b2b_pricing_add_custom_capabilities() {
    // Get the administrator role.
    $role = get_role( 'administrator' );

    // Add the custom capability if it doesn't exist.
    if ( $role && ! $role->has_cap( 'manage_b2b_pricing' ) ) {
        $role->add_cap( 'manage_b2b_pricing' );
    }

    // You can also add this capability to other roles as needed.
    // For example, a custom 'B2B Manager' role.
    // $b2b_manager_role = get_role( 'b2b_manager' );
    // if ( $b2b_manager_role && ! $b2b_manager_role->has_cap( 'manage_b2b_pricing' ) ) {
    //     $b2b_manager_role->add_cap( 'manage_b2b_pricing' );
    // }
}
register_activation_hook( __FILE__, 'b2b_pricing_add_custom_capabilities' );

Overriding Roles for Specific Pricing Tiers

A common B2B requirement is to show different pricing tiers to different customer segments, often mapped to user roles or custom user meta. We can extend our REST API endpoint to handle this by checking user meta or specific role assignments.

Modifying the Callback for Tiered Pricing

We’ll modify the `b2b_pricing_get_grid_callback` to inspect the current user’s roles or custom meta and return only the relevant pricing tier.

function b2b_pricing_get_grid_callback( WP_REST_Request $request ) {
    $current_user = wp_get_current_user();
    $pricing_data = array();

    // Define your pricing tiers and their associated roles/meta.
    // This is a simplified example. You'd likely store this in options or a custom table.
    $all_pricing_tiers = array(
        'base' => array( // Default pricing for everyone or unassigned users
            'product_a' => array( 'price' => 12.00 ),
            'product_b' => array( 'price' => 30.00 ),
        ),
        'wholesale' => array(
            'product_a' => array( 'price' => 10.00 ),
            'product_b' => array( 'price' => 25.00 ),
        ),
        'distributor' => array(
            'product_a' => array( 'price' => 9.00 ),
            'product_b' => array( 'price' => 22.50 ),
        ),
    );

    $user_tier = 'base'; // Default tier

    // Check for specific roles that map to pricing tiers.
    if ( user_can( $current_user->ID, 'wholesale_customer' ) ) {
        $user_tier = 'wholesale';
    } elseif ( user_can( $current_user->ID, 'distributor_customer' ) ) {
        $user_tier = 'distributor';
    }
    // You could also check custom user meta:
    // $customer_level = get_user_meta( $current_user->ID, 'customer_level', true );
    // if ( $customer_level === 'wholesale' ) {
    //     $user_tier = 'wholesale';
    // }

    // Assign the determined tier's pricing.
    if ( isset( $all_pricing_tiers[ $user_tier ] ) ) {
        $pricing_data = $all_pricing_tiers[ $user_tier ];
    } else {
        // Fallback to base pricing if tier is not found.
        $pricing_data = $all_pricing_tiers['base'];
    }

    return new WP_REST_Response( $pricing_data, 200 );
}

Creating Custom Roles for Tiers

To use role-based checks like `user_can( $current_user->ID, ‘wholesale_customer’ )`, you need to define these roles and assign them to users. This can also be done via your plugin’s activation hook or a dedicated setup function.

function b2b_pricing_add_custom_roles() {
    // Add 'Wholesale Customer' role
    add_role( 'wholesale_customer', __( 'Wholesale Customer' ), array(
        'read' => true, // Basic capability
        // Add any other capabilities specific to wholesale customers if needed
    ) );

    // Add 'Distributor Customer' role
    add_role( 'distributor_customer', __( 'Distributor Customer' ), array(
        'read' => true,
        // Add any other capabilities specific to distributor customers if needed
    ) );
}
register_activation_hook( __FILE__, 'b2b_pricing_add_custom_roles' );

Securing the Endpoint with Authentication

By default, WordPress REST API endpoints are accessible to authenticated users. However, for B2B applications, you might need more robust authentication, especially if the API is consumed by external systems. WordPress REST API uses nonces for authenticated requests. When making requests from the frontend JavaScript, you’ll need to include a nonce.

Generating and Using Nonces

In your WordPress theme or plugin, you can pass a nonce to your JavaScript. This nonce is then included in the `X-WP-Nonce` header of your AJAX requests.

First, in your PHP, localize a script to pass the nonce:

function b2b_pricing_enqueue_scripts() {
    // Assuming you have a script registered for your frontend
    wp_enqueue_script( 'b2b-pricing-frontend', plugin_dir_url( __FILE__ ) . 'js/frontend.js', array( 'jquery' ), '1.0', true );

    // Localize the script with the REST API URL and nonce
    wp_localize_script( 'b2b-pricing-frontend', 'b2bPricingApi', array(
        'root' => esc_url_raw( rest_url() ),
        'nonce' => wp_create_nonce( 'wp_rest' ), // The nonce action for REST API
    ) );
}
add_action( 'wp_enqueue_scripts', 'b2b_pricing_enqueue_scripts' );

Then, in your JavaScript (js/frontend.js), make the AJAX request:

jQuery(document).ready(function($) {
    var apiRoot = b2bPricingApi.root;
    var nonce = b2bPricingApi.nonce;
    var endpoint = 'b2b-pricing/v1/pricing-grid';

    $.ajax({
        url: apiRoot + endpoint,
        method: 'GET',
        beforeSend: function ( xhr ) {
            xhr.setRequestHeader( 'X-WP-Nonce', nonce );
        }
    })
    .done(function(response) {
        console.log('Pricing data:', response);
        // Process the pricing data here
    })
    .fail(function(jqXHR, textStatus, errorThrown) {
        console.error('Error fetching pricing data:', textStatus, errorThrown);
        if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
            console.error('API Error Message:', jqXHR.responseJSON.message);
        }
    });
});

Handling Updates and Data Persistence

For a complete B2B pricing grid solution, you’ll need to handle updates. This involves creating a new REST API endpoint that accepts `POST` or `PUT` requests. The permission callback remains crucial here to ensure only authorized users can modify pricing.

Creating an Update Endpoint

We’ll register a new route for updating the pricing grid. This route will require a `WP_REST_Server::EDITABLE` method.

add_action( 'rest_api_init', function () {
    register_rest_route( 'b2b-pricing/v1', '/pricing-grid', array(
        'methods'  => WP_REST_Server::EDITABLE, // For POST, PUT, DELETE
        'callback' => 'b2b_pricing_update_grid_callback',
        'permission_callback' => 'b2b_pricing_permissions_check', // Reuse the same permission check
        'args' => array( // Define expected arguments for validation
            'pricing_data' => array(
                'required' => true,
                'type' => 'array',
                'sanitize_callback' => 'wp_slash_deep', // Sanitize nested arrays
                'validate_callback' => function($param, $request, $key) {
                    // Add more specific validation for the structure of pricing_data if needed
                    return is_array($param);
                }
            ),
        ),
    ) );
} );

The Update Callback Function

This callback will receive the new pricing data, validate it, and save it to your persistent storage (e.g., `wp_options` or a custom database table).

function b2b_pricing_update_grid_callback( WP_REST_Request $request ) {
    $pricing_data = $request->get_param( 'pricing_data' );

    // Basic validation: Ensure pricing_data is an array.
    if ( ! is_array( $pricing_data ) ) {
        return new WP_Error( 'rest_invalid_param', esc_html__( 'Invalid pricing data format.', 'b2b-pricing' ), array( 'status' => 400 ) );
    }

    // Further validation: Check the structure of the pricing data.
    // For example, ensure each product has a price.
    foreach ( $pricing_data as $product_key => $tiers ) {
        if ( ! is_array( $tiers ) ) {
            return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( 'Invalid tier data for product %s.', 'b2b-pricing' ), $product_key ), array( 'status' => 400 ) );
        }
        foreach ( $tiers as $tier_key => $price_info ) {
            if ( ! isset( $price_info['price'] ) || ! is_numeric( $price_info['price'] ) ) {
                return new WP_Error( 'rest_invalid_param', sprintf( esc_html__( 'Invalid price for product %s, tier %s.', 'b2b-pricing' ), $product_key, $tier_key ), array( 'status' => 400 ) );
            }
        }
    }

    // Save the validated pricing data.
    // Example: update_option( 'b2b_pricing_grid_data', $pricing_data );
    // In a real scenario, you might save this to a custom table.
    $saved = update_option( 'b2b_pricing_grid_data', $pricing_data );

    if ( $saved ) {
        return new WP_REST_Response( array( 'message' => esc_html__( 'Pricing grid updated successfully.', 'b2b-pricing' ) ), 200 );
    } else {
        return new WP_Error( 'rest_server_error', esc_html__( 'Failed to update pricing grid.', 'b2b-pricing' ), array( 'status' => 500 ) );
    }
}

Conclusion

By implementing custom REST API endpoints with robust permission callbacks and leveraging WordPress’s role management system, you can build secure and flexible B2B pricing grids. This approach allows for programmatic access, granular control over who sees what pricing, and a clear path for managing complex pricing structures within your WordPress ecosystem.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (48)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (182)
  • WordPress Plugin Development (197)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala