Completed
Push — master ( e9c24c...b0d32f )
by Arthur
04:30
created

MemberSubscriptionCharges::billMembers()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 53
Code Lines 22

Duplication

Lines 3
Ratio 5.66 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 3
loc 53
rs 8.7155
cc 6
eloc 22
nc 12
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php namespace BB\Services;
2
3
use BB\Entities\User;
4
use BB\Events\MemberBalanceChanged;
5
use BB\Events\SubscriptionPayment;
6
use BB\Helpers\GoCardlessHelper;
7
use BB\Repo\PaymentRepository;
8
use BB\Repo\SubscriptionChargeRepository;
9
use BB\Repo\UserRepository;
10
use Carbon\Carbon;
11
12
class MemberSubscriptionCharges
13
{
14
15
    /**
16
     * @var UserRepository
17
     */
18
    private $userRepository;
19
    /**
20
     * @var SubscriptionChargeRepository
21
     */
22
    private $subscriptionChargeRepository;
23
    /**
24
     * @var GoCardlessHelper
25
     */
26
    private $goCardless;
27
    /**
28
     * @var PaymentRepository
29
     */
30
    private $paymentRepository;
31
32
    function __construct(UserRepository $userRepository, SubscriptionChargeRepository $subscriptionChargeRepository, GoCardlessHelper $goCardless, PaymentRepository $paymentRepository)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
33
    {
34
        $this->userRepository = $userRepository;
35
        $this->subscriptionChargeRepository = $subscriptionChargeRepository;
36
        $this->goCardless = $goCardless;
37
        $this->paymentRepository = $paymentRepository;
38
    }
39
40
    /**
41
     * Create the sub charge for each member, only do this for members with dates matching the supplied date
42
     *
43
     * @param Carbon $targetDate
44
     */
45
    public function createSubscriptionCharges($targetDate)
46
    {
47
        $users = $this->userRepository->getBillableActive();
48
        foreach ($users as $user) {
49
            if (($user->payment_day == $targetDate->day) && ( ! $this->subscriptionChargeRepository->chargeExists($user->id, $targetDate))) {
50
                $this->subscriptionChargeRepository->createCharge($user->id, $targetDate);
51
            }
52
        }
53
    }
54
55
    /**
56
     * Locate all charges that are for today or the past and mark them as due
57
     */
58
    public function makeChargesDue()
59
    {
60
        $subCharges = $this->subscriptionChargeRepository->getPending();
61
        foreach ($subCharges as $charge) {
62
            if ($charge->charge_date->isToday() || $charge->charge_date->isPast()) {
63
                $this->subscriptionChargeRepository->setDue($charge->id);
64
            }
65
        }
66
    }
67
68
    /**
69
     * Bill members based on the sub charges that are due
70
     */
71
    public function billMembers()
72
    {
73
        $subCharges = $this->subscriptionChargeRepository->getDue();
74
75
        //Check each of the due charges, if they have previous attempted payments ignore them
76
        // we don't want to retry failed payments as for DD's this will generate bank charges
77
        $subCharges->reject(function ($charge) {
78
            return $this->paymentRepository->getPaymentsByReference($charge->id)->count() > 0;
79
        });
80
81
        //Filter the list into two gocardless and balance subscriptions
82
        $goCardlessUsers = $subCharges->filter(function ($charge) {
83
            return $charge->user->payment_method == 'gocardless-variable';
84
        });
85
86
        $balanceUsers = $subCharges->filter(function ($charge) {
87
            return $charge->user->payment_method == 'balance';
88
        });
89
90
91
        //Charge the balance users
92
        foreach ($balanceUsers as $charge) {
93
            if (($charge->user->monthly_subscription * 100) > $charge->user->cash_balance) {
94
                //user doesn't have enough money
95
96
                //If they have a secondary payment method of gocardless try that
97
                if ($charge->user->secondary_payment_method == 'gocardless-variable') {
98
99
                    //Add the charge to the gocardless list for processing
100
                    $goCardlessUsers->push($charge);
101
102
                    event(new SubscriptionPayment\InsufficientFundsTryingDirectDebit($charge->user->id, $charge->id));
103
                } else {
104
                    event(new SubscriptionPayment\FailedInsufficientFunds($charge->user->id, $charge->id));
105
                }
106
                continue;
107
            }
108
109
            $this->paymentRepository->recordSubscriptionPayment($charge->user->id, 'balance', '', $charge->user->monthly_subscription, 'paid', 0, $charge->id);
110
111
            event(new MemberBalanceChanged($charge->user->id));
112
        }
113
114
115
        //Charge the gocardless users
116
        foreach ($goCardlessUsers as $charge) {
117
            $bill = $this->goCardless->newBill($charge->user->subscription_id, $charge->user->monthly_subscription, $this->goCardless->getNameFromReason('subscription'));
118 View Code Duplication
            if ($bill) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
                $this->paymentRepository->recordSubscriptionPayment($charge->user->id, 'gocardless-variable', $bill->id, $bill->amount, $bill->status, $bill->gocardless_fees, $charge->id);
120
            }
121
        };
122
123
    }
124
125
126
    /**
127
     * Get a users latest sub payment
128
     * @param $userId
129
     * @return bool
130
     */
131
    public function lastUserChargeExpires($userId)
132
    {
133
        $charge = User::where('user_id', $userId)->where('status', ['processing', 'paid'])->orderBy('charge_date', 'DESC')->first();
134
        if ($charge) {
135
            return $charge->charge_date->addMonth();
136
        }
137
        return false;
138
    }
139
}