Passed
Push — main ( 350a1e...a10163 )
by Iain
05:08
created

SubscriptionManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
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 11
dl 0
loc 13
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright Iain Cambridge 2020-2023.
7
 *
8
 * Use of this software is governed by the Business Source License included in the LICENSE file and at https://getparthenon.com/docs/next/license.
9
 *
10
 * Change Date: TBD ( 3 years after 2.2.0 release )
11
 *
12
 * On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
13
 */
14
15
namespace Parthenon\Billing\Subscription;
16
17
use Obol\Model\CancelSubscription;
18
use Obol\Model\Enum\RefundType;
19
use Obol\Provider\ProviderInterface;
20
use Parthenon\Billing\Dto\StartSubscriptionDto;
21
use Parthenon\Billing\Entity\BillingAdminInterface;
22
use Parthenon\Billing\Entity\CustomerInterface;
23
use Parthenon\Billing\Entity\EmbeddedSubscription;
24
use Parthenon\Billing\Entity\PaymentDetails;
25
use Parthenon\Billing\Entity\Price;
26
use Parthenon\Billing\Entity\Refund;
27
use Parthenon\Billing\Entity\Subscription;
28
use Parthenon\Billing\Entity\SubscriptionPlan;
29
use Parthenon\Billing\Exception\SubscriptionCreationException;
30
use Parthenon\Billing\Obol\BillingDetailsFactoryInterface;
31
use Parthenon\Billing\Obol\PaymentFactoryInterface;
32
use Parthenon\Billing\Obol\SubscriptionFactoryInterface;
33
use Parthenon\Billing\Plan\Plan;
34
use Parthenon\Billing\Plan\PlanManagerInterface;
35
use Parthenon\Billing\Plan\PlanPrice;
36
use Parthenon\Billing\Repository\PaymentDetailsRepositoryInterface;
37
use Parthenon\Billing\Repository\PaymentRepositoryInterface;
38
use Parthenon\Billing\Repository\PriceRepositoryInterface;
39
use Parthenon\Billing\Repository\RefundRepositoryInterface;
40
use Parthenon\Billing\Repository\SubscriptionPlanRepositoryInterface;
41
use Parthenon\Billing\Repository\SubscriptionRepositoryInterface;
42
43
final class SubscriptionManager implements SubscriptionManagerInterface
44
{
45
    public function __construct(
46
        private PaymentDetailsRepositoryInterface $paymentDetailsRepository,
47
        private ProviderInterface $provider,
48
        private BillingDetailsFactoryInterface $billingDetailsFactory,
49
        private PaymentFactoryInterface $paymentFactory,
50
        private SubscriptionFactoryInterface $subscriptionFactory,
51
        private PaymentRepositoryInterface $paymentRepository,
52
        private PlanManagerInterface $planManager,
53
        private SubscriptionPlanRepositoryInterface $subscriptionPlanRepository,
54
        private PriceRepositoryInterface $priceRepository,
55
        private SubscriptionRepositoryInterface $subscriptionRepository,
56
        private RefundRepositoryInterface $refundRepository
57
    ) {
58
    }
59
60
    public function startSubscriptionWithEntities(CustomerInterface $customer, SubscriptionPlan $subscriptionPlan, Price $price, PaymentDetails $paymentDetails, int $seatNumbers): Subscription
61
    {
62
        $billingDetails = $this->billingDetailsFactory->createFromCustomerAndPaymentDetails($customer, $paymentDetails);
63
        $obolSubscription = $this->subscriptionFactory->createSubscriptionWithPrice($billingDetails, $price, $seatNumbers);
64
        $obolSubscription->setStoredPaymentReference($paymentDetails->getStoredPaymentReference());
65
66
        if ($this->subscriptionRepository->hasActiveSubscription($customer)) {
67
            $subscription = $this->subscriptionRepository->getOneActiveSubscriptionForCustomer($customer);
68
69
            if ($subscription->getCurrency() != $price->getCurrency()) {
70
                throw new SubscriptionCreationException("Can't add a child subscription for a different currency");
71
            }
72
73
            $obolSubscription->setParentReference($subscription->getMainExternalReference());
74
        }
75
76
        $subscriptionCreationResponse = $this->provider->payments()->startSubscription($obolSubscription);
77
        if ($subscriptionCreationResponse->hasCustomerCreation()) {
78
            $customer->setPaymentProviderDetailsUrl($subscriptionCreationResponse->getCustomerCreation()->getDetailsUrl());
79
            $customer->setExternalCustomerReference($subscriptionCreationResponse->getCustomerCreation()->getReference());
80
        }
81
        $payment = $this->paymentFactory->fromSubscriptionCreation($subscriptionCreationResponse, $customer);
82
        $this->paymentRepository->save($payment);
83
84
        $subscription = new Subscription();
85
        $subscription->setPlanName($subscriptionPlan->getName());
86
        $subscription->setPaymentSchedule($price->getSchedule());
0 ignored issues
show
Bug introduced by
It seems like $price->getSchedule() can also be of type null; however, parameter $paymentSchedule of Parthenon\Billing\Entity...n::setPaymentSchedule() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

86
        $subscription->setPaymentSchedule(/** @scrutinizer ignore-type */ $price->getSchedule());
Loading history...
87
        $subscription->setActive(true);
88
        $subscription->setMoneyAmount($price->getAsMoney());
89
        $subscription->setStatus(\Parthenon\Billing\Entity\EmbeddedSubscription::STATUS_ACTIVE);
90
        $subscription->setMainExternalReference($subscriptionCreationResponse->getSubscriptionId());
91
        $subscription->setChildExternalReference($subscriptionCreationResponse->getLineId());
92
        $subscription->setSeats($seatNumbers);
93
        $subscription->setCreatedAt(new \DateTime());
94
        $subscription->setUpdatedAt(new \DateTime());
95
        $subscription->setValidUntil($subscriptionCreationResponse->getBilledUntil());
96
        $subscription->setCustomer($customer);
97
        $subscription->setMainExternalReferenceDetailsUrl($subscriptionCreationResponse->getDetailsUrl());
98
        $subscription->setSubscriptionPlan($subscriptionPlan);
99
        $subscription->setPrice($price);
100
101
        $this->subscriptionRepository->save($subscription);
102
        $this->subscriptionRepository->updateValidUntilForAllActiveSubscriptions($customer, $subscription->getMainExternalReference(), $subscriptionCreationResponse->getBilledUntil());
103
104
        return $subscription;
105
    }
106
107
    public function startSubscription(CustomerInterface $customer, Plan $plan, PlanPrice $planPrice, PaymentDetails $paymentDetails, int $seatNumbers): Subscription
108
    {
109
        $billingDetails = $this->billingDetailsFactory->createFromCustomerAndPaymentDetails($customer, $paymentDetails);
110
        $obolSubscription = $this->subscriptionFactory->createSubscription($billingDetails, $planPrice, $seatNumbers);
111
        $obolSubscription->setStoredPaymentReference($paymentDetails->getStoredPaymentReference());
112
113
        if ($this->subscriptionRepository->hasActiveSubscription($customer)) {
114
            $subscription = $this->subscriptionRepository->getOneActiveSubscriptionForCustomer($customer);
115
116
            if ($subscription->getCurrency() != $planPrice->getCurrency()) {
117
                throw new SubscriptionCreationException("Can't add a child subscription for a different currency");
118
            }
119
120
            $obolSubscription->setParentReference($subscription->getMainExternalReference());
121
        }
122
123
        $subscriptionCreationResponse = $this->provider->payments()->startSubscription($obolSubscription);
124
        if ($subscriptionCreationResponse->hasCustomerCreation()) {
125
            $customer->setPaymentProviderDetailsUrl($subscriptionCreationResponse->getCustomerCreation()->getDetailsUrl());
126
            $customer->setExternalCustomerReference($subscriptionCreationResponse->getCustomerCreation()->getReference());
127
        }
128
        $payment = $this->paymentFactory->fromSubscriptionCreation($subscriptionCreationResponse, $customer);
129
        $this->paymentRepository->save($payment);
130
131
        $subscription = new Subscription();
132
        $subscription->setPlanName($plan->getName());
133
        $subscription->setPaymentSchedule($planPrice->getSchedule());
134
        $subscription->setActive(true);
135
        $subscription->setMoneyAmount($planPrice->getPriceAsMoney());
136
        $subscription->setStatus(\Parthenon\Billing\Entity\EmbeddedSubscription::STATUS_ACTIVE);
137
        $subscription->setMainExternalReference($subscriptionCreationResponse->getSubscriptionId());
138
        $subscription->setChildExternalReference($subscriptionCreationResponse->getLineId());
139
        $subscription->setSeats($seatNumbers);
140
        $subscription->setCreatedAt(new \DateTime());
141
        $subscription->setUpdatedAt(new \DateTime());
142
        $subscription->setValidUntil($subscriptionCreationResponse->getBilledUntil());
143
        $subscription->setCustomer($customer);
144
        $subscription->setMainExternalReferenceDetailsUrl($subscriptionCreationResponse->getDetailsUrl());
145
        $subscription->setPaymentExternalReference($subscriptionCreationResponse->getPaymentDetails()->getStoredPaymentReference());
146
147
        if ($plan->hasEntityId()) {
148
            $subscriptionPlan = $this->subscriptionPlanRepository->findById($plan->getEntityId());
149
            $subscription->setSubscriptionPlan($subscriptionPlan);
150
        }
151
152
        if ($planPrice->hasEntityId()) {
153
            $price = $this->priceRepository->findById($planPrice->getEntityId());
154
            $subscription->setPrice($price);
155
        }
156
157
        $this->subscriptionRepository->save($subscription);
158
        $this->subscriptionRepository->updateValidUntilForAllActiveSubscriptions($customer, $subscription->getMainExternalReference(), $subscriptionCreationResponse->getBilledUntil());
159
160
        return $subscription;
161
    }
162
163
    public function startSubscriptionWithDto(CustomerInterface $customer, StartSubscriptionDto $startSubscriptionDto): Subscription
164
    {
165
        if (!$startSubscriptionDto->getPaymentDetailsId()) {
166
            $paymentDetails = $this->paymentDetailsRepository->getDefaultPaymentDetailsForCustomer($customer);
167
        } else {
168
            $paymentDetails = $this->paymentDetailsRepository->findById($startSubscriptionDto->getPaymentDetailsId());
169
        }
170
171
        $plan = $this->planManager->getPlanByName($startSubscriptionDto->getPlanName());
172
        $planPrice = $plan->getPriceForPaymentSchedule($startSubscriptionDto->getSchedule(), $startSubscriptionDto->getCurrency());
173
174
        return $this->startSubscription($customer, $plan, $planPrice, $paymentDetails, $startSubscriptionDto->getSeatNumbers());
175
    }
176
177
    public function cancelSubscriptionAtEndOfCurrentPeriod(Subscription $subscription): Subscription
178
    {
179
        $obolSubscription = $this->subscriptionFactory->createSubscriptionFromEntity($subscription);
180
181
        $cancelRequest = new CancelSubscription();
182
        $cancelRequest->setSubscription($obolSubscription);
183
        $cancelRequest->setInstantCancel(false);
184
        $cancelRequest->setRefundType(RefundType::NONE);
185
186
        $cancellation = $this->provider->payments()->stopSubscription($cancelRequest);
0 ignored issues
show
Unused Code introduced by
The assignment to $cancellation is dead and can be removed.
Loading history...
187
188
        $subscription->setStatus(EmbeddedSubscription::STATUS_CANCELLED);
189
190
        return $subscription;
191
    }
192
193
    public function cancelSubscriptionWithFullRefund(Subscription $subscription, BillingAdminInterface $billingAdmin): Subscription
194
    {
195
        $obolSubscription = $this->subscriptionFactory->createSubscriptionFromEntity($subscription);
196
197
        $cancelRequest = new CancelSubscription();
198
        $cancelRequest->setSubscription($obolSubscription);
199
        $cancelRequest->setInstantCancel(true);
200
        $cancelRequest->setRefundType(RefundType::FULL);
201
202
        $cancellation = $this->provider->payments()->stopSubscription($cancelRequest);
203
204
        if ($cancellation->hasRefund()) {
205
            $refund = new Refund();
206
            $refund->setAmount($cancellation->getRefund()->getAmount());
207
            $refund->setCurrency($cancellation->getRefund()->getCurrency());
208
            $refund->setExternalReference($cancellation->getRefund()->getId());
209
            $refund->setStatus('refunded');
210
            $refund->setBillingAdmin($billingAdmin);
211
            $refund->setCustomer($subscription->getCustomer());
212
213
            $this->refundRepository->save($refund);
214
        }
215
216
        $subscription->setStatus(EmbeddedSubscription::STATUS_CANCELLED);
217
218
        return $subscription;
219
    }
220
}
221