Skip to content

Commit

Permalink
Merge pull request #263 from klaviyo/m2_v3_upgrade
Browse files Browse the repository at this point in the history
4.0.12 - v3 upgrade
  • Loading branch information
jordanleslie authored Sep 29, 2023
2 parents 325dde2 + 96bb603 commit 7c7d8a9
Show file tree
Hide file tree
Showing 17 changed files with 630 additions and 144 deletions.
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- BEGIN RELEASE NOTES -->
### [Unreleased]

### [4.1.0] - 2023-09-29

#### Added
- New Klaviyo onsite object
- New X-Klaviyo-User-Agent to headers to collect plugin usage meta data
- Added support for Klaviyo V3 API

#### Removed
- Support for V2 APIs: /track and /identify
- Removed _learnq onsite object in favor of klaviyo object

### [4.0.12] - 2023-07-20

#### Fixed
Expand Down Expand Up @@ -238,7 +249,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

<!-- END RELEASE NOTES -->
<!-- BEGIN LINKS -->
[Unreleased]: https://github.com/klaviyo/magento2-klaviyo/compare/4.0.12...HEAD
[Unreleased]: https://github.com/klaviyo/magento2-klaviyo/compare/4.1.0...HEAD
[4.1.0]: https://github.com/klaviyo/magento2-klaviyo/compare/4.0.12...4.1.0
[4.0.12]: https://github.com/klaviyo/magento2-klaviyo/compare/4.0.11...4.0.12
[4.0.11]: https://github.com/klaviyo/magento2-klaviyo/compare/4.0.10...4.0.11
[4.0.10]: https://github.com/klaviyo/magento2-klaviyo/compare/4.0.9...4.0.10
Expand Down
190 changes: 62 additions & 128 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

namespace Klaviyo\Reclaim\Helper;

use Klaviyo\Reclaim\Helper\ScopeSetting;
use Klaviyo\Reclaim\KlaviyoV3Sdk\KlaviyoV3Api;
use Magento\Framework\App\Helper\Context;
use Klaviyo\Reclaim\Helper\Logger;

class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
const USER_AGENT = 'Klaviyo/1.0';
const KLAVIYO_HOST = 'https://a.klaviyo.com/';
const LIST_V2_API = 'api/v2/list/';
const LIST_V3_API = 'api/list';

/**
* Klaviyo logger helper
Expand All @@ -30,6 +29,12 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper
*/
private $observerAtcPayload;

/**
* V3 API Wrapper
* @var KlaviyoV3Api $api
*/
protected $api;

public function __construct(
Context $context,
Logger $klaviyoLogger,
Expand All @@ -39,6 +44,7 @@ public function __construct(
$this->_klaviyoLogger = $klaviyoLogger;
$this->_klaviyoScopeSetting = $klaviyoScopeSetting;
$this->observerAtcPayload = null;
$this->api = new KlaviyoV3Api($this->_klaviyoScopeSetting->getPublicApiKey(), $this->_klaviyoScopeSetting->getPrivateApiKey(), $klaviyoScopeSetting);
}

public function getObserverAtcPayload()
Expand All @@ -56,81 +62,82 @@ public function unsetObserverAtcPayload()
$this->observerAtcPayload = null;
}

