Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
8 changes: 8 additions & 0 deletions Api/ReclaimInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ public function reclaim();
*/
public function getWebhookSecret();

/**
* Returns the registered webhooks
*
* @return mixed[]
* @api
*/
public function getWebhooks();

/**
* Returns the Klaviyo log file
*
Expand Down
96 changes: 96 additions & 0 deletions Cron/ProductsTopic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace Klaviyo\Reclaim\Cron;

use Klaviyo\Reclaim\Helper\Logger;
use Klaviyo\Reclaim\Model\SyncsFactory;
use Klaviyo\Reclaim\Model\ResourceModel\Products;
use Klaviyo\Reclaim\Model\ResourceModel\Products\CollectionFactory;

class ProductsTopic
{
/**
* Klaviyo Logger
* @var Logger
*/
protected $_klaviyoLogger;

/**
* Klaviyo Products Resource Model
* @var Products
*/
protected $_klProduct;

/**
* Klaviyo Products Collection
* @var CollectionFactory
*/
protected $_klProductCollectionFactory;

/**
* Klaviyo Syncs Model
* @var SyncsFactory
*/
protected $_klSyncFactory;

/**
* @param Logger $klaviyoLogger
* @param Products $klProduct
* @param SyncsFactory $klSyncFactory
* @param CollectionFactory $klProductCollectionFactory
*/
public function __construct(
Logger $klaviyoLogger,
Products $klProduct,
SyncsFactory $klSyncFactory,
CollectionFactory $klProductCollectionFactory
)
{
$this->_klaviyoLogger = $klaviyoLogger;
$this->_klProduct = $klProduct;
$this->_klSyncFactory = $klSyncFactory;
$this->_klProductCollectionFactory = $klProductCollectionFactory;
}

public function queueKlProductsForSync()
{
$klProductsCollection = $this->_klProductCollectionFactory->create();
$klProductsToSync = $klProductsCollection->getRowsForSync('NEW')
->addFieldToSelect(['id','payload','status','topic', 'klaviyo_id'])
->getData();

if (empty($klProductsToSync))
{
return;
}

$idsToUpdate = [];

foreach ($klProductsToSync as $klProductToSync)
{
$klSync = $this->_klSyncFactory->create();
$klSync->setData([
'payload'=> $klProductToSync['payload'],
'topic'=> $klProductToSync['topic'],
'klaviyo_id'=>$klProductToSync['klaviyo_id'],
'status'=> 'NEW'
]);
try {
$klSync->save();
array_push($idsToUpdate, $klProductToSync['id']);
} catch (\Exception $e) {
$this->_klaviyoLogger->log(sprintf('Unable to move row: %s', $e));
}
}

$klProductsCollection->updateRowStatus($idsToUpdate, 'MOVED');
}

public function clean()
{
$klProductsCollection = $this->_klProductCollectionFactory->create();
$idsToDelete = $klProductsCollection->getIdsToDelete('MOVED');

$klProductsCollection->deleteRows($idsToDelete);
}
}
24 changes: 21 additions & 3 deletions Helper/ScopeSetting.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class ScopeSetting extends \Magento\Framework\App\Helper\AbstractHelper

const WEBHOOK_SECRET = 'klaviyo_reclaim_webhook/klaviyo_webhooks/webhook_secret';
const PRODUCT_DELETE_BEFORE = 'klaviyo_reclaim_webhook/klaviyo_webhooks/using_product_delete_before_webhook';

const PRODUCT_SAVE_AFTER = 'klaviyo_reclaim_webhook/klaviyo_webhooks/using_product_save_after_webhook';

const KLAVIYO_OAUTH_NAME = 'klaviyo_reclaim_oauth/klaviyo_oauth/integration_name';

protected $_scopeConfig;
Expand Down Expand Up @@ -143,6 +144,19 @@ public function getWebhookSecret($storeId = null)
return $this->getScopeSetting(self::WEBHOOK_SECRET, $storeId);
}

public function getWebhooks()
{
$registeredWebhooks = [];

$product_delete = $this->getProductDeleteBeforeSetting();
$product_save = $this->getProductSaveAfterSetting();

array_push($registeredWebhooks, ['product/delete', $product_delete)];
array_push($registeredWebhooks, ['product/save', $product_save]);

return $registeredWebhooks;
}

