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')) { |
|||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
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); |
||||||
0 ignored issues
–
show
The method
getJsonRawBody() does not exist on Phalcon\Http\RequestInterface . It seems like you code against a sub-type of said class. However, the method does not exist in Gewaer\Contracts\RequestJwtInterface . Are you sure you never get one of those?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
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->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 |