public function getKlaviyoLists($api_key = null)
public function getKlaviyoLists()
{
if (!$api_key) {
$api_key = $this->_klaviyoScopeSetting->getPrivateApiKey();
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://a.klaviyo.com/api/v2/lists?api_key=' . $api_key);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);


$output = json_decode(curl_exec($ch));
$statusCode = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

if ($statusCode !== 200) {
if ($statusCode === 403) {
$reason = 'The Private Klaviyo API Key you have set is invalid.';
} elseif ($statusCode === 401) {
$reason = 'The Private Klaviyo API key you have set is no longer valid.';
} else {
$reason = 'Unable to verify Klaviyo Private API Key.';
try {
$lists_response = $this->api->getLists();
$lists = array();

foreach ($lists_response as $list) {
$lists[] = array(
'id' => $list['id'],
'name' => $list['attributes']['name']
);
}

$result = [
'success' => false,
'reason' => $reason
];
} else {
usort($output, function ($a, $b) {
return strtolower($a->list_name) > strtolower($b->list_name) ? 1 : -1;
});

$result = [
return [
'success' => true,
'lists' => $output
'lists' => $lists
];
} catch (\Exception $e) {
$this->_klaviyoLogger->log(sprintf('Unable to get list: %s', $e["detail"]));
return [
'success' => false,
'reason' => $e["detail"]
];
}

return $result;
}

/**
* @param string $email
* @param string|null $firstName
* @param string|null $lastName
* @param string|null $source
* @return bool|string
* @return array|false|null|string
*/
public function subscribeEmailToKlaviyoList($email, $firstName = null, $lastName = null, $source = null)
public function subscribeEmailToKlaviyoList($email, $firstName = null, $lastName = null)
{
$listId = $this->_klaviyoScopeSetting->getNewsletter();
$optInSetting = $this->_klaviyoScopeSetting->getOptInSetting();

$properties = [];
$properties['email'] = $email;
if ($firstName) {
$properties['$first_name'] = $firstName;
$properties['first_name'] = $firstName;
}
if ($lastName) {
$properties['$last_name'] = $lastName;
}
if ($source) {
$properties['$source'] = $source;
}
if ($optInSetting == ScopeSetting::API_SUBSCRIBE) {
$properties['$consent'] = ['email'];
$properties['last_name'] = $lastName;
}

$propertiesVal = ['profiles' => $properties];

$path = self::LIST_V2_API . $listId . $optInSetting;

try {
$response = $this->sendApiRequest($path, $propertiesVal, 'POST');
if ($optInSetting == ScopeSetting::API_SUBSCRIBE) {
// Subscribe profile using the profile creation endpoint for lists
$consent_profile_object = array(
'type' => 'profile',
'attributes' => array(
'email' => $email,
'subscriptions' => array(
'email' => [
'MARKETING'
]
)
)
);

$response = $this->api->subscribeMembersToList($listId, array($consent_profile_object));
} else {
// Search for profile by email using the api/profiles endpoint
$response = $this->api->searchProfileByEmail($email);
$profile_id = $response["profile_id"];
// If the profile exists, use the ID to add to a list
// If the profile does not exist, create
if ($profile_id) {
$this->api->addProfileToList($listId, $profile_id);
} else {
$new_profile = $this->api->createProfile($properties);
$this->api->addProfileToList($listId, $new_profile["profile_id"]);
}
}
} catch (\Exception $e) {
$this->_klaviyoLogger->log(sprintf('Unable to subscribe %s to list %s: %s', $email, $listId, $e));
$response = false;
Expand All @@ -141,19 +148,13 @@ public function subscribeEmailToKlaviyoList($email, $firstName = null, $lastName

/**
* @param string $email
* @return bool|string
* @return array|string|null
*/
public function unsubscribeEmailFromKlaviyoList($email)
{
$listId = $this->_klaviyoScopeSetting->getNewsletter();

$path = self::LIST_V2_API . $listId . ScopeSetting::API_SUBSCRIBE;
$fields = [
'emails' => [(string)$email],
];

try {
$response = $this->sendApiRequest($path, $fields, 'DELETE');
$response = $this->api->unsubscribeEmailFromKlaviyoList($email, $listId);
} catch (\Exception $e) {
$this->_klaviyoLogger->log(sprintf('Unable to unsubscribe %s from list %s: %s', $email, $listId, $e));
$response = false;
Expand All @@ -172,7 +173,6 @@ public function klaviyoTrackEvent($event, $customer_properties = [], $properties
return 'You must identify a user by email or ID.';
}
$params = array(
'token' => $this->_klaviyoScopeSetting->getPublicApiKey($storeId),
'event' => $event,
'properties' => $properties,
'customer_properties' => $customer_properties
Expand All @@ -181,72 +181,6 @@ public function klaviyoTrackEvent($event, $customer_properties = [], $properties
if (!is_null($timestamp)) {
$params['time'] = $timestamp;
}
return $this->make_request('api/track', $params);
}

protected function make_request($path, $params)
{
$url = self::KLAVIYO_HOST . $path;

$dataString = json_encode($params);
$options = array(
'http' => array(
'header' => "Content-type: application/json\r\n",
'method' => 'POST',
'content' => $dataString,
),
);

$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);

if ($response == '0') {
$this->_klaviyoLogger->log("Unable to send event to Track API with data: $dataString");
}

return $response == '1';
}

/**
* @param string $path
* @param array $params
* @param string $method
* @return mixed[]
* @throws \Exception
*/
private function sendApiRequest(string $path, array $params, string $method = null)
{
$url = self::KLAVIYO_HOST . $path;

//Add API Key to params
$params['api_key'] = $this->_klaviyoScopeSetting->getPrivateApiKey();

$curl = curl_init();
$encodedParams = json_encode($params);

curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => (!empty($method)) ? $method : 'POST',
CURLOPT_POSTFIELDS => $encodedParams,
CURLOPT_USERAGENT => self::USER_AGENT,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($encodedParams)
],
]);

// Submit the request
$response = curl_exec($curl);
$err = curl_errno($curl);

if ($err) {
throw new \Exception(curl_error($curl));
}

// Close cURL session handle
curl_close($curl);

return $response;
return $this->api->track($params);
}
}
7 changes: 7 additions & 0 deletions KlaviyoV3Sdk/Exception/KlaviyoApiException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Klaviyo\Reclaim\KlaviyoV3Sdk\Exception;

class KlaviyoApiException extends KlaviyoException
{
}
7 changes: 7 additions & 0 deletions KlaviyoV3Sdk/Exception/KlaviyoAuthenticationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Klaviyo\Reclaim\KlaviyoV3Sdk\Exception;

class KlaviyoAuthenticationException extends KlaviyoApiException
{
}
9 changes: 9 additions & 0 deletions KlaviyoV3Sdk/Exception/KlaviyoException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Klaviyo\Reclaim\KlaviyoV3Sdk\Exception;

use Exception;

class KlaviyoException extends Exception
{
}
7 changes: 7 additions & 0 deletions KlaviyoV3Sdk/Exception/KlaviyoRateLimitException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Klaviyo\Reclaim\KlaviyoV3Sdk\Exception;

class KlaviyoRateLimitException extends KlaviyoApiException
{
}
7 changes: 7 additions & 0 deletions KlaviyoV3Sdk/Exception/KlaviyoResourceNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Klaviyo\Reclaim\KlaviyoV3Sdk\Exception;

class KlaviyoResourceNotFoundException extends KlaviyoApiException
{
}
9 changes: 9 additions & 0 deletions KlaviyoV3Sdk/Exception/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;
Loading

0 comments on commit 7c7d8a9

Please sign in to comment.