Created
January 8, 2026 11:03
-
-
Save cftang0827/fd690a1ea1def7679cbfdf29ecefbdbb to your computer and use it in GitHub Desktop.
woocommerce-cvs-checkout-page.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Snippet Name: Paul WC CVS Checkout Tweaks (ECPay RY) - Setters Only | |
| * Description: For RY ECPay CVS shipping (7-11/FamilyMart). Hide billing address on CVS, enforce phone, and sync phone via WC setters (no meta). | |
| */ | |
| if (!defined('ABSPATH')) return; | |
| add_action('init', function () { | |
| if (!class_exists('WooCommerce')) return; | |
| // ✅ 依你貼的 shipping method value: | |
| // ry_ecpay_shipping_cvs_711:2 / ry_ecpay_shipping_cvs_family:3 | |
| if (!defined('PAUL_WC_CVS_METHOD_IDS')) { | |
| define('PAUL_WC_CVS_METHOD_IDS', [ | |
| 'ry_ecpay_shipping_cvs_711', | |
| 'ry_ecpay_shipping_cvs_family', | |
| ]); | |
| } | |
| if (!function_exists('paul_wc_get_chosen_shipping_method_ids')) { | |
| function paul_wc_get_chosen_shipping_method_ids(): array { | |
| if (!function_exists('WC') || !WC() || !WC()->session) return []; | |
| $chosen = WC()->session->get('chosen_shipping_methods'); | |
| if (!is_array($chosen)) return []; | |
| $method_ids = []; | |
| foreach ($chosen as $rate_id) { | |
| $parts = explode(':', (string)$rate_id, 2); | |
| $method_ids[] = $parts[0] ?? (string)$rate_id; | |
| } | |
| return array_values(array_unique(array_filter($method_ids))); | |
| } | |
| } | |
| if (!function_exists('paul_wc_is_cvs_pickup')) { | |
| function paul_wc_is_cvs_pickup(): bool { | |
| $cvs = defined('PAUL_WC_CVS_METHOD_IDS') ? PAUL_WC_CVS_METHOD_IDS : []; | |
| if (!is_array($cvs) || !$cvs) return false; | |
| $chosen = paul_wc_get_chosen_shipping_method_ids(); | |
| foreach ($chosen as $id) { | |
| if (in_array($id, $cvs, true)) return true; | |
| } | |
| return false; | |
| } | |
| } | |
| if (!function_exists('paul_wc_digits_phone')) { | |
| function paul_wc_digits_phone($v): string { | |
| return preg_replace('/\D+/', '', (string)$v); | |
| } | |
| } | |
| // ① checkout 欄位:CVS 才移除地址;並確保手機欄位存在且必填 | |
| add_filter('woocommerce_checkout_fields', function ($fields) { | |
| // 確保 billing_phone 存在 | |
| if (!isset($fields['billing']['billing_phone'])) { | |
| $fields['billing']['billing_phone'] = [ | |
| 'type' => 'tel', | |
| 'label' => '手機', | |
| 'required' => true, | |
| 'class' => ['form-row-wide'], | |
| 'priority' => 65, | |
| 'placeholder' => '請輸入手機號碼(09 開頭共 10 碼)', | |
| ]; | |
| } else { | |
| $fields['billing']['billing_phone']['type'] = 'tel'; | |
| $fields['billing']['billing_phone']['required'] = true; | |
| $fields['billing']['billing_phone']['placeholder'] = '請輸入手機號碼(09 開頭共 10 碼)'; | |
| } | |
| // 姓名/email 基本必填 | |
| if (isset($fields['billing']['billing_first_name'])) $fields['billing']['billing_first_name']['required'] = true; | |
| if (isset($fields['billing']['billing_last_name'])) $fields['billing']['billing_last_name']['required'] = true; | |
| if (isset($fields['billing']['billing_email'])) $fields['billing']['billing_email']['required'] = true; | |
| // CVS 才移除地址欄位 | |
| if (paul_wc_is_cvs_pickup()) { | |
| $remove = [ | |
| 'billing_company', | |
| 'billing_country', | |
| 'billing_address_1', | |
| 'billing_address_2', | |
| 'billing_city', | |
| 'billing_state', | |
| 'billing_postcode', | |
| ]; | |
| foreach ($remove as $key) { | |
| if (isset($fields['billing'][$key])) unset($fields['billing'][$key]); | |
| } | |
| } | |
| return $fields; | |
| }, 20); | |
| // ② CVS:不需要 shipping address(避免一些流程硬驗) | |
| add_filter('woocommerce_cart_needs_shipping_address', function ($needs) { | |
| if (paul_wc_is_cvs_pickup()) return false; | |
| return $needs; | |
| }, 20); | |
| /** | |
| * ③ 選門市/更新結帳(下單前): | |
| * 把結帳填的手機寫進 WC()->customer(很多物流流程在下單前讀 customer/session) | |
| * ✅ 只用 set_*,不碰 meta,不動 fragments,不輸出 JS | |
| */ | |
| add_action('woocommerce_checkout_update_order_review', function ($posted_data) { | |
| if (!paul_wc_is_cvs_pickup()) return; | |
| if (!function_exists('WC') || !WC() || !WC()->customer) return; | |
| parse_str($posted_data, $data); | |
| $digits = paul_wc_digits_phone($data['billing_phone'] ?? ''); | |
| if (!preg_match('/^09\d{8}$/', $digits)) return; | |
| // ✅ WooCommerce 正規 API | |
| WC()->customer->set_billing_phone($digits); | |
| if (method_exists(WC()->customer, 'set_shipping_phone')) { | |
| WC()->customer->set_shipping_phone($digits); | |
| } | |
| WC()->customer->save(); | |
| }, 20, 1); | |
| /** | |
| * ④ 按下單時:驗證手機 + 正規化(避免後續流程炸) | |
| * 這裡只做 validation + 正規化,仍不寫 meta | |
| */ | |
| add_action('woocommerce_checkout_process', function () { | |
| if (!paul_wc_is_cvs_pickup()) return; | |
| $phone = isset($_POST['billing_phone']) ? wc_clean(wp_unslash($_POST['billing_phone'])) : ''; | |
| $digits = paul_wc_digits_phone($phone); | |
| if ($digits === '' || !preg_match('/^09\d{8}$/', $digits)) { | |
| wc_add_notice('請填寫收件人手機(09 開頭共 10 碼),否則無法取得物流代碼。', 'error'); | |
| return; | |
| } | |
| // 讓後續 order/customer setters 都吃到乾淨格式 | |
| $_POST['billing_phone'] = $digits; | |
| }); | |
| /** | |
| * ⑤ 建單時:用 set_* 寫入 order(不寫 meta) | |
| * - billing_phone | |
| * - (若存在) shipping_phone | |
| * - 同步 shipping 姓名(避免外掛只讀 shipping name) | |
| */ | |
| add_action('woocommerce_checkout_create_order', function ($order) { | |
| if (!$order || !is_a($order, 'WC_Order')) return; | |
| // 先把 billing_phone 正規化 | |
| $digits = paul_wc_digits_phone($order->get_billing_phone()); | |
| if (!$digits && isset($_POST['billing_phone'])) { | |
| $digits = paul_wc_digits_phone(wc_clean(wp_unslash($_POST['billing_phone']))); | |
| } | |
| if ($digits && preg_match('/^09\d{8}$/', $digits)) { | |
| $order->set_billing_phone($digits); | |
| // WooCommerce 若支援 shipping_phone,就用 setter | |
| if (method_exists($order, 'set_shipping_phone')) { | |
| $order->set_shipping_phone($digits); | |
| } | |
| } | |
| // CVS 才同步 shipping name(你原本的行為保留) | |
| if (!paul_wc_is_cvs_pickup()) return; | |
| $order->set_shipping_first_name($order->get_billing_first_name()); | |
| $order->set_shipping_last_name($order->get_billing_last_name()); | |
| }, 30, 1); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment