PAYATHON 2026

如何使用 `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;
}

整个过程是:

  1. 用户修改邮编。
  2. 前端触发 update_checkout。
  3. WooCommerce 将结账字段提交到服务端,并更新客户地址。
  4. woocommerce_available_payment_gateways 根据最新邮编判断 COD 是否可用。
  5. 如果当前邮编不支持 COD,过滤器移除 $gateways['cod']。
  6. 邮编重新符合条件后,过滤器保留 $gateways['cod']。
  7. 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 模板的覆盖,以及缓存或结账优化插件。

备注:内容仅供参考。