~/blog/woocommerce-reactivate-dormant-customers-automation-code-guide.md
電商與 WooCommerce · 2026 / 02 / 01

營收卡關?別只顧著投廣告!教你用 WooCommerce 自動化代碼喚醒「沉睡 90 天」的殭屍客群

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
營收卡關?別只顧著投廣告!教你用 WooCommerce 自動化代碼喚醒「沉睡 90 天」的殭屍客群
目錄 table-of-contents.md

資料庫裡躺著幾千筆會員資料,每個月真正下單的卻只有那幾百人——營收卡關的真凶,往往是這批被遺忘的舊名單,而不是廣告投得不夠。與其繼續把預算倒進新流量,不如寫一段 WooCommerce 自動化代碼,偵測「沉睡 90 天」的殭屍客群並自動寄出帶優惠券的召回信。這篇連為什麼選 90 天的 RFM 邏輯都會講清楚。

每次開會聽到行銷部門喊著「廣告費越來越貴」、「ROAS(廣告投資報酬率)一直掉」,我心裡都會默默翻個白眼(工程師的內心戲)。大家總是拚命想把新流量倒進來,卻看著手上的舊名單像一灘死水。你知道嗎?開發一個新客戶的成本,是挽回舊客戶的 5 到 25 倍。在 2025 年這個流量紅利消失的年代,誰能喚醒「沈睡客群」,誰才是贏家。

今天我不談虛無縹緲的行銷理論,我們要來點硬核的。我會教你如何利用 WordPress 和 WooCommerce 的底層機制,寫一段自動化程式碼,精準偵測「90 天未互動」的殭屍客戶,並自動發送帶有優惠券的召回郵件。把這套機制埋進你的 `functions.php` 或自製外掛裡,你的網站就會變成一台 24 小時不休息的自動印鈔機。

為什麼是 90 天?RFM 模型中的黃金關鍵點

在寫 Code 之前,我們得先懂一點邏輯。為什麼我不設 30 天或 180 天?

  • 30 天: 客戶可能只是剛買完東西,還在體驗期,這時候發信騷擾只會增加退訂率。
  • 180 天: 客戶早就忘記你是誰了,這時候喚醒的難度極高,甚至可能被標記為垃圾信件。
  • 90 天(一季): 這是一個非常微妙的心理門檻。客戶對你的品牌還有印象,但購買習慣開始淡化。這時候用「專屬優惠」推一把,挽回的成功率最高。

這在數據分析領域被稱為 RFM 模型(Recency, Frequency, Monetary) 中的 Recency(最近一次消費)。我們的程式碼邏輯,就是要抓出 Recency > 90 days 的這群人。

技術實戰:如何找出沉睡客戶?

很多新手工程師會犯一個錯誤:直接用 PHP 迴圈跑所有的 `wc_get_orders()`。拜託,千萬別這樣做!如果你的網站有幾萬筆訂單,這種寫法會直接把你的伺服器記憶體吃光,導致網站掛掉 (504 Gateway Timeout)。

身為資深工程師,我們要用 SQL 查詢或是高效的 `WP_User_Query` 配合 `meta_query` 來精準打擊。

核心邏輯流程圖

  1. 排程觸發: 設定每日一次的 WP-Cron 排程(建議在半夜流量低峰期)。
  2. 撈取名單: 找出最後一次登入或購買時間在 90 天前的會員。
  3. 排除清單: 排除掉最近已經發送過喚醒信的人(避免重複騷擾)。
  4. 生成優惠: 動態建立一組專屬的 Coupon Code。
  5. 發送郵件: 寄出客製化 HTML 郵件。
  6. 寫入紀錄: 在 User Meta 標記「已發送」,並記錄日期。

Show Me The Code:支援經典編輯器的完整範例

這段程式碼可以直接放在你的子佈景主題的 `functions.php` 中,或者封裝成一個簡單的外掛。為了確保效能,我加了 `LIMIT` 限制,每次排程只處理 50 人,避免超時。


<?php
/**
 * 註冊排程事件
 */
add_action( 'wp', 'roamer_schedule_dormant_customer_recovery' );
function roamer_schedule_dormant_customer_recovery() {
    if ( ! wp_next_scheduled( 'roamer_daily_dormant_check' ) ) {
        wp_schedule_event( time(), 'daily', 'roamer_daily_dormant_check' );
    }
}

/**
 * 核心邏輯:喚醒沉睡客戶
 */
