You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PrestaShop自定义外部支付网关:无用户交互WebHook响应处理

解决PrestaShop外部支付网关的无用户交互订单确认问题

问题背景

基于PrestaShop外部支付示例框架开发支付模块时,原流程依赖用户支付后跳转回validation.php完成订单确认,存在以下问题:

  • 用户关闭页面、退出登录时,订单无法自动确认
  • 完全依赖用户端交互,可靠性不足

网关支持服务器端WebHook回调,但不清楚如何配置和实现无用户交互的订单处理逻辑。


解决方案步骤

1. 创建独立的WebHook处理控制器

在模块目录下新建webhook.php,专门接收网关的服务器端回调,无需依赖用户上下文直接处理订单确认:

<?php
require_once _PS_MODULE_DIR_ . 'your_module_name/your_module_name.php';

class YourModuleNameWebhookModuleFrontController extends ModuleFrontController
{
    // 关闭所有页面渲染,仅返回API响应
    public $ssl = true;
    public $display_header = false;
    public $display_footer = false;
    public $display_column_left = false;
    public $display_column_right = false;

    public function postProcess()
    {
        // 1. 验证网关请求合法性(必须实现,防止伪造请求)
        if (!$this->validateGatewayRequest()) {
            http_response_code(403);
            exit('Invalid request');
        }

        // 2. 从网关回调参数中获取购物车ID(需根据网关实际返回字段调整)
        $cartId = (int)Tools::getValue('cart_id');
        $cart = new Cart($cartId);

        if (!Validate::isLoadedObject($cart)) {
            http_response_code(404);
            exit('Cart not found');
        }

        // 3. 验证购物车归属用户
        $customer = new Customer($cart->id_customer);
        if (!Validate::isLoadedObject($customer)) {
            http_response_code(400);
            exit('Invalid customer');
        }

        // 4. 检查订单是否已处理,避免重复回调
        $existingOrderId = Order::getOrderByCartId($cartId);
        if ($existingOrderId) {
            http_response_code(200);
            echo json_encode(['status' => 'success', 'order_id' => $existingOrderId]);
            exit;
        }

        // 5. 执行订单确认核心逻辑
        try {
            $orderStateId = Configuration::get('PS_OS_PAYMENT'); // 使用PrestaShop默认已付款状态
            $this->module->validateOrder(
                $cart->id,
                $orderStateId,
                $cart->getOrderTotal(true, Cart::BOTH),
                $this->module->displayName,
                null,
                [],
                $this->context->currency->id,
                false,
                $customer->secure_key
            );

            // 6. 返回网关要求的成功响应(格式按需调整)
            http_response_code(200);
            echo json_encode(['status' => 'success', 'order_id' => $this->module->currentOrder]);
        } catch (Exception $e) {
            http_response_code(500);
            echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
        }
        exit;
    }

    /**
     * 验证网关请求合法性,示例为HMAC签名验证(需根据网关文档调整)
     */
    private function validateGatewayRequest()
    {
        $gatewaySignature = Tools::getValue('signature');
        $rawPayload = file_get_contents('php://input');
        // 从模块配置中获取网关密钥
        $secretKey = Configuration::get('YOUR_MODULE_GATEWAY_SECRET');
        
        $expectedSignature = hash_hmac('sha256', $rawPayload, $secretKey);
        return hash_equals($gatewaySignature, $expectedSignature);
    }
}

2. 配置WebHook地址

修改external.php中的$ok_webhook,设置为新建的WebHook控制器地址,同时传递购物车ID给网关:

// 替换原有的$ok_webhook = "???????????";
$ok_webhook = $this->context->link->getModuleLink(
    $this->module->name,
    'webhook',
    [],
    true // 强制使用HTTPS保障安全
);

// 调整传递给网关的参数,增加cart_id以便回调识别
$payload = [
    'cart_id'    => $cart->id,
    'redirect_ok' => $ok_redirect,
    'webhook_ok'  => $ok_webhook,
    'amount'     => $cart->getOrderTotal(true, Cart::BOTH),
    'currency'   => $this->context->currency->iso_code
];

3. 兼容原用户跳转流程(可选)

保留validation.php的逻辑,但增加订单重复处理判断,避免用户跳转时重复创建订单:

public function postProcess()
{
    $cart = $this->context->cart;
    // 检查购物车是否已转为订单
    $existingOrderId = Order::getOrderByCartId($cart->id);
    if ($existingOrderId) {
        // 直接跳转到订单确认页
        Tools::redirect($this->context->link->getPageLink(
            'order-confirmation',
            true,
            $this->context->language->id,
            [
                'id_cart' => $cart->id,
                'id_module' => $this->module->id,
                'id_order' => $existingOrderId,
                'key' => $this->context->customer->secure_key,
            ]
        ));
        exit;
    }

    // 原订单验证逻辑(WebHook未触发时,用户跳转仍可处理)
    $customer = new Customer($cart->id_customer);
    $this->module->validateOrder(
        (int)$cart->id,
        (int)$this->getOrderState(),
        (float)$cart->getOrderTotal(true, Cart::BOTH),
        $this->module->displayName,
        null,
        [],
        (int)$this->context->currency->id,
        false,
        $customer->secure_key
    );

    Tools::redirect($this->context->link->getPageLink(
        'order-confirmation',
        true,
        (int)$this->context->language->id,
        [
            'id_cart' => (int)$cart->id,
            'id_module' => (int)$this->module->id,
            'id_order' => (int)$this->module->currentOrder,
            'key' => $customer->secure_key,
        ]
    ));
}

4. 关键注意事项

  • 安全验证不可省略:必须实现validateGatewayRequest逻辑,可结合网关签名、IP白名单双重验证,防止恶意伪造订单。
  • 确保WebHook地址可访问:检查服务器防火墙、PrestaShop安全插件是否拦截网关请求,必要时添加网关IP到白名单。
  • 添加日志记录:建议在WebHook控制器中增加日志,方便排查问题:
    PrestaShopLogger::addLog(
        "WebHook received for cart {$cartId}: " . json_encode($_POST),
        1,
        null,
        'Order',
        $cartId,
        true
    );
    

内容的提问来源于stack exchange,提问作者DeLac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:01:08