You are here

PardotClient.php in Pardot Integration 2.x

File

src/Service/PardotClient.php
View source
<?php

namespace Drupal\pardot\Service;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\salesforce\SalesforceAuthProviderPluginManagerInterface;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use Drupal\Component\Serialization\Json;
use OAuth\OAuth2\Service\Exception\MissingRefreshTokenException;
use Drupal\Core\Config\ConfigFactory;

/**
 * Provides methods to execute Pardot API operations.
 *
 * @package Drupal\pardot\Service
 */
class PardotClient implements PardotClientInterface {
  use StringTranslationTrait;

  /**
   * The config factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactory
   */
  protected $configFactory;

  /**
   * The Pardot settings configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $pardotSettings;

  /**
   * Logger channel.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected $logger;

  /**
   * HTTP client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * State storage.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * Time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $time;

  /**
   * Auth provider manager.
   *
   * @var \Drupal\salesforce\SalesforceAuthProviderPluginManagerInterface
   */
  protected $authManager;

  /**
   * The Api base url.
   *
   * @var string
   */
  protected $apibaseUrl;

  /**
   * The Business id attached to the pardot user..
   *
   * @var string
   */
  protected $businessId;

  /**
   * PersonifySsoClient constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactory $configFactory
   *   The config factory service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   Logger channel factory.
   * @param \GuzzleHttp\Client $http_client
   *   HTTP client.
   * @param \Drupal\Core\State\StateInterface $state
   *   State storage.
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
   *   Translation.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   Time service.
   * @param \Drupal\salesforce\SalesforceAuthProviderPluginManagerInterface $authManager
   *   Auth manager service.
   */
  public function __construct(ConfigFactory $configFactory, LoggerChannelFactoryInterface $logger_factory, Client $http_client, StateInterface $state, TranslationInterface $string_translation, TimeInterface $time, SalesforceAuthProviderPluginManagerInterface $authManager) {
    $this->configFactory = $configFactory;
    $this->pardotSettings = $this->configFactory
      ->get('pardot.settings');
    $this->apibaseUrl = $this->pardotSettings
      ->get('api_url');
    $this->businessId = $this->pardotSettings
      ->get('salesforce_pardot_business_unit_id');
    $this->logger = $logger_factory
      ->get('pardot');
    $this->httpClient = $http_client;
    $this->state = $state;
    $this->stringTranslation = $string_translation;
    $this->time = $time;
    $this->authManager = $authManager;
    $auth_providers = $authManager
      ->getProviders();
    $selected_auth_provider = $this->pardotSettings
      ->get('salesforce_auth_provider');
    $this->authProvider = !empty($auth_providers[$selected_auth_provider]) ? $auth_providers[$selected_auth_provider] : FALSE;
    $this->authConfig = $authManager
      ->getConfig();
    $this->authToken = $authManager
      ->getToken();
  }

