PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 1.2.11
Booking for Appointments and Events Calendar – Amelia v1.2.11
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 / Infrastructure / Services / Payment / SquareService.php
ameliabooking / src / Infrastructure / Services / Payment Last commit date
CurrencyService.php 2 years ago SquareMiddlewareService.php 1 year ago SquareService.php 1 year ago StarterPaymentService.php 2 years ago
SquareService.php
404 lines
1 <?php
2 /**
3 * @copyright © TMS-Plugins. All rights reserved.
4 * @licence See LICENCE.md for license details.
5 */
6
7 namespace AmeliaBooking\Infrastructure\Services\Payment;
8
9 use AmeliaBooking\Domain\Services\DateTime\DateTimeService;
10 use AmeliaBooking\Domain\Services\Payment\AbstractPaymentService;
11 use AmeliaBooking\Domain\Services\Payment\PaymentServiceInterface;
12 use AmeliaBooking\Domain\Services\Settings\SettingsService;
13 use AmeliaBooking\Domain\ValueObjects\Number\Float\Price;
14 use Square\Environment;
15 use Square\Exceptions\ApiException;
16 use Square\Http\ApiResponse;
17 use Square\Models\Address;
18 use Square\Models\CheckoutOptions;
19 use Square\Models\CompletePaymentRequest;
20 use Square\Models\CreatePaymentLinkRequest;
21 use Square\Models\Location;
22 use Square\Models\Money;
23 use Square\Models\Order;
24 use Square\Models\OrderLineItem;
25 use Square\Models\PaymentLink;
26 use Square\Models\PrePopulatedData;
27 use Square\Models\RefundPaymentRequest;
28 use Square\Models\UpdatePaymentLinkRequest;
29 use Square\SquareClient;
30
31 /**
32 * Class SquareService
33 */
34 class SquareService extends AbstractPaymentService implements PaymentServiceInterface
35 {
36
37 /**
38 * @var SquareMiddlewareService $middlewareService
39 */
40 private $middlewareService;
41
42 /**
43 * SquareService constructor.
44 *
45 * @param SettingsService $settingsService
46 * @param CurrencyService $currencyService
47 */
48 public function __construct(
49 SettingsService $settingsService,
50 CurrencyService $currencyService
51 ) {
52 parent::__construct($settingsService, $currencyService);
53 $this->middlewareService = new SquareMiddlewareService();
54 }
55
56 /**
57 *
58 * @return mixed
59 * @throws \Exception
60 */
61 public function getClient()
62 {
63 $squareSettings = $this->settingsService->getCategorySettings('payments')['square'];
64 $accessToken = $this->middlewareService->getAccessToken($squareSettings['accessToken']);
65 return new SquareClient(['accessToken' => $accessToken['access_token'], 'environment' => $squareSettings['testMode'] ? Environment::SANDBOX : Environment::PRODUCTION]);
66 }
67
68 /**
69 * @param string $apiName
70 * @param string $functionName
71 * @param array $args
72 *
73 * @return ApiResponse
74 * @throws \Exception
75 */
76 private function getApiResponse($apiName, $functionName, $args)
77 {
78 $client = $this->getClient();
79 /** @var ApiResponse $response */
80 $response = call_user_func_array([$client->{$apiName}(), $functionName], $args);
81 if ($response->getStatusCode() === 401) {
82 $this->refreshAccessToken();
83 $client = $this->getClient();
84 $response = call_user_func_array([$client->{$apiName}(), $functionName], $args);
85 }
86
87 return $response;
88 }
89
90 /**
91 *
92 * @return Location
93 * @throws \Exception
94 */
95 private function getLocation()
96 {
97 $locationId = $this->settingsService->getCategorySettings('payments')['square']['locationId'];
98
99 $apiResponse = $this->getApiResponse('getLocationsApi', 'retrieveLocation', [$locationId]);
100
101 return $apiResponse->isSuccess() ? $apiResponse->getResult()->getLocation() : null;
102 }
103
104 /**
105 * @param array $data
106 * @param array $transfers
107 *
108 * @return ApiResponse
109 * @throws \Exception
110 */
111 public function execute($data, &$transfers)
112 {
113 // Monetary amounts are specified in the smallest unit of the applicable currency.
114 // This amount is in cents
115 // Set currency to the currency for the location
116 $location = $this->getLocation();
117 if (empty($location)) {
118 return null;
119 }
120 $currency = $location->getCurrency();
121 $price = new Money();
122 $price->setCurrency($currency);
123 $price->setAmount($data['amount']);
124
125 $appointment = new OrderLineItem(1);
126 $appointment->setName($data['description']);
127 $appointment->setBasePriceMoney($price);
128
129 // Create a new order and add the line items as necessary.
130 $order = new Order($location->getId());
131 $order->setLineItems([$appointment]);
132 if (!empty($data['metaData'])) {
133 $order->setMetadata($data['metaData']);
134 }
135
136 $checkoutOptions = new CheckoutOptions();
137 $checkoutOptions->setRedirectUrl($data['redirectUrl']);
138
139 $paymentLinkRequest = new CreatePaymentLinkRequest();
140 $paymentLinkRequest->setIdempotencyKey(uniqid());
141 $paymentLinkRequest->setOrder($order);
142
143 $paymentLinkRequest->setCheckoutOptions($checkoutOptions);
144 if (!empty($data['customer'])) {
145 $prePopulatedData = new PrePopulatedData();
146 if (!empty($data['customer']['phone'])) {
147 $prePopulatedData->setBuyerPhoneNumber($data['customer']['phone']);
148 }
149 if (!empty($data['customer']['email'])) {
150 $prePopulatedData->setBuyerEmail($data['customer']['email']);
151 }
152 $address = new Address();
153 if (!empty($data['customer']['firstName'])) {
154 $address->setFirstName($data['customer']['firstName']);
155 }
156 if (!empty($data['customer']['lastName'])) {
157 $address->setLastName($data['customer']['lastName']);
158 }
159 $prePopulatedData->setBuyerAddress($address);
160 $paymentLinkRequest->setPrePopulatedData($prePopulatedData);
161 }
162
163 return $this->getApiResponse('getCheckoutApi', 'createPaymentLink', [$paymentLinkRequest]);
164 }
165
166
167 /**
168 * @param PaymentLink $paymentLink
169 * @param string $redirectUrl
170 *
171 * @return ApiResponse
172 * @throws \Exception
173 */
174 public function updatePaymentLink($paymentLink, $redirectUrl, $paymentId)
175 {
176 if (empty($paymentLink)) {
177 return null;
178 }
179
180 if ($paymentId) {
181 $paymentLink->setPaymentNote("Amelia - Transaction " . $paymentId);
182 }
183
184 if ($redirectUrl) {
185 $checkoutOptions = $paymentLink->getCheckoutOptions();
186 $checkoutOptions->setRedirectUrl($redirectUrl);
187 $paymentLink->setCheckoutOptions($checkoutOptions);
188 }
189
190 $updatePaymentResponse = new UpdatePaymentLinkRequest($paymentLink);
191
192 return $this->getApiResponse('getCheckoutApi', 'updatePaymentLink', [$paymentLink->getId(), $updatePaymentResponse]);
193 }
194
195 /**
196 * @param $data
197 *
198 * @return array
199 * @throws \Exception
200 */
201 public function getPaymentLink($data)
202 {
203 $transfers = [];
204
205 $apiResponse = $this->execute($data, $transfers);
206
207 if (!empty($apiResponse) && $apiResponse->isSuccess() && $apiResponse->getResult() && $apiResponse->getResult()->getPaymentLink()) {
208 /**@var PaymentLink $paymentLink */
209 $paymentLink = $apiResponse->getResult()->getPaymentLink();
210
211 $orderId = $paymentLink->getOrderId();
212
213 $this->updatePaymentLink($paymentLink, $data['redirectUrl'] . '&squareOrderId=' . $orderId, !empty($data['paymentId']) ? $data['paymentId'] : null);
214
215 return [
216 'link' => $paymentLink->getUrl(),
217 'status' => 200
218 ];
219 }
220
221 return [
222 'message' => $apiResponse ? $this->getErrorMessage($apiResponse) : null,
223 'status' => $apiResponse ? $apiResponse->getStatusCode() : null
224 ];
225 }
226
227 /**
228 *
229 * @param string $orderId
230 * @return ApiResponse
231 *
232 * @throws ApiException
233 * @throws \Exception
234 */
235 public function getOrderResponse($orderId)
236 {
237 return $this->getApiResponse('getOrdersApi', 'retrieveOrder', [$orderId]);
238 }
239
240 /**
241 *
242 * @param string $paymentId
243 * @return ApiResponse
244 *
245 * @throws ApiException
246 * @throws \Exception
247 */
248 public function completePayment($paymentId)
249 {
250 return $this->getApiResponse('getPaymentsApi', 'completePayment', [$paymentId, new CompletePaymentRequest()]);
251 }
252
253 /**
254 * @param array $data
255 *
256 * @return array
257 * @throws \Exception
258 */
259 public function refund($data)
260 {
261 $location = $this->getLocation();
262 $currency = $location->getCurrency();
263
264 $money = new Money();
265 $money->setAmount(intval($this->currencyService->getAmountInFractionalUnit(new Price($data['amount']))));
266 $money->setCurrency($currency);
267
268 $body = new RefundPaymentRequest(uniqid(), $money);
269 $body->setPaymentId($data['id']);
270
271 $apiResponse = $this->getApiResponse('getRefundsApi', 'refundPayment', [$body]);
272
273 return ['error' => $apiResponse->isSuccess() ? false : $this->getErrorMessage($apiResponse)];
274 }
275
276 /**
277 *
278 * @param ApiResponse $response
279 * @return string
280 *
281 * @throws \Exception
282 */
283 public function getErrorMessage($response)
284 {
285 $errors = $response->getErrors();
286 $errors = array_map(
287 function ($error) {
288 return $error->getDetail();
289 },
290 $errors
291 );
292 return implode('; ', $errors);
293 }
294
295 /**
296 *
297 * @param string $authCode
298 * @param string $state
299 *
300 * @return array
301 *
302 * @throws \Exception
303 */
304 public function getLocations()
305 {
306 $apiResponse = $this->getApiResponse('getLocationsApi', 'listLocations', []);
307
308 $result = $apiResponse->isSuccess() ? $apiResponse->getResult() : null;
309 return $result ? array_filter(
310 $result->getLocations(),
311 function ($location) {
312 return $location->getStatus() === 'ACTIVE' && in_array('CREDIT_CARD_PROCESSING', $location->getCapabilities());
313 }
314 ) : [];
315 }
316
317
318 /**
319 * @param string $id
320 * @param array|null $transfers
321 *
322 * @return mixed
323 * @throws \Exception
324 */
325 public function getTransactionAmount($id, $transfers)
326 {
327 $apiResponse = $this->getApiResponse('getPaymentsApi', 'getPayment', [$id]);
328
329 if ($apiResponse->isSuccess() && $apiResponse->getResult()) {
330 return $apiResponse->getResult()->getPayment()->getAmountMoney()->getAmount()/100;
331 }
332
333 return null;
334 }
335
336 /**
337 *
338 * @return boolean
339 *
340 * @throws \Exception
341 */
342 public function disconnectAccount($fromSquare = false)
343 {
344 $squareSettings = $this->settingsService->getCategorySettings('payments')['square'];
345
346 if (!$fromSquare) {
347 $this->middlewareService->disconnectAccount($squareSettings['accessToken'], $squareSettings['testMode']);
348 }
349
350 $squareSettings['accessToken'] = null;
351 $squareSettings['locationId'] = null;
352 $this->settingsService->setSetting('payments', 'square', $squareSettings);
353 delete_transient('amelia_square_access_token');
354
355 return true;
356 }
357
358 public function isAccessTokenExpired($accessToken)
359 {
360 return DateTimeService::getNowDateTimeObject() >= DateTimeService::getCustomDateTimeObject($accessToken['expires_at']);
361 }
362
363 /**
364 *
365 * @param string $authCode
366 * @param string $state
367 *
368 * @return boolean
369 *
370 * @throws \Exception
371 */
372 public function refreshAccessToken()
373 {
374 $squareSettings = $this->settingsService->getCategorySettings('payments')['square'];
375
376 if (empty($squareSettings['accessToken']['refresh_token'])) {
377 return true;
378 }
379
380 $response = $this->middlewareService->refreshAccessToken($squareSettings['accessToken'], $squareSettings['testMode']);
381
382 if ($response) {
383 $accessToken = $response['result'];
384
385 set_transient('amelia_square_access_token', ['access_token' => $accessToken['decrypted_access_token'], 'refresh_token' => $accessToken['decrypted_refresh_token']], 604800);
386
387 unset($accessToken['decrypted_access_token']);
388 unset($accessToken['decrypted_refresh_token']);
389
390 $squareSettings['accessToken'] = $accessToken;
391 $this->settingsService->setSetting('payments', 'square', $squareSettings);
392 }
393
394 return true;
395 }
396
397 public function getAuthUrl()
398 {
399 $squareSettings = $this->settingsService->getCategorySettings('payments')['square'];
400
401 return $this->middlewareService->getAuthUrl($squareSettings['testMode']);
402 }
403 }
404