Passed
Push — main ( b21561...f38fd7 )
by Iain
09:13 queued 04:17
created

SubscriptionManager::changeSubscriptionPlan()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2020-2024 Iain Cambridge
7
 *
8
 *     This program is free software: you can redistribute it and/or modify
9
 *     it under the terms of the GNU General Public License as published by
10
 *     the Free Software Foundation, either version 3 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 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\Billing\BillaBear;
23
24
use BillaBear\ApiException;
0 ignored issues
show
Bug introduced by
The type BillaBear\ApiException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use BillaBear\Model\SubscriptionStartBody;
0 ignored issues
show
Bug introduced by
The type BillaBear\Model\SubscriptionStartBody was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use Parthenon\Billing\Dto\StartSubscriptionDto;
27
use Parthenon\Billing\Entity\CustomerInterface;
28
use Parthenon\Billing\Entity\PaymentCard;
29
use Parthenon\Billing\Entity\Price;
30
use Parthenon\Billing\Entity\Subscription;
31
use Parthenon\Billing\Entity\SubscriptionPlan;
32
use Parthenon\Billing\Enum\BillingChangeTiming;
33
use Parthenon\Billing\Event\SubscriptionCreated;
34
use Parthenon\Billing\Exception\NoPaymentDetailsException;
35
use Parthenon\Billing\Exception\PaymentFailureException;
36
use Parthenon\Billing\Plan\Plan;
37
use Parthenon\Billing\Plan\PlanManagerInterface;
38
use Parthenon\Billing\Plan\PlanPrice;
39
use Parthenon\Billing\Subscription\SubscriptionManagerInterface;
40
use Parthenon\Common\Exception\GeneralException;
41
use Psr\Http\Message\ResponseInterface;
42
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
43
44
class SubscriptionManager implements SubscriptionManagerInterface
45
{
46
    public function __construct(
47
        private SdkFactory $sdkFactory,
48
        private PlanManagerInterface $planManager,
49
        private EventDispatcherInterface $dispatcher,
50
    ) {
51
    }
52
53
    public function startSubscription(
54
        CustomerInterface $customer,
55
        SubscriptionPlan|Plan $plan,
56
        Price|PlanPrice $planPrice,
57
        ?PaymentCard $paymentDetails = null,
58
        int $seatNumbers = 1,
59
        ?bool $hasTrial = null,
60
        ?int $trialLengthDays = 0,
61
    ): Subscription {
62
        if (!$plan instanceof Plan) {
63
            throw new GeneralException('Invalid type of plan given');
64
        }
65
66
        $customerId = $customer->getExternalCustomerReference();
67
        $payload = [
68
            'subscription_plan' => $plan->getEntityId(),
69
            'price' => $planPrice->getEntityId(),
0 ignored issues
show
Bug introduced by
The method getEntityId() does not exist on Parthenon\Billing\Entity\Price. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

69
            'price' => $planPrice->/** @scrutinizer ignore-call */ getEntityId(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
70
            'seat_numbers' => $seatNumbers,
71
        ];
72
73
        if ($paymentDetails) {
74
            $payload['payment_details'] = $paymentDetails->getId();
75
        }
76
77
        $subscriptionStart = new SubscriptionStartBody($payload);
78
79
        try {
80
            $response = $this->sdkFactory->createSubscriptionsApi()->customerStartSubscription($subscriptionStart, $customerId);
81
        } catch (ApiException $apiException) {
82
            if ($apiException->getResponseObject() instanceof ResponseInterface) {
83
                match ($apiException->getResponseObject()->getStatusCode()) {
84
                    402 => new PaymentFailureException('Payment failed', previous: $apiException),
85
                    406 => new NoPaymentDetailsException('No payment details', previous: $apiException),
86
                    default => new GeneralException(previous: $apiException),
87
                };
88
            }
89
            throw new GeneralException(previous: $apiException);
90
        } catch (\Throwable $exception) {
91
            new GeneralException($exception->getMessage(), previous: $exception);
92
        }
93
        $subscription = new Subscription();
94
        $subscription->setCustomer($customer);
95
        $subscription->setId($response->getId());
96
        $subscription->setValidUntil(new \DateTime($response->getValidUntil()));
97
        $subscription->setMainExternalReference($response->getMainExternalReference());
98
        $subscription->setChildExternalReference($response->getChildExternalReference());
99
100
        $this->dispatcher->dispatch(new SubscriptionCreated($subscription), SubscriptionCreated::NAME);
101
102
        return $subscription;
103
    }
104
105
    public function startSubscriptionWithDto(CustomerInterface $customer, StartSubscriptionDto $startSubscriptionDto): Subscription
106
    {
107
        $plan = $this->planManager->getPlanByName($startSubscriptionDto->getPlanName());
108
        $planPrice = $plan->getPriceForPaymentSchedule($startSubscriptionDto->getSchedule(), $startSubscriptionDto->getCurrency());
109
110
        return $this->startSubscription($customer, $plan, $planPrice, null, $startSubscriptionDto->getSeatNumbers());
111
    }
112
113
    public function cancelSubscriptionAtEndOfCurrentPeriod(Subscription $subscription): Subscription
114
    {
115
        // TODO: Implement cancelSubscriptionAtEndOfCurrentPeriod() method.
116
    }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Parthenon\Billing\Entity\Subscription. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
117
118
    public function cancelSubscriptionInstantly(Subscription $subscription): Subscription
119
    {
120
        // TODO: Implement cancelSubscriptionInstantly() method.
121
    }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Parthenon\Billing\Entity\Subscription. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
122
123
    public function cancelSubscriptionOnDate(Subscription $subscription, \DateTimeInterface $dateTime): Subscription
124
    {
125
        // TODO: Implement cancelSubscriptionOnDate() method.
126
    }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Parthenon\Billing\Entity\Subscription. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
127
128
    public function changeSubscriptionPrice(Subscription $subscription, Price $price, BillingChangeTiming $billingChangeTiming): void
129
    {
130
        // TODO: Implement changeSubscriptionPrice() method.
131
    }
132
133
    public function changeSubscriptionPlan(Subscription $subscription, SubscriptionPlan $plan, Price $price, BillingChangeTiming $billingChangeTiming): void
134
    {
135
        // TODO: Implement changeSubscriptionPlan() method.
136
    }
137
}
138