Skip to content

Instantly share code, notes, and snippets.

@goranefbl
Created August 13, 2026 11:42
Show Gist options
  • Select an option

  • Save goranefbl/a4f64a40bd047fc6e375439b93bc0880 to your computer and use it in GitHub Desktop.

Select an option

Save goranefbl/a4f64a40bd047fc6e375439b93bc0880 to your computer and use it in GitHub Desktop.
cap referrals per rank
<?php
/**
* Example: Per-rank yearly cap on referral email invitations
*
* Each VIP rank is allowed to send a different number of referral email
* invitations per calendar year. Once a customer reaches their rank's limit,
* the "invite friends by email" action is rejected with a friendly message
* until January 1, when the counter resets automatically.
*
* Example caps used below: Bronze 3, Silver 5, Gold 10, Platinum 15.
*
* NOTE: This caps the EMAIL invitation action only (the /widget/customer/invite
* REST endpoint). Copying the referral link and the social-share buttons happen
* entirely in the visitor's browser with no server call, so they cannot be
* capped. This is a hard technical limit, not an oversight.
*
* HOW TO FIND YOUR RANK IDS:
* Go to Loyalty Program -> Ranks -> Edit Rank. The modal header shows the
* rank ID, e.g. "Edit Rank (#2)". Adjust the $caps map below to match your
* actual rank IDs. Any rank not listed, or a customer with no rank yet,
* uses WPGL_INVITE_CAP_DEFAULT.
*
* Copy this snippet to your theme's functions.php or a small custom plugin.
* Requires: Loyalty Program plugin with Referrals + Ranks modules active.
*/
if (!defined('ABSPATH')) {
exit;
}
// Fallback cap for customers with no rank (or a rank not listed in the map below).
if (!defined('WPGL_INVITE_CAP_DEFAULT')) {
define('WPGL_INVITE_CAP_DEFAULT', 3);
}
/**
* Per-rank yearly invitation caps.
*
* Each line has the format: RANK_ID => INVITATIONS_PER_YEAR
*
* - Left of "=>" is the RANK ID (the "#2" shown under
* Loyalty Program -> Ranks -> Edit Rank).
* - Right of "=>" is the NUMBER of referral invitations that rank
* may send per calendar year.
*
* Example: "3 => 10" means rank #3 can send 10 invitations per year.
* Edit the IDs and numbers to match this store's VIP ranks.
*/
function wpgl_referral_invite_caps()
{
return array(
// RANK ID => INVITATIONS PER YEAR
1 => 3, // e.g. Bronze
2 => 5, // e.g. Silver
3 => 10, // e.g. Gold
4 => 15, // e.g. Platinum
);
}
/**
* Resolve the current user's yearly invite cap from their rank.
*
* @param int $user_id
* @return int
*/
function wpgl_get_user_invite_cap($user_id)
{
$cap = WPGL_INVITE_CAP_DEFAULT;
if (class_exists('WPGL_Ranks_Core')) {
$rank = WPGL_Ranks_Core::get_user_rank($user_id);
if ($rank && !empty($rank->id)) {
$caps = wpgl_referral_invite_caps();
if (isset($caps[(int) $rank->id])) {
$cap = (int) $caps[(int) $rank->id];
}
}
}
return (int) $cap;
}
/**
* The user-meta key that stores how many invites the user has sent this year.
* Includes the year so it resets automatically every January 1.
*
* @param int $user_id
* @return string
*/
function wpgl_invite_count_meta_key($user_id)
{
$year = (int) current_time('Y');
return '_wpgens_referral_invites_sent_' . $year;
}
/**
* Block the invite request BEFORE any email is sent if it would exceed the cap.
* Hooks the loyalty invite REST route: POST {namespace}/widget/customer/invite
*/
add_filter('rest_pre_dispatch', 'wpgl_enforce_referral_invite_cap', 10, 3);
function wpgl_enforce_referral_invite_cap($result, $server, $request)
{
// Only act on the referral invite endpoint.
if (strpos((string) $request->get_route(), '/widget/customer/invite') === false) {
return $result;
}
$user_id = get_current_user_id();
if (!$user_id) {
return $result; // let the plugin's own auth check handle it
}
// Count how many valid emails this request is trying to send.
$emails = $request->get_param('emails');
$requested = 0;
if (is_array($emails)) {
foreach ($emails as $email) {
if (is_email($email)) {
$requested++;
}
}
}
if ($requested < 1) {
return $result; // nothing valid to send, let the plugin respond
}
$cap = wpgl_get_user_invite_cap($user_id);
$already_sent = (int) get_user_meta($user_id, wpgl_invite_count_meta_key($user_id), true);
$remaining = max(0, $cap - $already_sent);
if ($requested > $remaining) {
return new WP_Error(
'wpgl_invite_limit_reached',
sprintf(
/* translators: 1: yearly cap, 2: remaining invites */
__('You can send up to %1$d referral invitations per year. You have %2$d left this year.', 'wpgens-loyalty'),
$cap,
$remaining
),
array('status' => 429)
);
}
return $result;
}
/**
* Increment the counter each time an invite email is actually sent.
* Fires once per email from WPGL_Emails::send_referral_invite().
*/
add_action('wpgens_loyalty_before_send_email', 'wpgl_count_referral_invite', 10, 2);
function wpgl_count_referral_invite($email_type, $user_id)
{
if ($email_type !== 'referralInvite' || !$user_id) {
return;
}
$key = wpgl_invite_count_meta_key($user_id);
$count = (int) get_user_meta($user_id, $key, true);
update_user_meta($user_id, $key, $count + 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment