PaymentsController::handleChargeFailed()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 9
ccs 0
cts 8
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Canvas\Api\Controllers;
6
7
use Canvas\Http\Exception\NotFoundException;
8
use Phalcon\Cashier\Traits\StripeWebhookHandlersTrait;
0 ignored issues
show
Bug introduced by
The type Phalcon\Cashier\Traits\StripeWebhookHandlersTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Phalcon\Http\Response;
0 ignored issues
show
Bug introduced by
The type Phalcon\Http\Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Canvas\Models\Users;
11
use Canvas\Models\Subscription;
12
use Canvas\Models\CompaniesSettings;
13
use Canvas\Template;
14
use Phalcon\Di;
15
16
/**
17
 * Class PaymentsController.
18
 *
19
 * Class to handle payment webhook from our cashier library
20
 *
21
 * @package Canvas\Api\Controllers
22
 * @property Log $log
23
 * @property App $app
24
 *
25
 */
26
class PaymentsController extends BaseController
27
{
28
    /**
29
     * Stripe Webhook Handlers.
30
     */
31
    use StripeWebhookHandlersTrait;
32
33
    /**
34
     * Handle stripe webhoook calls.
35
     *
36
     * @return Response
37
     */
38
    public function handleWebhook(): Response
39
    {
40
        //we cant processs if we dont find the stripe header
41
        if (!$this->request->hasHeader('Stripe-Signature')) {
42
            throw new NotFoundException('Route not found for this call');
43
        }
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
52
        if (method_exists($this, $method)) {
53
            return $this->{$method}($request, $method);
54
        } else {
55
            return $this->response(['Missing Method to Handled']);
56
        }
57
    }
58
59
    /**
60
     * Handle customer subscription updated.
61
     *
62
     * @param  array $payload
63
     * @return Response
64
     */
65
    protected function handleCustomerSubscriptionUpdated(array $payload, string $method): Response
66
    {
67
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
68
        if ($user) {
69
            //We need to send a mail to the user
70
            $this->sendWebhookResponseEmail($user, $payload, $method);
71
        }
72
        return $this->response(['Webhook Handled']);
73
    }
74
75
    /**
76
     * Handle customer subscription cancellation.
77
     *
78
     * @param  array $payload
79
     * @return Response
80
     */
81
    protected function handleCustomerSubscriptionDeleted(array $payload, string $method): Response
82
    {
83
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
84
        if ($user) {
85
            //Update current subscription's paid column to false and store date of payment
86
            $this->updateSubscriptionPaymentStatus($user, $payload);
87
            $this->sendWebhookResponseEmail($user, $payload, $method);
88
        }
89
        return $this->response(['Webhook Handled']);
90
    }
91
92
    /**
93
     * Handle customer subscription free trial ending.
94
     *
95
     * @param  array $payload
96
     * @return Response
97
     */
98
    protected function handleCustomerSubscriptionTrialwillend(array $payload, string $method): Response
99
    {
100
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
101
        if ($user) {
102
            //We need to send a mail to the user
103
            $this->sendWebhookResponseEmail($user, $payload, $method);
104
            $this->log->info("Email was sent to: {$user->email}\n");
105
        }
106
        return $this->response(['Webhook Handled']);
107
    }
108
109
    /**
110
     * Handle sucessfull payment.
111
     *
112
     * @param array $payload
113
     * @return Response
114
     */
115
    protected function handleChargeSucceeded(array $payload, string $method): Response
116
    {
117
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
118
        if ($user) {
119
            //Update current subscription's paid column to true and store date of payment
120
            $this->updateSubscriptionPaymentStatus($user, $payload);
121
            $this->sendWebhookResponseEmail($user, $payload, $method);
122
        }
123
        return $this->response(['Webhook Handled']);
124
    }
125
126
    /**
127
     * Handle bad payment.
128
     *
129
     * @param array $payload
130
     * @return Response
131
     */
132
    protected function handleChargeFailed(array $payload, string $method) : Response
133
    {
134
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
135
        if ($user) {
136
            //We need to send a mail to the user
137
            $this->updateSubscriptionPaymentStatus($user, $payload);
138
            $this->sendWebhookResponseEmail($user, $payload, $method);
139
        }
140
        return $this->response(['Webhook Handled']);
141
    }
142
143
    /**
144
     * Handle pending payments.
145
     *
146
     * @param array $payload
147
     * @return Response
148
     */
149
    protected function handleChargePending(array $payload, string $method) : Response
150
    {
151
        $user = Users::findFirstByStripeId($payload['data']['object']['customer']);
152
        if ($user) {
153
            //We need to send a mail to the user
154
            $this->sendWebhookResponseEmail($user, $payload, $method);
155
        }
156
        return $this->response(['Webhook Handled']);
157
    }
158
159
    /**
160
     * Send webhook related emails to user.
161
     * @param Users $user
162
     * @param array $payload
163
     * @param string $method
164
     * @return void
165
     */
166
    protected function sendWebhookResponseEmail(Users $user, array $payload, string $method): void
167
    {
168
        $templateName = '';
169
        $title = null;
170
171
        switch ($method) {
172
            case 'handleCustomerSubscriptionTrialwillend':
173
                $templateName = 'users-trial-end';
174
                $title = 'Free Trial Ending';
175
                break;
176
            case 'handleCustomerSubscriptionUpdated':
177
                $templateName = 'users-subscription-updated';
178
                break;
179
180
            case 'handleCustomerSubscriptionDeleted':
181
                $templateName = 'users-subscription-canceled';
182
                $title = 'Subscription Cancelled';
183
                break;
184
185
            case 'handleChargeSucceeded':
186
                $templateName = 'users-charge-success';
187
                $title = 'Invoice';
188
189
                break;
190
191
            case 'handleChargeFailed':
192
                $templateName = 'users-charge-failed';
193
                $title = 'Payment Failed';
194
195
                break;
196
197
            case 'handleChargePending':
198
                $templateName = 'users-charge-pending';
199
                break;
200
201
            default:
202
                break;
203
        }
204
205
        //Search for actual template by templateName
206
        if ($templateName) {
207
            $emailTemplate = Template::generate($templateName, $user->toArray());
208
209
            Di::getDefault()->getMail()
210
            ->to($user->email)
211
            ->subject($this->app->name . ' - ' . $title)
212
            ->content($emailTemplate)
213
            ->sendNow();
214
        }
215
    }
216
217
    /**
218
     * Updates subscription payment status depending on charge event.
219
     * @param $user
220
     * @param $payload
221
     * @return void
222
     */
223
    public function updateSubscriptionPaymentStatus(Users $user, array $payload): void
224
    {
225
        $chargeDate = date('Y-m-d H:i:s', $payload['data']['object']['created']);
226
227
        //Fetch current user subscription
228
        $subscription = Subscription::getByDefaultCompany($user);
229
230
        if (is_object($subscription)) {
231
            $subscription->paid = $payload['data']['object']['paid'] ? 1 : 0;
232
            $subscription->charge_date = $chargeDate;
233
234
            $subscription->validateByGracePeriod();
235
236
            if ($subscription->paid) {
237
                $subscription->is_freetrial = 0;
238
                $subscription->trial_ends_days = 0;
239
            }
240
241
            //Paid status is false if plan has been canceled
242
            if ($payload['data']['object']['status'] == 'canceled') {
243
                $subscription->paid = 0;
244
                $subscription->charge_date = null;
245
            }
246
247
            if ($subscription->update()) {
248
                //Update companies setting
249
                $paidSetting = CompaniesSettings::findFirst([
250
                    'conditions' => "companies_id = ?0 and name = 'paid' and is_deleted = 0",
251
                    'bind' => [$user->getDefaultCompany()->getId()]
252
                ]);
253
254
                $paidSetting->value = (string) $subscription->paid;
255
                $paidSetting->update();
256
            }
257
            $this->log->info("User with id: {$user->id} charged status was {$payload['data']['object']['paid']} \n");
258
        } else {
259
            $this->log->error("Subscription not found\n");
260
        }
261
    }
262
}
263