PAYATHON 2026

使用 jQuery 验证 billing address form 后再进入支付页

支付老李

结论

不要给同一个按钮分别绑定“验证”和“支付”两个 click 事件。验证函数中的 return false 只能结束当前函数,无法可靠阻止另一个独立监听器继续运行,所以 AJAX 请求仍可能发出。

把验证和支付放在同一个事件处理函数中即可解决这个问题。先检查所有必填字段,验证失败就立即 return,只有 isValid === true 时才执行 $.ajax()。

建议统一处理表单提交

让按钮提交表单,再监听表单的 submit 事件。这样无论用户点击按钮,还是按 Enter 提交,都能走同一套处理逻辑。

假设表单结构如下:

<form id="billing-form">
    <input
        id="form7Example1"
        class="form-control"
        type="text"
        name="firstName"
        required
    >

    <input
        id="form7Example2"
        class="form-control"
        type="text"
        name="lastName"
        required
    >

    <input
        id="form7Example4"
        class="form-control"
        type="text"
        name="address"
        required
    >

    <input
        id="form7Example5"
        class="form-control"
        type="email"
        name="email"
        required
    >

    <input
        id="form7Example6"
        class="form-control"
        type="tel"
        name="phone"
        required
    >

    <button id="proceed-to-payment" type="submit">
        Proceed To Payment
    </button>
</form>

对应的 jQuery 代码可以这样写:

$(document).ready(function () {
    const requiredFields = [
        '#form7Example1',
        '#form7Example2',
        '#form7Example4',
        '#form7Example5',
        '#form7Example6'
    ];

    function validateField(field) {
        const $field = $(field);
        const fieldValue = String($field.val() ?? '').trim();
        const isValid = fieldValue !== '';

        $field.css('border-color', isValid ? 'green' : 'red');

        return isValid;
    }

    // 用户输入时更新当前字段的边框
    $('.form-control').on('input', function () {
        validateField(this);
    });

    $('#billing-form').on('submit', function (event) {
        // 阻止浏览器执行默认表单提交
        event.preventDefault();

        let isFormValid = true;

        requiredFields.forEach(function (fieldSelector) {
            if (!validateField(fieldSelector)) {
                isFormValid = false;
            }
        });

        // 验证失败:到此结束,不会执行后面的 AJAX
        if (!isFormValid) {
            alert('Please fill in all required fields.');

            // 可选:让第一个未填写的字段获得焦点
            $(requiredFields.join(','))
                .filter(function () {
                    return String($(this).val() ?? '').trim() === '';
                })
                .first()
                .trigger('focus');

            return;
        }

        // 只有全部字段通过验证后,才准备支付数据
        var cartItems = /* PHP 生成的购物车数据 */;
        var subtotal = /* PHP 生成的小计 */;
        var shippingCost = /* PHP 生成的运费 */;
        var totalPrice = Number(subtotal) + Number(shippingCost);

        var dataToSend = {
            cartItems: cartItems,
            totalPrice: totalPrice
        };

        // 防止用户连续点击造成重复请求
        const $button = $('#proceed-to-payment');
        $button.prop('disabled', true);

        $.ajax({
            type: 'POST',
            url: 'payment_integration.php',
            data: JSON.stringify(dataToSend),
            contentType: 'application/json',
            dataType: 'json',
            success: function (response) {
                /*
                 * 推荐由服务端保存订单数据,并返回支付页面地址。
                 * response.redirectUrl 的具体字段名需要与 PHP 返回值一致。
                 */
                if (response.redirectUrl) {
                    window.location.href = response.redirectUrl;
                    return;
                }

                console.error('The server did not return a redirect URL.');
                $button.prop('disabled', false);
            },
            error: function (xhr, status, error) {
                console.error('Payment request failed:', error);
                $button.prop('disabled', false);
            }
        });
    });
});

重点是这段逻辑:

if (!isFormValid) {
    alert('Please fill in all required fields.');
    return;
}

