getparthenon /
parthenon
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||||
| 2 | |||||
| 3 | declare(strict_types=1); |
||||
| 4 | |||||
| 5 | /* |
||||
| 6 | * Copyright (C) 2020-2025 Iain Cambridge |
||||
| 7 | * |
||||
| 8 | * This program is free software: you can redistribute it and/or modify |
||||
| 9 | * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by |
||||
| 10 | * the Free Software Foundation, either version 2.1 of the License, or |
||||
| 11 | * (at your option) any later version. |
||||
| 12 | * |
||||
| 13 | * This program is distributed in the hope that it will be useful, |
||||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
| 16 | * GNU Lesser General Public License for more details. |
||||
| 17 | * |
||||
| 18 | * You should have received a copy of the GNU General Public License |
||||
| 19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
| 20 | */ |
||||
| 21 | |||||
| 22 | namespace Parthenon\DependencyInjection\Modules; |
||||
| 23 | |||||
| 24 | use Parthenon\Billing\Athena\CustomerTeamSection; |
||||
| 25 | use Parthenon\Billing\Athena\CustomerUserSection; |
||||
| 26 | use Parthenon\Billing\BillaBear\Webhook\ProcessorInterface; |
||||
| 27 | use Parthenon\Billing\CustomerProviderInterface; |
||||
| 28 | use Parthenon\Billing\Plan\CachedPlanManager; |
||||
| 29 | use Parthenon\Billing\Plan\CounterInterface; |
||||
| 30 | use Parthenon\Billing\Plan\PlanManager; |
||||
| 31 | use Parthenon\Billing\Plan\PlanManagerInterface; |
||||
| 32 | use Parthenon\Billing\Repository\CustomerRepositoryInterface; |
||||
| 33 | use Parthenon\Billing\TeamCustomerProvider; |
||||
| 34 | use Parthenon\Billing\UserCustomerProvider; |
||||
| 35 | use Parthenon\Billing\Webhook\HandlerInterface; |
||||
| 36 | use Parthenon\Common\Exception\ParameterNotSetException; |
||||
| 37 | use Parthenon\User\Repository\TeamRepositoryInterface; |
||||
| 38 | use Parthenon\User\Repository\UserRepositoryInterface; |
||||
| 39 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; |
||||
| 40 | use Symfony\Component\Config\Definition\Builder\NodeBuilder; |
||||
| 41 | use Symfony\Component\Config\Definition\Builder\NodeDefinition; |
||||
| 42 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; |
||||
| 43 | use Symfony\Component\Config\FileLocator; |
||||
| 44 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
||||
| 45 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; |
||||
| 46 | |||||
| 47 | class Billing implements ModuleConfigurationInterface |
||||
| 48 | { |
||||
| 49 | public function addConfig(NodeBuilder $nodeBuilder): void |
||||
| 50 | { |
||||
| 51 | $nodeBuilder->arrayNode('billing') |
||||
| 52 | ->children() |
||||
| 53 | ->booleanNode('enabled')->defaultFalse()->end() |
||||
| 54 | ?->scalarNode('customer_type')->defaultValue('team')->end() |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 55 | ?->scalarNode('plan_management')->defaultValue('config')->end() |
||||
| 56 | ?->arrayNode('billabear') |
||||
| 57 | ->children() |
||||
| 58 | ->booleanNode('enabled')->defaultFalse()->end() |
||||
| 59 | ->scalarNode('api_url')->end() |
||||
| 60 | ->scalarNode('api_key')->end() |
||||
| 61 | ->end() |
||||
| 62 | ->end() |
||||
| 63 | ?->arrayNode('payments') |
||||
| 64 | ->children() |
||||
| 65 | ->scalarNode('provider')->end() |
||||
| 66 | ?->booleanNode('pci_mode')->end() |
||||
| 67 | ?->scalarNode('return_url')->end() |
||||
| 68 | ?->scalarNode('cancel_url')->end() |
||||
| 69 | ?->arrayNode('adyen') |
||||
| 70 | ->children() |
||||
| 71 | ->scalarNode('api_key')->end() |
||||
| 72 | ?->scalarNode('merchant_account')->end() |
||||
| 73 | ?->booleanNode('test_mode')->end() |
||||
| 74 | ?->scalarNode('webhook_secret')->end() |
||||
| 75 | ?->scalarNode('prefix')->end() |
||||
| 76 | ?->scalarNode('cse_url')->end() |
||||
| 77 | ?->end() |
||||
| 78 | ->end() |
||||
| 79 | ?->arrayNode('stripe') |
||||
| 80 | ->children() |
||||
| 81 | ->scalarNode('private_api_key')->end() |
||||
| 82 | ->scalarNode('webhook_secret')->end() |
||||
| 83 | ?->scalarNode('public_api_key')->end() |
||||
| 84 | ?->scalarNode('product_id')->end() |
||||
| 85 | ?->arrayNode('payment_methods') |
||||
| 86 | ->scalarPrototype()->end() |
||||
| 87 | ?->end() |
||||
| 88 | ->end() |
||||
| 89 | ->end() |
||||
| 90 | ->end() |
||||
| 91 | ->end() |
||||
| 92 | ->end() |
||||
| 93 | ->fixXmlConfig('plans') |
||||
| 94 | ->append($this->getPlansNode()) |
||||
| 95 | ?->end(); |
||||
| 96 | } |
||||
| 97 | |||||
| 98 | public function handleDefaultParameters(ContainerBuilder $container): void |
||||
| 99 | { |
||||
| 100 | $container->setParameter('parthenon_billing_payments_obol_config', []); |
||||
| 101 | $container->setParameter('parthenon_billing_customer_type', 'team'); |
||||
| 102 | $container->setParameter('parthenon_billing_config_frontend_info', ''); |
||||
| 103 | $container->setParameter('parthenon_billing_config_webhook_secret', ''); |
||||
| 104 | $container->setParameter('parthenon_billing_plan_plans', []); |
||||
| 105 | $container->setParameter('parthenon_billing_product_id', null); |
||||
| 106 | $container->setParameter('parthenon_billing_billabear_enabled', false); |
||||
| 107 | $container->setParameter('parthenon_billing_billabear_api_url', false); |
||||
| 108 | $container->setParameter('parthenon_billing_billabear_api_key', false); |
||||
| 109 | } |
||||
| 110 | |||||
| 111 | public function handleConfiguration(array $config, ContainerBuilder $container): void |
||||
| 112 | { |
||||
| 113 | if (!isset($config['billing']) || !isset($config['billing']['enabled']) || false === $config['billing']['enabled']) { |
||||
| 114 | return; |
||||
| 115 | } |
||||
| 116 | |||||
| 117 | $container->registerForAutoconfiguration(CounterInterface::class)->addTag('parthenon.billing.plan.counter'); |
||||
| 118 | $container->registerForAutoconfiguration(HandlerInterface::class)->addTag('parthenon.billing.webhooks.handler'); |
||||
| 119 | $container->registerForAutoconfiguration(ProcessorInterface::class)->addTag('parthenon.billing.billabear.webhooks.handler'); |
||||
| 120 | |||||
| 121 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../../Resources/config')); |
||||
| 122 | $loader->load('services/billing.xml'); |
||||
| 123 | $loader->load('services/orm/billing.xml'); |
||||
| 124 | |||||
| 125 | $billingConfig = $config['billing']; |
||||
| 126 | $paymentsConfig = $billingConfig['payments']; |
||||
| 127 | |||||
| 128 | if ('team' === strtolower($billingConfig['customer_type'])) { |
||||
| 129 | $this->handleTeamCustomer($config, $container); |
||||
| 130 | } elseif ('user' === strtolower($billingConfig['customer_type'])) { |
||||
| 131 | $this->handleUserCustomer($config, $container); |
||||
| 132 | } |
||||
| 133 | |||||
| 134 | if (isset($billingConfig['billabear']) && $billingConfig['billabear']['enabled']) { |
||||
| 135 | $loader->load('services/billing/billabear.xml'); |
||||
| 136 | $container->setAlias(PlanManagerInterface::class, CachedPlanManager::class); |
||||
| 137 | $this->handleBillaBearConfig($billingConfig['billabear'], $container); |
||||
| 138 | $container->setParameter('parthenon_billing_enabled', false); |
||||
| 139 | } elseif ('athena' === strtolower($billingConfig['plan_management'])) { |
||||
| 140 | $container->setParameter('parthenon_billing_enabled', true); |
||||
| 141 | $loader->load('services/billing/athena_plans.xml'); |
||||
| 142 | $container->setAlias(PlanManagerInterface::class, CachedPlanManager::class); |
||||
| 143 | } else { |
||||
| 144 | $container->setParameter('parthenon_billing_enabled', true); |
||||
| 145 | $container->setAlias(PlanManagerInterface::class, PlanManager::class); |
||||
| 146 | } |
||||
| 147 | |||||
| 148 | $container->setParameter('parthenon_billing_plan_plans', $config['billing']['plan']); |
||||
| 149 | |||||
| 150 | $obolConfig = match ($paymentsConfig['provider']) { |
||||
| 151 | 'stripe' => $this->handleStripeConfig($paymentsConfig, $container), |
||||
| 152 | 'adyen' => $this->handleAdyen($paymentsConfig, $container), |
||||
| 153 | 'custom' => [], |
||||
| 154 | default => throw new ParameterNotSetException('billing.payments.provider must be valid'), |
||||
| 155 | }; |
||||
| 156 | |||||
| 157 | $container->setParameter('parthenon_billing_payments_obol_config', $obolConfig); |
||||
| 158 | $container->setParameter('parthenon_billing_plan_plans', $config['billing']['plan']); |
||||
| 159 | } |
||||
| 160 | |||||
| 161 | public function buildPricesNode() |
||||
| 162 | { |
||||
| 163 | $treeBuilder = new TreeBuilder('prices'); |
||||
| 164 | $node = $treeBuilder->getRootNode(); |
||||
| 165 | |||||
| 166 | $priceNode = $node->requiresAtLeastOneElement() |
||||
|
0 ignored issues
–
show
The method
requiresAtLeastOneElement() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. It seems like you code against a sub-type of Symfony\Component\Config...\Builder\NodeDefinition such as Symfony\Component\Config...der\ArrayNodeDefinition.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 167 | ->useAttributeAsKey('payment_schedule') |
||||
| 168 | ->prototype('array'); |
||||
| 169 | assert($priceNode instanceof ArrayNodeDefinition); |
||||
| 170 | |||||
| 171 | $priceNode |
||||
| 172 | ->arrayPrototype() |
||||
| 173 | ->children() |
||||
| 174 | ->scalarNode('amount')->end() |
||||
| 175 | ->scalarNode('price_id')->end() |
||||
| 176 | ->booleanNode('public')->defaultTrue()->end() |
||||
| 177 | ->end() |
||||
| 178 | ->end() |
||||
| 179 | ->end(); |
||||
| 180 | |||||
| 181 | return $node; |
||||
| 182 | } |
||||
| 183 | |||||
| 184 | protected function handleTeamCustomer(array $config, ContainerBuilder $containerBuilder): void |
||||
|
0 ignored issues
–
show
The parameter
$config is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||
| 185 | { |
||||
| 186 | $containerBuilder->setAlias(CustomerProviderInterface::class, TeamCustomerProvider::class); |
||||
| 187 | $containerBuilder->setAlias(CustomerRepositoryInterface::class, TeamRepositoryInterface::class); |
||||
| 188 | $containerBuilder->removeDefinition(CustomerUserSection::class); |
||||
| 189 | } |
||||
| 190 | |||||
| 191 | protected function handleUserCustomer(array $config, ContainerBuilder $containerBuilder): void |
||||
|
0 ignored issues
–
show
The parameter
$config is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. Loading history...
|
|||||
| 192 | { |
||||
| 193 | $containerBuilder->setAlias(CustomerProviderInterface::class, UserCustomerProvider::class); |
||||
| 194 | $containerBuilder->setAlias(CustomerRepositoryInterface::class, UserRepositoryInterface::class); |
||||
| 195 | $containerBuilder->removeDefinition(CustomerTeamSection::class); |
||||
| 196 | } |
||||
| 197 | |||||
| 198 | protected function handleBillaBearConfig(array $billabearConfig, ContainerBuilder $containerBuilder): void |
||||
| 199 | { |
||||
| 200 | if (true !== $billabearConfig['enabled']) { |
||||
| 201 | return; |
||||
| 202 | } |
||||
| 203 | |||||
| 204 | $containerBuilder->setParameter('parthenon_billing_billabear_enabled', true); |
||||
| 205 | $containerBuilder->setParameter('parthenon_billing_billabear_api_key', $billabearConfig['api_key']); |
||||
| 206 | $containerBuilder->setParameter('parthenon_billing_billabear_api_url', $billabearConfig['api_url']); |
||||
| 207 | } |
||||
| 208 | |||||
| 209 | protected function handleStripeConfig(array $paymentsConfig, ContainerBuilder $containerBuilder): array |
||||
| 210 | { |
||||
| 211 | if (!isset($paymentsConfig['stripe']['private_api_key'])) { |
||||
| 212 | throw new ParameterNotSetException('billing.payments.stripe.private_api_key must be set.'); |
||||
| 213 | } |
||||
| 214 | |||||
| 215 | $pciMode = false; |
||||
| 216 | |||||
| 217 | if (isset($paymentsConfig['pci_mode'])) { |
||||
| 218 | $pciMode = $paymentsConfig['pci_mode']; |
||||
| 219 | } |
||||
| 220 | |||||
| 221 | $config = [ |
||||
| 222 | 'provider' => 'stripe', |
||||
| 223 | 'api_key' => $paymentsConfig['stripe']['private_api_key'], |
||||
| 224 | 'pci_mode' => $pciMode, |
||||
| 225 | ]; |
||||
| 226 | |||||
| 227 | $containerBuilder->setParameter('parthenon_billing_product_id', $paymentsConfig['stripe']['product_id'] ?? null); |
||||
| 228 | $containerBuilder->setParameter('parthenon_billing_config_frontend_info', $paymentsConfig['stripe']['public_api_key']); |
||||
| 229 | $containerBuilder->setParameter('parthenon_billing_config_webhook_secret', $paymentsConfig['stripe']['webhook_secret'] ?? ''); |
||||
| 230 | |||||
| 231 | if (isset($paymentsConfig['stripe']['payment_methods'])) { |
||||
| 232 | $config['payment_methods'] = $paymentsConfig['stripe']['payment_methods']; |
||||
| 233 | } |
||||
| 234 | |||||
| 235 | if (isset($paymentsConfig['return_url'])) { |
||||
| 236 | $config['success_url'] = $paymentsConfig['return_url']; |
||||
| 237 | $config['cancel_url'] = $paymentsConfig['return_url']; |
||||
| 238 | } |
||||
| 239 | |||||
| 240 | if (isset($paymentsConfig['cancel_url'])) { |
||||
| 241 | $config['cancel_url'] = $paymentsConfig['cancel_url']; |
||||
| 242 | } |
||||
| 243 | |||||
| 244 | return $config; |
||||
| 245 | } |
||||
| 246 | |||||
| 247 | protected function handleAdyen(array $paymentsConfig, ContainerBuilder $containerBuilder): array |
||||
| 248 | { |
||||
| 249 | if (!isset($paymentsConfig['adyen']['api_key'])) { |
||||
| 250 | throw new ParameterNotSetException('billing.payments.adyen.api_key must be set.'); |
||||
| 251 | } |
||||
| 252 | if (!isset($paymentsConfig['adyen']['merchant_account'])) { |
||||
| 253 | throw new ParameterNotSetException('billing.payments.adyen.merchant_account must be set.'); |
||||
| 254 | } |
||||
| 255 | |||||
| 256 | $pciMode = false; |
||||
| 257 | if (isset($paymentsConfig['pci_mode'])) { |
||||
| 258 | $pciMode = $paymentsConfig['pci_mode']; |
||||
| 259 | } |
||||
| 260 | |||||
| 261 | $testMode = true; |
||||
| 262 | if (isset($paymentsConfig['adyen']['test_mode'])) { |
||||
| 263 | $testMode = $paymentsConfig['adyen']['test_mode']; |
||||
| 264 | } |
||||
| 265 | |||||
| 266 | $config = [ |
||||
| 267 | 'provider' => 'adyen', |
||||
| 268 | 'api_key' => $paymentsConfig['adyen']['api_key'], |
||||
| 269 | 'merchant_account' => $paymentsConfig['adyen']['merchant_account'], |
||||
| 270 | 'pci_mode' => $pciMode, |
||||
| 271 | 'test_mode' => $testMode, |
||||
| 272 | ]; |
||||
| 273 | |||||
| 274 | if ($paymentsConfig['adyen']['prefix']) { |
||||
| 275 | $config['prefix'] = $paymentsConfig['adyen']['prefix']; |
||||
| 276 | } |
||||
| 277 | |||||
| 278 | if (isset($paymentsConfig['return_url'])) { |
||||
| 279 | $config['return_url'] = $paymentsConfig['return_url']; |
||||
| 280 | } |
||||
| 281 | |||||
| 282 | $containerBuilder->setParameter('parthenon_billing_config_frontend_info', $paymentsConfig['adyen']['cse_url']); |
||||
| 283 | $containerBuilder->setParameter('parthenon_billing_config_webhook_secret', $paymentsConfig['adyen']['webhook_secret'] ?? ''); |
||||
| 284 | |||||
| 285 | return $config; |
||||
| 286 | } |
||||
| 287 | |||||
| 288 | private function getPlansNode(): NodeDefinition |
||||
| 289 | { |
||||
| 290 | $treeBuilder = new TreeBuilder('plan'); |
||||
| 291 | $node = $treeBuilder->getRootNode(); |
||||
| 292 | |||||
| 293 | /** @var ArrayNodeDefinition $planNode */ |
||||
| 294 | $planNode = $node |
||||
| 295 | ->requiresAtLeastOneElement() |
||||
| 296 | ->useAttributeAsKey('name') |
||||
| 297 | ->prototype('array'); |
||||
| 298 | |||||
| 299 | $planNode |
||||
| 300 | ->fixXmlConfig('limits') |
||||
| 301 | ->children() |
||||
| 302 | ->booleanNode('is_free')->defaultFalse()->end() |
||||
| 303 | ?->booleanNode('is_per_seat')->defaultFalse()->end() |
||||
|
0 ignored issues
–
show
The method
booleanNode() does not exist on Symfony\Component\Config...der\NodeParentInterface. It seems like you code against a sub-type of Symfony\Component\Config...der\NodeParentInterface such as Symfony\Component\Config...ion\Builder\NodeBuilder.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 304 | ?->booleanNode('public')->defaultTrue()->end() |
||||
| 305 | ?->booleanNode('has_trial')->defaultFalse()->end() |
||||
| 306 | ?->scalarNode('trial_length_days')->defaultValue(0)->end() |
||||
| 307 | ?->scalarNode('user_count')->end() |
||||
| 308 | ->arrayNode('features') |
||||
| 309 | ->scalarPrototype()->end() |
||||
| 310 | ->end() |
||||
| 311 | ->arrayNode('limit') |
||||
| 312 | ->useAttributeAsKey('name') |
||||
| 313 | ->prototype('array') |
||||
| 314 | ->children() |
||||
| 315 | ->integerNode('limit')->end() |
||||
| 316 | ->scalarNode('description')->end() |
||||
| 317 | ->end() |
||||
| 318 | ->end() |
||||
| 319 | ->end() |
||||
| 320 | ->append($this->buildPricesNode()) |
||||
| 321 | ->end(); |
||||
| 322 | |||||
| 323 | return $node; |
||||
| 324 | } |
||||
| 325 | } |
||||
| 326 |