PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
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 2 months ago AbstractWhatsAppNotificationService.php 3 months ago ApplicationNotificationService.php 2 months ago AppointmentNotificationService.php 2 weeks ago BasicWhatsAppNotificationService.php 6 months ago EmailNotificationService.php 1 month ago NotificationHelperService.php 6 months ago SMSAPIService.php 2 months ago SMSNotificationService.php 1 month ago
SMSNotificationService.php
531 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 }
161 }
162 }
163 }
164
165
166 private function sendSmsBalanceLowEmail()
167 {
168 /** @var SettingsService $settingsService */
169 $settingsService = $this->container->get('domain.settings.service');
170 /** @var SMSAPIService $smsApiService */
171 $smsApiService = $this->container->get('application.smsApi.service');
172
173 $smsLowEmail = $settingsService->getSetting('notifications', 'smsBalanceEmail');
174 if ($smsLowEmail && $smsLowEmail['enabled']) {
175 try {
176 $userResponse = $smsApiService->getUserInfo();
177 if (
178 !empty($userResponse) &&
179 $userResponse->status === 'OK' &&
180 !empty($userResponse->user) &&
181 $userResponse->user->balance <= $smsLowEmail['minimum']
182 ) {
183 /** @var EmailNotificationService $notificationService */
184 $notificationService = $this->container->get('application.emailNotification.service');
185 $notificationService->sendSmsBalanceLowEmail($smsLowEmail['email']);
186
187 $this->sentSmsLowEmail = true;
188 }
189 } catch (\Exception $e) {
190 }
191 }
192 }
193
194 /**
195 * @throws ContainerValueNotFoundException
196 * @throws QueryExecutionException
197 * @throws InvalidArgumentException
198 */
199 public function sendUndeliveredNotifications()
200 {
201 /** @var NotificationLogRepository $notificationLogRepository */
202 $notificationLogRepository = $this->container->get('domain.notificationLog.repository');
203
204 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
205 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
206
207 /** @var Collection $undeliveredNotifications */
208 $undeliveredNotifications = $notificationLogRepository->getUndeliveredNotifications('sms');
209
210 /** @var SMSAPIService $smsApiService */
211 $smsApiService = $this->container->get('application.smsApi.service');
212
213 /** @var NotificationLog $undeliveredNotification */
214 foreach ($undeliveredNotifications->getItems() as $undeliveredNotification) {
215 try {
216 $data = json_decode($undeliveredNotification->getData()->getValue(), true);
217
218 if ($history = $notificationsSMSHistoryRepo->getItemById($data['historyId'])) {
219 $apiResponse = $smsApiService->send(
220 $history['phone'],
221 $data['body'],
222 AMELIA_ACTION_URL . '/notifications/sms/history/' . $data['historyId']
223 );
224
225 if ($apiResponse->status === 'OK') {
226 $this->updateSmsHistory($data['historyId'], $apiResponse);
227
228 $notificationLogRepository->updateFieldById(
229 $undeliveredNotification->getId()->getValue(),
230 1,
231 'sent'
232 );
233 }
234 }
235 } catch (Exception $e) {
236 }
237 }
238 }
239
240 /**
241 * @throws QueryExecutionException
242 * @throws InvalidArgumentException
243 * @throws Exception
244 */
245 public function sendBirthdayGreetingNotifications()
246 {
247 /** @var Collection $notifications */
248 $notifications = $this->getByNameAndType('customer_birthday_greeting', $this->type);
249
250 /** @var Notification $notification */
251 $notification = $notifications->getItem($notifications->keys()[0]);
252
253 // Check if notification is enabled and it is time to send notification
254 if (
255 $notification->getStatus()->getValue() === NotificationStatus::ENABLED &&
256 $notification->getTime() &&
257 DateTimeService::getNowDateTimeObject() >=
258 DateTimeService::getCustomDateTimeObject($notification->getTime()->getValue())
259 ) {
260 /** @var NotificationLogRepository $notificationLogRepo */
261 $notificationLogRepo = $this->container->get('domain.notificationLog.repository');
262 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
263 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
264 /** @var SMSAPIService $smsApiService */
265 $smsApiService = $this->container->get('application.smsApi.service');
266 /** @var PlaceholderService $placeholderService */
267 $placeholderService = $this->container->get('application.placeholder.appointment.service');
268 /** @var SettingsService $settingsService */
269 $settingsService = $this->container->get('domain.settings.service');
270
271 $customers = $notificationLogRepo->getBirthdayCustomers($this->type);
272
273 $companyData = $placeholderService->getCompanyData();
274
275 $customersArray = $customers->toArray();
276
277 foreach ($customersArray as $bookingKey => $customerArray) {
278 $data = [
279 'customer_email' => $customerArray['email'],
280 'customer_first_name' => $customerArray['firstName'],
281 'customer_last_name' => $customerArray['lastName'],
282 'customer_full_name' => $customerArray['firstName'] . ' ' . $customerArray['lastName'],
283 'customer_phone' => $customerArray['phone'],
284 'customer_id' => $customerArray['id'],
285 ];
286
287 /** @noinspection AdditionOperationOnArraysInspection */
288 $data += $companyData;
289
290 $text = $placeholderService->applyPlaceholders(
291 $notification->getContent()->getValue(),
292 $data
293 );
294
295 if ($data['customer_phone']) {
296 try {
297 $historyId = $notificationsSMSHistoryRepo->add(
298 [
299 'notificationId' => $notification->getId()->getValue(),
300 'userId' => $data['customer_id'],
301 'text' => $text,
302 'phone' => $data['customer_phone'],
303 'alphaSenderId' => $settingsService->getSetting('notifications', 'smsAlphaSenderId'),
304 ]
305 );
306
307 $smsData = apply_filters(
308 'amelia_manipulate_sms_data',
309 [
310 'text' => $text,
311 'to' => $data['customer_phone']
312 ]
313 );
314
315 $apiResponse = null;
316
317 if (empty($smsData['skipSending'])) {
318 $apiResponse = $smsApiService->send(
319 $smsData['to'],
320 $smsData['text'],
321 AMELIA_ACTION_URL . '/notifications/sms/history/' . $historyId
322 );
323 }
324
325 if ($apiResponse && $apiResponse->status === 'OK') {
326 $this->updateSmsHistory($historyId, $apiResponse);
327
328 $logNotificationId = $notificationLogRepo->add(
329 $notification,
330 $data['customer_id']
331 );
332
333 if ($logNotificationId) {
334 $notificationLogRepo->updateFieldById($logNotificationId, 1, 'sent');
335 }
336 }
337 } catch (QueryExecutionException $e) {
338 }
339 }
340 }
341 }
342 }
343
344 /** @noinspection MoreThanThreeArgumentsInspection */
345 /**
346 * @param Notification $notification
347 * @param array $user
348 * @param array $appointmentArray
349 * @param array $reParsedData
350 * @param bool $logNotification
351 * @param string $sendTo
352 *
353 * @return void
354 *
355 * @throws QueryExecutionException
356 * @throws InvalidArgumentException
357 */
358 private function saveAndSend($notification, $user, $appointmentArray, $reParsedData, $logNotification, $sendTo)
359 {
360
361 /** @var NotificationLogRepository $notificationsLogRepository */
362 $notificationsLogRepository = $this->container->get('domain.notificationLog.repository');
363 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
364 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
365 /** @var SettingsService $settingsService */
366 $settingsService = $this->container->get('domain.settings.service');
367 /** @var SMSAPIService $smsApiService */
368 $smsApiService = $this->container->get('application.smsApi.service');
369
370 if ($user && !empty($appointmentArray['isRetry'])) {
371 /** @var Collection $sentNotifications */
372 $sentNotifications = $notificationsLogRepository->getSentNotificationsByUserAndEntity(
373 $user['id'],
374 'sms',
375 $appointmentArray['type'],
376 $appointmentArray['type'] === Entities::PACKAGE ?
377 $appointmentArray['packageCustomerId'] : $appointmentArray['id']
378 );
379
380 if ($sentNotifications->length()) {
381 return;
382 }
383 }
384
385 $historyId = $notificationsSMSHistoryRepo->add(
386 [
387 'notificationId' => $notification->getId()->getValue(),
388 'userId' => $user ? $user['id'] : null,
389 'appointmentId' =>
390 $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['id'] : null,
391 'eventId' =>
392 $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : null,
393 'packageCustomerId' => $appointmentArray['type'] === Entities::PACKAGE ?
394 $appointmentArray['packageCustomerId'] : null,
395 'text' => $reParsedData['body'],
396 'phone' => $user ? $user['phone'] : $sendTo,
397 'alphaSenderId' => $settingsService->getSetting('notifications', 'smsAlphaSenderId')
398 ]
399 );
400
401 $logNotificationId = null;
402
403 if ($logNotification) {
404 $logNotificationId = $notificationsLogRepository->add(
405 $notification,
406 $user ? $user['id'] : null,
407 $appointmentArray['type'] === Entities::APPOINTMENT ? $appointmentArray['id'] : null,
408 $appointmentArray['type'] === Entities::EVENT ? $appointmentArray['id'] : null,
409 $appointmentArray['type'] === Entities::PACKAGE ? $appointmentArray['packageCustomerId'] : null,
410 json_encode(
411 [
412 'subject' => '',
413 'body' => $reParsedData['body'],
414 'icsFiles' => [],
415 'historyId' => $historyId,
416 ]
417 )
418 );
419 }
420
421
422
423 $data = [
424 'sendTo' => $sendTo,
425 'body' => $reParsedData['body'],
426 'historyId' => $historyId,
427 'logNotificationId' => $logNotificationId,
428 ];
429
430 if ($this->getSend()) {
431 $this->sendSms($data);
432 } else {
433 $this->addPreparedNotificationData($data);
434
435 if ($data['logNotificationId']) {
436 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 1, 'sent');
437 }
438 }
439 }
440
441 /**
442 * @param int $historyId
443 * @param mixed $apiResponse
444 * @throws QueryExecutionException
445 */
446 public function updateSmsHistory($historyId, $apiResponse)
447 {
448 /** @var NotificationSMSHistoryRepository $notificationsSMSHistoryRepo */
449 $notificationsSMSHistoryRepo = $this->container->get('domain.notificationSMSHistory.repository');
450
451 $notificationsSMSHistoryRepo->update(
452 $historyId,
453 [
454 'logId' => $apiResponse->message->logId,
455 'status' => $apiResponse->message->status,
456 'price' => $apiResponse->message->price,
457 'dateTime' => DateTimeService::getNowDateTimeInUtc(),
458 'segments' => $apiResponse->message->segments
459 ]
460 );
461 }
462
463 /**
464 * @param $data array
465 *
466 * @return void
467 * @throws QueryExecutionException
468 */
469 protected function sendSms($data)
470 {
471 /** @var NotificationLogRepository $notificationsLogRepository */
472 $notificationsLogRepository = $this->container->get('domain.notificationLog.repository');
473 /** @var SMSAPIService $smsApiService */
474 $smsApiService = $this->container->get('application.smsApi.service');
475
476 $smsData = apply_filters(
477 'amelia_manipulate_sms_data',
478 [
479 'text' => $data['body'],
480 'to' => $data['sendTo']
481 ]
482 );
483
484 $data = array_merge(
485 $data,
486 [
487 'text' => $data['body'],
488 'to' => $data['sendTo']
489 ],
490 is_array($smsData) ? $smsData : []
491 );
492
493 if (!empty($data['skipSending'])) {
494 return;
495 }
496
497 $apiResponse = $smsApiService->send(
498 $data['to'],
499 $data['text'],
500 AMELIA_ACTION_URL . '/notifications/sms/history/' . $data['historyId']
501 );
502
503 if ($apiResponse->status === 'OK') {
504 $this->updateSmsHistory($data['historyId'], $apiResponse);
505
506 if ($data['logNotificationId']) {
507 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 1, 'sent');
508 }
509
510 if (!$this->sentSmsLowEmail) {
511 $this->sendSmsBalanceLowEmail();
512 }
513 } else {
514 if ($data['logNotificationId']) {
515 $notificationsLogRepository->updateFieldById((int)$data['logNotificationId'], 0, 'sent');
516 }
517 }
518 }
519
520 /**
521 * @return void
522 * @throws QueryExecutionException
523 */
524 public function sendPreparedNotifications()
525 {
526 foreach ($this->getPreparedNotificationData() as $item) {
527 $this->sendSms($item);
528 }
529 }
530 }
531