Home / eCommerce / AffiliateWP – Second Affiliate Selection at Checkout (WooCommerce Checkout Block)
Duplicate Snippet

Embed Snippet on Your Site

AffiliateWP – Second Affiliate Selection at Checkout (WooCommerce Checkout Block)

Adds a second affiliate dropdown to the WooCommerce Checkout Block, allowing customers to select two affiliates to receive commission on a single order. Works alongside the AffiliateWP Checkout Referrals addon, which handles the first affiliate selection.

The second affiliate's commission is calculated using their individual rate settings in AffiliateWP. A separate referral is created, and its lifecycle is fully managed — marked as unpaid when the order is completed/processing, and rejected if the order is refunded.

Requires: AffiliateWP, Checkout Referrals addon, and WooCommerce with the Checkout Block enabled.

Code Preview
php
<?php
/**
 * Second Affiliate Selection at WooCommerce Checkout (Checkout Block Compatible)
 *
 * Adds a second affiliate dropdown alongside the Checkout Referrals addon's
 * first dropdown and creates a separate referral for the second affiliate.
 *
 * Requires: AffiliateWP + Checkout Referrals addon (handles the first affiliate)
 * Compatible with: WooCommerce Checkout Block
 *
 * Installation: Add via WPCode plugin ("Run Everywhere") or child theme's functions.php
 */
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Register the second affiliate select dropdown for WooCommerce Checkout Block.
 * Uses the same API as the Checkout Referrals addon (woocommerce_register_additional_checkout_field).
 */
add_action( 'woocommerce_init', function() {
    if ( ! function_exists( 'woocommerce_register_additional_checkout_field' ) || ! function_exists( 'affiliate_wp' ) ) {
        return;
    }
    $affiliates = affiliate_wp()->affiliates->get_affiliates( array(
        'status' => 'active',
        'number' => -1,
    ) );
    if ( empty( $affiliates ) ) {
        return;
    }
    $display = function_exists( 'affwp_cr_affiliate_display' )
        ? affwp_cr_affiliate_display()
        : 'display_name';
    $options = array();
    foreach ( $affiliates as $affiliate ) {
        $user_info = get_userdata( $affiliate->user_id );
        if ( false !== $user_info ) {
            $options[] = array(
                'value' => absint( $affiliate->affiliate_id ),
                'label' => (string) $user_info->$display,
            );
        }
    }
    if ( empty( $options ) ) {
        return;
    }
    woocommerce_register_additional_checkout_field( array(
        'id'                         => 'affiliatewp/second_checkout_affiliate',
        'label'                      => __( 'Second person to receive commission', 'affiliate-wp' ),
        'location'                   => 'order',
        'type'                       => 'select',
        'required'                   => false,
        'show_in_order_confirmation' => true,
        'options'                    => $options,
    ) );
} );
/**
 * Save the selected second affiliate ID to order meta when the field value is persisted.
 * This fires at Step #5 of the checkout block flow, before the order is fully processed.
 */
add_action( 'woocommerce_set_additional_field_value', function( $key, $value, $group, $wc_object ) {
    if ( 'affiliatewp/second_checkout_affiliate' !== $key ) {
        return;
    }
    if ( ! function_exists( 'affwp_is_active_affiliate' ) ) {
        return;
    }
    $affiliate_id = absint( $value );
    if ( $affiliate_id <= 0 || ! affwp_is_active_affiliate( $affiliate_id ) ) {
        return;
    }
    $wc_object->update_meta_data( '_affwp_second_affiliate_id', $affiliate_id );
    $wc_object->save();
}, 10, 4 );
/**
 * Create a pending referral for the second affiliate after the order is processed.
 * Runs at priority 20, after AffiliateWP's own referral creation at default priority 10.
 *
 * woocommerce_store_api_checkout_order_processed passes a WC_Order object.
 */
