PAYATHON 2026

如何在 PrestaShop 进入付款流程时执行操作?

支付阿杰

结论

可以执行,但需要先确定触发时机:

  • 如果想在用户选中付款方式后立即运行 PHP,需要用 JavaScript 监听付款选项的变化,再通过 AJAX 请求模块的前台控制器。选择单选框本身不会向服务器发送请求,所以不能直接触发 PHP。
  • 如果操作应该在订单创建后,或付款模块确认订单后执行,使用 actionValidateOrder Hook 更合适。

以下示例适用于 PrestaShop 1.7/8 的模块结构。部分主题会修改付款区域的 HTML,此时需要根据实际页面调整选择器。

选择付款方式后立即执行 PHP

1. 在模块中加载监听脚本

先在模块主类的 install() 方法中注册 displayHeader:

public function install()
{
    return parent::install()
        && $this->registerHook('displayHeader');
}

接着只在结账页面加载 JavaScript,并将 AJAX 地址和令牌传给前端:

public function hookDisplayHeader()
{
    if ($this->context->controller->php_self !== 'order') {
        return;
    }

    $this->context->controller->registerJavascript(
        'module-' . $this->name . '-payment-choice',
        'modules/' . $this->name . '/views/js/payment-choice.js',
        [
            'position' => 'bottom',
            'priority' => 150,
        ]
    );

    Media::addJsDef([
        'paymentChoiceUrl' => $this->context->link->getModuleLink(
            $this->name,
            'choice',
            [],
            true
        ),
        'paymentChoiceToken' => Tools::getToken(false),
    ]);
}

如果模块还要兼容不支持 registerJavascript() 的旧版本,可以改用该版本提供的脚本加载方法。

2. 监听付款选项变化

创建文件:

modules/yourmodule/views/js/payment-choice.js

示例代码:

document.addEventListener('change', function (event) {
    const target = event.target;

    if (!target.matches('input[name="payment-option"]')) {
        return;
    }

    const body = new URLSearchParams({
        ajax: '1',
        token: paymentChoiceToken,
        payment_option: target.value
    });

    fetch(paymentChoiceUrl, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        },
        credentials: 'same-origin',
        body: body.toString()
    })
        .then(function (response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }

            return response.json();
        })
        .then(function (result) {
            if (!result.success) {
                console.error(result.message);
            }
        })
        .catch(function (error) {
            console.error('Payment choice request failed:', error);
        });
});

这里采用事件委托,因为 PrestaShop 的结账页面可能通过 AJAX 重新渲染付款选项。如果直接给当前的单选框绑定事件,该区域刷新后,原来的事件监听可能失效。

3. 创建处理 PHP 请求的前台控制器

创建文件:

modules/yourmodule/controllers/front/choice.php

假设模块的技术名称是 yourmodule:

<?php

class YourModuleChoiceModuleFrontController extends ModuleFrontController
{
    public function postProcess()
    {
        header('Content-Type: application/json; charset=utf-8');

        if (!Tools::isSubmit('ajax')) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Invalid request.',
            ]));
        }

        $token = (string) Tools::getValue('token');

        if (!hash_equals(Tools::getToken(false), $token)) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Invalid token.',
            ]));
        }

        $paymentOption = (string) Tools::getValue('payment_option');

        if ($paymentOption === '' || strlen($paymentOption) > 255) {
            $this->ajaxDie(json_encode([
                'success' => false,
                'message' => 'Invalid payment option.',
            ]));
        }

        /*
         * 在这里执行需要的 PHP 操作。
         *
         * 不要仅凭 payment_option 的值执行敏感操作,因为该参数可以被用户伪造。
         * 如需处理购物车或客户信息,应从当前 Context 获取并重新验证。
         */

        PrestaShopLogger::addLog(
            sprintf('Payment option selected: %s', $paymentOption),
            1
        );

        $this->ajaxDie(json_encode([
            'success' => true,
        ]));
    }
}

类名前缀必须与模块的技术名称一致。例如,模块名为 custompaymentaction 时,类名应写成:

class CustomPaymentActionChoiceModuleFrontController extends ModuleFrontController

订单创建后再执行

如果 PHP 代码涉及库存、积分、发票、ERP 同步,或其他只能执行一次的业务逻辑,不适合在用户选择付款方式时运行。用户可能多次切换付款方式、刷新页面,也可能最终放弃下单。

这类操作可以注册 actionValidateOrder:

public function install()
{
    return parent::install()
        && $this->registerHook('actionValidateOrder');
}

然后在模块主类中处理:

public function hookActionValidateOrder(array $params)
{
    /** @var Order $order */
    $order = $params['order'];

    /** @var Cart $cart */
    $cart = $params['cart'];

    /** @var Customer $customer */
    $customer = $params['customer'];

    // 使用订单 ID 做幂等检查,避免重复处理。
    $orderId = (int) $order->id;

    // 在这里执行订单创建后的业务逻辑。
}

触发 actionValidateOrder 说明订单已经通过 validateOrder() 创建,但不代表资金已经成功到账。信用卡异步通知、银行转账等场景,还需要结合付款模块的回调和订单状态判断实际付款结果。

注意事项

  • 前端提交的付款方式、购物车 ID、金额和客户 ID 都不可信,服务器端必须重新读取并验证。
  • 选择付款方式的事件可能触发多次。相关 PHP 操作应当允许重复执行,或者通过幂等记录避免重复处理。
  • 不要在”选择付款方式”阶段扣减库存、生成正式订单或确认收款。
  • 自定义主题未必使用 input[name="payment-option"]。请检查页面的实际 HTML,再调整 JavaScript 选择器。
  • 有些付款模块会在选中后显示附加表单,有些则会跳转到外部支付页面。因此,“选择付款方式”、“创建订单”和”支付成功”属于三个不同的生命周期节点。
  • 修改模块文件后,如果代码没有生效,需要清理 PrestaShop 缓存,并确认模块已经重新安装,或已经补注册所需的 Hook。

备注:内容仅供参考。