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

Shopware 6.6自定义支付插件出现CHECKOUT__PAYMENT_TOKEN_INVALIDATED错误

自定义Shopware支付插件对接问题求助

问题概述

  • Shopware生成的return url超出支付提供商255字符限制,已通过创建伪return url并将真实URL存入数据库解决,想确认该方案是否最优
  • 支付交易成功时流程正常
  • 支付交易失败后,重试同一支付方式会触发CHECKOUT__PAYMENT_TOKEN_INVALIDATED异常

相关代码

Pay方法代码

public function pay(AsyncPaymentTransactionStruct $transaction, RequestDataBag $dataBag, SalesChannelContext $salesChannelContext): RedirectResponse
    {

        $context = $salesChannelContext->getContext();
        $buy_order = $transaction->getOrder()->getOrderNumber();
        $session_id = $_COOKIE["session-"];
        $amount = $transaction->getOrderTransaction()->getAmount()->getTotalPrice();
        $return_url = $transaction->getReturnUrl(); // Too long, max 256

        $return_url_fake = "http://" . $_SERVER['SERVER_NAME'] . "/gentawebpay/finish?order=" . $buy_order;

        $redirectUrl = $this->sendReturnUrlToExternalGateway($return_url_fake, $buy_order, $session_id, $amount);

        $this->gentaPaymentTransactionRepository->create([
            [
                'order_number' => $buy_order,
                'transaction_token' => $redirectUrl->getToken(),
                'sw_return_url' => $return_url,
            ]
        ], $context);

        // Redirect to external gateway
        return new RedirectResponse( $redirectUrl->getUrl().'?token_ws='.$redirectUrl->getToken() );
    }

Finalize方法代码

public function finalize(AsyncPaymentTransactionStruct $transaction, Request $request, SalesChannelContext $salesChannelContext): void
    {

        $token_ws = $request->get('token_ws') ?? null;

        $transactionId = $transaction->getOrderTransaction()->getId();

        if (!$token_ws) {
            die ('not a normal flow.');  
        }

        $response = (new Transaction)->commit($token_ws);
        $context = $salesChannelContext->getContext();

        if ($response->isApproved()) {

            $this->transactionStateHandler->paid($transaction->getOrderTransaction()->getId(), $context);

        } else {
            $this->transactionStateHandler->fail($transaction->getOrderTransaction()->getId(), $context);
            
            throw PaymentException::asyncFinalizeInterrupted(
                $transaction->getOrderTransaction()->getId(),
                'An error occurred during the communication with external payment gateway'
            );
            
        }
    }

发送return url至外部网关方法代码

private function sendReturnUrlToExternalGateway(string $getReturnUrl, string $buy_order, string $session_id, float $amount): object
    {
        $paymentProviderUrl = '';

        // Do some API Call to your payment provider
        $transaction = new Transaction();
        $response = $transaction->create($buy_order, $session_id, $amount, $getReturnUrl);

        $paymentProviderUrl = $response;

        return $paymentProviderUrl;
    }

错误截图

CHECKOUT__PAYMENT_TOKEN_INVALIDATED异常截图

解决方案建议

1. 伪Return URL方案评估

你的方案是可行的,但可以优化细节:

  • 避免直接使用$_COOKIE["session-"],改用Shopware内置会话管理方法$salesChannelContext->getSession()->getId(),更安全合规
  • 伪URL建议使用HTTPS协议,避免明文传输订单号
  • 数据库存储时,建议关联order_transaction_id而非仅order_number,一个订单可能存在多笔交易,避免数据冲突
  • 给伪URL添加签名验证(比如用订单号+密钥生成哈希),防止恶意请求

2. CHECKOUT__PAYMENT_TOKEN_INVALIDATED异常解决

该异常由Shopware抛出,核心原因是重试支付时引用了已失效的旧支付令牌,解决思路:

  • 创建新交易重试:支付失败后,不要复用原订单交易,调用Shopware订单交易API生成新的交易记录,再发起支付请求
  • 清理旧令牌关联:在finalize方法标记交易失败后,清理数据库中关联的旧支付令牌,避免重试时引用
  • 确认提供商侧状态:支付失败后,支付提供商通常会作废对应交易的token,重试必须生成新的交易token,不能复用之前的token_ws
  • 调整重试逻辑:在前端或插件中,用户触发重试时,重新调用pay方法生成新的交易和令牌,而非复用原有流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 08:31:01