-
Notifications
You must be signed in to change notification settings - Fork 171
/
Copy pathWebhooks.php
126 lines (113 loc) · 3.74 KB
/
Webhooks.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/
namespace craft\commerce\services;
use Craft;
use craft\commerce\base\GatewayInterface;
use craft\commerce\events\WebhookEvent;
use Exception;
use Throwable;
use yii\base\Component;
use yii\web\BadRequestHttpException;
use yii\web\Response;
/**
* Webhooks service.
*
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 3.1.9
*/
class Webhooks extends Component
{
/**
* @event WebhookEvent The event that is triggered before a Webhook is processed.
* @since 3.2.9
*
* ```php
* use craft\commerce\events\WebhookEvent;
* use craft\commerce\services\Webhooks;
* use craft\commerce\base\GatewayInterface;
* use yii\base\Event;
*
* Event::on(
* Webhooks::class,
* Webhooks::EVENT_BEFORE_PROCESS_WEBHOOK,
* function(WebhookEvent $event) {
* // @var GatewayInterface $gateway
* $gateway = $event->gateway;
*
* // ...
* }
* );
* ```
*/
public const EVENT_BEFORE_PROCESS_WEBHOOK = 'beforeProcessWebhook';
/**
* @event WebhookEvent The event that is triggered after a Webhook is processed.
* @since 3.2.9
*
* ```php
* use craft\commerce\events\WebhookEvent;
* use craft\commerce\services\Webhooks;
* use yii\base\Event;
*
* Event::on(
* Webhooks::class,
* Webhooks::EVENT_AFTER_PROCESS_WEBHOOK,
* function(WebhookEvent $event) {
* // @var Response $response
* $response = $event->response;
*
* // ...
* }
* );
* ```
*/
public const EVENT_AFTER_PROCESS_WEBHOOK = 'afterProcessWebhook';
/**
* @throws Exception
*/
public function processWebhook(GatewayInterface $gateway): Response
{
// Fire a 'beforeProcessWebhook' event
if ($this->hasEventHandlers(self::EVENT_BEFORE_PROCESS_WEBHOOK)) {
$this->trigger(self::EVENT_BEFORE_PROCESS_WEBHOOK, new WebhookEvent([
'gateway' => $gateway,
]));
}
$transactionHash = $gateway->getTransactionHashFromWebhook();
$useMutex = (bool)$transactionHash;
$transactionLockName = 'commerceTransaction:' . $transactionHash;
$mutex = Craft::$app->getMutex();
if ($useMutex && !$mutex->acquire($transactionLockName, 15)) {
throw new Exception('Unable to acquire a lock for transaction: ' . $transactionHash);
}
try {
if ($gateway->supportsWebhooks()) {
$response = $gateway->processWebhook();
} else {
throw new BadRequestHttpException('Gateway not found or does not support webhooks.');
}
} catch (Throwable $exception) {
$message = 'Exception while processing webhook: ' . $exception->getMessage() . "\n";
$message .= 'Exception thrown in ' . $exception->getFile() . ':' . $exception->getLine() . "\n";
$message .= 'Stack trace:' . "\n" . $exception->getTraceAsString();
Craft::error($message, 'commerce');
$response = Craft::$app->getResponse();
$response->setStatusCodeByException($exception);
}
if ($useMutex) {
$mutex->release($transactionLockName);
}
// Fire a 'afterProcessWebhook' event
if ($this->hasEventHandlers(self::EVENT_AFTER_PROCESS_WEBHOOK)) {
$this->trigger(self::EVENT_AFTER_PROCESS_WEBHOOK, new WebhookEvent([
'gateway' => $gateway,
'response' => $response,
]));
}
return $response;
}
}