public function isEnabled($storeId = null)
{
return $this->getScopeSetting(self::ENABLE, $storeId);
Expand Down Expand Up @@ -221,7 +235,7 @@ public function getConsentAtCheckoutSMSListId($storeId = null)
{
return $this->getScopeSetting(self::CONSENT_AT_CHECKOUT_SMS_LIST_ID, $storeId);
}

public function getConsentAtCheckoutSMSConsentText($storeId = null)
{
return $this->getScopeSetting(self::CONSENT_AT_CHECKOUT_SMS_CONSENT_TEXT, $storeId);
Expand Down Expand Up @@ -264,5 +278,9 @@ public function getProductDeleteBeforeSetting($storeId = null)
return $this->getScopeSetting(self::PRODUCT_DELETE_BEFORE, $storeId);
}

}
public function getProductSaveAfterSetting($storeId = null)
{
return $this->getScopeSetting(self::PRODUCT_SAVE_AFTER, $storeId);
}

}
18 changes: 8 additions & 10 deletions Helper/Webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ public function __construct(

/**
* @param string $webhookType
* @param array $data
* @param string $data
* @param string $klaviyoId
* @return string
* @throws Exception
*/
public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
{

if (!$klaviyoId) {
$klaviyoId = $this->_klaviyoScopeSetting->getPublicApiKey();
}
Expand All @@ -51,12 +50,12 @@ public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_POSTFIELDS => $data,
CURLOPT_USERAGENT => self::USER_AGENT,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Magento-two-signature: ' . $this->createWebhookSecurity($data),
'Content-Length: '. strlen(json_encode($data)),
'Content-Length: '. strlen($data),
'Topic: ' . $webhookType
),
]);
Expand All @@ -66,24 +65,23 @@ public function makeWebhookRequest($webhookType, $data, $klaviyoId=null)
$err = curl_errno($curl);

if ($err) {
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, json_encode($data)));
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, $data));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe we could use double-quotes for string interpolation

Suggested change
$this->_klaviyoLogger->log(sprintf('Unable to send webhook to %s with data: %s', $url, $data));
$this->_klaviyoLogger->log("Unable to send webhook to $url with data: $data");

}

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

return $response;
}

/**
* @param array data
* @param string data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a description to this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work?

* @param string $data json payload used to create hmac signature

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param string data
* Returns an HMAC signature for webhooks
* @param string data

* @return string
* @throws Exception
*/
private function createWebhookSecurity(array $data)
private function createWebhookSecurity(string $data)
{
$webhookSecret = $this->_klaviyoScopeSetting->getWebhookSecret();
return hash_hmac('sha256', json_encode($data), $webhookSecret);

return hash_hmac('sha256', $data, $webhookSecret);
}
}

5 changes: 5 additions & 0 deletions Model/Reclaim.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function getWebhookSecret()
return $this->_klaviyoScopeSetting->getWebhookSecret();
}

public function getWebhooks()
{
return $this->_klaviyoScopeSetting->getWebhooks();
}

/**
* Returns the Klaviyo log file
*
Expand Down
150 changes: 150 additions & 0 deletions Observer/ProductSaveAfter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

namespace Klaviyo\Reclaim\Observer;

use Exception;
use Klaviyo\Reclaim\Helper\ScopeSetting;
use Klaviyo\Reclaim\Helper\Webhook;
use Klaviyo\Reclaim\Helper\Logger;
use Klaviyo\Reclaim\Model\ProductsFactory;

use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\Product;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;


class ProductSaveAfter implements ObserverInterface
{
/**
* Klaviyo scope setting helper
* @var ScopeSetting $klaviyoScopeSetting
*/
protected $_klaviyoScopeSetting;

/**
* Klaviyo logger helper
* @var \Klaviyo\Reclaim\Helper\Logger $klaviyoLogger
*/
protected $_klaviyoLogger;

/**
* @var Webhook $webhookHelper
*/
protected $_webhookHelper;
protected $_categoryFactory;
protected $_klProductFactory;
protected $_stockRegistry;
protected $product_category_names = [];

