PluginProbe
The Innovative Form Builder – IvyForms / trunk
The Innovative Form Builder – IvyForms vtrunk
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 / SquareTokenRefreshService.php

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

166 lines 4.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\Integrations\MiddlewareRequestAuthService;
6 use IvyForms\Services\Integrations\MiddlewareUrl;
7 use IvyForms\Services\Settings\SettingsService;
8
9 /**
10 * Refreshes Square OAuth tokens via ivyforms-middleware.
11 */
12 class SquareTokenRefreshService
13 {
14 private const TOKEN_REFRESH_BUFFER_SECONDS = 432000; // 5 days
15
16 private SettingsService $settingsService;
17 private TokenEncryptionService $tokenEncryption;
18 private MiddlewareRequestAuthService $middlewareRequestAuthService;
19
20 public function __construct(
21 SettingsService $settingsService,
22 TokenEncryptionService $tokenEncryption,
23 MiddlewareRequestAuthService $middlewareRequestAuthService
24 ) {
25 $this->settingsService = $settingsService;
26 $this->tokenEncryption = $tokenEncryption;
27 $this->middlewareRequestAuthService = $middlewareRequestAuthService;
28 }
29
30 /**
31 * @param array<string, mixed> $settings
32 */
33 public function refreshIfNeeded(array $settings): void
34 {
35 if (!$this->shouldRefresh($settings)) {
36 return;
37 }
38
39 $tokenData = $this->requestRefreshedTokens($settings);
40 if ($tokenData === null) {
41 return;
42 }
43
44 $this->persistRefreshedTokens($settings, $tokenData);
45 }
46
47 /**
48 * @param array<string, mixed> $settings
49 */
50 private function shouldRefresh(array $settings): bool
51 {
52 if (empty($settings['connected'])) {
53 return false;
54 }
55
56 $expiresAt = isset($settings['expiresAt']) ? strtotime((string) $settings['expiresAt']) : false;
57 if ($expiresAt === false) {
58 return false;
59 }
60
61 if ($expiresAt - time() > self::TOKEN_REFRESH_BUFFER_SECONDS) {
62 return false;
63 }
64
65 return (string) ($settings['refreshToken'] ?? '') !== '';
66 }
67
68 /**
69 * @param array<string, mixed> $settings
70 * @return array<string, mixed>|null
71 */
72 private function requestRefreshedTokens(array $settings): ?array
73 {
74 $middlewareUrl = MiddlewareUrl::getBaseUrl();
75 if ($middlewareUrl === '') {
76 return null;
77 }
78
79 $refreshToken = $this->resolveRefreshToken($settings);
80
81 return $this->fetchRefreshedTokenData($middlewareUrl, $refreshToken);
82 }
83
84 /**
85 * @param array<string, mixed> $settings
86 */
87 private function resolveRefreshToken(array $settings): string
88 {
89 $refreshToken = (string) ($settings['refreshToken'] ?? '');
90 $decryptedRefresh = $this->tokenEncryption->decrypt($refreshToken);
91
92 return $decryptedRefresh !== '' ? $decryptedRefresh : $refreshToken;
93 }
94
95 /**
96 * @return array<string, mixed>|null
97 */
98 private function fetchRefreshedTokenData(string $middlewareUrl, string $refreshToken): ?array
99 {
100 $body = wp_json_encode(['refresh_token' => $refreshToken]);
101 if (!is_string($body)) {
102 return null;
103 }
104
105 $refreshUrl = $middlewareUrl . '/square/refresh';
106 $response = wp_remote_post(
107 $refreshUrl,
108 [
109 'headers' => $this->middlewareRequestAuthService->buildSignedHeaders($body, $refreshUrl, 'POST'),
110 'body' => $body,
111 'timeout' => 30,
112 ]
113 );
114
115 return $this->parseRefreshResponse($response);
116 }
117
118 /**
119 * @param array<string, mixed>|\WP_Error $response
120 * @return array<string, mixed>|null
121 */
122 private function parseRefreshResponse($response): ?array
123 {
124 if (is_wp_error($response)) {
125 return null;
126 }
127
128 if ((int) wp_remote_retrieve_response_code($response) !== 200) {
129 return null;
130 }
131
132 $decoded = json_decode((string) wp_remote_retrieve_body($response), true);
133 $tokenData = is_array($decoded) ? ($decoded['result'] ?? []) : [];
134
135 if (empty($tokenData['access_token']) || !is_array($tokenData)) {
136 return null;
137 }
138
139 return $tokenData;
140 }
141
142 /**
143 * @param array<string, mixed> $settings
144 * @param array<string, mixed> $tokenData Plaintext tokens from authenticated middleware response.
145 */
146 private function persistRefreshedTokens(array $settings, array $tokenData): void
147 {
148 $accessToken = (string) ($tokenData['access_token'] ?? '');
149 $refreshToken = (string) ($tokenData['refresh_token'] ?? '');
150
151 if ($accessToken === '') {
152 return;
153 }
154
155 $settings['accessToken'] = $this->tokenEncryption->encrypt($accessToken);
156 if ($refreshToken !== '') {
157 $settings['refreshToken'] = $this->tokenEncryption->encrypt($refreshToken);
158 }
159 if (!empty($tokenData['expires_at'])) {
160 $settings['expiresAt'] = (string) $tokenData['expires_at'];
161 }
162
163 $this->settingsService->setSetting('payment', 'square', $settings);
164 }
165 }
166