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\Common\Exception\ParameterNotSetException; |
||||
25 | use Parthenon\Payments\Athena\TeamSubscriberSection; |
||||
26 | use Parthenon\Payments\Athena\UserSubscriberSection; |
||||
27 | use Parthenon\Payments\PaymentProvider\TransactionCloud\Config; |
||||
28 | use Parthenon\Payments\Plan\CounterInterface; |
||||
29 | use Parthenon\Payments\Repository\SubscriberRepositoryInterface; |
||||
30 | use Parthenon\Payments\Subscriber\SubscriberInterface; |
||||
31 | use Parthenon\Payments\Transition\ToActiveTransitionInterface; |
||||
32 | use Parthenon\Payments\Transition\ToCancelledTransitionInterface; |
||||
33 | use Parthenon\Payments\Transition\ToOverdueTransitionInterface; |
||||
34 | use Parthenon\User\Repository\TeamRepositoryInterface; |
||||
35 | use Parthenon\User\Repository\UserRepositoryInterface; |
||||
36 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; |
||||
37 | use Symfony\Component\Config\Definition\Builder\NodeBuilder; |
||||
38 | use Symfony\Component\Config\Definition\Builder\NodeDefinition; |
||||
39 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; |
||||
40 | use Symfony\Component\Config\FileLocator; |
||||
41 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
||||
42 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; |
||||
43 | |||||
44 | final class Payments implements ModuleConfigurationInterface |
||||
45 | { |
||||
46 | public function addConfig(NodeBuilder $nodeBuilder): void |
||||
47 | { |
||||
48 | $nodeBuilder |
||||
49 | ->arrayNode('payments') |
||||
50 | ->children() |
||||
51 | ->booleanNode('enabled')->end() |
||||
52 | ->scalarNode('provider')->end() |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
53 | ->scalarNode('success_redirect_route')->end() |
||||
54 | ->scalarNode('cancel_checkout_redirect_route')->end() |
||||
55 | ->arrayNode('stripe') |
||||
56 | ->children() |
||||
57 | ->scalarNode('private_api_key')->end() |
||||
58 | ->scalarNode('public_api_key')->end() |
||||
59 | ->scalarNode('success_url')->end() |
||||
60 | ->scalarNode('cancel_url')->end() |
||||
61 | ->scalarNode('return_url')->end() |
||||
62 | ->end() |
||||
63 | ->end() |
||||
64 | ->arrayNode('transaction_cloud') |
||||
65 | ->children() |
||||
66 | ->scalarNode('api_key')->end() |
||||
67 | ->scalarNode('api_key_password')->end() |
||||
68 | ->booleanNode('sandbox')->defaultFalse()->end() |
||||
69 | ->scalarNode('customer_id_parameter')->end() |
||||
70 | ->scalarNode('payment_id_parameter')->end() |
||||
71 | ->end() |
||||
72 | ->end() |
||||
73 | ->arrayNode('subscriptions') |
||||
74 | ->children() |
||||
75 | ->scalarNode('subscriber_type')->end() |
||||
76 | ->end() |
||||
77 | ->fixXmlConfig('plans') |
||||
78 | ->append($this->getPlansNode()) |
||||
79 | ->end() |
||||
80 | ->end() |
||||
81 | ->fixXmlConfig('prices') |
||||
82 | ->append($this->getPricesNode()) |
||||
83 | |||||
84 | ->end(); |
||||
85 | } |
||||
86 | |||||
87 | public function handleDefaultParameters(ContainerBuilder $container): void |
||||
88 | { |
||||
89 | $container->setParameter('parthenon_payments_subscriber_type', ''); |
||||
90 | |||||
91 | $container->setParameter('parthenon_payments_stripe_private_api_key', ''); |
||||
92 | $container->setParameter('parthenon_payments_stripe_public_api_key', ''); |
||||
93 | $container->setParameter('parthenon_payments_stripe_success_url', ''); |
||||
94 | $container->setParameter('parthenon_payments_stripe_cancel_url', ''); |
||||
95 | $container->setParameter('parthenon_payments_stripe_return_url', ''); |
||||
96 | |||||
97 | $container->setParameter('parthenon_payments_transaction_cloud_api_key', ''); |
||||
98 | $container->setParameter('parthenon_payments_transaction_cloud_api_key_password', ''); |
||||
99 | $container->setParameter('parthenon_payments_transaction_cloud_sandbox', false); |
||||
100 | $container->setParameter('parthenon_payments_transaction_cloud_customer_id_parameter', Config::DEFAULT_CUSTOMER_ID_PARAMETER); |
||||
101 | $container->setParameter('parthenon_payments_transaction_cloud_payment_id_parameter', Config::DEFAULT_PAYMENT_ID_PARAMETER); |
||||
102 | |||||
103 | $container->setParameter('parthenon_payments_prices', []); |
||||
104 | $container->setParameter('parthenon_payments_plan_plans', []); |
||||
105 | $container->setParameter('parthenon_payments_success_redirect_route', 'app_index'); |
||||
106 | $container->setParameter('parthenon_payments_cancel_checkout_redirect_route', null); |
||||
107 | } |
||||
108 | |||||
109 | public function handleConfiguration(array $config, ContainerBuilder $container): void |
||||
110 | { |
||||
111 | if (!isset($config['payments']) || !isset($config['payments']['enabled']) || false == $config['payments']['enabled']) { |
||||
112 | return; |
||||
113 | } |
||||
114 | $container->setParameter('parthenon_payments_enabled', true); |
||||
115 | $container->setParameter('parthenon_payments_plan_plans', $config['payments']['subscriptions']['plan']); |
||||
116 | $container->registerForAutoconfiguration(CounterInterface::class)->addTag('parthenon.payments.plan.counter'); |
||||
117 | $container->registerForAutoconfiguration(ToActiveTransitionInterface::class)->addTag('parthenon.payments.transitions.to_active'); |
||||
118 | $container->registerForAutoconfiguration(ToCancelledTransitionInterface::class)->addTag('parthenon.payments.transitions.to_cancelled'); |
||||
119 | $container->registerForAutoconfiguration(ToOverdueTransitionInterface::class)->addTag('parthenon.payments.transitions.to_overdue'); |
||||
120 | |||||
121 | $this->configureSubscriberType($config, $container); |
||||
122 | |||||
123 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../../Resources/config')); |
||||
124 | |||||
125 | if ('stripe' === strtolower($config['payments']['provider'])) { |
||||
126 | $this->handlePaymentsStripe($config, $container); |
||||
127 | $loader->load('services/payments/stripe.xml'); |
||||
128 | } elseif ('transaction_cloud' === strtolower($config['payments']['provider'])) { |
||||
129 | $this->handlePaymentsTransactionCloud($config, $container); |
||||
130 | $loader->load('services/payments/transaction_cloud.xml'); |
||||
131 | } |
||||
132 | $config = $this->configurePrice($config, $container); |
||||
0 ignored issues
–
show
|
|||||
133 | |||||
134 | $loader->load('services/payments.xml'); |
||||
135 | } |
||||
136 | |||||
137 | private function handlePaymentsTransactionCloud(array $config, ContainerBuilder $containerBuilder) |
||||
138 | { |
||||
139 | if (empty($config['payments']['transaction_cloud'])) { |
||||
140 | throw new ParameterNotSetException('Then payment.provider is transaction_cloud then payments.transaction_cloud must be provided'); |
||||
141 | } |
||||
142 | |||||
143 | $transactionCloudConfig = $config['payments']['transaction_cloud']; |
||||
144 | |||||
145 | if (!isset($transactionCloudConfig['api_key'])) { |
||||
146 | throw new ParameterNotSetException('Then payment.provider is transaction_cloud then payments.transaction_cloud.api_key must be provided'); |
||||
147 | } |
||||
148 | if (!isset($transactionCloudConfig['api_key_password'])) { |
||||
149 | throw new ParameterNotSetException('Then payment.provider is transaction_cloud then payments.transaction_cloud.api_key must be provided'); |
||||
150 | } |
||||
151 | |||||
152 | $containerBuilder->setParameter('parthenon_payments_transaction_cloud_api_key', $transactionCloudConfig['api_key']); |
||||
153 | $containerBuilder->setParameter('parthenon_payments_transaction_cloud_api_key_password', $transactionCloudConfig['api_key_password']); |
||||
154 | $containerBuilder->setParameter('parthenon_payments_transaction_cloud_sandbox', $transactionCloudConfig['sandbox'] ?? false); |
||||
155 | $containerBuilder->setParameter('parthenon_payments_transaction_cloud_customer_id_parameter', $transactionCloudConfig['customer_id_parameter'] ?? Config::DEFAULT_CUSTOMER_ID_PARAMETER); |
||||
156 | $containerBuilder->setParameter('parthenon_payments_transaction_cloud_payment_id_parameter', $transactionCloudConfig['payment_id_parameter'] ?? Config::DEFAULT_PAYMENT_ID_PARAMETER); |
||||
157 | } |
||||
158 | |||||
159 | private function handlePaymentsStripe(array $config, ContainerBuilder $containerBuilder) |
||||
160 | { |
||||
161 | if (empty($config['payments']['stripe'])) { |
||||
162 | throw new ParameterNotSetException('When payment.provider is stripe then payments.stripe needs to be provided'); |
||||
163 | } |
||||
164 | |||||
165 | $stripeConfig = $config['payments']['stripe']; |
||||
166 | $stripeConfig = $this->configureStripePrivateApiKey($stripeConfig); |
||||
167 | $stripeConfig = $this->configureStripePublicKey($stripeConfig); |
||||
168 | $stripeConfig = $this->configureStripeSuccessUrl($stripeConfig); |
||||
169 | $stripeConfig = $this->configureCancelUrl($stripeConfig); |
||||
170 | |||||
171 | $containerBuilder->setParameter('parthenon_payments_stripe_private_api_key', $stripeConfig['private_api_key'] ?? ''); |
||||
172 | $containerBuilder->setParameter('parthenon_payments_stripe_public_api_key', $stripeConfig['public_api_key'] ?? ''); |
||||
173 | $containerBuilder->setParameter('parthenon_payments_stripe_success_url', $stripeConfig['success_url'] ?? ''); |
||||
174 | $containerBuilder->setParameter('parthenon_payments_stripe_cancel_url', $stripeConfig['cancel_url'] ?? ''); |
||||
175 | $containerBuilder->setParameter('parthenon_payments_stripe_return_url', $stripeConfig['return_url'] ?? ''); |
||||
176 | |||||
177 | $this->configureSuccessRedirectRoute($config, $containerBuilder); |
||||
178 | } |
||||
179 | |||||
180 | private function getPricesNode(): NodeDefinition |
||||
181 | { |
||||
182 | $treeBuilder = new TreeBuilder('price'); |
||||
183 | $node = $treeBuilder->getRootNode(); |
||||
184 | |||||
185 | /** @var ArrayNodeDefinition $planNode */ |
||||
186 | $planNode = $node |
||||
187 | ->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
![]() |
|||||
188 | ->useAttributeAsKey('name') |
||||
189 | ->prototype('array'); |
||||
190 | |||||
191 | $planNode |
||||
192 | ->fixXmlConfig('prices') |
||||
193 | ->requiresAtLeastOneElement() |
||||
194 | ->useAttributeAsKey('paymentSchedule') |
||||
195 | ->prototype('array') |
||||
196 | ->children() |
||||
197 | ->scalarNode('price')->end() |
||||
198 | ->scalarNode('price_id')->end() |
||||
199 | ->booleanNode('subscription')->end() |
||||
200 | ->scalarNode('trial_day_length')->end() |
||||
201 | ->end() |
||||
202 | ->end() |
||||
203 | ->end() |
||||
204 | ->end(); |
||||
205 | |||||
206 | return $node; |
||||
207 | } |
||||
208 | |||||
209 | private function configureSuccessRedirectRoute(array $config, ContainerBuilder $containerBuilder): void |
||||
210 | { |
||||
211 | if (isset($config['payments']['success_redirect_route'])) { |
||||
212 | $containerBuilder->setParameter('parthenon_payments_success_redirect_route', $config['payments']['success_redirect_route']); |
||||
213 | } |
||||
214 | if (isset($config['payments']['cancel_checkout_redirect_route']) && !empty($config['payments']['cancel_checkout_redirect_route'])) { |
||||
215 | $containerBuilder->setParameter('parthenon_payments_cancel_checkout_redirect_route', $config['payments']['cancel_checkout_redirect_route']); |
||||
216 | } else { |
||||
217 | $defaultRoute = $containerBuilder->getParameter('parthenon_payments_success_redirect_route'); |
||||
218 | $containerBuilder->setParameter('parthenon_payments_cancel_checkout_redirect_route', $defaultRoute); |
||||
219 | } |
||||
220 | } |
||||
221 | |||||
222 | private function configurePrice(array $config, ContainerBuilder $containerBuilder): array |
||||
223 | { |
||||
224 | if (isset($config['payments']['price'])) { |
||||
225 | $containerBuilder->setParameter('parthenon_payments_prices', $config['payments']['price']); |
||||
226 | } |
||||
227 | |||||
228 | return $config; |
||||
229 | } |
||||
230 | |||||
231 | /** |
||||
232 | * @throws ParameterNotSetException |
||||
233 | */ |
||||
234 | private function configureStripePrivateApiKey(mixed $stripeConfig): mixed |
||||
235 | { |
||||
236 | if (!isset($stripeConfig['private_api_key'])) { |
||||
237 | throw new ParameterNotSetException('When payment.provide is stripe then payments.stripe.private_api_key needs to be provided'); |
||||
238 | } |
||||
239 | |||||
240 | return $stripeConfig; |
||||
241 | } |
||||
242 | |||||
243 | /** |
||||
244 | * @throws ParameterNotSetException |
||||
245 | */ |
||||
246 | private function configureStripePublicKey(mixed $stripeConfig): mixed |
||||
247 | { |
||||
248 | if (!isset($stripeConfig['public_api_key'])) { |
||||
249 | throw new ParameterNotSetException('When payment.provide is stripe then payments.stripe.public_api_key needs to be provided'); |
||||
250 | } |
||||
251 | |||||
252 | return $stripeConfig; |
||||
253 | } |
||||
254 | |||||
255 | /** |
||||
256 | * @throws ParameterNotSetException |
||||
257 | */ |
||||
258 | private function configureStripeSuccessUrl(mixed $stripeConfig): mixed |
||||
259 | { |
||||
260 | if (!isset($stripeConfig['success_url'])) { |
||||
261 | throw new ParameterNotSetException('When payment.provide is stripe then payments.stripe.success_url needs to be provided'); |
||||
262 | } |
||||
263 | |||||
264 | return $stripeConfig; |
||||
265 | } |
||||
266 | |||||
267 | /** |
||||
268 | * @throws ParameterNotSetException |
||||
269 | */ |
||||
270 | private function configureCancelUrl(mixed $stripeConfig): mixed |
||||
271 | { |
||||
272 | if (!isset($stripeConfig['cancel_url'])) { |
||||
273 | throw new ParameterNotSetException('When payment.provide is stripe then payments.stripe.cancel_url needs to be provided'); |
||||
274 | } |
||||
275 | |||||
276 | return $stripeConfig; |
||||
277 | } |
||||
278 | |||||
279 | /** |
||||
280 | * @throws ParameterNotSetException |
||||
281 | */ |
||||
282 | private function configureSubscriberType(array $config, ContainerBuilder $containerBuilder): array |
||||
283 | { |
||||
284 | if (isset($config['payments']['subscriptions']['subscriber_type'])) { |
||||
285 | if (SubscriberInterface::TYPE_USER == $config['payments']['subscriptions']['subscriber_type']) { |
||||
286 | $containerBuilder->setAlias(SubscriberRepositoryInterface::class, UserRepositoryInterface::class); |
||||
287 | |||||
288 | // Remove TeamSubscriberSection so only UserSubscriberSection remains |
||||
289 | $containerBuilder->removeDefinition(TeamSubscriberSection::class); |
||||
290 | } elseif (SubscriberInterface::TYPE_TEAM == $config['payments']['subscriptions']['subscriber_type']) { |
||||
291 | $containerBuilder->setAlias(SubscriberRepositoryInterface::class, TeamRepositoryInterface::class); |
||||
292 | |||||
293 | // Remove TeamSubscriberSection so only UserSubscriberSection remains |
||||
294 | $containerBuilder->removeDefinition(UserSubscriberSection::class); |
||||
295 | } else { |
||||
296 | throw new ParameterNotSetException('Invalid setting for payment.subscriptions.subscriber_type'); |
||||
297 | } |
||||
298 | |||||
299 | $containerBuilder->setParameter('parthenon_payments_subscriber_type', $config['payments']['subscriptions']['subscriber_type']); |
||||
300 | } |
||||
301 | |||||
302 | return $config; |
||||
303 | } |
||||
304 | |||||
305 | private function getPlansNode(): NodeDefinition |
||||
306 | { |
||||
307 | $treeBuilder = new TreeBuilder('plan'); |
||||
308 | $node = $treeBuilder->getRootNode(); |
||||
309 | |||||
310 | /** @var ArrayNodeDefinition $planNode */ |
||||
311 | $planNode = $node |
||||
312 | ->requiresAtLeastOneElement() |
||||
313 | ->useAttributeAsKey('name') |
||||
314 | ->prototype('array'); |
||||
315 | |||||
316 | $planNode |
||||
317 | ->fixXmlConfig('limits') |
||||
318 | ->children() |
||||
319 | ->booleanNode('is_free')->defaultFalse()->end() |
||||
320 | ->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
![]() |
|||||
321 | ->scalarNode('user_count')->end() |
||||
322 | ->arrayNode('features') |
||||
323 | ->scalarPrototype()->end() |
||||
324 | ->end() |
||||
325 | ->arrayNode('limit') |
||||
326 | ->useAttributeAsKey('name') |
||||
327 | ->prototype('array') |
||||
328 | ->children() |
||||
329 | ->integerNode('limit')->end() |
||||
330 | ->scalarNode('description')->end() |
||||
331 | ->end() |
||||
332 | ->end() |
||||
333 | ->end() |
||||
334 | ->end(); |
||||
335 | |||||
336 | return $node; |
||||
337 | } |
||||
338 | } |
||||
339 |