Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.0.12 - v3 upgrade #263

Merged
merged 31 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6899d85
WIP
jordanleslie Sep 15, 2023
92375ee
WIP
jordanleslie Sep 18, 2023
bac8e6d
Move V3 logic to api wrapper
jordanleslie Sep 19, 2023
44a2f62
Cleanup and add newline
jordanleslie Sep 22, 2023
58d1429
Newlines
jordanleslie Sep 22, 2023
5ce38cf
Remove unused class, newline
jordanleslie Sep 22, 2023
1fbcfcc
Change getKlaviyoList to getLists in api wrapper
jordanleslie Sep 22, 2023
287812c
Remove union type from function
jordanleslie Sep 22, 2023
cfa59f5
Remove unused constructor
jordanleslie Sep 22, 2023
9898dda
V3 API patch latest
siddwarkhedkar Sep 25, 2023
37bcd47
codesniff but manual
siddwarkhedkar Sep 25, 2023
dbdc062
codesniff but manual 2
siddwarkhedkar Sep 25, 2023
bb7631e
codesniff but manual 3
siddwarkhedkar Sep 25, 2023
14c2ce5
return response data and id from get/create profile
jordanleslie Sep 25, 2023
d4d3153
Revert changes to API:SUBSCRIBE
jordanleslie Sep 25, 2023
f8a51f9
Merge pull request #265 from klaviyo/sw_v3_m2_revision
jordanleslie Sep 25, 2023
eb933f3
Update to use Klaviyo object
siddwarkhedkar Sep 26, 2023
db8d488
Codesniff commit
siddwarkhedkar Sep 26, 2023
57ebc34
Codesniff commit 2
siddwarkhedkar Sep 26, 2023
46b596b
Testing fixes 1
siddwarkhedkar Sep 26, 2023
a6cb08a
Testing fixes 1
siddwarkhedkar Sep 26, 2023
d50cfc7
Return success status and error detail when getLists fails
jordanleslie Sep 26, 2023
dda2a7f
Format
jordanleslie Sep 28, 2023
93e1ca6
Fix typo in subscribe profile request body
jordanleslie Sep 29, 2023
63955da
Retry request if statusCode is not 202
jordanleslie Sep 29, 2023
89fbd61
Expand success response status code to attempt retry
jordanleslie Sep 29, 2023
8599575
Check for false response before retry
jordanleslie Sep 29, 2023
d84ae18
Add required data key to unsubscribe email request body
jordanleslie Sep 29, 2023
a0cee94
Remove return statement from addProfileToList call
siddwarkhedkar Sep 29, 2023
55c0539
Update changelog
jordanleslie Sep 29, 2023
96bb603
Merge branch 'm2_v3_upgrade' of github.com:klaviyo/magento2-klaviyo i…
jordanleslie Sep 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
siddwarkhedkar marked this conversation as resolved.
Show resolved Hide resolved
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