Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .platform.app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,8 @@ web:
# The front-controller script to send non-static requests to.
passthru: "/index.php"


'/build':
expires: 1h
passthru: false
scripts: false
allow: true
12 changes: 12 additions & 0 deletions braintree_profiler.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git 1/lib/Braintree/Http.php 2/lib/Braintree/Http.php
index 3d4bae3..6ba23fd 100644
--- 1/lib/Braintree/Http.php
+++ 2/lib/Braintree/Http.php
@@ -143,6 +143,6 @@ class Http
{
$curlRequest = new CurlRequest($url);
// phpcs:ignore Generic.Files.LineLength
- return Curl::makeRequest($httpVerb, $url, $this->_config, $curlRequest, $requestBody, $file, $customHeaders, $this->_useClientCredentials);
+ return \Swag\Braintree\Framework\Profiler::trace('braintree::' . \parse_url($url, \PHP_URL_PATH), fn () => Curl::makeRequest($httpVerb, $url, $this->_config, $curlRequest, $requestBody, $file, $customHeaders, $this->_useClientCredentials));
}
}
15 changes: 11 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"ext-session": "*",
"ext-simplexml": "*",
"braintree/braintree_php": "^6.24",
"cweagans/composer-patches": "^1.7",
"doctrine/doctrine-bundle": "^2.13",
"doctrine/doctrine-migrations-bundle": "^3.4",
"doctrine/orm": "^3.3",
Expand All @@ -19,9 +20,9 @@
"shopware/app-bundle": "^4.1",
"symfony/console": "^7.2",
"symfony/dotenv": "^7.2",
"symfony/http-foundation": "^7.2",
"symfony/flex": "^2",
"symfony/framework-bundle": "^7.2",
"symfony/http-foundation": "^7.2",
"symfony/monolog-bundle": "^3.10",
"symfony/property-access": "^7.2",
"symfony/psr-http-message-bridge": "^7.2",
Expand All @@ -32,11 +33,12 @@
},
"config": {
"allow-plugins": {
"cweagans/composer-patches": true,
"infection/extension-installer": true,
"php-http/discovery": true,
"phpstan/extension-installer": true,
"symfony/flex": true,
"symfony/runtime": true,
"infection/extension-installer": true,
"phpstan/extension-installer": true
"symfony/runtime": true
},
"sort-packages": true
},
Expand Down Expand Up @@ -106,6 +108,11 @@
"symfony": {
"allow-contrib": false,
"require": "7.2.*"
},
"patches": {
"braintree/braintree_php": {
"profile": "./braintree_profiler.patch"
}
}
},
"require-dev": {
Expand Down
50 changes: 49 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions config/packages/property_info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
framework:
property_info:
with_constructor_extractor: true
2 changes: 2 additions & 0 deletions src/Braintree/Gateway/BraintreeGatewayFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Braintree\Configuration;
use Braintree\Gateway;
use Swag\Braintree\Entity\ShopEntity;
use Swag\Braintree\Framework\Profiler;
use Swag\Braintree\Framework\Request\ShopResolver;
use Symfony\Component\HttpFoundation\RequestStack;

Expand All @@ -13,6 +14,7 @@
public function __construct(
private readonly RequestStack $requestStack,
private readonly ShopResolver $shopResolver,
private readonly Profiler $profiler, // make sure stopwatch got injected

Check failure on line 17 in src/Braintree/Gateway/BraintreeGatewayFactory.php

View workflow job for this annotation

GitHub Actions / phpstan

Property Swag\Braintree\Braintree\Gateway\BraintreeGatewayFactory::$profiler is never read, only written.
) {
}

Expand Down
22 changes: 12 additions & 10 deletions src/Controller/GatewayController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Swag\Braintree\Controller;

use Braintree\Exception\NotFound;
use Braintree\Gateway;
use Psr\Http\Message\ResponseInterface;
use Shopware\App\SDK\Context\Cart\Error;
use Shopware\App\SDK\Context\Gateway\Checkout\CheckoutGatewayAction;
Expand All @@ -11,7 +13,6 @@
use Shopware\App\SDK\Gateway\Checkout\Command\RemovePaymentMethodCommand;
use Shopware\App\SDK\Response\GatewayResponse;
use Swag\Braintree\Braintree\Gateway\BraintreeConnectionService;
use Swag\Braintree\Braintree\Gateway\Connection\BraintreeConnectionStatus;
use Swag\Braintree\Braintree\Util\SalesChannelConfigService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Attribute\AsController;
Expand All @@ -24,8 +25,9 @@
public const CREDIT_CARD_TECHNICAL_NAME = 'payment_SwagBraintreeApp_credit_card';

public function __construct(
private readonly BraintreeConnectionService $connectionService,

Check failure on line 28 in src/Controller/GatewayController.php

View workflow job for this annotation

GitHub Actions / phpstan

Property Swag\Braintree\Controller\GatewayController::$connectionService is never read, only written.
private readonly SalesChannelConfigService $salesChannelConfigService,
private readonly Gateway $gateway,
) {
}

Expand All @@ -38,23 +40,23 @@
if (!$action->paymentMethods->has(self::CREDIT_CARD_TECHNICAL_NAME)) {
return GatewayResponse::createCheckoutGatewayResponse($commands);
}

$status = $this->connectionService->testConnection();

if ($status->connectionStatus !== BraintreeConnectionStatus::STATUS_CONNECTED) {
$commands->add(new RemovePaymentMethodCommand(self::CREDIT_CARD_TECHNICAL_NAME));
}

if (!$this->salesChannelConfigService->getMerchantId(

$merchantId = $this->salesChannelConfigService->getMerchantId(
$action->context->getSalesChannel()->getId(),
$action->context->getCurrencyId(),
$action->shop
)) {
);

try {
$this->gateway->merchantAccount()->find($merchantId);
} catch(NotFound) {
$commands->add(new RemovePaymentMethodCommand(self::CREDIT_CARD_TECHNICAL_NAME));

if ($action->context->getPaymentMethod()->getTechnicalName() === self::CREDIT_CARD_TECHNICAL_NAME) {
$commands->add(new AddCartErrorCommand('Checkout with Braintree is currently not available in your currency', true, Error::LEVEL_ERROR));
}
} catch (\Throwable) {
$commands->add(new RemovePaymentMethodCommand(self::CREDIT_CARD_TECHNICAL_NAME));
}

return GatewayResponse::createCheckoutGatewayResponse($commands);
Expand Down
31 changes: 31 additions & 0 deletions src/Framework/Profiler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php declare(strict_types=1);

namespace Swag\Braintree\Framework;

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Stopwatch\Stopwatch;

class Profiler
{
private static ?Stopwatch $stopwatch = null;

public function __construct(
#[Autowire(service: 'debug.stopwatch')]
?Stopwatch $stopwatch = null,
) {
self::$stopwatch = $stopwatch;
}

public static function trace(string $name, \Closure $closure): mixed
{
try {
self::$stopwatch?->start($name, 'app');

$result = $closure();
} finally {
self::$stopwatch?->stop($name, 'app');

Check failure on line 26 in src/Framework/Profiler.php

View workflow job for this annotation

GitHub Actions / phpstan

Method Symfony\Component\Stopwatch\Stopwatch::stop() invoked with 2 parameters, 1 required.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this actually log the time anywhere? I am not familiar with Symfony's Stopwatch Component

Copy link
Contributor Author

@cyl3x cyl3x Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symfony stopwatch is what you see in the Profiler > <Request> > Performance - therefore, it's for debug only. Before you just saw a span "controller" stretching a second, now you see spans for each Braintree request

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

}

return $result;
}
}
21 changes: 21 additions & 0 deletions symfony.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
"config/packages/dama_doctrine_test_bundle.yaml"
]
},
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": {
"version": "2.10",
"recipe": {
Expand Down Expand Up @@ -181,6 +190,18 @@
"config/packages/monolog.yaml"
]
},
"symfony/property-info": {
"version": "7.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.3",
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
},
"files": [
"config/packages/property_info.yaml"
]
},
"symfony/routing": {
"version": "6.3",
"recipe": {
Expand Down
Loading