From a6413da67a4579866f0fd0323ddb23a7b6b9654a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 18 Sep 2026 17:00:53 +0000
Subject: [PATCH] =Cleaning up the Integrations setup a bit, started the Helcim integration work too

---
 inc/integrations/OAuth.php |  136 +++++++++++++++++++++++++++------------------
 1 files changed, 81 insertions(+), 55 deletions(-)

diff --git a/inc/integrations/OAuth.php b/inc/integrations/OAuth.php
index 3bc410c..5750df4 100644
--- a/inc/integrations/OAuth.php
+++ b/inc/integrations/OAuth.php
@@ -10,8 +10,7 @@
 
 trait OAuth
 {
-	use _Base, Admin;
-	protected bool $hasOAuth = true;
+	use _Base, Admin, ServiceMap;
 	protected OAuthURLs $oauth;
 	/**
 	 * Child classes must set this (e.g. 'https://api.example.com/v1/')
@@ -21,7 +20,17 @@
 	protected int $oauthTTL = 300;
 	protected array $oathScopes = [];
 
+	protected ?RateLimits $oauthLimiter;
 
+	protected function initializeOAuthRateLimiter():void
+	{
+		$key = $this->service_name;
+		if (!is_null($this->userID)) {
+			$key .= '_'.$this->userID;
+		}
+		$key .= '_oauth';
+		$this->oauthLimiter = new RateLimits($key, ['s' => 1, 'm' => 10, 'h' => 100]);
+	}
 	/**
 	 * @return array Defaults to the base getRequestHeaders, but can optionally be overridden
 	 */
@@ -29,6 +38,10 @@
 	{
 		return $this->getRequestHeaders();
 	}
+	public function getOAuthBaseURL():?string
+	{
+		return $this->baseOAuthUrl;
+	}
 
 	/**
 	 * Default to the base handleResponse method, but extensions can modify accordingly
@@ -45,33 +58,40 @@
 
 	function addOAuthActions():void
 	{
-		$actions = [
-			[
-				'name'		=> 'Connect to Service',
-				'label'		=> 'Connect',
-				'icon'		=> 'plugs-connected',
-				'action'	=> [$this, 'handleOAuthConnect']
-			],
-			[
-				'name'		=> 'Disconnect from Service',
-				'icon'		=> 'plugs-disconnected',
-				'action'	=> [$this, 'handleOAuthDisconnect']
-			],
+		if ($this->isSetUp()) {
+			$actions = [
+				[
+					'name'		=> 'Connect to Service',
+					'label'		=> 'Connect',
+					'icon'		=> 'plugs-connected',
+					'action'	=> [$this, 'handleOAuthConnect']
+				],
+				[
+					'name'		=> 'Disconnect from Service',
+					'label'		=> 'Disconnect',
+					'icon'		=> 'plugs-disconnected',
+					'action'	=> [$this, 'handleOAuthDisconnect']
+				],
 //			[
 //				'name'		=> 'Refresh Token',
 //				'icon'		=> 'arrows-clockwise',
 //				'action'	=> [$this, 'handleOAuthRefreshToken']
 //			]
-		];
-		foreach ($actions as $a) {
-			$this->addAction($a['name'], $a['action'], $a['slug']??null, $a['icon']??null, $a['label']??null);
+			];
+			foreach ($actions as $a) {
+				$this->addAction($a['name'], $a['action'], $a['slug']??null, $a['icon']??null, $a['label']??null);
+			}
 		}
 	}
+		public function hasOAuthCredentials(?int $userID = null):bool
+		{
+			if ($userID !== $this->userID) {
+				$this->switchUser($userID);
+			}
+			return $this->hasValidOAuth();
+		}
 		public function handleOAuthConnect():array
 		{
-			if (!$this->hasOAuth) {
-				return $this->response(false, 'Not an OAuth service');
-			}
 			$url = $this->getOAuthUrl();
 			if ($url) {
 				return [
@@ -92,10 +112,7 @@
 
 		public function revokeOAuth():bool
 		{
-			if (!$this->hasOAuth) {
-				return false;
-			}
-			if (empty($this->oauth['revoke'])) {
+			if (empty($this->oauth->getRevoke())) {
 				error_log('No revoke url set for '.$this->service_name);
 				return false;
 			}
@@ -103,7 +120,7 @@
 			if (empty($credentials['access_token'])) {
 				error_log('Could not send revoke request, no access_token for '.$this->service_name);
 			} else {
-				$response = $this->postOAuthRequest($this->oauth['revoke'], [
+				$response = $this->postOAuthRequest($this->oauth->getRevoke(), [
 					'body'	=> [
 						'token'	=> $credentials['access_token']
 					]
@@ -281,12 +298,14 @@
 		}
 
 		// Get integration instance
-		$integration = JVB()->connect($this->service_name, $user_id);
+		$integration = $this->getService($this->service_name);
 
-		if (!$integration) {
+		if (!$integration || !method_exists($integration, 'switchUser')) {
 			wp_die('Invalid service: ' . esc_html($this->service_name), 'OAuth Error');
 		}
 
+		$integration->switchUser($user_id);
+
 
 		// Exchange code for tokens
 		$result = $integration->handleOAuthCode($code, $state);
@@ -312,20 +331,23 @@
 		$credentials = $this->loadCredentials();
 		$hasCredentials = $this->hasValidOAuth();
 		$returnURL = $this->getReturnURL();
-
-		if ($hasCredentials) {
+		if (!$this->isSetUp()) {
+			return '';
+		}
+		if (!$hasCredentials) {
 			return sprintf(
 			'<div class="oauth-connect">
 				<h3>Connect to your %s account</h3>
-				<a href="%s" class="btn jvb-oauth-connect" data-service="%s">%s Authorize Connection</a>',
+				<a href="%s" class="btn jvb-oauth-connect" data-service="%s">%s Authorize Connection</a>
+				</div>',
 				$this->title,
 				$this->getOAuthURL($returnURL),
 				esc_attr($this->service_name),
 				$this->outputIcon($this->icon)
 			);
-		}
-		$form = sprintf(
-			'<details open>
+		} else {
+			$form = sprintf(
+				'<details open>
 			<summary>Connected Account
 				<div class="connection-status connected">
 					<span class="status-indicator">●</span>
@@ -334,22 +356,23 @@
 			</summary>
 			<span class="label">OAuth Redirect URL:</span>
 			<code>%s</code>',
-			$this->getRedirectUri()
-		);
-		if (!empty($credentials['updated_at'])) {
-			$form .= sprintf(
-				'<div class="oauth-meta"><small>Token expires: %s</small></div>',
-				array_key_exists('expires_at', $credentials) ?
+				$this->getRedirectUri()
+			);
+			if (!empty($credentials['updated_at'])) {
+				$form .= sprintf(
+					'<div class="oauth-meta"><small>Token expires: %s</small></div>',
+					array_key_exists('expires_at', $credentials) ?
 						human_time_diff($credentials['expires_at']) :
 						'Never'
-			);
+				);
+			}
+			//
+			if (method_exists($this, 'renderOAuthConnectedOptions')) {
+				$form .= $this->renderOAuthConnectedOptions();
+			}
+			$form .= '</details>';
+			return $form;
 		}
-		//
-		if (method_exists($this, 'renderOAuthConnectedOptions')) {
-			$form .= $this->renderOAuthConnectedOptions();
-		}
-		$form .= '</details';
-		return $form;
 	}
 
 	/**
@@ -362,11 +385,9 @@
 
 	public function getOAuthURL(?string $return_url = null):string
 	{
-		if (!$this->hasOAuth) {
-			return '';
-		}
+
 		$credentials = $this->loadCredentials();
-		if (empty($this->oauth['authorize'])) {
+		if (empty($this->oauth->getAuthorize())) {
 			$this->logError('getOAuthUrl', 'OAuth authorize URL not configured');
 			return '';
 		}
@@ -404,14 +425,19 @@
 			$params = $this->addOAuthParams($params);
 		}
 
-		return $this->oauth['authorize'] . '?' . http_build_query($params);
+		return $this->oauth->getAuthorize() . '?' . http_build_query($params);
 	}
 
 	protected function getRedirectUri():string
 	{
-		if ($this->hasOAuth) {
-			return admin_url('admin-ajax.php?action='.BASE . $this->service_name . '_oauth_callback');
-		}
-		return rest_url('jvb/v1/oauth/callback?service=' . $this->service_name);
+		return admin_url('admin-ajax.php?action='.BASE . $this->service_name . '_oauth_callback');
+	}
+
+	/****************************************************
+	 * UTILITY
+	****************************************************/
+	protected function getAccessToken():string
+	{
+		return $this->loadCredentials()['access_token']??'';
 	}
 }

--
Gitblit v1.10.0