add_action( 'roamer_daily_dormant_check', 'roamer_process_dormant_customers' );
function roamer_process_dormant_customers() {
    global $wpdb;

    // 設定沉睡天數閾值
    $days_inactive = 90;
    $cutoff_date = date( 'Y-m-d H:i:s', strtotime( "-{$days_inactive} days" ) );

    // 1. 找出潛在目標:最後一次活躍時間早於 90 天前,且沒有被標記過「已喚醒」的客戶
    // 注意:這裡簡化邏輯,假設我們用 WooCommerce 的最後訂單時間作為判斷依據
    // 為了效能,每次只抓 20 人
    
    $args = array(
        'role'    => 'customer',
        'number'  => 20, 
        'meta_query' => array(
            'relation' => 'AND',
            array(
                'key'     => '_last_order_date', // 需確保你的系統有記錄這個欄位,若無則需透過 hook 在訂單完成時寫入
                'value'   => $cutoff_date,
                'compare' => ' 'DATETIME'
            ),
            array(
                'key'     => '_roamer_dormant_email_sent',
                'compare' => 'NOT EXISTS' // 排除已經發過信的人
            )
        )
    );

    $user_query = new WP_User_Query( $args );
    $customers = $user_query->get_results();

    if ( empty( $customers ) ) {
        return;
    }

    $mailer = WC()->mailer();

    foreach ( $customers as $customer ) {
        // 2. 雙重檢查:確認該客戶真的在 90 天內沒有任何新訂單 (避免 User Meta 更新延遲)
        if ( roamer_check_recent_orders( $customer->ID, $days_inactive ) ) {
            continue; // 如果最近有單,跳過並更新 Meta
        }

        // 3. 生成專屬優惠券 (譬如 9 折)
        $coupon_code = 'COMEBACK-' . strtoupper( wp_generate_password( 6, false ) );
        $amount = '10'; // 10% off
        $discount_type = 'percent';

        $coupon = array(
            'post_title' => $coupon_code,
            'post_content' => '自動喚醒優惠券',
            'post_status' => 'publish',
            'post_author' => 1,
            'post_type' => 'shop_coupon'
        );

        $new_coupon_id = wp_insert_post( $coupon );
        update_post_meta( $new_coupon_id, 'discount_type', $discount_type );
        update_post_meta( $new_coupon_id, 'coupon_amount', $amount );
        update_post_meta( $new_coupon_id, 'individual_use', 'yes' );
        update_post_meta( $new_coupon_id, 'usage_limit', '1' );
        update_post_meta( $new_coupon_id, 'expiry_date', date( 'Y-m-d', strtotime( '+7 days' ) ) ); // 7天後過期
        update_post_meta( $new_coupon_id, 'customer_email', array( $customer->user_email ) );

        // 4. 發送 Email
        $subject = '嘿 ' . $customer->display_name . ',我們好久不見了!(內含專屬優惠)';
        $message = "<p>親愛的 {$customer->display_name},</p>";
        $message .= "<p>我們發現您已經有一段時間沒回來看看了。為了表達想念,這是您的專屬優惠碼:<strong>{$coupon_code}</strong></p>";
        $message .= "<p>結帳時輸入可享 9 折優惠,限時 7 天有效!</p>";
        
        // 使用 WooCommerce 的郵件樣式發送
        $email_heading = '我們想念你';
        $content = $mailer->wrap_message( $email_heading, $message );
        $mailer->send( $customer->user_email, $subject, $content );

        // 5. 標記已發送,避免重複寄信
        update_user_meta( $customer->ID, '_roamer_dormant_email_sent', time() );
    }
}

/**
 * 輔助函式:檢查最近是否有訂單
 */
function roamer_check_recent_orders( $user_id, $days ) {
    $orders = wc_get_orders( array(
        'customer_id' => $user_id,
        'limit' => 1,
        'date_after' => date( 'Y-m-d', strtotime( "-{$days} days" ) ),
        'return' => 'ids',
    ) );
    
    if ( ! empty( $orders ) ) {
        // 如果發現其實有訂單,更新一下 Meta 資料,避免下次誤判
        update_user_meta( $user_id, '_roamer_dormant_email_sent', 'active' );
        return true;
    }
    return false;
}

// 確保每次訂單完成時,更新使用者的 _last_order_date
add_action( 'woocommerce_order_status_completed', 'roamer_update_last_order_date' );
function roamer_update_last_order_date( $order_id ) {
    $order = wc_get_order( $order_id );
    if ( $user_id = $order->get_user_id() ) {
        update_user_meta( $user_id, '_last_order_date', date( 'Y-m-d H:i:s' ) );
        // 清除喚醒標記,以便未來再次進入循環
        delete_user_meta( $user_id, '_roamer_dormant_email_sent' );
    }
}
?>

