PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.9
GiveWP – Donation Plugin and Fundraising Platform v4.16.9
4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 All 255 releases
give / src / PaymentGateways / PayPalCommerce / Repositories / Webhooks.php

Webhooks.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.9, at src/PaymentGateways/PayPalCommerce/Repositories/Webhooks.php

295 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\PaymentGateways\PayPalCommerce\Repositories;
4
5 use Give\Framework\Exceptions\Primitives\Exception;
6 use Give\Log\Log;
7 use Give\PaymentGateways\PayPalCommerce\DataTransferObjects\PayPalWebhookHeaders;
8 use Give\PaymentGateways\PayPalCommerce\Models\WebhookConfig;
9 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\Requests\CreateWebhook;
10 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\Requests\DeleteWebhook;
11 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\Requests\UpdateWebhook;
12 use Give\PaymentGateways\PayPalCommerce\PayPalCheckoutSdk\Requests\VerifyWebhookSignature;
13 use Give\PaymentGateways\PayPalCommerce\PayPalClient;
14 use Give\PaymentGateways\PayPalCommerce\Repositories\Traits\HasMode;
15 use Give\PaymentGateways\PayPalCommerce\Webhooks\WebhookRegister;
16 use Give\Route\PayPalWebhooks as WebhooksRoute;
17
18 class Webhooks
19 {
20 use HasMode;
21
22 /**
23 * @since 2.9.0
24 *
25 * @var WebhooksRoute
26 */
27 private $webhookRoute;
28
29 /**
30 * @var WebhookRegister
31 */
32 private $webhooksRegister;
33
34 /**
35 * @var PayPalClient
36 */
37 private $payPalClient;
38
39 /**
40 * Webhooks constructor.
41 *
42 * @since 2.9.0
43 *
44 * @param WebhooksRoute $webhookRoute
45 * @param PayPalClient $payPalClient
46 * @param WebhookRegister $webhooksRegister
47 */
48 public function __construct(
49 WebhooksRoute $webhookRoute,
50 PayPalClient $payPalClient,
51 WebhookRegister $webhooksRegister
52 ) {
53 $this->webhookRoute = $webhookRoute;
54 $this->payPalClient = $payPalClient;
55 $this->webhooksRegister = $webhooksRegister;
56 }
57
58 /**
59 * Verifies with PayPal that the given event is securely from PayPal and not some sneaking sneaker
60 *
61 * @see https://developer.paypal.com/docs/api/webhooks/v1/#verify-webhook-signature
62 *
63 * @since 2.32.0 Use PayPal client for rest api calls.
64 * @since 2.9.0
65 *
66 * @param object $event The event to verify
67 * @param PayPalWebhookHeaders $payPalHeaders The PayPal headers from the request
68 *
69 * @return bool
70 */
71 public function verifyEventSignature($event, $payPalHeaders)
72 {
73 $webhookConfig = $this->getWebhookConfig();
74
75 $requestData = [
76 'transmission_id' => $payPalHeaders->transmissionId,
77 'transmission_time' => $payPalHeaders->transmissionTime,
78 'transmission_sig' => $payPalHeaders->transmissionSig,
79 'cert_url' => $payPalHeaders->certUrl,
80 'auth_algo' => $payPalHeaders->authAlgo,
81 'webhook_id' => $webhookConfig->id,
82 'webhook_event' => $event,
83 ];
84
85 $response = $this->payPalClient
86 ->getHttpClient()
87 ->execute(new VerifyWebhookSignature($requestData));
88
89 if (
90 $response->statusCode !== 200 ||
91 ! property_exists($response->result, 'verification_status')
92 || $response->result->verification_status !== 'SUCCESS'
93 ) {
94 Log::http(
95 'Webhook signature failure response',
96 [
97 'category' => 'PayPal Commerce Webhook',
98 'response' => $response,
99 'event' => $event,
100 'headers' => getallheaders(),
101 ]
102 );
103
104 return false;
105 }
106
107 return true;
108 }
109
110 /**
111 * Creates a webhook with the given event types registered.
112 *
113 * @see https://developer.paypal.com/docs/api/webhooks/v1/#webhooks_post
114 *
115 * @since 4.1.0 Add PayPal-Partner-Attribution-Id header
116 * @since 2.32.0 Use PayPal client for rest api calls.
117 * @since 2.9.0
118 *
119 * @return WebhookConfig
120 * @throws Exception
121 */
122 public function createWebhook(): WebhookConfig
123 {
124 $events = $this->webhooksRegister->getRegisteredEvents();
125 $webhookUrl = $this->webhookRoute->getRouteUrl();
126
127 $request = new CreateWebhook([
128 'url' => $webhookUrl,
129 'event_types' => array_map(
130 static function ($eventType) {
131 return [
132 'name' => $eventType,
133 ];
134 },
135 $events
136 ),
137 ]);
138
139 $request->headers["PayPal-Partner-Attribution-Id"] = give('PAYPAL_COMMERCE_ATTRIBUTION_ID');
140
141 try {
142 $response = $this->payPalClient
143 ->getHttpClient()
144 ->execute($request);
145
146 if (201 !== $response->statusCode || ! property_exists($response->result, 'id')) {
147 Log::error(
148 'Create PayPal Commerce Webhook Failure',
149 [
150 'category' => 'PayPal Commerce Webhook',
151 'Response' => $response
152 ]
153 );
154
155 throw new Exception('Failed to create webhook');
156 }
157
158 return new WebhookConfig($response->result->id, $webhookUrl, $events);
159 } catch (\Exception $exception) {
160 throw new Exception($exception->getMessage());
161 }
162 }
163
164 /**
165 * Updates the webhook url and events
166 *
167 * @since 2.32.0 Use PayPal client for rest api calls.
168 * @since 2.9.0
169 *
170 * @param string $webhookId
171 *
172 * @throws Exception
173 */
174 public function updateWebhook($webhookId)
175 {
176 $webhookUrl = $this->webhookRoute->getRouteUrl();
177 $requestBody = [
178 [
179 'op' => 'replace',
180 'path' => '/url',
181 'value' => $webhookUrl,
182 ],
183 [
184 'op' => 'replace',
185 'path' => '/event_types',
186 'value' => array_map(
187 static function ($eventType) {
188 return [
189 'name' => $eventType,
190 ];
191 },
192 $this->webhooksRegister->getRegisteredEvents()
193 ),
194 ],
195 ];
196
197 $response = $this->payPalClient
198 ->getHttpClient()
199 ->execute(new UpdateWebhook($webhookId, $requestBody));
200
201 if (200 !== $response->statusCode || ! property_exists($response->result, 'id')) {
202 Log::error(
203 'Failed to update PayPal Commerce webhook',
204 [
205 'category' => 'PayPal Commerce Webhook',
206 'Webhook ID' => $webhookId,
207 'Response' => $response
208 ]
209 );
210
211 throw new Exception('Failed to update PayPal Commerce webhook');
212 }
213 }
214
215 /**
216 * Deletes the webhook with the given id.
217 *
218 * @since 2.32.0 Use PayPal client for rest api calls.
219 * @since 2.9.0
220 *
221 * @param string $token
222 * @param string $webhookId
223 *
224 * @return bool Whether or not the deletion was successful
225 */
226 public function deleteWebhook($token, $webhookId)
227 {
228 $response = $this->payPalClient
229 ->getHttpClient()
230 ->execute(new DeleteWebhook($webhookId));
231
232 $code = $response->statusCode;
233 $isDeleted = $code >= 200 && $code < 300;
234
235 if (! $isDeleted) {
236 Log::error(
237 'Failed to delete PayPal Commerce webhook',
238 [
239 'category' => 'PayPal Commerce Webhook',
240 'Webhook ID' => $webhookId,
241 'Response' => $response
242 ]
243 );
244 }
245
246 return $isDeleted;
247 }
248
249 /**
250 * Saves the webhook config in the database
251 *
252 * @since 2.9.0
253 *
254 * @param WebhookConfig $config
255 */
256 public function saveWebhookConfig(WebhookConfig $config)
257 {
258 update_option($this->getOptionKey(), $config->toArray(), false);
259 }
260
261 /**
262 * Retrieves the WebhookConfig from the database
263 *
264 * @since 2.9.0
265 *
266 * @return WebhookConfig|null
267 */
268 public function getWebhookConfig()
269 {
270 $data = get_option($this->getOptionKey(), null);
271
272 return $data ? WebhookConfig::fromArray($data) : null;
273 }
274
275 /**
276 * Deletes the stored webhook config
277 *
278 * @since 2.9.0
279 */
280 public function deleteWebhookConfig()
281 {
282 delete_option($this->getOptionKey());
283 }
284
285 /**
286 * Returns the option key for the given mode
287 *
288 * @return string
289 */
290 private function getOptionKey()
291 {
292 return "give_paypal_commerce_{$this->mode}_webhook_config";
293 }
294 }
295