/**
* @param Webhook $webhookHelper
* @param ScopeSetting $klaviyoScopeSetting
* @param CategoryFactory $categoryFactory
* @param ProductsFactory $klProductFactory
* @param StockRegistryInterface $stockRegistry
* @param Logger $klaviyoLogger
*/
public function __construct(
Webhook $webhookHelper,
ScopeSetting $klaviyoScopeSetting,
CategoryFactory $categoryFactory,
ProductsFactory $klProductFactory,
StockRegistryInterface $stockRegistry,
Logger $klaviyoLogger
) {
$this->_webhookHelper = $webhookHelper;
$this->_klaviyoScopeSetting = $klaviyoScopeSetting;
$this->_categoryFactory = $categoryFactory;
$this->_klProductFactory = $klProductFactory;
$this->_klaviyoLogger = $klaviyoLogger;
$this->_stockRegistry = $stockRegistry;
}

/**
* customer register event handler
*
* @param Observer $observer
* @return void
* @throws Exception
*/
public function execute(Observer $observer)
{
$product = $observer->getEvent()->getProduct();
$storeIds = $product->getStoreIds();
$storeIdKlaviyoMap = $this->_klaviyoScopeSetting->getStoreIdKlaviyoAccountSetMap($storeIds);

foreach ($storeIdKlaviyoMap as $klaviyoId => $storeIds) {
if (empty($storeIds)) {
continue;
}

if ($this->_klaviyoScopeSetting->getWebhookSecret() && $this->_klaviyoScopeSetting->getProductSaveAfterSetting($storeIds[0])) {

$normalizedProduct = $this->normalizeProduct($product);
$data = [
"status"=>"NEW",
"topic"=>"product/save",
"klaviyo_id"=>$klaviyoId,
"payload"=>json_encode($normalizedProduct)
];
$klProduct = $this->_klProductFactory->create();
$klProduct->setData($data);
$klProduct->save();

// $this->_klaviyoLogger->log( print_r("Created new row?", true));

// $this->_webhookHelper->makeWebhookRequest('product/save', $normalizedProduct, $klaviyoId);
}
}
}

private function normalizeProduct($product=null)
{
if ($product == null) {
return;
}

$product_id = $product->getId();

// remove thumnbail image url?
$product_info = array(
'product' => array(
'store_ids' => $product->getStoreIds(),
'ID' => $product_id,
'TypeID' => $product->getTypeId(),
'Name' => $product->getName(),
'qty' => $this->_stockRegistry->getStockItem($product_id)->getQty(),
'Visibility' => $product->getVisibility(),
'IsInStock' => $product->isInStock(),
'Status' => $product->getStatus(),
'CreatedAt' => $product->getCreatedAt(),
'UpdatedAt' => $product->getUpdatedAt(),
'FirstImageURL' => $product->getImage(),
'ThumbnailImageURL' => $product->getThumbnail(),
'metadata' => array(
'price' => $product->getPrice(),
'sku' => $product->getSku()
),
'categories' => []
)
);

if ($product->getSpecialPrice()) {
$product_info['metadata']['special_price'] = $product->getSpecialPrice();
$product_info['metadata']['special_from_date'] = $product->getSpecialFromDate();
$product_info['metadata']['special_to_date'] = $product->getSpecialToDate();
}

$product_category_ids = $product->getCategoryIds();
$category_factory = $this->_categoryFactory->create();
foreach ($product_category_ids as $category_id) {
$category = $category_factory->load($category_id);
$product_info['categories'][$category_id] = $category->getName();
}

return $product_info;
}
}
3 changes: 3 additions & 0 deletions etc/adminhtml/events.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<event name="catalog_product_delete_before">
<observer name="klaviyo_reclaim" instance="Klaviyo\Reclaim\Observer\ProductDeleteBefore" />
</event>
<event name="catalog_product_save_after">
<observer name="klaviyo_reclaim" instance="Klaviyo\Reclaim\Observer\ProductSaveAfter"/>
</event>
<event name="admin_system_config_changed_section_klaviyo_reclaim_oauth">
<observer name="custom_admin_system_config_changed_section_klaviyo_reclaim_oauth" instance="Klaviyo\Reclaim\Observer\KlaviyoOAuthObserver"/>
</event>
Expand Down
Loading