程式碼解析 (工程師的小囉嗦)

這段程式碼有幾個眉角要注意:

  • User Meta 的重要性: 我使用 `_last_order_date` 作為判斷依據。這意味著你需要一個機制(如代碼最後一段)在每次訂單完成時去更新這個欄位。如果不這樣做,單純靠 query 所有訂單來排序,資料庫會哭出來。
  • Batch Processing (批次處理): 注意到了嗎?我在 `WP_User_Query` 裡設了 `'number' => 20`。這是為了保護你的伺服器。每天跑一次排程,每次處理 20-50 人,細水長流,總比一次發 5000 封信然後被 Gmail 封鎖 IP 來得好。
  • 優惠券過期機制: 我設定了 `expiry_date`。這很重要,「稀缺性」是行銷轉換的關鍵。如果不設期限,客戶就會想「下次再買」,然後就沒有下次了。

進階優化:搭配 CRM 與 AI 讓自動化更有溫度

上面的程式碼是一個標準的「規則基礎 (Rule-based)」自動化。但在 2025 年,我們可以用更聰明的方法。

如果你的 WordPress 有串接 CRM(如 HubSpot 或 Salesforce),或是透過 n8n 串接了 OpenAI,你可以做更多事:

  • AI 生成主旨: 根據該客戶過去買過的產品(例如「貓飼料」),用 GPT-4 生成相關的信件主旨:「家裡的貓咪餓了嗎?90 天補貨優惠來了!」絕對比罐頭訊息有效。
  • 多渠道觸發: 不只發 Email。如果系統偵測到 Email 開信率低,可以改用 LINE 官方帳號發送 Flex Message 通知(前提是有串接)。

結論:資料庫是資產,別讓它變遺產

很多老闆以為網站架好、廣告投了就有業績,殊不知真正的金礦就在後台的「使用者列表」裡。這段程式碼雖然不長,但它實現了電商最核心的價值——客戶終身價值 (CLV) 的最大化

寫程式不只是為了修 bug,而是為了解決商業問題。把你手上的殭屍名單喚醒,你會發現,這比再去投 Facebook 廣告便宜太多了。

如果你覺得這段程式碼對你來說有點太硬核,或者你的會員數量已經大到需要更複雜的架構(例如搭配 Redis Queue 或專屬的行銷自動化伺服器),不要硬幹,這時候你需要專業的技術團隊來幫你規劃。

不想讓寶貴的客戶名單繼續沉睡?

無論是 WooCommerce 自動化開發、CRM 深度串接,還是企業級的資料庫優化,浪花科技都能為你量身打造最適合的技術解方。別讓你的資料庫變成數位垃圾場。

立即填寫表單聯繫 Eric

延伸閱讀

// FAQ

常見問題

喚醒沉睡客戶為什麼設定 90 天這個門檻?
90 天(約一季)是個微妙的心理門檻:客戶對品牌還有印象、但購買習慣開始淡化,用專屬優惠推一把的成功率最高。相較之下 30 天客戶可能還在剛購買的體驗期,發信只會增加退訂;180 天客戶幾乎已遺忘品牌,喚醒難度極高甚至可能被標記為垃圾信。
找出沉睡客戶為什麼不該用 PHP 迴圈跑所有訂單?
若網站有數萬筆訂單,直接用 PHP 迴圈跑 wc_get_orders() 會吃光伺服器記憶體,導致網站掛掉(504 Gateway Timeout)。較好的做法是用 SQL 查詢或搭配 meta_query 的 WP_User_Query 精準鎖定目標,並用 LIMIT 限制每次排程處理的人數(例如每次 20 至 50 人)以避免逾時。
自動喚醒機制如何避免重複騷擾同一位客戶?
在發信流程中加入排除清單:用 meta_query 過濾掉已標記「已喚醒」的客戶(例如以 NOT EXISTS 比對自訂的 _roamer_dormant_email_sent 欄位),並在成功寄信後寫入 User Meta 標記已發送與日期。發信前再做一次雙重檢查,確認該客戶 90 天內確實沒有新訂單。
如何在喚醒信中發送專屬限時優惠券?
可在程式中用 wp_insert_post 動態建立 shop_coupon,並透過 update_post_meta 設定折扣類型與金額、individual_use、usage_limit(限用一次)、expiry_date(例如 7 天後過期)以及綁定 customer_email 限定該客戶使用。再以 WooCommerce 內建的 mailer 套用郵件樣式寄出。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?