Install and adjust settings

The first step for any integration to work with Kommo is installing it in the account; that’s why this is the first thing you should consider once you decide to integrate with Kommo.

Contents

  1. Authorization in Kommo
  2. Authorization in chat service ( OAuth, QR code, API key)
  3. Integration settings
  4. Connect chat channel
  5. Save integration settings

Authorization in Kommo

In Kommo, we use OAuth 2.0 authorization. Once an administrator installs the integration in their account, Kommo will produce the tokens needed for authorizing the integration into the account (Access token, Refresh token, Bearer). See our step-by-step example to accomplish this easily.

All produced tokens should be saved in your database.

use KommoChat\Task\CreateChannelTask;

class SaveOAuthTokenUseCase implements OAuthServiceInterface
{
    protected WidgetSettingRepository $widgetSettingRepository;
    
    public function saveOAuthToken(OAuthSaveTask $task): void
    {
            $widgetSettingRepository->setAccessToken($accessToken->getToken());
            $widgetSettingRepository->setRefreshToken($accessToken->getRefreshToken());
            $widgetSettingRepository->setExpiresAt($accessToken->getExpires());
            $widgetSettingRepository->setKommoAccountId($kommoAccountId);
            $domainExplode = explode('.', $task->getReferrer());
            $widgetSettingRepository->setTld(end($domainExplode));
            $widgetSettingRepository->save());
            $task->setSuccess();
    }
}

Authorization in chat service

Once the account administrator installs the integration, you should provide the ability to set the integration settings, including the authorization in the chat service you are integrating using your side’s preferred method ( API key, OAuth, QR code. …).

In the widget settings, you can provide input fields for the integration settings in the widget UI/UX settings.

OAuth

If your chat service requires integrations to use OAuth authentication, you can provide a button on the integration setting window.

And when clicking on the sign-in button, the authorization in the chat service should be called.

