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