Failed Conditions
Pull Request — master (#250)
by Rafael
03:12
created

handleCustomerSubscriptionUpdated()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 9
ccs 0
cts 6
cp 0
crap 6
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Canvas\Api\Controllers;
6
7
use Phalcon\Cashier\Traits\StripeWebhookHandlersTrait;
8
use Phalcon\Http\Response;
9
use Canvas\Models\Users;
10
use Canvas\Models\EmailTemplates;
11
use Canvas\Models\Subscription;
12
use Canvas\Models\CompaniesSettings;
13
use Phalcon\Di;
14
use Exception;
15
use Carbon\Carbon;
16
17
/**
18
 * Class PaymentsController.
19
 *
20
 * Class to handle payment webhook from our cashier library
21
 *
22
 * @package Canvas\Api\Controllers
23
 * @property Log $log
24
 * @property App $app
25
 *
26
 */
27
class PaymentsController extends BaseController
28
{
29
    /**
30
     * Stripe Webhook Handlers.
31
     */
32
    use StripeWebhookHandlersTrait;
33
34
    /**
35
     * Handle stripe webhoook calls.
36
     *
37
     * @return Response
38
     */
39
    public function handleWebhook(): Response
40
    {
41
        //we cant processs if we dont find the stripe header
42
        if (!$this->request->hasHeader('Stripe-Signature')) {
43
            throw new Exception('Route not found for this call');
44
        }
45
        $request = $this->request->getPostData();
46
        $type = str_replace('.', '', ucwords(str_replace('_', '', $request['type']), '.'));
47
        $method = 'handle' . $type;
48
        $payloadContent = json_encode($request);
49
        $this->log->info("Webhook Handler Method: {$method} \n");
50
        $this->log->info("Payload: {$payloadContent} \n");
51
        if (method_exists($this, $method)) {
52
            return $this->{$method}($request, $method);
53
        } else {
54
            return $this->response(['Missing Method to Handled']);
55
        }
56
    }
57
58
    /**
59
     * Handle customer subscription updated.
60
     *
61
     * @param  array $payload
62
     * @return Response
63
     */
64
    protected function handleCustomerSubscriptionUpdated(array $payload, string $method): Response
65
    {
66
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
67
        if ($user) {
68
            //We need to send a mail to the user
69
            $this->sendWebhookResponseEmail($user, $payload, $method);
70
        }
71
        return $this->response(['Webhook Handled']);
72
    }
73
74
    /**
75
     * Handle customer subscription cancellation.
76
     *
77
     * @param  array $payload
78
     * @return Response
79
     */
80
    protected function handleCustomerSubscriptionDeleted(array $payload, string $method): Response
81
    {
82
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
83
        if ($user) {
84
            //Update current subscription's paid column to false and store date of payment
85
            $this->updateSubscriptionPaymentStatus($user, $payload);
86
            $this->sendWebhookResponseEmail($user, $payload, $method);
87
        }
88
        return $this->response(['Webhook Handled']);
89
    }
90
91
    /**
92
     * Handle customer subscription free trial ending.
93
     *
94
     * @param  array $payload
95
     * @return Response
96
     */
97
    protected function handleCustomerSubscriptionTrialwillend(array $payload, string $method): Response
98
    {
99
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
100
        if ($user) {
101
            //We need to send a mail to the user
102
            $this->sendWebhookResponseEmail($user, $payload, $method);
103
            $this->log->info("Email was sent to: {$user->email}\n");
104
        }
105
        return $this->response(['Webhook Handled']);
106
    }
107
108
    /**
109
     * Handle sucessfull payment.
110
     *
111
     * @param array $payload
112
     * @return Response
113
     */
114
    protected function handleChargeSucceeded(array $payload, string $method): Response
115
    {
116
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
117
        if ($user) {
118
            //Update current subscription's paid column to true and store date of payment
119
            $this->updateSubscriptionPaymentStatus($user, $payload);
120
            $this->sendWebhookResponseEmail($user, $payload, $method);
121
        }
122
        return $this->response(['Webhook Handled']);
123
    }
124
125
    /**
126
     * Handle bad payment.
127
     *
128
     * @param array $payload
129
     * @return Response
130
     */
131
    protected function handleChargeFailed(array $payload, string $method) : Response
132
    {
133
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
134
        if ($user) {
135
            //We need to send a mail to the user
136
            $this->updateSubscriptionPaymentStatus($user, $payload);
137
            $this->sendWebhookResponseEmail($user, $payload, $method);
138
        }
139
        return $this->response(['Webhook Handled']);
140
    }
141
142
    /**
143
     * Handle pending payments.
144
     *
145
     * @param array $payload
146
     * @return Response
147
     */
148
    protected function handleChargePending(array $payload, string $method) : Response
149
    {
150
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
151
        if ($user) {
152
            //We need to send a mail to the user
153
            $this->sendWebhookResponseEmail($user, $payload, $method);
154
        }
155
        return $this->response(['Webhook Handled']);
156
    }
157
158
    /**
159
     * Send webhook related emails to user.
160
     * @param Users $user
161
     * @param array $payload
162
     * @param string $method
163
     * @return void
164
     */
165
    protected function sendWebhookResponseEmail(Users $user, array $payload, string $method): void
166
    {
167
        switch ($method) {
168
            case 'handleCustomerSubscriptionTrialwillend':
169
                $templateName = 'users-trial-end';
170
                break;
171
            case 'handleCustomerSubscriptionUpdated':
172
                $templateName = 'users-subscription-updated';
173
                break;
174
175
            case 'handleCustomerSubscriptionDeleted':
176
                $templateName = 'users-subscription-canceled';
177
                break;
178
179
            case 'handleChargeSucceeded':
180
                $templateName = 'users-charge-success';
181
                break;
182
183
            case 'handleChargeFailed':
184
                $templateName = 'users-charge-failed';
185
                break;
186
187
            case 'handleChargePending':
188
                $templateName = 'users-charge-pending';
189
                break;
190
191
            default:
192
                break;
193
        }
194
195
        //Search for actual template by templateName
196
        $emailTemplate = EmailTemplates::getByName($templateName);
0 ignored issues
show
Bug introduced by
The variable $templateName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
197
198
        Di::getDefault()->getMail()
199
            ->to($user->email)
200
            ->subject('Canvas Payments and Subscriptions')
201
            ->content($emailTemplate->template)
202
            ->sendNow();
203
    }
204
205
    /**
206
     * Updates subscription payment status depending on charge event.
207
     * @param $user
208
     * @param $payload
209
     * @return void
210
     */
211
    public function updateSubscriptionPaymentStatus(Users $user, array $payload): void
212
    {
213
        $chargeDate = date('Y-m-d H:i:s', $payload['data']['object']['created']);
214
215
        //Fetch current user subscription
216
        $subscription = Subscription::getByDefaultCompany($user);
217
218
        if (is_object($subscription)) {
219
            $subscription->paid = $payload['data']['object']['paid'] ? 1 : 0;
220
            $subscription->charge_date = $chargeDate;
221
222
            $subscription = $this->validateByGracePeriod($subscription);
223
224
            if ($subscription->paid) {
225
                $subscription->is_freetrial = 0;
226
                $subscription->trial_ends_days = 0;
227
            }
228
229
            //Paid status is false if plan has been canceled
230
            if ($payload['data']['object']['status'] == 'canceled') {
231
                $subscription->paid = 0;
232
                $subscription->charge_date = null;
233
            }
234
235
            if ($subscription->update()) {
236
                //Update companies setting
237
                $paidSetting = CompaniesSettings::findFirst([
238
                    'conditions' => "companies_id = ?0 and name = 'paid' and is_deleted = 0",
239
                    'bind' => [$user->getDefaultCompany()->getId()]
240
                ]);
241
242
                $paidSetting->value = (string)$subscription->paid;
243
                $paidSetting->update();
244
            }
245
            $this->log->info("User with id: {$user->id} charged status was {$payload['data']['object']['paid']} \n");
246
        } else {
247
            $this->log->error("Subscription not found\n");
248
        }
249
    }
250
251
    /**
252
     * Validate subscription status by grace period date.
253
     *
254
     * @param Subscription $subscription
255
     * @return Subscription
256
     */
257
    private function validateByGracePeriod(Subscription $subscription): Subscription
258
    {
259
        if (isset($subscription->grace_period_ends)) {
260
            if (($subscription->charge_date == $subscription->grace_period_ends) && !$subscription->paid) {
261
                $subscription->is_active = 0;
262
                $subscription->grace_period_ends = Carbon::now()->addDays(Subscription::DEFAULT_GRACE_PERIOD_DAYS)->toDateTimeString();
263
            }
264
        } else {
265
            $subscription->grace_period_ends = Carbon::now()->addDays(Subscription::DEFAULT_GRACE_PERIOD_DAYS)->toDateTimeString();
266
        }
267
268
        return $subscription;
269
    }
270
}
271