Jake Vanderwerf
2026-09-16 a239ef33ec489ce6d0f25ac2f256df0ac54a7e50
=Tackling the integrations again. This time Helcim, with some minnor edits to GMB and Square
3 files modified
218 ■■■■■ changed files
inc/integrations/services/GoogleMyBusiness.php 69 ●●●●● patch | view | raw | blame | history
inc/integrations/services/Helcim.php 130 ●●●●● patch | view | raw | blame | history
inc/integrations/services/Square.php 19 ●●●●● patch | view | raw | blame | history
inc/integrations/services/GoogleMyBusiness.php
@@ -169,12 +169,81 @@
    protected function refreshAccessToken(OAuthCredentials $credentials): array
    {
        if (empty($this->getClientID()) || empty($this->getClientSecret()) || empty($credentials->refresh_token)) {
            $this->logError('refreshAccessToken', 'Missing refresh token');
            return $this->response(false, 'Missing client ID, client Secret, or refresh token');
        }
        $response = wp_remote_post(
            $this->oauth->getToken(),
            [
                'timeout'   => 15,
                'body'  => [
                    'grant_type'    => 'refresh_token',
                    'refresh_token' => $credentials->refresh_token,
                    'client_id'     =>$this->getClientID(),
                    'client_secret' =>$this->getClientSecret()
                ],
                'headers'   => $this->getOAuthRequestHeaders()
            ]
        );
        if (is_wp_error($response)) {
            $this->logError(
                'refreshAccessToken',
                'Failed to refresh GMB Token',
                [
                    'error' => $response->get_error_message()
                ]
            );
            return $this->response(false, 'Failed to refresh GMB Token');
        }
        $data = json_decode(wp_remote_retrieve_body($response), true);
        if (empty($data['access_token'])) {
            $this->logError('refreshAccessToken', 'No access token in response');
            return $this->response(false, 'No access token in response');
        }
        $credentials = [
            'access_token'  => $data['access_token'],
            'expires_at'    => time() + (int)($data['expires_in']??3600),
            // Google usually omits refresh_token on refresh — keep the old one
            'refresh_token' => $body['refresh_token'] ?? $credentials->refresh_token,
        ];
        $this->saveCredentials($credentials);
        return $this->response(true, 'Successfully refreshed token');
    }
    /*********************************************************
     * UTILITY
    *********************************************************/
    protected function getClientID():string|false
    {
        $switch = false;
        if ($this->userID !== null) {
            $switch = $this->userID;
            $this->switchUser(null);
        }
        $ID =  $this->loadCredentials()['client_id']??false;
        if ($switch) {
            $this->switchUser($switch);
        }
        return $ID;
    }
    protected function getClientSecret():string|false
    {
        $switch = false;
        if ($this->userID !== null) {
            $switch = $this->userID;
            $this->switchUser(null);
        }
        $ID = $this->loadCredentials()['client_secret']??false;
        if ($switch) {
            $this->switchUser($switch);
        }
        return $ID;
    }
    protected function getAccountID():string|false
    {
        return $this->loadCredentials()['account_id']??false;
inc/integrations/services/Helcim.php
@@ -4,6 +4,12 @@
use JVBase\integrations\Admin;
use JVBase\Instance;
use JVBase\integrations\Integrations;
use JVBase\integrations\OAuth;
use JVBase\integrations\OAuthCredentials;
use JVBase\integrations\Requests;
use JVBase\integrations\SyncToPost;
use JVBase\integrations\UserConnection;
use WP_Error;
if (!defined('ABSPATH')) {
    exit;
@@ -22,10 +28,132 @@
 */
class Helcim extends Integrations
{
    use Admin, Instance;
    use Admin, Instance, OAuth, Requests, UserConnection, SyncToPost;
    protected function __construct()
    {
        $this->service_name = 'helcim';
        $this->title = 'Helcim';
        $this->icon = 'currency-dollar';
        $this->canSync = [
            'initial'   => true,
            'update'    => true,
            'delete'    => true,
        ];
        $this->baseUrl = 'https://api.helcim.com/v2';
        $this->defineFields();
        $this->defineInstructions();
        $this->defineActions();
        parent::__construct();
    }
        protected function defineFields():void
        {
            $api_token = $this->addField(
                'api_token',
                'API Token',
                'text',
                [$this, 'testUserCanConnect']
            );
                $api_token->setSubType('password');
                $api_token->setRequired();
                $api_token->setHint('Found in Helcim Dashboard → Settings → API Access');
            $currency = $this->addField(
                'currency',
                'Currency',
                'select',
                [$this, 'testUserSetup']
            );
                $currency->setOptions([
                    'CAD'   => 'CAD',
                    'USD'   => 'USD'
                ]);
                $currency->setDefault('CAD');
            $feeSaver = $this->addField(
                'fee_saver',
                'Fee Saver',
                'true_false',
                [$this, 'testUserSetup']
            );
                $feeSaver->setHint('Pass processing fees to customers? (Not compatible with Google Pay)');
            $allowACH = $this->addField(
                'allow_ach',
                'Allow ACH/Bank Payments',
                'true_false',
                [$this, 'testUserSetup']
            );
                $allowACH->setHint('Enable bank account payments alongside credit card');
        }
            public function testUserCanConnect():bool
            {
                $user = get_current_user_id();
                return $this->userCanConnect($user);
            }
            public function testUserSetup():bool
            {
                return $this->isSetUp() && $this->testUserCanConnect() && !empty($this->getApiToken());
            }
        protected function defineInstructions():void
        {
            $this->instructions = [
                'Go to <a href="https://my.helcim.com" target="_blank">Helcim Dashboard</a>',
                'Navigate to Settings → API Access',
                'Create a new API Access Configuration',
                'Enable permissions: General (Customers, Invoices, Products), Transaction Processing',
                'Copy the API Token and paste it below',
            ];
        }
        protected function defineActions():void
        {
            $this->addAction(
                'Initialize Checkout',
                [$this, 'initializeCheckout'],
                'initialize_checkout'
            );
        }
    protected function initialize(): void
    {
        // TODO: Implement initialize() method.
    }
    protected function refreshAccessToken(OAuthCredentials $credentials): array
    {
        // TODO: Implement refreshAccessToken() method.
    }
    protected function getRequestHeaders(): array
    {
        // TODO: Implement getRequestHeaders() method.
    }
    protected function handleResponse(WP_Error|array $response, string $method, string $endpoint): array
    {
        // TODO: Implement handleResponse() method.
    }
    protected function setContentTypes(): void
    {
        // TODO: Implement setContentTypes() method.
    }
    protected function formatForService(int $itemID, string $type = 'post'): array
    {
        // TODO: Implement formatForService() method.
    }
    /*************************************************************************
     * UTILITY
    *************************************************************************/
    protected function getApiToken():string|false
    {
        return $this->loadCredentials()['api_token']??false;
    }
}
inc/integrations/services/Square.php
@@ -254,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(),
@@ -279,17 +284,19 @@
        }
        $data = json_decode(wp_remote_retrieve_body($response));
        if (isset($data['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');
        }
            $credentials = [
                'access_token'  =>  $data['access_token'],
                'expires_at'    => time() + ($data['expires_in'] ?? 2592000) // 30 days default
            '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');
        }
        return $this->response(false, 'Something went wrong that wasn\'t caught - could not refresh access token');
    }
    protected function getRequestHeaders(): array
    {