$.ajax({
    // ...
});

验证和 AJAX 在同一个函数里,因此验证失败后的 return 会结束整个支付流程,后面的请求不会执行。

暂时无法改用表单提交时

可以继续监听按钮的 click,但只能保留一个监听器:

$(document).ready(function () {
    const requiredFields = [
        '#form7Example1',
        '#form7Example2',
        '#form7Example4',
        '#form7Example5',
        '#form7Example6'
    ];

    function validateField(field) {
        const $field = $(field);
        const value = String($field.val() ?? '').trim();
        const isValid = value !== '';

        $field.css('border-color', isValid ? 'green' : 'red');

        return isValid;
    }

    $('.form-control').on('input', function () {
        validateField(this);
    });

    $('#proceed-to-payment').on('click', function (event) {
        event.preventDefault();

        let isValid = true;

        requiredFields.forEach(function (fieldSelector) {
            if (!validateField(fieldSelector)) {
                isValid = false;
            }
        });

        if (!isValid) {
            alert('Please fill in all required fields.');
            return;
        }

        var cartItems = /* PHP 生成的购物车数据 */;
        var subtotal = /* PHP 生成的小计 */;
        var shippingCost = /* PHP 生成的运费 */;

        var dataToSend = {
            cartItems: cartItems,
            totalPrice: Number(subtotal) + Number(shippingCost)
        };

        $.ajax({
            type: 'POST',
            url: 'payment_integration.php',
            data: JSON.stringify(dataToSend),
            contentType: 'application/json',
            success: function () {
                var redirectUrl =
                    'payment_integration.php?cartData=' +
                    encodeURIComponent(JSON.stringify(dataToSend));

                window.location.href = redirectUrl;
            },
            error: function (xhr, status, error) {
                console.error(error);
            }
        });
    });
});

同时删除原来的原生 JavaScript 监听器:

document
    .getElementById('proceed-to-payment')
    .addEventListener('click', function () {
        // ...
    });

否则同一个按钮上仍有两套互不依赖的处理流程。

用 required 属性选择必填字段

如果 HTML 已经用 required 标记必填字段,就不需要在 JavaScript 中另外维护 ID 数组:

const $requiredFields = $('#billing-form').find(':input[required]');
let isValid = true;

$requiredFields.each(function () {
    if (!validateField(this)) {
        isValid = false;
    }
});

以后新增必填字段时,只需加上 required 属性,不容易漏掉对 requiredFields 的更新。

如果还需要检查邮箱、电话号码或其他 HTML 约束,可以配合原生 checkValidity():

function validateField(field) {
    const $field = $(field);
    const element = $field[0];
    const value = String($field.val() ?? '').trim();

    const isValid =
        value !== '' &&
        (!element || typeof element.checkValidity !== 'function' || element.checkValidity());

    $field.css('border-color', isValid ? 'green' : 'red');

    return isValid;
}

注意事项

前端验证只能改善用户体验,不能代替 PHP 服务端验证。用户可以绕过 JavaScript,直接向 payment_integration.php 发送请求,所以服务端仍要检查 billing address、购物车金额和其他支付参数。

当前流程还有一处问题:先通过 POST 发送 JSON,成功后又把同一份数据放进 URL 查询参数,通过 GET 跳转。这会带来两个风险:

  • URL 有长度限制,购物车数据较大时可能被截断。
  • billing、购物车或支付相关数据会出现在地址栏、浏览器历史和日志中。

更合适的流程是让 payment_integration.php 接收 POST 数据,在服务端完成校验并将数据保存到 session 或数据库,然后返回一个简短的跳转地址:

{
    "redirectUrl": "payment_page.php?orderId=12345"
}

前端只负责使用这个地址跳转:

success: function (response) {
    window.location.href = response.redirectUrl;
}

这样,验证、请求和跳转都走同一条执行路径,也能降低重复提交和参数泄露的风险。

备注:内容仅供参考。