feat(affiliate): add core module, controllers, and route registration
This commit is contained in:
252
includes/Modules/Affiliate/AffiliateLifecycle.php
Normal file
252
includes/Modules/Affiliate/AffiliateLifecycle.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Affiliate Lifecycle
|
||||
*
|
||||
* Handles order status changes (refunds, cancellations) and auto-approvals.
|
||||
*
|
||||
* @package WooNooW\Modules\Affiliate
|
||||
*/
|
||||
|
||||
namespace WooNooW\Modules\Affiliate;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
class AffiliateLifecycle
|
||||
{
|
||||
/**
|
||||
* Initialize lifecycle hooks
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
// Cancel/Revert on order refund or cancellation
|
||||
add_action('woocommerce_order_status_refunded', [__CLASS__, 'handle_order_cancelled']);
|
||||
add_action('woocommerce_order_status_cancelled', [__CLASS__, 'handle_order_cancelled']);
|
||||
add_action('woocommerce_order_status_failed', [__CLASS__, 'handle_order_cancelled']);
|
||||
|
||||
// Handle order completion - immediate approval
|
||||
add_action('woocommerce_order_status_completed', [__CLASS__, 'handle_order_completed']);
|
||||
|
||||
// HPOS compatible hooks
|
||||
add_action('woocommerce_order_status_changed', [__CLASS__, 'handle_hpos_status_changed'], 10, 4);
|
||||
add_action('woocommerce_update_order', [__CLASS__, 'handle_order_updated'], 10, 2);
|
||||
|
||||
// Handle order deletion (trash + permanent delete)
|
||||
add_action('before_delete_post', [__CLASS__, 'handle_order_deleted'], 10, 1);
|
||||
add_action('woocommerce_delete_order', [__CLASS__, 'handle_order_deleted'], 10, 1);
|
||||
|
||||
// Action Scheduler Hook for auto-approval
|
||||
add_action('woonoow_approve_referral', [__CLASS__, 'auto_approve_referral']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle cancelled, refunded, or failed orders
|
||||
*/
|
||||
public static function handle_order_cancelled($order_id)
|
||||
{
|
||||
global $wpdb;
|
||||
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
|
||||
|
||||
// Get the order to determine the reason
|
||||
$order = wc_get_order($order_id);
|
||||
$reason = 'order_cancelled';
|
||||
if ($order) {
|
||||
$status = $order->get_status();
|
||||
$reason = 'order_' . $status;
|
||||
}
|
||||
|
||||
// Find pending or approved referral
|
||||
$referral = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $referrals_table WHERE order_id = %d AND status IN ('pending', 'approved')",
|
||||
$order_id
|
||||
));
|
||||
|
||||
if ($referral) {
|
||||
// If was already approved, this is a clawback - decrease affiliate earnings
|
||||
if ($referral->status === 'approved') {
|
||||
$affiliates_table = $wpdb->prefix . 'woonoow_affiliates';
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"UPDATE $affiliates_table SET total_earnings = total_earnings - %f WHERE id = %d",
|
||||
$referral->commission_amount,
|
||||
$referral->affiliate_id
|
||||
));
|
||||
}
|
||||
|
||||
// Update status to rejected with reason
|
||||
$wpdb->update(
|
||||
$referrals_table,
|
||||
[
|
||||
'status' => 'rejected',
|
||||
'cancelled_reason' => $reason,
|
||||
'cancelled_at' => current_time('mysql')
|
||||
],
|
||||
['id' => $referral->id]
|
||||
);
|
||||
|
||||
// Unschedule action if action scheduler exists
|
||||
if (function_exists('as_unschedule_all_actions')) {
|
||||
as_unschedule_all_actions('woonoow_approve_referral', ['referral_id' => $referral->id], 'woonoow_affiliate');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle order completion - check if referral should be approved
|
||||
*/
|
||||
public static function handle_order_completed($order_id)
|
||||
{
|
||||
global $wpdb;
|
||||
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
|
||||
|
||||
// Find pending referral for this order
|
||||
$referral = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $referrals_table WHERE order_id = %d AND status = 'pending'",
|
||||
$order_id
|
||||
));
|
||||
|
||||
if (!$referral) return;
|
||||
|
||||
// Check if holding period is 0 (immediate approval on completion)
|
||||
$holding_period = (int) get_option('woonoow_affiliate_holding_period', 14);
|
||||
|
||||
if ($holding_period === 0) {
|
||||
// Immediate approval
|
||||
self::auto_approve_referral($referral->id);
|
||||
} else {
|
||||
// If order was completed BEFORE the scheduled action time, approve now
|
||||
// Otherwise, the scheduled action will approve later
|
||||
// Check if the scheduled action time has already passed
|
||||
$approval_time = strtotime($referral->created_at) + ($holding_period * DAY_IN_SECONDS);
|
||||
|
||||
if (time() >= $approval_time) {
|
||||
self::auto_approve_referral($referral->id);
|
||||
}
|
||||
// If not, the scheduled Action Scheduler job will handle it
|
||||
}
|
||||
|
||||
// Cancel the scheduled auto-approval since we're handling it now
|
||||
if (function_exists('as_unschedule_all_actions')) {
|
||||
as_unschedule_all_actions('woonoow_approve_referral', ['referral_id' => $referral->id], 'woonoow_affiliate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle HPOS order status change
|
||||
*/
|
||||
public static function handle_hpos_status_changed($order_id, $from_status, $to_status, $order)
|
||||
{
|
||||
if ($to_status === 'completed') {
|
||||
self::handle_order_completed($order_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle order update (HPOS compatible)
|
||||
*/
|
||||
public static function handle_order_updated($order_id, $order)
|
||||
{
|
||||
if (!$order) return;
|
||||
|
||||
// Check if order was just completed
|
||||
$status = $order->get_status();
|
||||
if ($status === 'completed') {
|
||||
self::handle_order_completed($order_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle order deletion (permanent delete or trash)
|
||||
*/
|
||||
public static function handle_order_deleted($order_id)
|
||||
{
|
||||
// Check if this is a WooCommerce order
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) return;
|
||||
|
||||
// Only process shop orders
|
||||
$post_type = get_post_type($order_id);
|
||||
if ($post_type !== 'shop_order') return;
|
||||
|
||||
global $wpdb;
|
||||
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
|
||||
|
||||
// Find any referral for this order
|
||||
$referral = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $referrals_table WHERE order_id = %d",
|
||||
$order_id
|
||||
));
|
||||
|
||||
if ($referral) {
|
||||
// If was already approved, this is a clawback - decrease affiliate earnings
|
||||
if ($referral->status === 'approved') {
|
||||
$affiliates_table = $wpdb->prefix . 'woonoow_affiliates';
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"UPDATE $affiliates_table SET total_earnings = total_earnings - %f WHERE id = %d",
|
||||
$referral->commission_amount,
|
||||
$referral->affiliate_id
|
||||
));
|
||||
}
|
||||
|
||||
// Mark as rejected with "order_deleted" reason
|
||||
$wpdb->update(
|
||||
$referrals_table,
|
||||
[
|
||||
'status' => 'rejected',
|
||||
'cancelled_reason' => 'order_deleted',
|
||||
'cancelled_at' => current_time('mysql')
|
||||
],
|
||||
['id' => $referral->id]
|
||||
);
|
||||
|
||||
// Unschedule any pending approval action
|
||||
if (function_exists('as_unschedule_all_actions')) {
|
||||
as_unschedule_all_actions('woonoow_approve_referral', ['referral_id' => $referral->id], 'woonoow_affiliate');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action Scheduler callback for auto-approving a referral after the holding period
|
||||
*/
|
||||
public static function auto_approve_referral($referral_id)
|
||||
{
|
||||
global $wpdb;
|
||||
$referrals_table = $wpdb->prefix . 'woonoow_referrals';
|
||||
$affiliates_table = $wpdb->prefix . 'woonoow_affiliates';
|
||||
|
||||
// Find pending referral
|
||||
$referral = $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT * FROM $referrals_table WHERE id = %d AND status = 'pending'",
|
||||
$referral_id
|
||||
));
|
||||
|
||||
if (!$referral) return; // Already processed or deleted
|
||||
|
||||
// Double check order status
|
||||
$order = wc_get_order($referral->order_id);
|
||||
if (!$order || in_array($order->get_status(), ['refunded', 'cancelled', 'failed'])) {
|
||||
self::handle_order_cancelled($referral->order_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Approve referral
|
||||
$wpdb->update(
|
||||
$referrals_table,
|
||||
[
|
||||
'status' => 'approved',
|
||||
'approved_at' => current_time('mysql')
|
||||
],
|
||||
['id' => $referral_id]
|
||||
);
|
||||
|
||||
// Update Affiliate totals
|
||||
$wpdb->query($wpdb->prepare(
|
||||
"UPDATE $affiliates_table SET
|
||||
total_referrals = total_referrals + 1,
|
||||
total_earnings = total_earnings + %f
|
||||
WHERE id = %d",
|
||||
$referral->commission_amount,
|
||||
$referral->affiliate_id
|
||||
));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user