From a239ef33ec489ce6d0f25ac2f256df0ac54a7e50 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 16 Sep 2026 00:07:34 +0000
Subject: [PATCH] =Tackling the integrations again. This time Helcim, with some minnor edits to GMB and Square

---
 inc/integrations/services/Square.php |  112 +++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 79 insertions(+), 33 deletions(-)

diff --git a/inc/integrations/services/Square.php b/inc/integrations/services/Square.php
index a2b6c9a..c642247 100644
--- a/inc/integrations/services/Square.php
+++ b/inc/integrations/services/Square.php
@@ -4,7 +4,7 @@
 use Exception;
 use JVBase\integrations\Admin;
 use JVBase\integrations\APIEndpoint;
-use JVBase\integrations\Instance;
+use JVBase\Instance;
 use JVBase\integrations\Integrations;
 use JVBase\integrations\OAuth;
 use JVBase\integrations\OAuthCredentials;
@@ -14,10 +14,13 @@
 use JVBase\integrations\SyncFrom;
 use JVBase\integrations\SyncTo;
 use JVBase\integrations\Webhooks;
+use JVBase\managers\Dashboard\Dashboard;
 use JVBase\managers\queue\executors\IntegrationExecutor;
+use JVBase\managers\queue\Queue;
 use JVBase\managers\queue\TypeConfig;
 use JVBase\meta\Sanitizer;
 use JVBase\registrar\Registrar;
+use JVBase\ui\CRUDSkeleton;
 use WP_Error;
 
 if (!defined('ABSPATH')) {
@@ -36,18 +39,16 @@
 		Webhooks;
 
 	protected string $orderPostType = '_square_order';
-	protected string $apiVersion = '2026-07-15';
-	public bool $hasBatchCreate = true;
-	public bool $hasBatchUpdate = true;
-	public bool $hasBatchDelete = true;
 	public array $apiEndpoints = [
 
 	];
 
 	protected function __construct()
 	{
+		$this->service_name = 'square';
 		$this->title = 'Square';
 		$this->icon = 'square-logo';
+		$this->apiVersion = '2026-07-15';
 		$this->supportsWebp = false;
 
 		$this->canSync = [
@@ -55,6 +56,9 @@
 			'update'	=> true,
 			'delete'	=> true,
 		];
+		$this->hasBatchCreate = true;
+		$this->hasBatchUpdate = true;
+		$this->hasBatchDelete = true;
 
 		$this->defineFields();
 		$this->defineInstructions();
@@ -89,8 +93,8 @@
 			$locs->setHidden();
 			$locs->setOptions($this->getLocationOptions());
 
-		$loc = $this->addField('location_id', 'Default Location', 'select', 'manage_options');
-			$loc->setOptions($this->loadCredentials()['locations']);
+		$loc = $this->addField('location_id', 'Set Location', 'select', 'manage_options');
+			$loc->setOptions($this->loadCredentials()['locations']??[]);
 			$loc->setHint('Select default Square location for transactions');
 	}
 
@@ -162,6 +166,37 @@
 		add_action(BASE.'dashboard_page_orders', [$this, 'renderDashPage']);
 	}
 
