WooCommerce ส่งฟรีหายตอนใช้คูปอง — บั๊กลำดับการคำนวณที่เจอบ่อย
มีร้าน WooCommerce ร้านหนึ่งที่ผมดูแล ตั้งเกณฑ์ “ส่งฟรีเมื่อยอดถึง 900 บาท” และรับคูปองส่วนลดด้วย อยู่มาวันหนึ่งลูกค้าทักมาว่า ยอดสินค้าเกิน 900 อยู่แล้ว แต่พอใส่คูปองกลับโดนคิดค่าส่ง ทั้งที่ควรได้ส่งฟรี
ฟังดูเหมือน bug ของธีมหรือปลั๊กอินคูปอง แต่จริง ๆ มันคือพฤติกรรม default ของ WooCommerce core เอง บทความนี้เล่าว่ามันเกิดอะไรขึ้น ทำไม และแก้ยังไงให้จบ
อาการ
สมมติเกณฑ์ส่งฟรี = 900 บาท ลูกค้าใช้คูปองลด 150 บาท:
| ขั้นตอน | ค่า |
|---|---|
| ยอดสินค้า | 1,020 |
| หักคูปอง | −150 |
| ยอดหลังคูปอง | 870 → ต่ำกว่า 900 |
| ระบบบวกค่าส่ง | +100 |
| สุทธิที่ลูกค้าจ่าย (ผิด) | 970 |
ลูกค้ามองว่า “ฉันซื้อตั้ง 1,020 ทำไมไม่ได้ส่งฟรี” — ซึ่งถูกของเขา เพราะยอดสินค้าจริงเกินเกณฑ์ไปแล้ว ส่วนลดคูปองไม่ควรมาทำให้เสียสิทธิ์ส่งฟรี
ต้นเหตุ
WooCommerce เทียบเกณฑ์ส่งฟรีกับ “ยอดหลังหักส่วนลด” ไม่ใช่ “ยอดสินค้าก่อนคูปอง”
ถ้าไปดูใน core ที่คลาส WC_Shipping_Free_Shipping::is_available() มันคำนวณยอดมาเทียบเกณฑ์ประมาณนี้:
$total = WC()->cart->get_displayed_subtotal();
if ( 'no' === $this->ignore_discounts ) {
// หักส่วนลดออกจากยอดก่อนเอาไปเทียบเกณฑ์
$total = $total - WC()->cart->get_discount_total();
}
$has_met_min_amount = $total >= $this->min_amount;
ประเด็นอยู่ที่ $this->ignore_discounts — ค่า default คือ 'no' แปลว่า WooCommerce จะ หักคูปองออกก่อน แล้วค่อยเอายอดที่เหลือไปเทียบกับเกณฑ์ส่งฟรี นี่คือต้นเหตุทั้งหมด ไม่ใช่บั๊กของธีมหรือปลั๊กอินอื่นเลย
วิธีแก้
ในการตั้งค่า Free Shipping method ของ WooCommerce มี checkbox อยู่ตัวหนึ่ง:
“Apply minimum order rule before coupon discount”
ติ๊กอันนี้ (ค่านี้แมปกับ ignore_discounts = yes ภายใน) WooCommerce จะเอา ยอดสินค้าก่อนหักคูปอง ไปเทียบเกณฑ์ส่งฟรีแทน
ตั้งที่: WooCommerce → Settings → Shipping → เลือก Zone → แก้ Free Shipping method
ผลหลังแก้ จากเคสเดิม:
1,020 ≥ 900 → เข้าเกณฑ์ส่งฟรี
สุทธิ = 1,020 − 150 = 870 บาท (ไม่มีค่าส่ง) ✅
ถ้าธีมเขียน logic ค่าส่งเอง
บางธีม/ปลั๊กอินไม่ได้ใช้ free shipping มาตรฐานของ Woo แต่เขียนเงื่อนไขเอง กรณีนั้น setting ด้านบนจะไม่มีผล ต้องไปแก้ที่จุดเทียบยอด หรือ hook ตัวนี้:
add_filter( 'woocommerce_shipping_free_shipping_is_available', function ( $is_available, $package, $method ) {
// ใช้ยอดก่อนหักคูปองมาเทียบเกณฑ์เอง
$subtotal = WC()->cart->get_displayed_subtotal();
return $subtotal >= 900;
}, 10, 3 );
⚠️ ถ้าแก้ในไฟล์ธีมโดยตรง พอ Woo หรือธีมอัปเดตทับ fix จะหาย — ใส่ใน child theme หรือปลั๊กอินเล็ก ๆ ของตัวเองเสมอ
เช็กลิสต์ก่อนปิดงาน
บั๊กนี้เจอได้กับ ทุกร้าน WooCommerce ที่มีส่งฟรี + คูปองพร้อมกัน ก่อนส่งงานลองเทสเคสขอบพวกนี้:
- เช็กค่า
ignore_discounts/ checkbox “before coupon discount” เป็นอย่างแรก - ยอดพอดีเกณฑ์เป๊ะ ๆ (เช่น 900 พอดี)
- คูปองแบบ % เทียบกับคูปองจำนวนเงินคงที่
- คูปองชนิด “ส่งฟรี” โดยเฉพาะ
- ยอดตอนสร้างออเดอร์ฝั่ง backend ใช้ลำดับเดียวกับหน้า checkout
จุดที่คนมองข้ามคือข้อสุดท้าย — หน้า cart/checkout แสดงถูก แต่ตอนระบบสร้างออเดอร์จริงคำนวณคนละแบบ ทำให้ยอดในระบบหลังบ้านเพี้ยน ต้องยืนยันว่าทั้งสองฝั่งใช้ตรรกะเดียวกัน
บทเรียนนี้กลั่นจากงานจริง — ถ้าเจอเคสคล้าย ๆ กันหรืออยากเสริม ทักมาคุยกันได้ที่ ช่องทางติดต่อ