const Oauth = ({ link, setIsAuth }) => {
  const { api, self } = useContext(WidgetContext);
  const [windowClosed, setWindowClosed] = useState(false);
  const [isButtonDisable, setIsButtonDisable] = useState(false);

  const onClick = () => {
    setIsButtonDisable(true);
    Amplitude.logEvent('GBMSignIn');
    const oauthWindow = window.open(
      link, 'YOURSERVICE', `width=600, height=600, top=${((window.screen.height - 600) / 2)},left=${((window.screen.width - 600) / 2)}`,
    );
    const oauthInterval = setInterval(() => {
      if (oauthWindow.closed) {
        clearInterval(oauthInterval);
        setWindowClosed(true);
      }
    }, 250);
  };

<button
      onClick={onClick}
      disabled={isButtonDisable}
      className={styles.button}
    >
    <span>{self.langs['Sign in to SERVICE (button)']}</span>
</button>

QR-code

If your chat service provides authentication using a QR code, you can show a window on your integration settings to scan the code.

<div className="qr-wrapper">
    {qrCode && qrCode.qr_code
        ? <img
            className="qr-image"
            src={`data:image/png;base64,${qrCode.qr_code}`}
        />
        : <div style={{ height: 229, width: 240, position: 'relative' }}>
            <Loader isAbsoluteCentered />
        </div>}
</div>

API key

If your chat service uses API keys to authenticate applications, you can easily provide the required input fields.
In the UI part of the integration, after installing the integration, you can provide an input field with a button to log in to the chat service using the ID.

<div className="settings__connect">
     <label className="settins__lable" htmlFor="clientID">
	{i18n('Chat client ID')}
  </label>
  <input
	className="settins__input"
	type="text"
	value={id}
	onChange={(event) => setId(event.target.value)}
	id="clientID"
  />
  {errorId && <p className="error__text">{errorId}</p>}
  <Button
	className="settings__connect-button setting__button"
	onClick={handleClick}
	isLoading={isLoading}
	isBlue={isLoading}
  >
	{i18n('Connect')}
  </Button>
</div>

Disposable tokens

You can transfer one-time tokens together with other types of authentications. The token contains encrypted information about the user who authenticated the integration in Kommo.

export default class Api {
  constructor(options) {
    const { createReqWithDisposableToken, onTokenExpired } = options;

    this.createReqWithDisposableToken = (params) => {
      const $def = $.Deferred();

      const sendRequest = (ajaxParams) => createReqWithDisposableToken(ajaxParams)
        .then(
          (data, textStatus, jqXHR) => {
            $def.resolveWith(jqXHR, [data, textStatus, jqXHR]);
          },
          (jqXHR, textStatus, errorThrown) => {
            if (jqXHR && jqXHR.responseJSON && jqXHR.responseJSON.error_code === 1015) {
              onTokenExpired(jqXHR.responseJSON.refresh_token_url);
            }
            $def.rejectWith(jqXHR, [jqXHR, textStatus, errorThrown]);
          },
        );

      sendRequest(params);

      return $def;
    };
  }
  getChannels() {
    return this.createReqWithDisposableToken({
      url: `${BASE_URL}/messenger/channels`,
      type: 'GET',
    });
  }
  addNewChannel() {
    return this.createReqWithDisposableToken({
      url: `${BASE_URL}/messenger/channels`,
      contentType: 'application/json',
      type: 'POST',
    });
  }

}

Integration settings

You can include as many essential configurations as you wish in the first step. Once the administrator saves the configurations, you should put them into your database and show the second group of configurations if you want. You can allow users to add templates in the integration or use the templates saved in the account.

You should choose how many chat service accounts can be registered in the integration and the relationship between these accounts and the account pipelines.

You can choose which of the settings might be changed while the integration is still working, and there is no need to uninstall and reinstall it, and which of them require reinstalling the widget.

You can also provide the ability to synchronize some settings automatically between the integration and the Kommo account, like the language and country.

Connect chat channel

After authenticating the integration in both Kommo and the chat service, you should connect the chat channel that you have registered before to the Kommo account. The connected chat channel should be saved with the other widget settings.

nnamespace Widget\UseCase;

use AmoJoModule\AmoJoClient;
use Widget\Task\AmoJoChannelConnectTask;

class ConnectAmoJoChannelUseCase
{
    private AmoJoClient $AmoJoClient;

    /*performs AmoJoChannelConnectTask*/
    /*That has $client, $scopeId, $success*/ 
    public function handle(AmoJoChannelConnectTask $task): void
    {
        $apiClient = $task->getClient();
        $AmoJoId = $apiClient->getAmoJoId();
        $baseDomain = $apiClient->getBaseUrl();
        $body = $this->AmoJoClient->connectAccount($AmoJoId);
        if (!empty($body)) {
            $task->setScopeId($body['scope_id']);
            $task->setSuccess();
        }
    }
}

Suppose you decided that the users can connect more than one chat service account with one Kommo account. In that case, you should consider connecting a chat channel for each of them, with a limit you choose that satisfies your business needs and doesn’t complicate the situation for you or the users.

class CreateChannelUseCase
{
    const MAX_CHAT_CHANNELS = 50;
    protected $channelsRepository;

    /** Task parameters 
      * @var int $amocrmAccountId
      * @var WhatsappChannels $channel
      * @var bool $success
     **/
    public function handle(CreateChannelTask $task): void
    {
        $accountId = $task->getKommoAccountId();
        $channels = $this->channelsRepository->getByKommoAccountId($accountId);
        if ($channels->count() > self::MAX_CHAT_CHANNELS) {
            throw TooMuchChannelsException::create();
        }

        $channel = $this->channelsRepository->createNew($accountId);
        $channel->save();

        $task->setSuccess($channel);
    }
}

After performing this use case and connecting the chat channel, sources must be created in the desired pipelines.

$channel = $createChannelTask->getChannel();

$responseResource = $this->resourceGenerator ->fromArray([
                'id' => $channel->getId(),
                'name' => $channel->getName(),
                'phone' => '',
                'pipeline_id' => $pipelineid,
                'is_multidevice_mode_on' => 1,
                'is_default' => $widgetSettings->getDefaultChannelId() === $channel->getId(),
            ])
            ->withLink(new Link(
                'self',
                (string)$request->getUri()
            ));

Save the integration settings

After setting up the integration in the account, all settings should be saved in your database.

Kommo will only consider your integration configured successfully once the settings are saved correctly. This task is implemented in the Ui/UX by calling the onSave callback function when the administrator chooses to save the integration settings. This function should return true once the widget is successfully configured and false otherwise.

var CustomWidget = function () {
  	self.clickSaveButton = function() {
	    /*
	     * This function describes what to do when clicking on the saving button
	     */
	    var $save_button = $(document).find('.js-widget-save');
	    $save_button.click();
	};
	widget_register: function (e) {
	   /* 
	    * Registering the widget
	    */
	   self.tools.request({}, 'register_widget', function (res) {
	        self.registered = self.checked = true;
		self.clickSaveButton();
          });
	},
	
	this.callbacks = {
		//other callbacks
		//...
	      	onSave: function () {
		   /*
		    * Here you should perform your settings 
		    */
		    if(/*Here you should test if settings were successfully saved*/) {
		      alert ('Your integration settings have been successfully saved.');
		      return true;
		    }
		    alert('Kommo could not register your widget.');
		    return false;
	      }
	      //other callbacks
        }
        return this;
}

And you should save the provided settings and the authorization in the Kommo account in the widget part of the integration backend.

namespace Widget\UseCase;

use Console\Worker\SyncSourcesWorker;
use Queue\Beanstalkd;
use Widget\Model\WidgetSettings;
use Widget\Task\AmoJoChannelConnectTask;
use Widget\Task\SaveWidgetSettingsTask;

class SaveWidgetSettingUseCase
{
    private ConnectAmoJoChannelUseCase $connectAmoJoChannelUseCase;
    private Beanstalkd $queue;

    public function handle(SaveWidgetSettingsTask $task): void
    {
        $widgetSettings = $task->getWidgetSettings();

        $widgetSettings->setBusinessId($task->getchatService());

        $apiClient = $this->aPPClientFactory->byWidgetSettings($widgetSettings);

        $AmoJoConnectTask = new AmoJoChannelConnectTask($apiClient);
        $this->connectAmoJoChannelUseCase->handle($AmoJoConnectTask);

        if ($AmoJoConnectTask->isSuccess()) {
            $scopeId = $AmoJoConnectTask->getScopeId();
            $widgetSettings->setAmoJoScopeId($scopeId);
            $widgetSettings->setStatus(WidgetSettings::WIDGET_ACTIVE);
            if ($widgetSettings->save()) {
                $syncSourceTask = new QueueTask(
                    SyncSourcesWorker::QUEUE_NAME,
                    [
                        'kommo_account_id' => $widgetSettings->getKommoAccountId(),
                    ]
                );
                $this->queue->send($syncSourceTask->toArray());
                $task->setSuccess();
            }
        }
    }
}

Now, let’s move to the exchange messages use case.