1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Gewaer\Api\Controllers; |
6
|
|
|
|
7
|
|
|
use Phalcon\Cashier\Traits\StripeWebhookHandlersTrait; |
8
|
|
|
use Phalcon\Http\Response; |
9
|
|
|
use Gewaer\Models\Users; |
10
|
|
|
use Gewaer\Models\EmailTemplates; |
11
|
|
|
use Gewaer\Models\Subscription; |
12
|
|
|
use Gewaer\Models\CompaniesSettings; |
13
|
|
|
use Phalcon\Di; |
14
|
|
|
use Exception; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Class PaymentsController |
18
|
|
|
* |
19
|
|
|
* Class to handle payment webhook from our cashier library |
20
|
|
|
* |
21
|
|
|
* @package Gewaer\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
|
4 |
|
public function handleWebhook(): Response |
39
|
|
|
{ |
40
|
|
|
//we cant processs if we dont find the stripe header |
41
|
4 |
|
if (!$this->request->hasHeader('Stripe-Signature')) { |
42
|
|
|
throw new Exception('Route not found for this call'); |
43
|
|
|
} |
44
|
4 |
|
$request = $this->request->getPost(); |
45
|
4 |
|
if (empty($request)) { |
46
|
|
|
$request = $this->request->getJsonRawBody(true); |
47
|
|
|
} |
48
|
4 |
|
$type = str_replace('.', '', ucwords(str_replace('_', '', $request['type']), '.')); |
49
|
4 |
|
$method = 'handle' . $type; |
50
|
4 |
|
$payloadContent = json_encode($request); |
51
|
4 |
|
$this->log->info("Webhook Handler Method: {$method} \n"); |
52
|
4 |
|
$this->log->info("Payload: {$payloadContent} \n"); |
53
|
4 |
|
if (method_exists($this, $method)) { |
54
|
4 |
|
return $this->{$method}($request, $method); |
55
|
|
|
} else { |
56
|
|
|
return $this->response(['Missing Method to Handled']); |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Handle customer subscription updated. |
62
|
|
|
* |
63
|
|
|
* @param array $payload |
64
|
|
|
* @return Response |
65
|
|
|
*/ |
66
|
|
|
protected function handleCustomerSubscriptionUpdated(array $payload, string $method): Response |
67
|
|
|
{ |
68
|
|
|
$user = Users::findFirstByStripeId($payload['data']['object']['customer']); |
69
|
|
|
if ($user) { |
70
|
|
|
//We need to send a mail to the user |
71
|
|
|
$this->sendWebhookResponseEmail($user, $payload, $method); |
72
|
|
|
} |
73
|
|
|
return $this->response(['Webhook Handled']); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Handle customer subscription free trial ending. |
78
|
|
|
* |
79
|
|
|
* @param array $payload |
80
|
|
|
* @return Response |
81
|
|
|
*/ |
82
|
1 |
|
protected function handleCustomerSubscriptionTrialwillend(array $payload, string $method): Response |
83
|
|
|
{ |
84
|
1 |
|
$user = Users::findFirstByStripeId($payload['data']['object']['customer']); |
85
|
1 |
|
if ($user) { |
86
|
|
|
//We need to send a mail to the user |
87
|
|
|
$this->sendWebhookResponseEmail($user, $payload, $method); |
88
|
|
|
} |
89
|
1 |
|
return $this->response(['Webhook Handled']); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Handle sucessfull payment |
94
|
|
|
* |
95
|
|
|
* @param array $payload |
96
|
|
|
* @return Response |
97
|
|
|
*/ |
98
|
1 |
|
protected function handleChargeSucceeded(array $payload, string $method): Response |
99
|
|
|
{ |
100
|
1 |
|
$user = Users::findFirstByStripeId($payload['data']['object']['customer']); |
101
|
1 |
|
if ($user) { |
102
|
|
|
//Update current subscription's paid column to true and store date of payment |
103
|
|
|
$this->response($this->updateSubscriptionPaymentStatus($user, $payload)); |
|
|
|
|
104
|
|
|
$this->sendWebhookResponseEmail($user, $payload, $method); |
105
|
|
|
} |
106
|
1 |
|
return $this->response(['Webhook Handled']); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Handle bad payment |
111
|
|
|
* |
112
|
|
|
* @param array $payload |
113
|
|
|
* @return Response |
114
|
|
|
*/ |
115
|
1 |
|
protected function handleChargeFailed(array $payload, string $method) : Response |
116
|
|
|
{ |
117
|
1 |
|
$user = Users::findFirstByStripeId($payload['data']['object']['customer']); |
118
|
1 |
|
if ($user) { |
119
|
|
|
//We need to send a mail to the user |
120
|
|
|
$this->updateSubscriptionPaymentStatus($user, $payload); |
121
|
|
|
$this->sendWebhookResponseEmail($user, $payload, $method); |
122
|
|
|
} |
123
|
1 |
|
return $this->response(['Webhook Handled']); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Handle pending payments |
128
|
|
|
* |
129
|
|
|
* @param array $payload |
130
|
|
|
* @return Response |
131
|
|
|
*/ |
132
|
1 |
|
protected function handleChargePending(array $payload, string $method) : Response |
133
|
|
|
{ |
134
|
1 |
|
$user = Users::findFirstByStripeId($payload['data']['object']['customer']); |
135
|
1 |
|
if ($user) { |
136
|
|
|
//We need to send a mail to the user |
137
|
|
|
$this->sendWebhookResponseEmail($user, $payload, $method); |
138
|
|
|
} |
139
|
1 |
|
return $this->response(['Webhook Handled']); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Send webhook related emails to user |
144
|
|
|
* @param Users $user |
145
|
|
|
* @param array $payload |
146
|
|
|
* @param string $method |
147
|
|
|
* @return void |
148
|
|
|
*/ |
149
|
|
|
protected function sendWebhookResponseEmail(Users $user, array $payload, string $method): void |
150
|
|
|
{ |
151
|
|
|
switch ($method) { |
152
|
|
|
case 'handleCustomerSubscriptionTrialwillend': |
153
|
|
|
$templateName = 'users-trial-end'; |
154
|
|
|
break; |
155
|
|
|
case 'handleCustomerSubscriptionUpdated': |
156
|
|
|
$templateName = 'users-subscription-updated'; |
157
|
|
|
break; |
158
|
|
|
|
159
|
|
|
case 'handleChargeSucceeded': |
160
|
|
|
$templateName = 'users-charge-success'; |
161
|
|
|
break; |
162
|
|
|
|
163
|
|
|
case 'handleChargeFailed': |
164
|
|
|
$templateName = 'users-charge-failed'; |
165
|
|
|
break; |
166
|
|
|
|
167
|
|
|
case 'handleChargePending': |
168
|
|
|
$templateName = 'users-charge-pending'; |
169
|
|
|
break; |
170
|
|
|
|
171
|
|
|
default: |
172
|
|
|
break; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
//Search for actual template by templateName |
176
|
|
|
$emailTemplate = EmailTemplates::getByName($templateName); |
|
|
|
|
177
|
|
|
|
178
|
|
|
Di::getDefault()->getMail() |
179
|
|
|
->to($user->email) |
180
|
|
|
->subject('Canvas Payments and Subscriptions') |
181
|
|
|
->content($emailTemplate->template) |
182
|
|
|
->sendNow(); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Updates subscription payment status depending on charge event |
187
|
|
|
* @param $user |
188
|
|
|
* @param $payload |
189
|
|
|
* @return void |
190
|
|
|
*/ |
191
|
|
|
public function updateSubscriptionPaymentStatus(Users $user, array $payload): void |
192
|
|
|
{ |
193
|
|
|
$chargeDate = date('Y-m-d H:i:s', $payload['data']['object']['created']); |
194
|
|
|
|
195
|
|
|
//Fetch current user subscription |
196
|
|
|
$subscription = Subscription::getByDefaultCompany($user); |
197
|
|
|
|
198
|
|
|
if (is_object($subscription)) { |
199
|
|
|
$subscription->paid = $payload['data']['object']['paid'] ? 1 : 0; |
200
|
|
|
$subscription->charge_date = $chargeDate; |
201
|
|
|
|
202
|
|
|
if ($subscription->paid) { |
203
|
|
|
$subscription->is_freetrial = 0; |
204
|
|
|
$subscription->trial_ends_days = 0; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
if ($subscription->update()) { |
208
|
|
|
//Update companies setting |
209
|
|
|
$paidSetting = CompaniesSettings::findFirst([ |
210
|
|
|
'conditions' => "companies_id = ?0 and name = 'paid' and is_deleted = 0", |
211
|
|
|
'bind' => [$user->default_company] |
212
|
|
|
]); |
213
|
|
|
|
214
|
|
|
$paidSetting->value = (string)$subscription->paid; |
215
|
|
|
$paidSetting->update(); |
216
|
|
|
} |
217
|
|
|
$this->log->info("User with id: {$user->id} charged status was {$payload['data']['object']['paid']} \n"); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$this->log->error("Subscription not found\n"); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.