Passed
Push — main ( e4206e...255a41 )
by Iain
04:28
created

SubscriptionManager   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 78
dl 0
loc 130
rs 10
c 5
b 0
f 0
wmc 13

4 Methods

Rating   Name   Duplication   Size   Complexity  
A startSubscriptionWithDto() 0 12 2
A startSubscriptionWithEntities() 0 45 4
A __construct() 0 12 1
B startSubscription() 0 53 6
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\Provider\ProviderInterface;
18
use Parthenon\Billing\Dto\StartSubscriptionDto;
19
use Parthenon\Billing\Entity\CustomerInterface;
20
use Parthenon\Billing\Entity\PaymentDetails;
21
use Parthenon\Billing\Entity\Price;
22
use Parthenon\Billing\Entity\Subscription;
23
use Parthenon\Billing\Entity\SubscriptionPlan;
24
use Parthenon\Billing\Exception\SubscriptionCreationException;
25
use Parthenon\Billing\Obol\BillingDetailsFactoryInterface;
26
use Parthenon\Billing\Obol\PaymentFactoryInterface;
27
use Parthenon\Billing\Obol\SubscriptionFactoryInterface;
28
use Parthenon\Billing\Plan\Plan;
29
use Parthenon\Billing\Plan\PlanManagerInterface;
30
use Parthenon\Billing\Plan\PlanPrice;
31
use Parthenon\Billing\Repository\PaymentDetailsRepositoryInterface;
32
use Parthenon\Billing\Repository\PaymentRepositoryInterface;
33
use Parthenon\Billing\Repository\PriceRepositoryInterface;
34
use Parthenon\Billing\Repository\SubscriptionPlanRepositoryInterface;
35
use Parthenon\Billing\Repository\SubscriptionRepositoryInterface;
36
37
final class SubscriptionManager implements SubscriptionManagerInterface
38
{
39
    public function __construct(
40
        private PaymentDetailsRepositoryInterface $paymentDetailsRepository,
41
        private ProviderInterface $provider,
42
        private BillingDetailsFactoryInterface $billingDetailsFactory,
43
        private PaymentFactoryInterface $paymentFactory,
44
        private SubscriptionFactoryInterface $subscriptionFactory,
45
        private PaymentRepositoryInterface $paymentRepository,
46
        private PlanManagerInterface $planManager,
47
        private SubscriptionPlanRepositoryInterface $subscriptionPlanRepository,
48
        private PriceRepositoryInterface $priceRepository,
49
        private SubscriptionRepositoryInterface $subscriptionRepository,
50
    ) {
51
    }
52
53
    public function startSubscriptionWithEntities(CustomerInterface $customer, SubscriptionPlan $subscriptionPlan, Price $price, PaymentDetails $paymentDetails, int $seatNumbers): Subscription
54
    {
55
        $billingDetails = $this->billingDetailsFactory->createFromCustomerAndPaymentDetails($customer, $paymentDetails);
56
        $obolSubscription = $this->subscriptionFactory->createSubscriptionWithPrice($billingDetails, $price, $seatNumbers);
57
        $obolSubscription->setStoredPaymentReference($paymentDetails->getStoredPaymentReference());
58
59
        if ($this->subscriptionRepository->hasActiveSubscription($customer)) {
60
            $subscription = $this->subscriptionRepository->getOneActiveSubscriptionForCustomer($customer);
61
62
            if ($subscription->getCurrency() != $planPrice->getCurrency()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $planPrice seems to be never defined.
Loading history...
63
                throw new SubscriptionCreationException("Can't add a child subscription for a different currency");
64
            }
65
66
            $obolSubscription->setParentReference($subscription->getMainExternalReference());
67
        }
68
69
        $subscriptionCreationResponse = $this->provider->payments()->startSubscription($obolSubscription);
70
        if ($subscriptionCreationResponse->hasCustomerCreation()) {
71
            $customer->setPaymentProviderDetailsUrl($subscriptionCreationResponse->getCustomerCreation()->getDetailsUrl());
72
            $customer->setExternalCustomerReference($subscriptionCreationResponse->getCustomerCreation()->getReference());
73
        }
74
        $payment = $this->paymentFactory->fromSubscriptionCreation($subscriptionCreationResponse);
75
        $this->paymentRepository->save($payment);
76
77
        $subscription = new Subscription();
78
        $subscription->setPlanName($subscriptionPlan->getName());
79
        $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

79
        $subscription->setPaymentSchedule(/** @scrutinizer ignore-type */ $price->getSchedule());
Loading history...
80
        $subscription->setActive(true);
81
        $subscription->setMoneyAmount($price->getAsMoney());
82
        $subscription->setStatus(\Parthenon\Billing\Entity\EmbeddedSubscription::STATUS_ACTIVE);
83
        $subscription->setMainExternalReference($subscriptionCreationResponse->getSubscriptionId());
84
        $subscription->setChildExternalReference($subscriptionCreationResponse->getLineId());
85
        $subscription->setSeats($seatNumbers);
86
        $subscription->setCreatedAt(new \DateTime());
87
        $subscription->setUpdatedAt(new \DateTime());
88
        $subscription->setValidUntil($subscriptionCreationResponse->getBilledUntil());
89
        $subscription->setCustomer($customer);
90
        $subscription->setMainExternalReferenceDetailsUrl($subscriptionCreationResponse->getDetailsUrl());
0 ignored issues
show
Bug introduced by
It seems like $subscriptionCreationResponse->getDetailsUrl() can also be of type null; however, parameter $mainExternalReferenceDetailsUrl of Parthenon\Billing\Entity...alReferenceDetailsUrl() 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

90
        $subscription->setMainExternalReferenceDetailsUrl(/** @scrutinizer ignore-type */ $subscriptionCreationResponse->getDetailsUrl());
Loading history...
91
        $subscription->setSubscriptionPlan($subscriptionPlan);
92
        $subscription->setPrice($price);
93
94
        $this->subscriptionRepository->save($subscription);
95
        $this->subscriptionRepository->updateValidUntilForAllActiveSubscriptions($customer, $subscription->getMainExternalReference(), $subscriptionCreationResponse->getBilledUntil());
96
97
        return $subscription;
98
    }
99
100
    public function startSubscription(CustomerInterface $customer, Plan $plan, PlanPrice $planPrice, PaymentDetails $paymentDetails, int $seatNumbers): Subscription
101
    {
102
        $billingDetails = $this->billingDetailsFactory->createFromCustomerAndPaymentDetails($customer, $paymentDetails);
103
        $obolSubscription = $this->subscriptionFactory->createSubscription($billingDetails, $planPrice, $seatNumbers);
104
        $obolSubscription->setStoredPaymentReference($paymentDetails->getStoredPaymentReference());
105
106
        if ($this->subscriptionRepository->hasActiveSubscription($customer)) {
107
            $subscription = $this->subscriptionRepository->getOneActiveSubscriptionForCustomer($customer);
108
109
            if ($subscription->getCurrency() != $planPrice->getCurrency()) {
110
                throw new SubscriptionCreationException("Can't add a child subscription for a different currency");
111
            }
112
113
            $obolSubscription->setParentReference($subscription->getMainExternalReference());
114
        }
115
116
        $subscriptionCreationResponse = $this->provider->payments()->startSubscription($obolSubscription);
117
        if ($subscriptionCreationResponse->hasCustomerCreation()) {
118
            $customer->setPaymentProviderDetailsUrl($subscriptionCreationResponse->getCustomerCreation()->getDetailsUrl());
119
            $customer->setExternalCustomerReference($subscriptionCreationResponse->getCustomerCreation()->getReference());
120
        }
121
        $payment = $this->paymentFactory->fromSubscriptionCreation($subscriptionCreationResponse);
122
        $this->paymentRepository->save($payment);
123
124
        $subscription = new Subscription();
125
        $subscription->setPlanName($plan->getName());
126
        $subscription->setPaymentSchedule($planPrice->getSchedule());
127
        $subscription->setActive(true);
128
        $subscription->setMoneyAmount($planPrice->getPriceAsMoney());
129
        $subscription->setStatus(\Parthenon\Billing\Entity\EmbeddedSubscription::STATUS_ACTIVE);
130
        $subscription->setMainExternalReference($subscriptionCreationResponse->getSubscriptionId());
131
        $subscription->setChildExternalReference($subscriptionCreationResponse->getLineId());
132
        $subscription->setSeats($seatNumbers);
133
        $subscription->setCreatedAt(new \DateTime());
134
        $subscription->setUpdatedAt(new \DateTime());
135
        $subscription->setValidUntil($subscriptionCreationResponse->getBilledUntil());
136
        $subscription->setCustomer($customer);
137
        $subscription->setMainExternalReferenceDetailsUrl($subscriptionCreationResponse->getDetailsUrl());
0 ignored issues
show
Bug introduced by
It seems like $subscriptionCreationResponse->getDetailsUrl() can also be of type null; however, parameter $mainExternalReferenceDetailsUrl of Parthenon\Billing\Entity...alReferenceDetailsUrl() 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

137
        $subscription->setMainExternalReferenceDetailsUrl(/** @scrutinizer ignore-type */ $subscriptionCreationResponse->getDetailsUrl());
Loading history...
138
139
        if ($plan->hasEntityId()) {
140
            $subscriptionPlan = $this->subscriptionPlanRepository->findById($plan->getEntityId());
141
            $subscription->setSubscriptionPlan($subscriptionPlan);
142
        }
143
144
        if ($planPrice->hasEntityId()) {
145
            $price = $this->priceRepository->findById($planPrice->getEntityId());
146
            $subscription->setPrice($price);
147
        }
148
149
        $this->subscriptionRepository->save($subscription);
150
        $this->subscriptionRepository->updateValidUntilForAllActiveSubscriptions($customer, $subscription->getMainExternalReference(), $subscriptionCreationResponse->getBilledUntil());
151
152
        return $subscription;
153
    }
154
155
    public function startSubscriptionWithDto(CustomerInterface $customer, StartSubscriptionDto $startSubscriptionDto): Subscription
156
    {
157
        if (!$startSubscriptionDto->getPaymentDetailsId()) {
158
            $paymentDetails = $this->paymentDetailsRepository->getDefaultPaymentDetailsForCustomer($customer);
159
        } else {
160
            $paymentDetails = $this->paymentDetailsRepository->findById($startSubscriptionDto->getPaymentDetailsId());
161
        }
162
163
        $plan = $this->planManager->getPlanByName($startSubscriptionDto->getPlanName());
164
        $planPrice = $plan->getPriceForPaymentSchedule($startSubscriptionDto->getSchedule(), $startSubscriptionDto->getCurrency());
165
166
        return $this->startSubscription($customer, $plan, $planPrice, $paymentDetails, $startSubscriptionDto->getSeatNumbers());
167
    }
168
}
169