Skip to content

Instantly share code, notes, and snippets.

@cftang0827
Created January 8, 2026 11:03
Show Gist options
  • Select an option

  • Save cftang0827/fd690a1ea1def7679cbfdf29ecefbdbb to your computer and use it in GitHub Desktop.

Select an option

Save cftang0827/fd690a1ea1def7679cbfdf29ecefbdbb to your computer and use it in GitHub Desktop.
woocommerce-cvs-checkout-page.php
<?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