add_action( 'woocommerce_store_api_checkout_order_processed', function( $order ) {
    if ( ! function_exists( 'affiliate_wp' ) ) {
        return;
    }
    $second_affiliate_id = absint( $order->get_meta( '_affwp_second_affiliate_id' ) );
    if ( $second_affiliate_id <= 0 || ! affwp_is_active_affiliate( $second_affiliate_id ) ) {
        return;
    }
    $items       = array();
    $order_total = 0;
    foreach ( $order->get_items() as $item ) {
        $items[] = $item->get_name();
        $item_total = $item->get_total();
        if ( affiliate_wp()->settings->get( 'exclude_tax' ) ) {
            $order_total += $item_total;
        } else {
            $order_total += $item_total + $item->get_total_tax();
        }
    }
    if ( ! affiliate_wp()->settings->get( 'exclude_shipping' ) ) {
        $order_total += floatval( $order->get_shipping_total() );
        if ( ! affiliate_wp()->settings->get( 'exclude_tax' ) ) {
            $order_total += floatval( $order->get_shipping_tax() );
        }
    }
    $rate      = affwp_get_affiliate_rate( $second_affiliate_id );
    $rate_type = affwp_get_affiliate_rate_type( $second_affiliate_id );
    if ( 'percentage' === $rate_type ) {
        $amount = round( $order_total * $rate, 2 );
    } else {
        $amount = $rate;
    }
    if ( $amount <= 0 ) {
        return;
    }
    $order_id = $order->get_id();
    $referral_id = affiliate_wp()->referrals->add( array(
        'affiliate_id' => $second_affiliate_id,
        'amount'       => $amount,
        'reference'    => $order_id,
        'context'      => 'woocommerce',
        'description'  => implode( ', ', $items ),
        'type'         => 'sale',
        'status'       => 'pending',
        'custom'       => 'second_checkout_affiliate',
    ) );
    if ( $referral_id ) {
        $order->update_meta_data( '_affwp_second_referral_id', $referral_id );
        $order->save();
        affwp_increase_affiliate_referral_count( $second_affiliate_id );
        $order->add_order_note( sprintf(
            'AffiliateWP: Second referral #%d created for %s (Affiliate #%d) — %s',
            $referral_id,
            affwp_get_affiliate_name( $second_affiliate_id ),
            $second_affiliate_id,
            affwp_currency_filter( affwp_format_amount( $amount ) )
        ) );
    }
}, 20 );
/**
 * Mark the second referral as unpaid when the order is completed or processing.
 * AffiliateWP marks its own referral on both statuses, so we mirror that.
 */
function affwp_second_referral_mark_complete( $order_id ) {
    if ( ! function_exists( 'affiliate_wp' ) ) {
        return;
    }
    $order = wc_get_order( $order_id );
    if ( ! $order ) {
        return;
    }
    $referral_id = absint( $order->get_meta( '_affwp_second_referral_id' ) );
    if ( $referral_id <= 0 ) {
        return;
    }
    $referral = affwp_get_referral( $referral_id );
    if ( $referral && 'pending' === $referral->status ) {
        affwp_set_referral_status( $referral_id, 'unpaid' );
        affwp_increase_affiliate_earnings( $referral->affiliate_id, $referral->amount );
    }
}
add_action( 'woocommerce_order_status_completed', 'affwp_second_referral_mark_complete' );
add_action( 'woocommerce_order_status_processing', 'affwp_second_referral_mark_complete' );
/**
 * If the order is refunded, reject the second referral.
 */
function affwp_second_referral_revoke_on_refund( $order_id ) {
    if ( ! function_exists( 'affiliate_wp' ) ) {
        return;
    }
    $order = wc_get_order( $order_id );
    if ( ! $order ) {
        return;
    }
    $referral_id = absint( $order->get_meta( '_affwp_second_referral_id' ) );
    if ( $referral_id <= 0 ) {
        return;
    }
    $referral = affwp_get_referral( $referral_id );
    if ( $referral && in_array( $referral->status, array( 'pending', 'unpaid' ), true ) ) {
        if ( 'unpaid' === $referral->status ) {
            affwp_decrease_affiliate_earnings( $referral->affiliate_id, $referral->amount );
        }
        affwp_set_referral_status( $referral_id, 'rejected' );
        affwp_decrease_affiliate_referral_count( $referral->affiliate_id );
    }
}
add_action( 'woocommerce_order_status_completed_to_refunded', 'affwp_second_referral_revoke_on_refund' );
add_action( 'woocommerce_order_status_processing_to_refunded', 'affwp_second_referral_revoke_on_refund' );
add_action( 'woocommerce_order_status_pending_to_refunded', 'affwp_second_referral_revoke_on_refund' );
add_action( 'woocommerce_order_status_on-hold_to_refunded', 'affwp_second_referral_revoke_on_refund' );

Comments

Add a Comment