PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Services / Payment / SquarePaymentService.php

SquarePaymentService.php in The Innovative Form Builder – IvyForms 1.5, at backend/src/Services/Payment/SquarePaymentService.php

254 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace IvyForms\Services\Payment;
4
5 use IvyForms\Services\Settings\SettingsService;
6 use IvyForms\Services\Translations\BackendStrings;
7
8 /**
9 * Square Payments API client and token lifecycle.
10 */
11 class SquarePaymentService
12 {
13 private SettingsService $settingsService;
14 private TokenEncryptionService $tokenEncryption;
15 private SquareTokenRefreshService $tokenRefreshService;
16
17 public function __construct(
18 SettingsService $settingsService,
19 TokenEncryptionService $tokenEncryption,
20 SquareTokenRefreshService $tokenRefreshService
21 ) {
22 $this->settingsService = $settingsService;
23 $this->tokenEncryption = $tokenEncryption;
24 $this->tokenRefreshService = $tokenRefreshService;
25 }
26
27 /**
28 * @param array<string, mixed> $buyerInfo
29 * @return array<string, mixed>
30 */
31 public function processPayment(
32 string $sourceId,
33 string $idempotencyKey,
34 int $amountCents,
35 string $currency,
36 string $locationId,
37 string $note,
38 array $buyerInfo = [],
39 ?string $orderId = null,
40 ?string $customerId = null
41 ): array {
42 $accessToken = $this->getDecryptedAccessToken();
43 if ($accessToken === '') {
44 return SquarePaymentResponseParser::failure(
45 BackendStrings::getPaymentStrings()['square_not_connected']
46 );
47 }
48
49 $response = wp_remote_post(
50 SquareEnvironment::getApiBaseUrl() . '/payments',
51 [
52 'headers' => $this->squareJsonHeaders($accessToken),
53 'body' => wp_json_encode(
54 $this->buildPaymentRequestBody(
55 $sourceId,
56 $idempotencyKey,
57 $amountCents,
58 $currency,
59 $locationId,
60 $note,
61 $buyerInfo,
62 $orderId,
63 $customerId
64 )
65 ),
66 'timeout' => 30,
67 ]
68 );
69
70 if (is_wp_error($response)) {
71 return SquarePaymentResponseParser::failure($response->get_error_message());
72 }
73
74 return SquarePaymentLookup::confirmCreated(
75 SquarePaymentResponseParser::fromHttp($response),
76 $amountCents,
77 $currency,
78 $accessToken
79 );
80 }
81
82 public function getDecryptedAccessToken(): string
83 {
84 $this->refreshTokenIfNeeded();
85 $settings = $this->getSquareSettings();
86 $stored = (string) ($settings['accessToken'] ?? '');
87
88 if ($stored === '') {
89 return '';
90 }
91
92 $decrypted = $this->tokenEncryption->decrypt($stored);
93
94 return $decrypted !== '' ? $decrypted : $stored;
95 }
96
97 public function refreshTokenIfNeeded(): void
98 {
99 $this->tokenRefreshService->refreshIfNeeded($this->getSquareSettings());
100 }
101
102 /**
103 * @return array<int, array<string, mixed>>
104 */
105 public function getLocations(): array
106 {
107 $accessToken = $this->getDecryptedAccessToken();
108 if ($accessToken === '') {
109 return [];
110 }
111
112 $response = wp_remote_get(
113 SquareEnvironment::getApiBaseUrl() . '/locations',
114 [
115 'headers' => $this->squareHeaders($accessToken),
116 'timeout' => 30,
117 ]
118 );
119
120 if (is_wp_error($response)) {
121 return [];
122 }
123
124 $decoded = json_decode((string) wp_remote_retrieve_body($response), true);
125 $locations = is_array($decoded) ? ($decoded['locations'] ?? []) : [];
126
127 return is_array($locations) ? $locations : [];
128 }
129
130 /**
131 * @return array<string, mixed>
132 */
133 public function getMerchantInfo(): array
134 {
135 $settings = $this->getSquareSettings();
136 $merchantId = (string) ($settings['merchantId'] ?? '');
137 $accessToken = $this->getDecryptedAccessToken();
138
139 if ($merchantId === '' || $accessToken === '') {
140 return [];
141 }
142
143 $response = wp_remote_get(
144 SquareEnvironment::getApiBaseUrl() . '/merchants/' . rawurlencode($merchantId),
145 [
146 'headers' => $this->squareHeaders($accessToken),
147 'timeout' => 30,
148 ]
149 );
150
151 if (is_wp_error($response)) {
152 return [];
153 }
154
155 $decoded = json_decode((string) wp_remote_retrieve_body($response), true);
156 $merchant = is_array($decoded) ? ($decoded['merchant'] ?? []) : [];
157
158 return is_array($merchant) ? $merchant : [];
159 }
160
161 /**
162 * @return array<string, string>
163 */
164 private function squareHeaders(string $accessToken): array
165 {
166 return [
167 'Authorization' => 'Bearer ' . $accessToken,
168 'Square-Version' => '2024-10-17',
169 ];
170 }
171
172 /**
173 * @return array<string, string>
174 */
175 private function squareJsonHeaders(string $accessToken): array
176 {
177 return $this->squareHeaders($accessToken) + ['Content-Type' => 'application/json'];
178 }
179
180 /**
181 * @return array<string, mixed>
182 */
183 public function getSquareSettings(): array
184 {
185 $settings = $this->settingsService->getSetting('payment', 'square');
186
187 return is_array($settings) ? $settings : [];
188 }
189
190 /**
191 * @return array<string, mixed>
192 */
193 public function getPrimaryLocationFromApi(): array
194 {
195 $locations = $this->getLocations();
196 if (empty($locations)) {
197 return [];
198 }
199
200 foreach ($locations as $location) {
201 if (!empty($location['status']) && $location['status'] === 'ACTIVE') {
202 return $location;
203 }
204 }
205
206 return $locations[0];
207 }
208
209 /**
210 * @param array<string, mixed> $buyerInfo
211 * @return array<string, mixed>
212 */
213 private function buildPaymentRequestBody(
214 string $sourceId,
215 string $idempotencyKey,
216 int $amountCents,
217 string $currency,
218 string $locationId,
219 string $note,
220 array $buyerInfo,
221 ?string $orderId = null,
222 ?string $customerId = null
223 ): array {
224 $body = [
225 'source_id' => $sourceId,
226 'idempotency_key' => $idempotencyKey,
227 'amount_money' => [
228 'amount' => $amountCents,
229 'currency' => strtoupper($currency),
230 ],
231 'location_id' => $locationId,
232 'note' => $note,
233 ];
234
235 if ($orderId !== null && $orderId !== '') {
236 $body['order_id'] = $orderId;
237 }
238
239 if ($customerId !== null && $customerId !== '') {
240 $body['customer_id'] = $customerId;
241 }
242
243 if (!empty($buyerInfo['email'])) {
244 $body['buyer_email_address'] = (string) $buyerInfo['email'];
245 }
246
247 if (!empty($buyerInfo['billingAddress'])) {
248 $body['billing_address'] = $buyerInfo['billingAddress'];
249 }
250
251 return $body;
252 }
253 }
254