如何使用 `Ajax` 更新 `Wordpress` 的 `Checkout Page`
结论
不要直接清空 .payment_method_cod,也不用调用 location.reload()。WooCommerce 自带结账页更新机制:邮编变化后,前端触发 update_checkout,后端再通过 woocommerce_available_payment_gateways 判断 COD 是否可用。随后,WooCommerce 会重新计算订单并刷新付款方式区域。这样一来,COD 会在不符合条件时隐藏,并在条件恢复后重新显示。
触发更新只需执行:
jQuery(document.body).trigger('update_checkout');
为什么不能直接清空 HTML
下面这段代码只会删除当前页面里的 COD 内容:
jQuery('.payment_method_cod').html('');
它不会改变 WooCommerce 后端对支付方式可用性的判断。删除后的表单字段和支付说明也无法自行恢复,即使之后重新允许使用 COD,前端仍然拿不到原来的内容。
COD 是否可用应该由服务端判断。前端只需通知 WooCommerce,结账信息发生了变化,需要重新计算并渲染页面。
前端触发结账页刷新
如果规则基于配送邮编,应优先监听 #shipping_postcode。用户没有勾选“配送到不同地址”时,再使用账单邮编。
可以将下面的脚本加入结账页加载的 JavaScript 文件:
jQuery(function ($) {
let postcodeTimer;
$(document.body).on(
'change blur',
'#billing_postcode, #shipping_postcode',
function () {
clearTimeout(postcodeTimer);
postcodeTimer = setTimeout(function () {
$(document.body).trigger('update_checkout');
}, 300);
}
);
});
WooCommerce 通常会在地址字段变化时自动更新结账信息。如果当前主题或插件已经正常触发更新,就不必重复添加这段监听代码。
这里设置了一个短暂延迟,避免 change 和 blur 接连触发时发送重复请求。
在服务端控制 COD 是否可用
下面的示例使用 woocommerce_available_payment_gateways 过滤可用支付方式,并假设 COD 使用 WooCommerce 默认的网关 ID cod。
add_filter(
'woocommerce_available_payment_gateways',
'custom_control_cod_by_postcode'
);
function custom_control_cod_by_postcode($gateways)
{
if (is_admin() && !wp_doing_ajax()) {
return $gateways;
}
if (!isset($gateways['cod']) || !WC()->customer) {
return $gateways;
}
$postcode = WC()->customer->get_shipping_postcode();
if ($postcode === '') {
$postcode = WC()->customer->get_billing_postcode();
}
$postcode = strtoupper(
preg_replace('/\s+/', '', wc_clean($postcode))
);
// 请替换为实际允许使用 COD 的邮编。
$cod_postcodes = array(
'100000',
'200000',
'518000',
);
if (!in_array($postcode, $cod_postcodes, true)) {
unset($gateways['cod']);
}
return $gateways;
}
整个过程是:
- 用户修改邮编。
- 前端触发
update_checkout。 - WooCommerce 将结账字段提交到服务端,并更新客户地址。
woocommerce_available_payment_gateways根据最新邮编判断 COD 是否可用。- 如果当前邮编不支持 COD,过滤器移除
$gateways['cod']。 - 邮编重新符合条件后,过滤器保留
$gateways['cod']。 - WooCommerce 返回并替换付款区域的 HTML,COD 随之重新出现。
所以,重新启用 COD 时不必手动拼接 HTML。邮编符合条件时,让过滤器保留 $gateways['cod'] 即可。
根据规则判断邮编
如果允许范围不是一组固定邮编,可以把判断逻辑封装成函数:
function custom_postcode_supports_cod($postcode)
{
$postcode = strtoupper(
preg_replace('/\s+/', '', wc_clean($postcode))
);
if ($postcode === '') {
return false;
}
// 示例规则,请替换为实际业务逻辑。
return str_starts_with($postcode, '51');
}
然后修改过滤器中的判断:
if (!custom_postcode_supports_cod($postcode)) {
unset($gateways['cod']);
}
如果当前 PHP 环境不支持 str_starts_with(),可以改用:
return strpos($postcode, '51') === 0;
如果必须调用自定义 Ajax
如果需要通过外部服务查询某个邮编是否支持 COD,可以保留自定义 Ajax。查询成功后仍应触发 WooCommerce 的结账更新,不要直接修改支付方式的 HTML:
jQuery(function ($) {
$(document.body).on(
'blur',
'#billing_postcode, #shipping_postcode',
function () {
const postcode = $(this).val();
$.ajax({
url: MyAjax.ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'pincheck_ajax_submit',
pin: postcode
}
}).done(function (response) {
if (response.success) {
$(document.body).trigger('update_checkout');
}
});
}
);
});
对应的 Ajax 回调需要校验 nonce、清理输入,并把查询结果保存到 WooCommerce Session,供支付网关过滤器读取:
add_action('wp_ajax_pincheck_ajax_submit', 'pincheck_ajax_submit');
add_action('wp_ajax_nopriv_pincheck_ajax_submit', 'pincheck_ajax_submit');
function pincheck_ajax_submit()
{
check_ajax_referer('pincheck_nonce', 'nonce');
$postcode = isset($_POST['pin'])
? wc_clean(wp_unslash($_POST['pin']))
: '';
// 用实际查询逻辑替换此处。
$cod_available = custom_postcode_supports_cod($postcode);
if (WC()->session) {
WC()->session->set(
'custom_cod_available',
$cod_available ? 'yes' : 'no'
);
}
wp_send_json_success(
array('cod_available' => $cod_available)
);
}
过滤支付方式时读取 Session:
add_filter(
'woocommerce_available_payment_gateways',
function ($gateways) {
if (
isset($gateways['cod']) &&
WC()->session &&
WC()->session->get('custom_cod_available') === 'no'
) {
unset($gateways['cod']);
}
return $gateways;
}
);
发送请求时,还要包含由 wp_create_nonce('pincheck_nonce') 生成的 nonce。如果只是检查本地邮编列表或执行简单规则,直接在支付网关过滤器中判断即可,这样可以减少额外的 Ajax 请求,也能避免 Session 状态不同步。
注意事项
.payment_method_cod只是渲染结果,不能用来判断 COD 当前是否可用。- 配送规则应优先读取 shipping postcode,不要始终使用 billing postcode。
- WooCommerce 默认的 COD 支付方式 ID 是
cod。如果使用第三方 COD 插件,需要确认实际的网关 ID。 - 不要只在浏览器端控制支付方式。前端脚本可以被绕过,服务端必须再次检查。
- 判断前要清理邮编并统一格式,特别是可能包含空格或字母的邮编。
- 如果当前选中的支付方式因邮编变化而失效,WooCommerce 会在刷新后重新处理可用方式,不需要手动修改单选按钮。
- 不要让自定义 Ajax 和 WooCommerce 更新请求重复执行同一套判断,否则可能出现竞态或重复请求。
- 修改代码后,如果付款区域仍未刷新,需要检查浏览器控制台错误、主题对 WooCommerce 模板的覆盖,以及缓存或结账优化插件。
备注:内容仅供参考。
