Building secure B2B pricing grids with custom Heartbeat API endpoints and role overrides
Leveraging WordPress’s Heartbeat API for Dynamic B2B Pricing
Building a robust and secure B2B pricing grid within WordPress requires more than just standard post types and custom fields. For dynamic pricing that adapts to user roles, purchase history, or even real-time market data, we need a more sophisticated approach. The WordPress Heartbeat API, often used for auto-saves and revision management, can be repurposed as a powerful, low-latency communication channel between the client and server. This allows for real-time updates to pricing information without full page reloads, enhancing the user experience for B2B clients who expect immediate feedback on their tailored pricing.
This post details how to extend the Heartbeat API to create custom endpoints for fetching and updating B2B pricing, incorporating role-based access control and custom logic for price calculation. We’ll focus on a practical implementation using PHP within a custom WordPress plugin.
Custom Heartbeat Endpoint Registration
The Heartbeat API operates by sending periodic AJAX requests from the browser to the server. We can hook into this process to register our own custom data handlers. The key is to use the `heartbeat_send` filter. This filter allows us to add our own data to the Heartbeat payload that is sent to the server, and also to process data sent from the server back to the client.
First, let’s define a function that will be responsible for handling our custom pricing data. This function will be called by the Heartbeat API whenever it sends data to the server. We’ll register this function using the `heartbeat_send` filter.
PHP Implementation for Heartbeat Data Handling
/**
* Handles custom pricing data for the Heartbeat API.
*
* @param array $response The Heartbeat response data.
* @return array Modified Heartbeat response data.
*/
function my_custom_pricing_heartbeat_data( $response ) {
// Check if the current user is logged in.
if ( is_user_logged_in() ) {
$user_id = get_current_user_id();
$user_roles = wp_get_current_user()->roles;
// Fetch pricing data based on user role or other criteria.
// This is where your custom pricing logic will reside.
$pricing_data = get_b2b_pricing_for_user( $user_id, $user_roles );
// Add our custom pricing data to the Heartbeat response.
$response['my_custom_pricing'] = $pricing_data;
}
return $response;
}
add_filter( 'heartbeat_send', 'my_custom_pricing_heartbeat_data', 10, 1 );
/**
* Fetches B2B pricing data for a given user.
* This is a placeholder function; implement your actual pricing logic here.
*
* @param int $user_id The ID of the current user.
* @param array $user_roles The roles of the current user.
* @return array Pricing data.
*/
function get_b2b_pricing_for_user( $user_id, $user_roles ) {
// Example: Fetch pricing from a custom database table,
// meta data, or an external API based on user role.
$pricing = array();
if ( in_array( 'wholesale_customer', $user_roles ) ) {
$pricing['product_a'] = 10.50;
$pricing['product_b'] = 25.00;
} elseif ( in_array( 'premium_partner', $user_roles ) ) {
$pricing['product_a'] = 9.75;
$pricing['product_b'] = 23.50;
$pricing['special_offer'] = '10% off Product C';
} else {
// Default pricing or public pricing.
$pricing['product_a'] = 15.00;
$pricing['product_b'] = 30.00;
}
// You might also fetch user-specific discounts or contract pricing.
$user_specific_discount = get_user_meta( $user_id, '_b2b_contract_discount', true );
if ( ! empty( $user_specific_discount ) ) {
$pricing['contract_discount'] = floatval( $user_specific_discount );
}
return $pricing;
}
In this code:
- We define `my_custom_pricing_heartbeat_data` which hooks into `heartbeat_send`.
- Inside this function, we check if the user is logged in.
- We then call a placeholder function `get_b2b_pricing_for_user` to retrieve pricing information. This function should contain your core B2B pricing logic, potentially querying custom database tables, user meta, or even external pricing services.
- The retrieved pricing data is added to the `$response` array under a custom key, `my_custom_pricing`.
Client-Side JavaScript for Heartbeat Interaction
On the client-side, we need to listen for Heartbeat events and process the data received from the server. WordPress provides a JavaScript API for interacting with the Heartbeat. We’ll enqueue a custom JavaScript file and use `wp.heartbeat.connect()` to send and receive data.
JavaScript Implementation
jQuery(document).ready(function($) {
// Start the Heartbeat connection.
// The interval can be adjusted. Default is 60 seconds.
// For more frequent updates, consider a shorter interval, but be mindful of server load.
wp.heartbeat.connect( 60, {
// Optional: Send data to the server on each heartbeat.
// This can be used to signal user activity or request specific data.
// For this example, we're primarily interested in receiving data.
// data: {
// action: 'my_custom_pricing_request',
// // other data...
// }
} );
// Listen for Heartbeat events.
$(document).on( 'heartbeat-tick', function( event, data ) {
// Check if our custom pricing data is present in the response.
if ( data.hasOwnProperty( 'my_custom_pricing' ) ) {
var pricingData = data.my_custom_pricing;
console.log( 'Received custom pricing data:', pricingData );
// Update your pricing grid UI here.
// Example: Update prices for specific product elements.
if ( pricingData.hasOwnProperty( 'product_a' ) ) {
$( '#product-a-price' ).text( '$' + pricingData.product_a.toFixed(2) );
}
if ( pricingData.hasOwnProperty( 'product_b' ) ) {
$( '#product-b-price' ).text( '$' + pricingData.product_b.toFixed(2) );
}
if ( pricingData.hasOwnProperty( 'special_offer' ) ) {
$( '#special-offer-banner' ).text( pricingData.special_offer ).show();
}
if ( pricingData.hasOwnProperty( 'contract_discount' ) ) {
$( '#contract-discount-display' ).text( 'Contract Discount: ' + pricingData.contract_discount + '%' );
}
}
} );
// Handle Heartbeat connection errors or closures.
$(document).on( 'heartbeat-connection-lost', function() {
console.log( 'Heartbeat connection lost.' );
// Optionally, display a message to the user or attempt to re-establish connection.
} );
$(document).on( 'heartbeat-connection-established', function() {
console.log( 'Heartbeat connection established.' );
} );
});
To use this JavaScript:
- Enqueue this script in your plugin using `wp_enqueue_script`. Ensure it depends on `jquery` and `heartbeat-js`.
- The `wp.heartbeat.connect()` function initiates the connection. The first argument is the interval in seconds.
- We listen for the `heartbeat-tick` event. When this event fires, the `data` object contains the response from the server, including our `my_custom_pricing` payload.
- We then parse this data and update the relevant elements on the page (e.g., price displays, offer banners).
Role Overrides and Security Considerations
Security is paramount when dealing with pricing. The Heartbeat API itself is designed to be secure, operating over AJAX requests that are authenticated by WordPress’s nonces. However, your custom logic for fetching and displaying prices must be robust.
Server-Side Role Validation
Always validate user roles and capabilities on the server-side. Never trust client-side JavaScript to enforce pricing rules. The `get_b2b_pricing_for_user` function in our PHP example already includes role checks. Ensure that:
- The user is logged in.
- The user possesses the necessary roles or capabilities to access specific pricing tiers.
- If fetching data from an external API, ensure API keys or authentication tokens are stored securely and not exposed client-side.
Custom Capabilities for Fine-Grained Control
For more granular control beyond standard WordPress roles, consider defining custom capabilities. For instance, you might have a capability like `view_premium_pricing` that can be assigned to users or roles.
// Example of adding a custom capability (typically done on plugin activation)
function my_plugin_add_custom_capabilities() {
$role = get_role( 'wholesale_customer' );
if ( $role ) {
$role->add_cap( 'view_premium_pricing' );
}
}
// register_activation_hook( __FILE__, 'my_plugin_add_custom_capabilities' );
// Modified pricing function using custom capabilities
function get_b2b_pricing_for_user_with_caps( $user_id, $user_roles ) {
$pricing = array();
if ( current_user_can( 'view_premium_pricing' ) ) {
// Premium pricing logic
$pricing['product_a'] = 9.75;
$pricing['product_b'] = 23.50;
} elseif ( in_array( 'wholesale_customer', $user_roles ) ) {
// Standard wholesale pricing
$pricing['product_a'] = 10.50;
$pricing['product_b'] = 25.00;
} else {
// Default pricing
$pricing['product_a'] = 15.00;
$pricing['product_b'] = 30.00;
}
// ... other pricing logic
return $pricing;
}
By using `current_user_can()`, you can leverage WordPress’s built-in capability system for more robust access control.
Heartbeat Frequency and Performance
The Heartbeat API’s default interval is 60 seconds. While you can reduce this for more frequent updates, be cautious. Each heartbeat is an AJAX request that consumes server resources. For B2B pricing, which might not change by the second, a moderate interval (e.g., 30-60 seconds) is usually sufficient. If your pricing logic is computationally intensive, consider optimizing it or using caching mechanisms.
Advanced Scenarios and Integrations
The Heartbeat API’s flexibility allows for more complex scenarios:
- Dynamic Product Availability: Show or hide products based on user role or contract.
- Real-time Stock Updates: Integrate with inventory systems to reflect stock levels alongside pricing.
- Promotional Pricing: Trigger special offers or discounts based on user activity or time-sensitive events.
- Integration with E-commerce Platforms: If using WooCommerce, you can use Heartbeat to update cart prices or display custom pricing for logged-in B2B users.
WooCommerce Integration Example
For WooCommerce, you’d typically modify the `get_b2b_pricing_for_user` function to interact with WooCommerce product prices and user roles. You might also need to hook into WooCommerce’s AJAX actions to ensure prices are correctly reflected in the cart and checkout.
// Example snippet for WooCommerce integration within get_b2b_pricing_for_user
function get_b2b_pricing_for_user_wc( $user_id, $user_roles ) {
$pricing = array();
$product_ids = array( 123, 456 ); // Example product IDs
foreach ( $product_ids as $product_id ) {
$product = wc_get_product( $product_id );
if ( ! $product ) {
continue;
}
$base_price = $product->get_price();
$discounted_price = $base_price;
if ( in_array( 'wholesale_customer', $user_roles ) ) {
$wholesale_percentage = get_post_meta( $product_id, '_wholesale_price_percentage', true );
if ( ! empty( $wholesale_percentage ) ) {
$discounted_price = $base_price * ( floatval( $wholesale_percentage ) / 100 );
}
} elseif ( in_array( 'premium_partner', $user_roles ) ) {
$premium_percentage = get_post_meta( $product_id, '_premium_price_percentage', true );
if ( ! empty( $premium_percentage ) ) {
$discounted_price = $base_price * ( floatval( $premium_percentage ) / 100 );
}
}
// Apply user-specific contract discounts if applicable
// ...
$pricing[ 'product_' . $product_id ] = $discounted_price;
}
return $pricing;
}
// You would then call this function from your heartbeat_send hook.
// On the JS side, you'd target WooCommerce product elements.
Remember to ensure that your JavaScript correctly targets the HTML elements that display WooCommerce prices. You might need to use WooCommerce’s own JavaScript hooks or filters for a seamless integration, especially for cart and checkout updates.
Conclusion
By strategically employing the WordPress Heartbeat API, developers can build sophisticated, real-time B2B pricing grids that are both dynamic and secure. The key lies in robust server-side validation, careful management of Heartbeat frequency, and a clear separation of concerns between client-side presentation and server-side logic. This approach not only enhances the user experience for B2B clients but also provides a powerful framework for managing complex pricing structures within WordPress.