Symfony 5.4.12集成mnastalski/przelewy24-php的交易验证问题
问题分析与修复方案
你的核心问题是积分添加逻辑未与交易成功验证绑定,且错误地在发起交易的流程中调用了handleWebhook()——该方法用于接收Przelewy24的异步回调,发起交易时并无回调请求,导致当前代码跳过验证直接执行了积分添加。
修复步骤
1. 拆分发起交易与回调处理逻辑
将交易发起和Webhook回调验证拆分为两个独立的控制器方法:
发起交易的myAccount方法修改
namespace App\Controller; ... use Przelewy24\Przelewy24; use Przelewy24\Exceptions\Przelewy24Exception; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; ... public function myAccount( $partnerId = XXXXXX; $crc = 'XXXXXXXXXXXXXXXXXX'; $live = false; UrlGeneratorInterface $urlGenerator ... if ($buyPointsForm->isSubmitted() && $buyPointsForm->isValid()) { $session_id = session_id(); $przelewy24 = new Przelewy24([ 'merchant_id' => $partnerId, 'crc' => $crc, 'live' => $live, ]); try { $transaction = $przelewy24->transaction([ 'session_id' => $session_id, // 跳转至支付结果页面,不直接处理积分 'url_return' => $urlGenerator->generate('app_payment_return', ['itemId' => $item->getId()], UrlGeneratorInterface::ABSOLUTE_URL), // 指向专门的Webhook处理路由 'url_status' => $urlGenerator->generate('app_przelewy24_webhook', [], UrlGeneratorInterface::ABSOLUTE_URL), 'amount' => 1000, // 注意:Przelewy24金额单位为分,1000代表10PLN,按需调整 'description' => 'Test', 'email' => 'sample@sample.pl', // 传递自定义参数,用于后续Webhook验证后处理积分 'additional' => json_encode([ 'userId' => $this->getUser()->getId(), 'points' => intVal($item->getPoints()), 'itemId' => $item->getId() ]) ]); // 获取支付跳转URL并跳转 $redirectUrl = $transaction->redirectUrl(); return $this->redirect($redirectUrl); } catch (Przelewy24Exception $e) { dd($e); } }
新增Webhook处理控制器方法
创建专门方法接收Przelewy24异步回调,此处才是验证交易并添加积分的核心逻辑:
use Symfony\Component\HttpFoundation\Response; use Psr\Log\LoggerInterface; use App\Entity\User; use Doctrine\ORM\EntityManagerInterface; ... /** * @Route("/przelewy24/webhook", name="app_przelewy24_webhook", methods={"POST"}) */ public function handlePrzelewy24Webhook( $partnerId = XXXXXX, $crc = 'XXXXXXXXXXXXXXXXXX', $live = false, LoggerInterface $logger, EntityManagerInterface $em ): Response { $przelewy24 = new Przelewy24([ 'merchant_id' => $partnerId, 'crc' => $crc, 'live' => $live, ]); try { // 解析Webhook请求数据 $webhookData = $przelewy24->handleWebhook(); // 1. 验证交易真实性(必须执行,防止伪造请求) $verifyResponse = $przelewy24->verifyTransaction([ 'session_id' => $webhookData['sessionId'], 'order_id' => $webhookData['orderId'], 'amount' => $webhookData['amount'], ]); // 2. 检查交易状态是否成功 if ($verifyResponse['status'] === 'success' && $webhookData['status'] === 'success') { // 解析自定义参数 $additionalData = json_decode($webhookData['additional'], true); $userId = $additionalData['userId']; $pointsToAdd = $additionalData['points']; // 3. 执行添加积分逻辑 $user = $em->getRepository(User::class)->find($userId); if ($user) { $user->setPoints($user->getPoints() + $pointsToAdd); // 假设存在对应属性方法 $em->persist($user); $em->flush(); } } // 返回200确认接收回调,Przelewy24会停止重试 return new Response('OK', Response::HTTP_OK); } catch (Przelewy24Exception $e) { // 记录错误日志,不返回具体错误信息给外部 $logger->error('Przelewy24 Webhook Error: ' . $e->getMessage()); return new Response('Error', Response::HTTP_BAD_REQUEST); } }
2. 调整支付结果页面逻辑
原assign_points路由改为仅展示支付结果,不再处理积分添加:
/** * @Route("/payment/return/{itemId}", name="app_payment_return") */ public function paymentReturn(int $itemId): Response { $item = $this->getDoctrine()->getRepository(Item::class)->find($itemId); return $this->render('payment/return.html.twig', [ 'item' => $item, ]); }
关键注意事项
- 金额单位:Przelewy24的
amount参数单位为分,需根据实际支付金额换算。 - Webhook路由权限:确保
app_przelewy24_webhook路由未被Symfony防火墙拦截,回调请求为匿名访问。 - 参数安全性:避免将敏感参数(如积分数量、用户ID)暴露在URL中,使用
additional字段传递序列化参数更安全。 - 日志记录:在Webhook中添加日志,便于后续排查交易问题。
内容的提问来源于stack exchange,提问作者PKDEVELOP
相关产品推荐
相关产品推荐