  /**
   * {@inheritdoc}
   */
  public function isInit() {
    if (!$this->authProvider || !$this->authToken || !$this->authManager || !$this->apibaseUrl || !$this->businessId) {
      $this->logger
        ->error($this
        ->t('Unable to authenticate into Pardot API. One of the required parameters is missing'));
      return FALSE;
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function authenticate($refresh = FALSE) {
    if ($this
      ->isInit()) {
      $token = $this->authToken
        ->getAccessToken();
      if ($refresh) {
        try {
          $token = $this->authProvider
            ->authManager()
            ->refreshToken()
            ->getAccessToken();
        } catch (\Exception $e) {
          $this->logger
            ->error("Pardot API could not refesh token.");
          return FALSE;
        }
      }
      if ($token) {
        return $token;
      }
    }
    return NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function retry($url, $request_data) {
    $token = $this
      ->authenticate(TRUE);
    if ($token) {
      $request_data['headers']['Authorization'] = "Bearer {$token}";
      $request_data['form_params']['api_key'] = $token;
      return $this
        ->executePardotOperation($url, $request_data, TRUE);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function executePardotOperation(string $url, array $request_data, $retry = FALSE, $method = 'POST') {
    if (!$url) {
      $this->logger
        ->error($this
        ->t('Attempted to execute a Pardot API operation without the endpoint URL'));
      return FALSE;
    }

    // Send a Pardot API request.
    try {
      $response = $this->httpClient
        ->request($method, $url, $request_data);
      $decoded_response = Json::decode($response
        ->getBody()
        ->getContents());

      // Got an error from Pardot.
      // I'm not sure it will ever enter this if statement better safe than sorry i guess.
      if (is_array($decoded_response) && isset($decoded_response['err'])) {

        // If token is invalid then get new one.
        $this->logger
          ->error($this
          ->t("Pardot API operation failed with code: @code. Message: @message", [
          '@code' => $decoded_response['@attributes']['err_code'] ?? 'Unknown Code',
          '@message' => $decoded_response['err'],
        ]));
        return FALSE;
      }
      else {
        $this->logger
          ->notice($this
          ->t("Pardot submission successful. @code.", [
          '@code' => 200,
        ]));
        return $decoded_response;
      }
    } catch (\Exception $e) {
      $decoded_response = Json::decode($e
        ->getResponse()
        ->getBody()
        ->getContents());
      $err_code = $decoded_response['@attributes']['err_code'] ?? 0;
      $err_codes = [
        184,
        401,
      ];
      if (is_array($decoded_response) && isset($decoded_response['err']) && in_array($err_code, $err_codes) && !$retry) {
        return $this
          ->retry($url, $request_data);
      }
      $this->logger
        ->error("Pardot API operation failed. Operation URL: {$url}. Error message: {$e->getMessage()}");
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getPardotProspect(string $visitor_email) {

    // Build the request to retrieve prospect data from Pardot.
    $post_data = $this
      ->buildDefaultPostData();
    $prospect_options = [
      'email' => $visitor_email,
    ];
    $post_data[RequestOptions::FORM_PARAMS] = array_merge($post_data[RequestOptions::FORM_PARAMS], $prospect_options);

    // Execute the request.
    $prospect_endpoint = "{$this->apibaseUrl}/api/prospect/version/4/do/read/email/{$visitor_email}";
    $response = $this
      ->executePardotOperation($prospect_endpoint, $post_data);
    if (is_array($response)) {
      $prospect_id = $response['prospect']['id'] ?? '';
      return $prospect_id;
    }
    return '';
  }

  /**
   * {@inheritdoc}
   */
  public function assignPardotProspect(string $visitor_id, string $prospect_id) {
    if (!$visitor_id || !$prospect_id) {
      $this->logger
        ->error($this
        ->t('Unable to assign Pardot prospect. Either Visitor ID or Prospect ID are missing.'));
      return FALSE;
    }

    // Build the request to retrieve prospect data from Pardot.
    $post_data = $this
      ->buildDefaultPostData();
    $assign_options = [
      'id' => $visitor_id,
      'prospect_id' => $prospect_id,
    ];
    $post_data[RequestOptions::FORM_PARAMS] = array_merge($post_data[RequestOptions::FORM_PARAMS], $assign_options);

    // Execute the request.
    $assign_endpoint = "{$this->apibaseUrl}/api/visitor/version/4/do/assign/id/{$visitor_id}?prospect_id={$prospect_id}";
    $response = $this
      ->executePardotOperation($assign_endpoint, $post_data);
    if (is_array($response) && $response['@attributes']['stat'] == 'ok') {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Build the default post data used by most service operations.
   *
   * @return array|bool
   *   Array of default post data or FALSE on failure.
   */
  protected function buildDefaultPostData() {
    $token = $this
      ->authenticate();
    if (!$token) {
      $this->logger
        ->error($this
        ->t('Unable to build POST data for Pardot API. One of the required parameters is missing'));
      return FALSE;
    }

    // Prepare post data.
    $payload = [
      RequestOptions::HEADERS => [
        'Authorization' => "Bearer {$token}",
        'Content-Type' => 'application/x-www-form-urlencoded',
        'Pardot-Business-Unit-Id' => $this->businessId,
      ],
      'data-urlencode' => [
        'id' => 7676,
      ],
      RequestOptions::FORM_PARAMS => [
        'api_key' => $token,
        'format' => 'json',
      ],
    ];
    return $payload;
  }

  /**
   * Gets current user's visitor ID.
   *
   * @return null|string
   *   The cookie value if it exists, NULL otherwise.
   */
  public function getVisitorId() {

    // maybe better way of doing it.
    //    $cookies = \Drupal::request()->cookies->all();
    //    foreach ($cookies as $cookie_name => $cookie_value) {
    //      if (preg_match('/visitor_id\d+$/', $cookie_name)) {
    //        $visitor_id = $cookie_value;
    //        break;
    //      }
    //    }
    // Get Pardot piAId.
    $account_id = $this->pardotSettings
      ->get('account_id');

    // Visitor cookie ID is account ID - 1000, for some reason.
    $cookie_name = 'visitor_id' . ((int) $account_id - 1000);
    if (isset($_COOKIE[$cookie_name])) {
      return $_COOKIE[$cookie_name];
    }
    return NULL;
  }

}

Classes

Namesort descending Description
PardotClient Provides methods to execute Pardot API operations.