-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from yangyao/webhook
Init webhook for feed plugin
- Loading branch information
Showing
19 changed files
with
974 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
namespace AfterShip\Feed\Api; | ||
interface WebhookEntityInterface | ||
{ | ||
const DATA_ID = 'id'; | ||
const DATA_TOPIC = 'topic'; | ||
const DATA_APP_KEY = 'app_key'; | ||
const DATA_ADDRESS = 'address'; | ||
const DATA_INTEGRATION_ID = 'integration_id'; | ||
|
||
/** | ||
* @return string | ||
*/ | ||
public function getId(); | ||
/** | ||
* @return string | ||
*/ | ||
public function getTopic(); | ||
/** | ||
* @return string | ||
*/ | ||
public function getAppKey(); | ||
/** | ||
* @return string | ||
*/ | ||
public function getAddress(); | ||
/** | ||
* @return string | ||
*/ | ||
public function getIntegrationId(); | ||
|
||
|
||
/** | ||
* @param string $id | ||
* @return string | ||
*/ | ||
public function setId($id); | ||
/** | ||
* @param string $topic | ||
* @return $this | ||
*/ | ||
public function setTopic($topic); | ||
/** | ||
* @param string $app_key | ||
* @return $this | ||
*/ | ||
public function setAppKey($app_key); | ||
/** | ||
* @param string $address | ||
* @return $this | ||
*/ | ||
public function setAddress($address); | ||
/** | ||
* @param string $integrationId | ||
* @return $this | ||
*/ | ||
public function setIntegrationId($integrationId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace AfterShip\Feed\Api; | ||
use AfterShip\Feed\Api\WebhookEntityInterface; | ||
|
||
interface WebhookManagementInterface | ||
{ | ||
/** | ||
* Register webhook. | ||
* | ||
* @param WebhookEntityInterface $webhook | ||
* @return WebhookEntityInterface | ||
* @throws \Magento\Framework\Exception\LocalizedException | ||
*/ | ||
public function registerWebhook(WebhookEntityInterface $webhook); | ||
/** | ||
* List webhook. | ||
* | ||
* @return WebhookEntityInterface[] | ||
*/ | ||
public function listWebhooks(); | ||
/** | ||
* Delete webhook by id. | ||
* | ||
* @param string $webhookId | ||
* @return WebhookEntityInterface|null | ||
*/ | ||
public function deleteWebhook($webhookId); | ||
/** | ||
* Get webhook by id. | ||
* | ||
* @param string $webhookId | ||
* @return WebhookEntityInterface|null | ||
*/ | ||
public function getWebhook($webhookId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
namespace AfterShip\Feed; | ||
|
||
final class Constants { | ||
CONST AFTERSHIP_FEED_VERSION = '1.0.5'; | ||
CONST WEBHOOK_TOPIC_ORDERS_UPDATE = 'orders/update'; | ||
CONST WEBHOOK_TOPIC_PRODUCTS_UPDATE = 'products/update'; | ||
CONST WEBHOOK_TOPIC_VARIANTS_UPDATE = 'variants/update'; | ||
CONST WEBHOOK_TOPIC_PRODUCTS_DELETE = 'products/delete'; | ||
CONST WEBHOOK_TOPIC_VARIANTS_DELETE = 'variants/delete'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
<?php | ||
|
||
namespace AfterShip\Feed\Helper; | ||
|
||
use Exception; | ||
use Magento\Framework\App\Config\ScopeConfigInterface; | ||
use Magento\Integration\Api\IntegrationServiceInterface; | ||
use Magento\Integration\Api\OauthServiceInterface; | ||
use Psr\Log\LoggerInterface; | ||
|
||
class WebhookHelper | ||
{ | ||
|
||
private $webhookConfigKey = 'webhooks'; | ||
/** @var array */ | ||
private $webhooks = []; | ||
/** @var ScopeConfigInterface */ | ||
protected $scopeConfig; | ||
/** @var OauthServiceInterface */ | ||
private $oauthService; | ||
/** @var IntegrationServiceInterface */ | ||
private $integrationService; | ||
/** @var LoggerInterface */ | ||
private $logger; | ||
|
||
public function __construct( | ||
ScopeConfigInterface $scopeConfig, | ||
IntegrationServiceInterface $integrationService, | ||
OauthServiceInterface $oauthService, | ||
LoggerInterface $logger | ||
|
||
) { | ||
$this->scopeConfig = $scopeConfig; | ||
$this->oauthService = $oauthService; | ||
$this->logger = $logger; | ||
$this->integrationService = $integrationService; | ||
$webhooksJson = $this->scopeConfig->getValue( | ||
'aftership/feed/' . $this->webhookConfigKey, | ||
'default' | ||
); | ||
$this->webhooks = $webhooksJson ? json_decode($webhooksJson) : []; | ||
} | ||
|
||
/** | ||
* @param string $topic | ||
* @param array $data | ||
* @return void | ||
*/ | ||
public function makeWebhookRequest($topic, $data) | ||
{ | ||
foreach ($this->webhooks as $webhook) { | ||
if ($webhook->topic !== $topic) continue; | ||
$this->sendWebhook($webhook, $data); | ||
} | ||
} | ||
|
||
/** | ||
* @param $webhook | ||
* @param $data | ||
* @return bool|string | ||
*/ | ||
public function sendWebhook($webhook, $data) { | ||
$curl = curl_init(); | ||
curl_setopt_array($curl, [ | ||
CURLOPT_URL => $webhook->address, | ||
CURLOPT_RETURNTRANSFER => true, | ||
CURLOPT_CUSTOMREQUEST => 'POST', | ||
CURLOPT_POSTFIELDS => json_encode($data), | ||
CURLOPT_HTTPHEADER => array( | ||
'Content-Type: application/json', | ||
'X-Magento-Hmac-Sha256: ' . $this->createWebhookSecurity($webhook->integration_id, $data), | ||
'Content-Length: ' . strlen(json_encode($data)), | ||
'X-Webhook-Topic: ' . $webhook->topic, | ||
'X-App-Key: ' . $webhook->app_key | ||
), | ||
]); | ||
$response = curl_exec($curl); | ||
$err = curl_errno($curl); | ||
if ($err) { | ||
$this->logger->error(sprintf('[AfterShip Feed] Unable to send webhook to %s with data: %s, response: %s', $webhook->address, json_encode($data), $response)); | ||
} | ||
curl_close($curl); | ||
return $response; | ||
} | ||
/** | ||
* @param string $integrationId | ||
* @param array $data | ||
* @return string | ||
*/ | ||
private function createWebhookSecurity($integrationId, $data) | ||
{ | ||
$integration = $this->integrationService->get($integrationId); | ||
$consumer = $this->oauthService->loadConsumer($integration->getConsumerId()); | ||
return hash_hmac('sha256', json_encode($data), $consumer->getKey()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<?php | ||
|
||
namespace AfterShip\Feed\Model\Api; | ||
|
||
use AfterShip\Feed\Api\WebhookManagementInterface; | ||
use AfterShip\Feed\Api\WebhookEntityInterface; | ||
use Magento\Authorization\Model\UserContextInterface; | ||
use Magento\Framework\App\Config\ScopeConfigInterface; | ||
use Magento\Framework\App\Config\Storage\WriterInterface; | ||
use Magento\Framework\App\Cache\TypeListInterface; | ||
use Magento\Framework\Phrase; | ||
use Magento\Framework\Exception\LocalizedException; | ||
|
||
class WebhookManagement implements WebhookManagementInterface | ||
{ | ||
|
||
private $integrationId; | ||
/** @var ScopeConfigInterface */ | ||
private $scopeConfig; | ||
/** @var WriterInterface */ | ||
private $configWriter; | ||
/** @var TypeListInterface */ | ||
private $cacheTypeList; | ||
private $webhookConfigKey = 'webhooks'; | ||
|
||
/** @var array */ | ||
private $webhooks = []; | ||
|
||
public function __construct( | ||
UserContextInterface $userContext, | ||
ScopeConfigInterface $scopeConfig, | ||
WriterInterface $configWriter, | ||
TypeListInterface $cacheTypeList | ||
) | ||
{ | ||
$this->integrationId = $userContext->getUserId(); | ||
$this->cacheTypeList = $cacheTypeList; | ||
$this->scopeConfig = $scopeConfig; | ||
$this->configWriter = $configWriter; | ||
$webhooksJson = $this->scopeConfig->getValue( | ||
'aftership/feed/' . $this->webhookConfigKey, | ||
'default' | ||
); | ||
$this->webhooks = $webhooksJson ? json_decode($webhooksJson) : []; | ||
} | ||
|
||
/** | ||
* @param WebhookEntityInterface $request | ||
* @return mixed|null | ||
* @throws \Magento\Framework\Exception\LocalizedException | ||
*/ | ||
public function registerWebhook($request = null) | ||
{ | ||
if (!$request || !$request->getTopic() || !$request->getAddress() || !$request->getAppKey()) { | ||
throw new LocalizedException( | ||
new Phrase('The necessary parameters for creating a webhook are missing.'), | ||
null, | ||
400 | ||
); | ||
} | ||
$webhookId = $request->getId(); | ||
$request->setIntegrationId($this->integrationId); | ||
$webhook = [ | ||
"id" => $webhookId, | ||
"topic" => $request->getTopic(), | ||
"app_key" => $request->getAppKey(), | ||
"address" => $request->getAddress(), | ||
"integration_id" => $this->integrationId, | ||
]; | ||
$results = array_filter($this->webhooks, function ($item) use ($webhookId) { | ||
return $item->id === $webhookId; | ||
}); | ||
$done = !count($results) && array_push($this->webhooks, $webhook) && $this->configWriter->save( | ||
'aftership/feed/' . $this->webhookConfigKey, | ||
json_encode($this->webhooks), | ||
'default' | ||
); | ||
$this->cacheTypeList->cleanType('config'); | ||
return $request; | ||
} | ||
|
||
/** | ||
* @return WebhookEntityInterface[]|array | ||
*/ | ||
public function listWebhooks() | ||
{ | ||
$webhooks = []; | ||
foreach ($this->webhooks as $webhook) { | ||
$entity = new WebhookRequest(); | ||
$entity->setId($webhook->id); | ||
$entity->setTopic($webhook->topic); | ||
$entity->setAddress($webhook->address); | ||
$entity->setAppKey($webhook->app_key); | ||
$entity->setIntegrationId($webhook->integration_id); | ||
array_push($webhooks, $entity); | ||
} | ||
return $webhooks; | ||
} | ||
|
||
/** | ||
* @param $webhookId | ||
* @return WebhookRequest|null | ||
*/ | ||
public function getWebhook($webhookId) | ||
{ | ||
$entity = null; | ||
foreach ($this->webhooks as $webhook) { | ||
if ($webhook->id === $webhookId) { | ||
$entity = new WebhookRequest(); | ||
$entity->setId($webhook->id); | ||
$entity->setTopic($webhook->topic); | ||
$entity->setAddress($webhook->address); | ||
$entity->setAppKey($webhook->app_key); | ||
$entity->setIntegrationId($webhook->integration_id); | ||
} | ||
} | ||
return $entity; | ||
} | ||
|
||
/** | ||
* @param $webhookId | ||
* @return WebhookRequest|null | ||
*/ | ||
public function deleteWebhook($webhookId) | ||
{ | ||
$entity = null; | ||
$filteredWebhooks = []; | ||
foreach ($this->webhooks as $webhook) { | ||
if ($webhook->id === $webhookId) { | ||
$entity = new WebhookRequest(); | ||
$entity->setId($webhook->id); | ||
$entity->setTopic($webhook->topic); | ||
$entity->setAddress($webhook->address); | ||
$entity->setAppKey($webhook->app_key); | ||
$entity->setIntegrationId($webhook->integration_id); | ||
}else { | ||
array_push($filteredWebhooks, $webhook); | ||
} | ||
} | ||
$this->webhooks = $filteredWebhooks; | ||
$this->configWriter->save( | ||
'aftership/feed/' . $this->webhookConfigKey, | ||
json_encode($filteredWebhooks), | ||
'default' | ||
); | ||
$this->cacheTypeList->cleanType('config'); | ||
return $entity; | ||
} | ||
} |
Oops, something went wrong.