How to Stack Referral Rewards Into a Single Coupon

By default, every time a referral order is completed the advocate (the referrer) receives a brand new reward coupon. Some stores prefer the opposite behavior: keep a single coupon per advocate and simply increase its amount with every successful referral, so the customer always redeems the same growing coupon. This guide provides a code snippet that does exactly that, and explains an important limitation around percentage based coupons.

Before you start

This is a custom code snippet, not a built in setting. Add it through a child theme functions.php file or, recommended, a small mu-plugin. Always test on a staging site first.

The snippet

/**
 * Refer a Friend, stack the advocate reward onto ONE coupon
 * instead of issuing a new coupon every time a referral completes.
 */
add_action( 'gens_before_generate_user_coupon', function ( $user_id, $type, $order ) {

	// Only the advocate (referrer) reward. Leave friend/registration coupons alone.
	if ( 'referrer' !== $type || ! $order ) {
		return;
	}

	$prefix        = 'gens_raf';
	$discount_type = get_option( $prefix . '_coupon_type' );
	$amount        = get_option( $prefix . '_coupon_amount' );

	// "Percent of order total" is converted to a real currency amount per order,
	// exactly like the plugin does, so it CAN be stacked safely.
	if ( 'order_percent' === $discount_type ) {
		$order_total = $order->get_total() - $order->get_total_tax();
		if ( get_option( 'gens_raf_subscription_exclude_shipping' ) ) {
			$order_total -= $order->get_total_shipping();
		}
		$discount_type = 'fixed_cart';
		$amount        = number_format( $order_total * ( $amount / 100 ), 2, '.', '' );
	}

	$amount = apply_filters( $prefix . '_coupon_amount', $amount, $order->get_id() );

	// Only stack fixed-amount coupons. Stacking a straight percentage (10% + 10%)
	// is meaningless and unbounded, so for those we just let a normal new coupon be made.
	if ( 'fixed_cart' !== $discount_type && 'fixed_product' !== $discount_type ) {
		return;
	}

	// Resolve the advocate email the same way the plugin does.
	$email = '';
	if ( is_string( $user_id ) && strpos( $user_id, 'ref' ) === 0 ) {
		$db    = new WPGens_RAF_DB();
		$guest = $db->get_guest_referral_by_code( $user_id );
		if ( $guest ) {
			$email = $guest['email'];
		} elseif ( $numeric = WPGens_RAF_User::get_id_from_referral_code( $user_id ) ) {
			$u = get_userdata( $numeric );
			$email = $u ? $u->user_email : '';
		}
	} elseif ( is_email( $user_id ) ) {
		$email = $user_id;
	} elseif ( $u = get_userdata( $user_id ) ) {
		$email = $u->user_email;
	}
	if ( ! $email ) {
		return;
	}

	// Find this advocate existing (oldest) RAF coupon of the same type.
	$existing = get_posts( array(
		'post_type'   => 'shop_coupon',
		'post_status' => 'publish',
		'numberposts' => -1,
		'orderby'     => 'date',
		'order'       => 'ASC',
		'meta_query'  => array(
			array( 'key' => 'customer_email', 'value' => strtolower( $email ) ),
			array( 'key' => 'discount_type',  'value' => $discount_type ),
		),
	) );

	$master = null;
	foreach ( $existing as $c ) {
		if ( strpos( $c->post_title, 'RAF-' ) !== 0 ) {
			continue; // only our referral coupons
		}
		$used  = (int) get_post_meta( $c->ID, 'usage_count', true );
		$limit = (int) get_post_meta( $c->ID, 'usage_limit', true );
		if ( $limit && $used >= $limit ) {
			continue; // already redeemed, do not top up a used coupon
		}
		$master = $c;
		break;
	}

	// No existing unused coupon, let the plugin create one normally (first reward).
	if ( ! $master ) {
		return;
	}

	// Top up the existing coupon.
	$current = (float) get_post_meta( $master->ID, 'coupon_amount', true );
	update_post_meta( $master->ID, 'coupon_amount', $current + (float) $amount );

	// Prevent a NEW coupon from being created for this generation only
	// (self-removing so the friend coupon in the same request is unaffected).
	$one_shot = function () use ( &$one_shot ) {
		remove_filter( 'gens_raf_generate_coupon', $one_shot );
		return false;
	};
	add_filter( 'gens_raf_generate_coupon', $one_shot );

}, 10, 3 );

How it works

  1. It hooks into gens_before_generate_user_coupon, which runs right before the plugin builds the reward coupon, and it acts only on the advocate (referrer) reward. Friend and registration coupons keep their normal behavior.
  2. If the advocate already has an unused RAF- coupon, the snippet adds the current order reward to that coupon amount and cancels creation of a new one through the gens_raf_generate_coupon filter. The filter removes itself, so the friend coupon generated later in the same request is not affected.
  3. The growing total appears automatically under My Account, Refer a Friend, because that list reads the coupon amount live.

Important: percentage coupons

Whether a reward can be stacked depends on the coupon type you use.

Coupon typeStacked?Why
Fixed cart / Fixed productYesReal currency amounts add up cleanly, for example 5 plus 8 equals 13.
Percent of order total (order_percent)YesThe plugin already converts this into a fixed amount per order before saving, so each reward contributes a real currency value.
Plain percentage (percent, recurring_percent, and similar)NoAdding 10% plus 10% to reach 20% is unbounded and rarely intended. For these types the snippet keeps the default behavior and a separate new coupon is issued.

A note about emails

When the snippet tops up an existing coupon, the plugin does not send a reward email for that top up, and the order _raf_coupon_generated flag is not set. The advocate sees the increased balance in their My Account tab. A reward email is still sent the first time the coupon is created. If you also want a notification on every top up, that requires a few extra lines, so reach out to support.

Will this work with percentage coupons?

Yes for the Percent of order total type, because the plugin converts it to a real currency amount per order before saving. It does not stack plain percentage coupons, since adding 10% to 10% is unbounded and rarely intended. For plain percentage coupons a new separate coupon is issued as usual.

Does it affect the friend (referred customer) coupon?

No. The snippet only acts on the advocate (referrer) reward. Friend and registration coupons keep their normal behavior because the filter that blocks new coupon creation removes itself immediately.

Where does the customer see the growing amount?

In their account under My Account, Refer a Friend. That coupon list reads the coupon amount live, so the new total appears automatically after each successful referral.

Browse our plugins

Lightweight WooCommerce plugins built for speed. No bloat, no frameworks -- just clean code that works.

View all plugins
Stay in the loop

Get notified when we launch new plugins. No spam, just product updates.