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

MemberSubscriptionCharges::makeChargesDue()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 9.2
cc 4
eloc 5
nc 3
nop 0
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
}