+		public function addDashboardPages():void
+		{
+			$page = Dashboard::getInstance()->addPage('Your Orders', 'orders', 'receipt');
+			$page->setScripts(['jvb-crud']);
+		}
+
+	public function renderDashPage():void
+	{
+		$this->determineUser();
+
+		$crud = new CRUDSkeleton();
+		$crud->icon('receipt');
+		$crud->title('Your Orders','Here you can see your past orders, and reorder from there if you\'d like');
+		$crud->content($this->orderPostType, 'Order', 'Orders');
+		$crud->addSearch();
+		$crud->addCapabilities(['view']);
+		$crud->setEmptyState(sprintf(
+			'<div class="empty-state">
+				<h3>%sNothing here%s</h3>
+				<p>It doesn\'t look like you have any orders yet.</p>
+				<p>Head on over to <a href="%s">our menu</a> and make your first order!</p>
+			</div>',
+			jvbDashIcon($crud->getIcon()),
+			jvbDashIcon($crud->getIcon()),
+			get_post_type_archive_link(BASE.'menu_item')
+		));
+
+
+		$crud->render();
+	}
+
 	protected function initialize(): void
 	{
 		$this->defineEndpoints();
@@ -219,10 +254,15 @@
 
 	protected function refreshAccessToken(OAuthCredentials $credentials): array
 	{
-		if (!$this->getClientID() || !$this->getClientSecret())
+		if (!$this->getClientID() || !$this->getClientSecret() || empty($credentials->refresh_token)) {
+			$this->logError('refreshAccessToken', 'Missing client credentials or refresh token');
+			return $this->response(false, 'No client ID or client secret provided');
+		}
+
 		$response = wp_remote_post(
 			$this->oauth->getToken(),
 			[
+				'timeout'	=> 15,
 				'body'	=> [
 					'client_id'		=> $this->getClientID(),
 					'client_secret' => $this->getClientSecret(),
@@ -244,16 +284,18 @@
 		}
 
 		$data = json_decode(wp_remote_retrieve_body($response));
-		if (isset($data['access_token'])) {
-			$credentials = [
-				'access_token'	=>  $data['access_token'],
-				'expires_at'	=> time() + ($data['expires_in'] ?? 2592000) // 30 days default
-			];
-
-			$this->saveCredentials($credentials);
-			return $this->response(true, 'Successfully refreshed access token');
+		if (empty($data['access_token'])) {
+			$this->logError('refreshAccessToken', 'No access_token in refresh response', ['body' => $data]);
+			return $this->response(false, 'No Access Token in Response');
 		}
-		return $this->response(false, 'Something went wrong that wasn\'t caught - could not refresh access token');
+		$credentials = [
+			'access_token'  => $data['access_token'],
+			'expires_at'    => strtotime($data['expires_at'] ?? '') ?: (time() + 2592000),
+			'refresh_token' => $data['refresh_token'] ?? $credentials->refresh_token,
+		];
+		$this->saveCredentials($credentials);
+
+		return $this->response(true, 'Successfully refreshed access token');
 	}
 
 	protected function getRequestHeaders(): array
@@ -265,21 +307,18 @@
 		];
 	}
 
-	protected function handleResponse(\WP_Error|array $response, string $method, string $endpoint): array
+	protected function handleResponse(WP_Error|array $response, string $method, string $endpoint): array
 	{
 		if (is_wp_error($response) && $response->get_error_message() === 'rate_limit_exceeded') {
 			return $this->response(false, 'Rate limit exceeded');
 		}
 		$endpoint = str_replace($this->getBaseURL(), '', $endpoint);
-		switch ($endpoint) {
-			case '/v2/customers/search':
-				return $this->handleEmailSearchResponse($response, $method);
-			case '/v2/customers':
-				return $this->handleCreateUserResponse($response, $method);
-			case '/v2/locations':
-				return $this->handleGetLocationResponse($response, $method);
-		}
-		return $this->response(false, 'Unhandled Response for '.$endpoint.' endpoint, with method: '.$method);
+		return match ($endpoint) {
+			'/v2/customers/search' => $this->handleEmailSearchResponse($response, $method),
+			'/v2/customers' => $this->handleCreateUserResponse($response, $method),
+			'/v2/locations' => $this->handleGetLocationResponse($response, $method),
+			default => $this->response(false, 'Unhandled Response for ' . $endpoint . ' endpoint, with method: ' . $method),
+		};
 	}
 
 	protected function setContentTypes(): void
@@ -720,7 +759,7 @@
 	********************************************************************/
 	protected function registerAdditionalQueueTypes(IntegrationExecutor $executor): void
 	{
-		$queue = JVB()->queue();
+		$queue = Queue::getInstance();
 		$queue->registry()->register(
 			self::$syncCustomer,
 			new TypeConfig(
@@ -865,7 +904,7 @@
 	{
 		return $this->loadCredentials()['location_id']??false;
 	}
-	protected function getLocations():array|false
+	public function getLocations():array|false
 	{
 		if (!$this->getAccessToken()) {
 			return false;
@@ -914,10 +953,7 @@
 			? 'https://connect.squareup.com'
 			: 'https://connect.squareupsandbox.com';
 	}
-	protected function getAccessToken():string
-	{
-		return $this->loadCredentials()['access_token']??'';
-	}
+
 	/***************************************************************
 	 * ACTIONS
 	***************************************************************/
@@ -959,7 +995,17 @@
 		return $this->contentTypes[$content_type];
 	}
 
+	public function handleImportFromSquare():array
+	{
 
+		return $this->response(true, 'Did a thing');
+	}
+
+	public function handleSyncToSquare():array
+	{
+
+		return $this->response(true, 'Did a thing');
+	}
 
 
 }

--
Gitblit v1.10.0