PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.2
Booking for Appointments and Events Calendar – Amelia v2.2
2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Application / Services / Notification / SMSNotificationService.php
ameliabooking / src / Application / Services / Notification Last commit date
AbstractNotificationService.php 3 months ago AbstractWhatsAppNotificationService.php 4 months ago ApplicationNotificationService.php 4 months ago AppointmentNotificationService.php 4 months ago BasicWhatsAppNotificationService.php 6 months ago EmailNotificationService.php 6 months ago NotificationHelperService.php 6 months ago SMSAPIService.php 4 months ago SMSNotificationService.php 3 months ago
SMSNotificationService.php
511 lines
1 <?php
2
3 /**
4 * @copyright © Melograno Ventures. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace AmeliaBooking\Application\Services\Notification;
9
10 use AmeliaBooking\Application\Services\Helper\HelperService;
11 use AmeliaBooking\Application\Services\Placeholder\PlaceholderService;
12 use AmeliaBooking\Domain\Collection\Collection;
13 use AmeliaBooking\Domain\Common\Exceptions\InvalidArgumentException;
14 use AmeliaBooking\Domain\Entity\Entities;
15 use AmeliaBooking\Domain\Entity\Notification\Notification;
16 use AmeliaBooking\Domain\Entity\Notification\NotificationLog;
17 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
18 use AmeliaBooking\Domain\Services\Settings\SettingsService;
19 use AmeliaBooking\Domain\ValueObjects\String\NotificationStatus;
20 use AmeliaBooking\Infrastructure\Common\Exceptions\NotFoundException;
21 use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;
22 use AmeliaBooking\Infrastructure\Repository\Notification\NotificationLogRepository;
23 use AmeliaBooking\Infrastructure\Repository\Notification\NotificationSMSHistoryRepository;
24 use Exception;
25 use Slim\Exception\ContainerException;
26 use Slim\Exception\ContainerValueNotFoundException;
27
28 /**
29 * Class SMSNotificationService
30 *
31 * @package AmeliaBooking\Application\Services\Notification
32 */
33 class SMSNotificationService extends AbstractNotificationService
34 {
35 /** @var bool */
36 private $sentSmsLowEmail = false;
37
38 /** @noinspection MoreThanThreeArgumentsInspection */
39 /**
40 * @param array $appointmentArray
41 * @param Notification $notification
42 * @param bool $logNotification
43 * @param int|null $bookingKey
44 * @param null $allBookings
45 * @param array $invoice
46 * @throws InvalidArgumentException
47 * @throws NotFoundException
48 * @throws QueryExecutionException
49 * @throws Exception
50 */
51 public function sendNotification(
52 $appointmentArray,
53 $notification,
54 $logNotification,
55 $bookingKey = null,
56 $allBookings = null,
57 $invoice = []
58 ) {
59 /** @var \AmeliaBooking\Application\Services\Settings\SettingsService $settingsAS */
60 $settingsAS = $this->container->get('application.settings.service');
61 /** @var PlaceholderService $placeholderService */
62 $placeholderService = $this->container->get("application.placeholder.{$appointmentArray['type']}.service");
63 /** @var HelperService $helperService */
64 $helperService = $this->container->get('application.helper.service');
65
66 $data = $placeholderService->getPlaceholdersData(
67 $appointmentArray,
68 $bookingKey,
69 'sms',
70 null,
71 $allBookings,
72 false,
73 $notification->getName()->getValue()
74 );
75
76 $isCustomerPackage = isset($appointmentArray['isForCustomer']) && $appointmentArray['isForCustomer'];
77
78 if ($appointmentArray['type'] === Entities::PACKAGE) {
79 if (!empty($appointmentArray['recurring'][0]['booking']['info']) && $isCustomerPackage) {
80 $info = $appointmentArray['recurring'][0]['booking']['info'];
81
82 $infoArray = json_decode($info, true);
83
84 if (!empty($infoArray['phone'])) {
85 $appointmentArray['customer']['phone'] = $infoArray['phone'];
86 }
87 } else {
88 $info = $isCustomerPackage ? json_encode($appointmentArray['customer']) : null;
89 }
90 } else {
91 $info = $bookingKey !== null ? $appointmentArray['bookings'][$bookingKey]['info'] : null;
92 }
93
94 $notificationContent = $helperService->getBookingTranslation(
95 $helperService->getLocaleFromBooking($info),
96 $notification->getTranslations() ? $notification->getTranslations()->getValue() : null,
97 'content'
98 ) ?: $notification->getContent()->getValue();
99
100 $text = $placeholderService->applyPlaceholders($notificationContent, $data);
101
102 $users = $this->getUsersInfo(
103 $notification->getSendTo()->getValue(),
104 $appointmentArray,
105 $bookingKey,
106 $data
107 );
108
109 foreach ($users as $user) {
110 if (!empty($user['phone_country'])) {
111 $allowedCountries = apply_filters('amelia_whitelists_sms_countries', []);
112 if (!empty($allowedCountries) && !in_array(strtoupper($user['phone_country']), $allowedCountries)) {
113 continue;
114 }
115 }
116 if ($user['phone']) {
117 if (!$isCustomerPackage && !empty($data['providersAppointments'][$user['id']])) {
118 $text = $placeholderService->applyPlaceholders(
119 $notificationContent,
120 array_merge(
121 $data,
122 ['cart_appointments_details' => $data['providersAppointments'][$user['id']]]['cart_appointments_details']
123 )
124 );
125 }
126
127 $reParsedData = !$isCustomerPackage ?
128 $placeholderService->reParseContentForProvider(
129 $appointmentArray,
130 '',
131 $text,
132 $user['id']
133 ) : [
134 'body' => $text,
135 ];
136
137 try {
138 $this->saveAndSend(
139 $notification,
140 $user,
141 $appointmentArray,
142 $reParsedData,
143 $logNotification,
144 $user['phone']
145 );
146
147 $additionalPhoneNumbers = $settingsAS->getBccSms();
148
149 foreach ($additionalPhoneNumbers as $phoneNumber) {
150 $this->saveAndSend(
151 $notification,
152 null,
153 $appointmentArray,
154 $reParsedData,
155 $logNotification,
156 $phoneNumber
157 );
158 }
159 } catch (QueryExecutionException $e) {
160 } catch (ContainerException $e) {
161 }
162 }
163 }
164 }
165
166
167 private function sendSmsBalanceLowEmail()
168 {
169 /** @var SettingsService $settingsService */
170 $settingsService = $this->container->get('domain.settings.service');
171 /** @var SMSAPIService $smsApiService */
172 $smsApiService = $this->container->get('application.smsApi.service');
173
174 $smsLowEmail = $settingsService->getSetting('notifications', 'smsBalanceEmail');
175 if ($smsLowEmail && $smsLowEmail['enabled']) {
176 try {
177 $userResponse = $smsApiService->getUserInfo();
178 if (
179 !empty($userResponse) &&
180 $userResponse->status === 'OK' &&
181 !empty($userResponse->user) &&
182 $userResponse->user->balance <= $smsLowEmail['minimum']
183 ) {
184 /** @var EmailNotificationService $notificationService */
185 $notificationService = $this->container->get('application.emailNotification.service');
186 $notificationService->sendSmsBalanceLowEmail($smsLowEmail['email']);
187
188 $this->sentSmsLowEmail = true;
189 }
190 } catch (\Exception $e) {
191 }
192 }
193 }
194
195 /**
196 * @throws ContainerValueNotFoundException
197 * @throws QueryExecutionException
198 * @throws InvalidArgumentException
199 */
200 public function sendUndeliveredNotifications()
201 {
202 /** @var NotificationLogRepository $notificationLogRepository */
203 $notificationLogRepository = $this->container->get('domain.notificationLog.repository');
204
205 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
206 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
207
208 /** @var Collection $undeliveredNotifications */
209 $undeliveredNotifications = $notificationLogRepository->getUndeliveredNotifications('sms');
210
211 /** @var SMSAPIService $smsApiService */
212 $smsApiService = $this->container->get('application.smsApi.service');
213
214 /** @var NotificationLog $undeliveredNotification */
215 foreach ($undeliveredNotifications->getItems() as $undeliveredNotification) {
216 try {
217 $data = json_decode($undeliveredNotification->getData()->getValue(), true);
218
219 if ($history = $notificationsSMSHistoryRepo->getItemById($data['historyId'])) {
220 $apiResponse = $smsApiService->send(
221 $history['phone'],
222 $data['body'],
223 AMELIA_ACTION_URL . '/notifications/sms/history/' . $data['historyId']
224 );
225
226 if ($apiResponse->status === 'OK') {
227 $this->updateSmsHistory($data['historyId'], $apiResponse);
228
229 $notificationLogRepository->updateFieldById(
230 $undeliveredNotification->getId()->getValue(),
231 1,
232 'sent'
233 );
234 }
235 }
236 } catch (Exception $e) {
237 }
238 }
239 }
240
241 /**
242 * @throws QueryExecutionException
243 * @throws InvalidArgumentException
244 * @throws Exception
245 */
246 public function sendBirthdayGreetingNotifications()
247 {
248 /** @var Collection $notifications */
249 $notifications = $this->getByNameAndType('customer_birthday_greeting', $this->type);
250
251 /** @var Notification $notification */
252 $notification = $notifications->getItem($notifications->keys()[0]);
253
254 // Check if notification is enabled and it is time to send notification
255 if (
256 $notification->getStatus()->getValue() === NotificationStatus::ENABLED &&
257 $notification->getTime() &&
258 DateTimeService::getNowDateTimeObject() >=
259 DateTimeService::getCustomDateTimeObject($notification->getTime()->getValue())
260 ) {
261 /** @var NotificationLogRepository $notificationLogRepo */
262 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
263 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
264 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
265 /** @var SMSAPIService $smsApiService */
266 $smsApiService = $this->container->get('application.smsApi.service');
267 /** @var PlaceholderService $placeholderService */
268 $placeholderService = $this->container->get('application.placeholder.appointment.service');
269 /** @var SettingsService $settingsService */
270 $settingsService = $this->container->get('domain.settings.service');
271
272 $customers = $notificationLogRepo->getBirthdayCustomers($this->type);
273
274 $companyData = $placeholderService->getCompanyData();
275
276 $customersArray = $customers->toArray();
277
278 foreach ($customersArray as $bookingKey => $customerArray) {
279 $data = [
280 'customer_email' => $customerArray['email'],
281 'customer_first_name' => $customerArray['firstName'],
282 'customer_last_name' => $customerArray['lastName'],
283 'customer_full_name' => $customerArray['firstName'] . ' ' . $customerArray['lastName'],
284 'customer_phone' => $customerArray['phone'],
285 'customer_id' => $customerArray['id'],
286 ];
287
288 /** @noinspection AdditionOperationOnArraysInspection */
289 $data += $companyData;
290
291 $text = $placeholderService->applyPlaceholders(
292 $notification->getContent()->getValue(),
293 $data
294 );
295
296 if ($data['customer_phone']) {
297 try {
298 $historyId = $notificationsSMSHistoryRepo->add(
299 [
300 'notificationId' => $notification->getId()->getValue(),
301 'userId' => $data['customer_id'],
302 'text' => $text,
303 'phone' => $data['customer_phone'],
304 'alphaSenderId' => $settingsService->getSetting('notifications', 'smsAlphaSenderId'),
305 ]
306 );
307
308 $smsData = apply_filters(
309 'amelia_manipulate_sms_data',
310 [
311 'text' => $text,
312 'to' => $data['customer_phone']
313 ]
314 );
315
316 $apiResponse = null;
317
318 if (empty($smsData['skipSending'])) {
319 $apiResponse = $smsApiService->send(
320 $smsData['to'],
321 $smsData['text'],
322 AMELIA_ACTION_URL . '/notifications/sms/history/' . $historyId
323 );
324 }
325
326 if ($apiResponse && $apiResponse->status === 'OK') {
327 $this->updateSmsHistory($historyId, $apiResponse);
328
329 $logNotificationId = $notificationLogRepo->add(
330 $notification,
331 $data['customer_id']
332 );
333
334 if ($logNotificationId) {
335 $notificationLogRepo->updateFieldById($logNotificationId, 1, 'sent');
336 }
337 }
338 } catch (QueryExecutionException $e) {
339 }
340 }
341 }
342 }
343 }
344
345 /** @noinspection MoreThanThreeArgumentsInspection */
346 /**
347 * @param Notification $notification
348 * @param array $user
349 * @param array $appointmentArray
350 * @param array $reParsedData
351 * @param bool $logNotification
352 * @param string $sendTo
353 *
354 * @return void
355 *
356 * @throws QueryExecutionException
357 * @throws InvalidArgumentException
358 */
359 private function saveAndSend($notification, $user, $appointmentArray, $reParsedData, $logNotification, $sendTo)
360 {
361
362 /** @var NotificationLogRepository $notificationsLogRepository */
363 $notificationsLogRepository = $this->container->get('domain.notificationLog.repository');
364 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
365 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
366 /** @var SettingsService $settingsService */
367 $settingsService = $this->container->get('domain.settings.service');
368 /** @var SMSAPIService $smsApiService */
369 $smsApiService = $this->container->get('application.smsApi.service');
370
371 if ($user && !empty($appointmentArray['isRetry'])) {
372 /** @var Collection $sentNotifications */
373 $sentNotifications = $notificationsLogRepository->getSentNotificationsByUserAndEntity(
374 $user['id'],
375 'sms',
376 $appointmentArray['type'],
377 $appointmentArray['type'] === Entities::PACKAGE ?
378 $appointmentArray['packageCustomerId'] : $appointmentArray['id']
379 );
380
381 if ($sentNotifications->length()) {
382 return;
383 }
384 }
385
386 $historyId = $notificationsSMSHistoryRepo->add(
387 [
388 'notificationId' => $notification->getId()->getValue(),
389 'userId' => $user ? $user['id'] : null,
390 'appointmentId' =>
391 $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['id'] : null,
392 'eventId' =>
393 $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : null,
394 'packageCustomerId' => $appointmentArray['type'] === Entities::PACKAGE ?
395 $appointmentArray['packageCustomerId'] : null,
396 'text' => $reParsedData['body'],
397 'phone' => $user ? $user['phone'] : $sendTo,
398 'alphaSenderId' => $settingsService->getSetting('notifications', 'smsAlphaSenderId')
399 ]
400 );
401
402 $logNotificationId = null;
403
404 if ($logNotification) {
405 $logNotificationId = $notificationsLogRepository->add(
406 $notification,
407 $user ? $user['id'] : null,
408 $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['id'] : null,
409 $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : null,
410 $appointmentArray['type'] === Entities::PACKAGE ? $appointmentArray['packageCustomerId'] : null,
411 json_encode(
412 [
413 'subject' => '',
414 'body' => $reParsedData['body'],
415 'icsFiles' => [],
416 'historyId' => $historyId,
417 ]
418 )
419 );
420 }
421
422
423
424 $data = [
425 'sendTo' => $sendTo,
426 'body' => $reParsedData['body'],
427 'historyId' => $historyId,
428 'logNotificationId' => $logNotificationId,
429 ];
430
431 if ($this->getSend()) {
432 $this->sendSms($data);
433 } else {
434 $this->addPreparedNotificationData($data);
435
436 if ($data['logNotificationId']) {
437 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 1, 'sent');
438 }
439 }
440 }
441
442 /**
443 * @param int $historyId
444 * @param mixed $apiResponse
445 * @throws QueryExecutionException
446 */
447 public function updateSmsHistory($historyId, $apiResponse)
448 {
449 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
450 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
451
452 $notificationsSMSHistoryRepo->update(
453 $historyId,
454 [
455 'logId' => $apiResponse->message->logId,
456 'status' => $apiResponse->message->status,
457 'price' => $apiResponse->message->price,
458 'dateTime' => DateTimeService::getNowDateTimeInUtc(),
459 'segments' => $apiResponse->message->segments
460 ]
461 );
462 }
463
464 /**
465 * @param $data array
466 *
467 * @return void
468 * @throws QueryExecutionException
469 */
470 protected function sendSms($data)
471 {
472 /** @var NotificationLogRepository $notificationsLogRepository */
473 $notificationsLogRepository = $this->container->get('domain.notificationLog.repository');
474 /** @var SMSAPIService $smsApiService */
475 $smsApiService = $this->container->get('application.smsApi.service');
476
477 $apiResponse = $smsApiService->send(
478 $data['sendTo'],
479 $data['body'],
480 AMELIA_ACTION_URL . '/notifications/sms/history/' . $data['historyId']
481 );
482
483 if ($apiResponse->status === 'OK') {
484 $this->updateSmsHistory($data['historyId'], $apiResponse);
485
486 if ($data['logNotificationId']) {
487 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 1, 'sent');
488 }
489
490 if (!$this->sentSmsLowEmail) {
491 $this->sendSmsBalanceLowEmail();
492 }
493 } else {
494 if ($data['logNotificationId']) {
495 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 0, 'sent');
496 }
497 }
498 }
499
500 /**
501 * @return void
502 * @throws QueryExecutionException
503 */
504 public function sendPreparedNotifications()
505 {
506 foreach ($this->getPreparedNotificationData() as $item) {
507 $this->sendSms($item);
508 }
509 }
510 }
511