Loyalty Program for WooCommerce can award a flat number of birthday points to every member, but a tiered loyalty program often calls for tiered birthday rewards. With a short snippet you can hand out 50 points to regular members, 200 to VIPs, and 500 plus a one-time NT$200 birthday coupon to VVIPs. This guide shows the full code.
Find Your Rank IDs
The snippet below references ranks by numeric ID rather than name, so the same code keeps working if you rename a rank later. To find a rank’s ID, go to Loyalty Program > Ranks in WordPress admin, click Edit Rank on the row you want, and read the ID from the modal header: it appears as Edit Rank (#2). Use that number wherever the snippet shows a comment like // VIP or // VVIP.
How the Snippet Works
The example removes the plugin’s default WPGL_Points_Hooks::process_birthday_points handler and replaces it with a tier-aware one. The replacement runs on the same daily cron the plugin already schedules (wpgens_loyalty_daily_cron), so no extra scheduling is needed. Each user is still rewarded only once per calendar year, tracked through the same _wpgens_loyalty_last_birthday_points meta the plugin already uses.
Edit the $tier_rewards array to set points and an optional coupon per rank ID. The key 0 is used as the fallback for users with no rank assigned. Set 'coupon' => false when a tier should only receive points.
The Snippet
<?php
/**
* Example: Tier-specific birthday rewards
*
* Award different birthday rewards depending on the customer's membership tier
* (rank). For example:
* - Regular members -> 50 birthday points
* - VIP members -> 200 birthday points
* - VVIP members -> 500 birthday points + a NT$200 birthday coupon
*
* This snippet replaces the plugin's default birthday-points handler so we can
* fully control how much each user receives. It runs daily on the same cron
* the plugin already schedules (`wpgens_loyalty_daily_cron`).
*
* Rank IDs are visible in the admin: Loyalty Program -> Ranks -> Edit Rank.
* The modal header shows the rank ID, e.g. "Edit Rank (#2)".
*
* Copy this snippet to your theme's functions.php or a small custom plugin.
* Requires: Loyalty Program plugin (Points + Ranks modules active).
*/
if (!defined('ABSPATH')) {
exit;
}
// Replace the plugin's default birthday handler with our tier-aware one.
add_action('init', 'wpgl_replace_birthday_handler', 20);
function wpgl_replace_birthday_handler()
{
if (!class_exists('WPGL_Points_Hooks')) {
return;
}
remove_action('wpgens_loyalty_daily_cron', ['WPGL_Points_Hooks', 'process_birthday_points']);
add_action('wpgens_loyalty_daily_cron', 'wpgl_process_tier_birthday_rewards');
}
function wpgl_process_tier_birthday_rewards()
{
global $wpdb;
if (!class_exists('WPGL_Ranks_Core') || !class_exists('WPGL_Points_Source_Type')) {
return;
}
// Reward map keyed by rank ID. Use key 0 for users with no rank.
// `points` is awarded; `coupon` is optional (set false to skip).
$tier_rewards = [
0 => [ // No rank / default
'points' => 50,
'coupon' => false,
],
2 => [ // VIP
'points' => 200,
'coupon' => false,
],
3 => [ // VVIP
'points' => 500,
'coupon' => [
'amount' => 200,
'discount_type' => 'fixed_cart',
'expires_days' => 30,
],
],
];
$current_month_day = current_time('m-d');
$current_year = current_time('Y');
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- daily cron, no cache benefit
$users = $wpdb->get_results($wpdb->prepare(
"SELECT user_id, meta_value AS birthday
FROM {$wpdb->usermeta}
WHERE meta_key = '_wpgens_loyalty_birthday'
AND DATE_FORMAT(meta_value, '%%m-%%d') = %s",
$current_month_day
));
if (empty($users)) {
return;
}
foreach ($users as $user) {
$user_id = (int) $user->user_id;
// Skip if we've already processed this user this year.
$last_year = get_user_meta($user_id, '_wpgens_loyalty_last_birthday_points', true);
if ($last_year === $current_year) {
continue;
}
// Resolve which tier reward applies.
$rank = WPGL_Ranks_Core::get_user_rank($user_id);
$rank_id = ($rank && !empty($rank->id)) ? (int) $rank->id : 0;
$reward = isset($tier_rewards[$rank_id]) ? $tier_rewards[$rank_id] : $tier_rewards[0];
// Award points.
$points = (int) $reward['points'];
if ($points > 0) {
do_action(
'wpgens_loyalty_update_points',
$user_id,
$points,
WPGL_Points_Activity_Type::EARN,
WPGL_Points_Source_Type::BIRTHDAY,
null
);
}
// Award optional birthday coupon.
if (!empty($reward['coupon']) && class_exists('WPGL_Coupon_Service')) {
$coupon_args = wp_parse_args($reward['coupon'], [
'amount' => 0,
'discount_type' => 'fixed_cart',
'expires_days' => 30,
]);
$expires = strtotime('+' . (int) $coupon_args['expires_days'] . ' days');
WPGL_Coupon_Service::create_coupon([
'user_id' => $user_id,
'amount' => (float) $coupon_args['amount'],
'discount_type' => $coupon_args['discount_type'],
'description' => sprintf('Birthday reward (rank #%d)', $rank_id),
'usage_limit' => 1,
'usage_limit_per_user' => 1,
'individual_use' => false,
'date_expires' => $expires ? gmdate('Y-m-d H:i:s', $expires) : null,
'prefix' => 'birthday',
'loyalty_meta' => [
'_wpgl_birthday_reward_year' => $current_year,
'_wpgl_birthday_reward_rank_id' => $rank_id,
],
]);
}
update_user_meta($user_id, '_wpgens_loyalty_last_birthday_points', $current_year);
}
}
Where to Put This Snippet
Drop the snippet into a small site-specific plugin under wp-content/mu-plugins/ so it survives theme changes, or paste it into your child theme’s functions.php. It is also available in the plugin folder under examples/.
To test without waiting for a real birthday, temporarily set a test user’s birthday to today, then run wp cron event run wpgens_loyalty_daily_cron via WP-CLI. Verify the points are awarded and, for VVIP, that a coupon is created in the user’s account.