This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | if ( ! defined( 'ABSPATH' ) ) { |
||
4 | exit; // Exit if accessed directly |
||
5 | } |
||
6 | |||
7 | /** |
||
8 | * WooCommerce Webhook class. |
||
9 | * |
||
10 | * This class handles storing and retrieving webhook data from the associated. |
||
11 | * `shop_webhook` custom post type, as well as delivery logs from the `webhook_delivery`. |
||
12 | * comment type. |
||
13 | * |
||
14 | * Webhooks are enqueued to their associated actions, delivered, and logged. |
||
15 | * |
||
16 | * @author WooThemes |
||
17 | * @category Webhooks |
||
18 | * @package WooCommerce/Webhooks |
||
19 | * @since 2.2 |
||
20 | */ |
||
21 | class WC_Webhook { |
||
22 | |||
23 | /** @var int webhook ID (post ID) */ |
||
24 | public $id; |
||
25 | |||
26 | /** |
||
27 | * Setup webhook & load post data. |
||
28 | * |
||
29 | * @since 2.2 |
||
30 | * @param string|int $id |
||
31 | * @return \WC_Webhook |
||
0 ignored issues
–
show
|
|||
32 | */ |
||
33 | public function __construct( $id ) { |
||
34 | |||
35 | $id = absint( $id ); |
||
36 | |||
37 | if ( ! $id ) { |
||
38 | return; |
||
39 | } |
||
40 | |||
41 | $this->id = $id; |
||
42 | $this->post_data = get_post( $id ); |
||
43 | } |
||
44 | |||
45 | |||
46 | /** |
||
47 | * Magic isset as a wrapper around metadata_exists(). |
||
48 | * |
||
49 | * @since 2.2 |
||
50 | * @param string $key |
||
51 | * @return bool true if $key isset, false otherwise |
||
52 | */ |
||
53 | public function __isset( $key ) { |
||
54 | if ( ! $this->id ) { |
||
55 | return false; |
||
56 | } |
||
57 | return metadata_exists( 'post', $this->id, '_' . $key ); |
||
58 | } |
||
59 | |||
60 | |||
61 | /** |
||
62 | * Magic get, wraps get_post_meta() for all keys except $status. |
||
63 | * |
||
64 | * @since 2.2 |
||
65 | * @param string $key |
||
66 | * @return mixed value |
||
67 | */ |
||
68 | public function __get( $key ) { |
||
69 | |||
70 | if ( 'status' === $key ) { |
||
71 | $value = $this->get_status(); |
||
72 | } else { |
||
73 | $value = get_post_meta( $this->id, '_' . $key, true ); |
||
74 | } |
||
75 | |||
76 | return $value; |
||
77 | } |
||
78 | |||
79 | |||
80 | /** |
||
81 | * Enqueue the hooks associated with the webhook. |
||
82 | * |
||
83 | * @since 2.2 |
||
84 | */ |
||
85 | public function enqueue() { |
||
86 | $hooks = $this->get_hooks(); |
||
87 | $url = $this->get_delivery_url(); |
||
88 | |||
89 | if ( is_array( $hooks ) && ! empty( $url ) ) { |
||
90 | foreach ( $hooks as $hook ) { |
||
91 | add_action( $hook, array( $this, 'process' ) ); |
||
92 | } |
||
93 | } |
||
94 | } |
||
95 | |||
96 | |||
97 | /** |
||
98 | * Process the webhook for delivery by verifying that it should be delivered. |
||
99 | * and scheduling the delivery (in the background by default, or immediately). |
||
100 | * |
||
101 | * @since 2.2 |
||
102 | * @param mixed $arg the first argument provided from the associated hooks |
||
103 | */ |
||
104 | public function process( $arg ) { |
||
105 | |||
106 | // verify that webhook should be processed for delivery |
||
107 | if ( ! $this->should_deliver( $arg ) ) { |
||
108 | return; |
||
109 | } |
||
110 | |||
111 | // webhooks are processed in the background by default |
||
112 | // so as to avoid delays or failures in delivery from affecting the |
||
113 | // user who triggered it |
||
114 | if ( apply_filters( 'woocommerce_webhook_deliver_async', true, $this, $arg ) ) { |
||
115 | |||
116 | // deliver in background |
||
117 | wp_schedule_single_event( time(), 'woocommerce_deliver_webhook_async', array( $this->id, $arg ) ); |
||
118 | |||
119 | } else { |
||
120 | |||
121 | // deliver immediately |
||
122 | $this->deliver( $arg ); |
||
123 | } |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Helper to check if the webhook should be delivered, as some hooks. |
||
128 | * (like `wp_trash_post`) will fire for every post type, not just ours. |
||
129 | * |
||
130 | * @since 2.2 |
||
131 | * @param mixed $arg first hook argument |
||
132 | * @return bool true if webhook should be delivered, false otherwise |
||
133 | */ |
||
134 | private function should_deliver( $arg ) { |
||
135 | $should_deliver = true; |
||
136 | $current_action = current_action(); |
||
137 | |||
138 | // only active webhooks can be delivered |
||
139 | if ( 'active' != $this->get_status() ) { |
||
140 | $should_deliver = false; |
||
141 | |||
142 | // only deliver deleted event for coupons, orders, and products |
||
143 | } elseif ( 'delete_post' === $current_action && ! in_array( $GLOBALS['post_type'], array( 'shop_coupon', 'shop_order', 'product' ) ) ) { |
||
144 | $should_deliver = false; |
||
145 | |||
146 | } elseif ( 'delete_user' == $current_action ) { |
||
147 | $user = get_userdata( absint( $arg ) ); |
||
148 | |||
149 | // only deliver deleted customer event for users with customer role |
||
150 | if ( ! $user || ! in_array( 'customer', (array) $user->roles ) ) { |
||
151 | $should_deliver = false; |
||
152 | } |
||
153 | |||
154 | // check if the custom order type has chosen to exclude order webhooks from triggering along with its own webhooks. |
||
155 | } elseif ( 'order' == $this->get_resource() && ! in_array( get_post_type( absint( $arg ) ), wc_get_order_types( 'order-webhooks' ) ) ) { |
||
156 | $should_deliver = false; |
||
157 | |||
158 | } elseif ( 0 === strpos( $current_action, 'woocommerce_process_shop' ) || 0 === strpos( $current_action, 'woocommerce_process_product' ) ) { |
||
159 | // the `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks |
||
160 | // fire for create and update of products and orders, so check the post |
||
161 | // creation date to determine the actual event |
||
162 | $resource = get_post( absint( $arg ) ); |
||
163 | |||
164 | // a resource is considered created when the hook is executed within 10 seconds of the post creation date |
||
165 | $resource_created = ( ( time() - 10 ) <= strtotime( $resource->post_date_gmt ) ); |
||
166 | |||
167 | if ( 'created' == $this->get_event() && ! $resource_created ) { |
||
168 | $should_deliver = false; |
||
169 | } elseif ( 'updated' == $this->get_event() && $resource_created ) { |
||
170 | $should_deliver = false; |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /* |
||
175 | * Let other plugins intercept deliver for some messages queue like rabbit/zeromq |
||
176 | */ |
||
177 | return apply_filters( 'woocommerce_webhook_should_deliver', $should_deliver, $this, $arg ); |
||
178 | } |
||
179 | |||
180 | |||
181 | /** |
||
182 | * Deliver the webhook payload using wp_safe_remote_request(). |
||
183 | * |
||
184 | * @since 2.2 |
||
185 | * @param mixed $arg First hook argument. |
||
186 | */ |
||
187 | public function deliver( $arg ) { |
||
188 | |||
189 | $payload = $this->build_payload( $arg ); |
||
190 | |||
191 | // Setup request args. |
||
192 | $http_args = array( |
||
193 | 'method' => 'POST', |
||
194 | 'timeout' => MINUTE_IN_SECONDS, |
||
195 | 'redirection' => 0, |
||
196 | 'httpversion' => '1.0', |
||
197 | 'blocking' => true, |
||
198 | 'user-agent' => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ), |
||
199 | 'body' => trim( json_encode( $payload ) ), |
||
200 | 'headers' => array( 'Content-Type' => 'application/json' ), |
||
201 | 'cookies' => array(), |
||
202 | ); |
||
203 | |||
204 | $http_args = apply_filters( 'woocommerce_webhook_http_args', $http_args, $arg, $this->id ); |
||
205 | |||
206 | // Add custom headers. |
||
207 | $http_args['headers']['X-WC-Webhook-Source'] = home_url( '/' ); // Since 2.6.0. |
||
208 | $http_args['headers']['X-WC-Webhook-Topic'] = $this->get_topic(); |
||
209 | $http_args['headers']['X-WC-Webhook-Resource'] = $this->get_resource(); |
||
210 | $http_args['headers']['X-WC-Webhook-Event'] = $this->get_event(); |
||
211 | $http_args['headers']['X-WC-Webhook-Signature'] = $this->generate_signature( $http_args['body'] ); |
||
212 | $http_args['headers']['X-WC-Webhook-ID'] = $this->id; |
||
213 | $http_args['headers']['X-WC-Webhook-Delivery-ID'] = $delivery_id = $this->get_new_delivery_id(); |
||
214 | |||
215 | $start_time = microtime( true ); |
||
216 | |||
217 | // Webhook away! |
||
218 | $response = wp_safe_remote_request( $this->get_delivery_url(), $http_args ); |
||
219 | |||
220 | $duration = round( microtime( true ) - $start_time, 5 ); |
||
221 | |||
222 | $this->log_delivery( $delivery_id, $http_args, $response, $duration ); |
||
223 | |||
224 | do_action( 'woocommerce_webhook_delivery', $http_args, $response, $duration, $arg, $this->id ); |
||
225 | } |
||
226 | |||
227 | |||
228 | /** |
||
229 | * Build the payload data for the webhook. |
||
230 | * |
||
231 | * @since 2.2 |
||
232 | * @param mixed $resource_id first hook argument, typically the resource ID |
||
233 | * @return mixed payload data |
||
234 | */ |
||
235 | private function build_payload( $resource_id ) { |
||
236 | |||
237 | // build the payload with the same user context as the user who created |
||
238 | // the webhook -- this avoids permission errors as background processing |
||
239 | // runs with no user context |
||
240 | $current_user = get_current_user_id(); |
||
241 | wp_set_current_user( $this->get_user_id() ); |
||
242 | |||
243 | $resource = $this->get_resource(); |
||
244 | $event = $this->get_event(); |
||
245 | |||
246 | // if a resource has been deleted, just include the ID |
||
247 | if ( 'deleted' == $event ) { |
||
248 | |||
249 | $payload = array( |
||
250 | 'id' => $resource_id, |
||
251 | ); |
||
252 | |||
253 | } else { |
||
254 | |||
255 | // include & load API classes |
||
256 | WC()->api->includes(); |
||
257 | WC()->api->register_resources( new WC_API_Server( '/' ) ); |
||
258 | |||
259 | switch( $resource ) { |
||
260 | |||
261 | case 'coupon': |
||
262 | $payload = WC()->api->WC_API_Coupons->get_coupon( $resource_id ); |
||
263 | break; |
||
264 | |||
265 | case 'customer': |
||
266 | $payload = WC()->api->WC_API_Customers->get_customer( $resource_id ); |
||
267 | break; |
||
268 | |||
269 | case 'order': |
||
270 | $payload = WC()->api->WC_API_Orders->get_order( $resource_id ); |
||
271 | break; |
||
272 | |||
273 | case 'product': |
||
274 | $payload = WC()->api->WC_API_Products->get_product( $resource_id ); |
||
275 | break; |
||
276 | |||
277 | // custom topics include the first hook argument |
||
278 | case 'action': |
||
279 | $payload = array( |
||
280 | 'action' => current( $this->get_hooks() ), |
||
281 | 'arg' => $resource_id, |
||
282 | ); |
||
283 | break; |
||
284 | |||
285 | default: |
||
286 | $payload = array(); |
||
287 | } |
||
288 | } |
||
289 | |||
290 | // restore the current user |
||
291 | wp_set_current_user( $current_user ); |
||
292 | |||
293 | return apply_filters( 'woocommerce_webhook_payload', $payload, $resource, $resource_id, $this->id ); |
||
294 | } |
||
295 | |||
296 | |||
297 | /** |
||
298 | * Generate a base64-encoded HMAC-SHA256 signature of the payload body so the. |
||
299 | * recipient can verify the authenticity of the webhook. Note that the signature. |
||
300 | * is calculated after the body has already been encoded (JSON by default). |
||
301 | * |
||
302 | * @since 2.2 |
||
303 | * @param string $payload payload data to hash |
||
304 | * @return string hash |
||
305 | */ |
||
306 | public function generate_signature( $payload ) { |
||
307 | |||
308 | $hash_algo = apply_filters( 'woocommerce_webhook_hash_algorithm', 'sha256', $payload, $this->id ); |
||
309 | |||
310 | return base64_encode( hash_hmac( $hash_algo, $payload, $this->get_secret(), true ) ); |
||
311 | } |
||
312 | |||
313 | |||
314 | /** |
||
315 | * Create a new comment for log the delivery request/response and. |
||
316 | * return the ID for inclusion in the webhook request. |
||
317 | * |
||
318 | * @since 2.2 |
||
319 | * @return int delivery (comment) ID |
||
320 | */ |
||
321 | public function get_new_delivery_id() { |
||
322 | |||
323 | $comment_data = apply_filters( 'woocommerce_new_webhook_delivery_data', array( |
||
324 | 'comment_author' => __( 'WooCommerce', 'woocommerce' ), |
||
325 | 'comment_author_email' => sanitize_email( sprintf( '%s@%s', strtolower( __( 'WooCommerce', 'woocommerce' ) ), isset( $_SERVER['HTTP_HOST'] ) ? str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ) : 'noreply.com' ) ), |
||
326 | 'comment_post_ID' => $this->id, |
||
327 | 'comment_agent' => 'WooCommerce Hookshot', |
||
328 | 'comment_type' => 'webhook_delivery', |
||
329 | 'comment_parent' => 0, |
||
330 | 'comment_approved' => 1, |
||
331 | ), $this->id ); |
||
332 | |||
333 | $comment_id = wp_insert_comment( $comment_data ); |
||
334 | |||
335 | return $comment_id; |
||
336 | } |
||
337 | |||
338 | |||
339 | /** |
||
340 | * Log the delivery request/response. |
||
341 | * |
||
342 | * @since 2.2 |
||
343 | * @param int $delivery_id previously created comment ID |
||
344 | * @param array $request request data |
||
345 | * @param array|WP_Error $response response data |
||
346 | * @param float $duration request duration |
||
347 | */ |
||
348 | public function log_delivery( $delivery_id, $request, $response, $duration ) { |
||
349 | |||
350 | // save request data |
||
351 | add_comment_meta( $delivery_id, '_request_method', $request['method'] ); |
||
352 | add_comment_meta( $delivery_id, '_request_headers', array_merge( array( 'User-Agent' => $request['user-agent'] ), $request['headers'] ) ); |
||
353 | add_comment_meta( $delivery_id, '_request_body', $request['body'] ); |
||
354 | |||
355 | // parse response |
||
356 | if ( is_wp_error( $response ) ) { |
||
357 | $response_code = $response->get_error_code(); |
||
358 | $response_message = $response->get_error_message(); |
||
359 | $response_headers = $response_body = array(); |
||
360 | |||
361 | } else { |
||
362 | $response_code = wp_remote_retrieve_response_code( $response ); |
||
363 | $response_message = wp_remote_retrieve_response_message( $response ); |
||
364 | $response_headers = wp_remote_retrieve_headers( $response ); |
||
365 | $response_body = wp_remote_retrieve_body( $response ); |
||
366 | } |
||
367 | |||
368 | // save response data |
||
369 | add_comment_meta( $delivery_id, '_response_code', $response_code ); |
||
370 | add_comment_meta( $delivery_id, '_response_message', $response_message ); |
||
371 | add_comment_meta( $delivery_id, '_response_headers', $response_headers ); |
||
372 | add_comment_meta( $delivery_id, '_response_body', $response_body ); |
||
373 | |||
374 | // save duration |
||
375 | add_comment_meta( $delivery_id, '_duration', $duration ); |
||
376 | |||
377 | // set a summary for quick display |
||
378 | $args = array( |
||
379 | 'comment_ID' => $delivery_id, |
||
380 | 'comment_content' => sprintf( 'HTTP %s %s: %s', $response_code, $response_message, $response_body ), |
||
381 | ); |
||
382 | |||
383 | wp_update_comment( $args ); |
||
384 | |||
385 | // track failures |
||
386 | if ( intval( $response_code ) >= 200 && intval( $response_code ) < 300 ) { |
||
387 | delete_post_meta( $this->id, '_failure_count' ); |
||
388 | } else { |
||
389 | $this->failed_delivery(); |
||
390 | } |
||
391 | |||
392 | // keep the 25 most recent delivery logs |
||
393 | $log = wp_count_comments( $this->id ); |
||
394 | if ( $log->total_comments > apply_filters( 'woocommerce_max_webhook_delivery_logs', 25 ) ) { |
||
395 | global $wpdb; |
||
396 | |||
397 | $comment_id = $wpdb->get_var( $wpdb->prepare( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_post_ID = %d ORDER BY comment_date_gmt ASC LIMIT 1", $this->id ) ); |
||
398 | |||
399 | if ( $comment_id ) { |
||
400 | wp_delete_comment( $comment_id, true ); |
||
401 | } |
||
402 | } |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Track consecutive delivery failures and automatically disable the webhook. |
||
407 | * if more than 5 consecutive failures occur. A failure is defined as a. |
||
408 | * non-2xx response. |
||
409 | * |
||
410 | * @since 2.2 |
||
411 | */ |
||
412 | private function failed_delivery() { |
||
413 | |||
414 | $failures = $this->get_failure_count(); |
||
415 | |||
416 | if ( $failures > apply_filters( 'woocommerce_max_webhook_delivery_failures', 5 ) ) { |
||
417 | |||
418 | $this->update_status( 'disabled' ); |
||
419 | |||
420 | } else { |
||
421 | |||
422 | update_post_meta( $this->id, '_failure_count', ++$failures ); |
||
423 | } |
||
424 | } |
||
425 | |||
426 | |||
427 | /** |
||
428 | * Get the delivery logs for this webhook. |
||
429 | * |
||
430 | * @since 2.2 |
||
431 | * @return array |
||
432 | */ |
||
433 | public function get_delivery_logs() { |
||
434 | |||
435 | $args = array( |
||
436 | 'post_id' => $this->id, |
||
437 | 'status' => 'approve', |
||
438 | 'type' => 'webhook_delivery', |
||
439 | ); |
||
440 | |||
441 | remove_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_webhook_comments' ), 10, 1 ); |
||
442 | |||
443 | $logs = get_comments( $args ); |
||
444 | |||
445 | add_filter( 'comments_clauses', array( 'WC_Comments', 'exclude_webhook_comments' ), 10, 1 ); |
||
446 | |||
447 | $delivery_logs = array(); |
||
448 | |||
449 | foreach ( $logs as $log ) { |
||
450 | |||
451 | $log = $this->get_delivery_log( $log->comment_ID ); |
||
452 | |||
453 | $delivery_logs[] = ( ! empty( $log ) ? $log : array() ); |
||
454 | } |
||
455 | |||
456 | return $delivery_logs; |
||
457 | } |
||
458 | |||
459 | |||
460 | /** |
||
461 | * Get the delivery log specified by the ID. The delivery log includes: |
||
462 | * |
||
463 | * + duration |
||
464 | * + summary |
||
465 | * + request method/url |
||
466 | * + request headers/body |
||
467 | * + response code/message/headers/body |
||
468 | * |
||
469 | * @since 2.2 |
||
470 | * @param int $delivery_id |
||
471 | * @return bool|array false if invalid delivery ID, array of log data otherwise |
||
472 | */ |
||
473 | public function get_delivery_log( $delivery_id ) { |
||
474 | |||
475 | $log = get_comment( $delivery_id ); |
||
476 | |||
477 | // valid comment and ensure delivery log belongs to this webhook |
||
478 | if ( is_null( $log ) || $log->comment_post_ID != $this->id ) { |
||
479 | return false; |
||
480 | } |
||
481 | |||
482 | $delivery_log = array( |
||
483 | 'id' => intval( $delivery_id ), |
||
484 | 'duration' => get_comment_meta( $delivery_id, '_duration', true ), |
||
485 | 'summary' => $log->comment_content, |
||
486 | 'request_method' => get_comment_meta( $delivery_id, '_request_method', true ), |
||
487 | 'request_url' => $this->get_delivery_url(), |
||
488 | 'request_headers' => get_comment_meta( $delivery_id, '_request_headers', true ), |
||
489 | 'request_body' => get_comment_meta( $delivery_id, '_request_body', true ), |
||
490 | 'response_code' => get_comment_meta( $delivery_id, '_response_code', true ), |
||
491 | 'response_message' => get_comment_meta( $delivery_id, '_response_message', true ), |
||
492 | 'response_headers' => get_comment_meta( $delivery_id, '_response_headers', true ), |
||
493 | 'response_body' => get_comment_meta( $delivery_id, '_response_body', true ), |
||
494 | 'comment' => $log, |
||
495 | ); |
||
496 | |||
497 | return apply_filters( 'woocommerce_webhook_delivery_log', $delivery_log, $delivery_id, $this->id ); |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * Set the webhook topic and associated hooks. The topic resource & event. |
||
502 | * are also saved separately. |
||
503 | * |
||
504 | * @since 2.2 |
||
505 | * @param string $topic |
||
506 | */ |
||
507 | public function set_topic( $topic ) { |
||
508 | |||
509 | $topic = strtolower( $topic ); |
||
510 | |||
511 | list( $resource, $event ) = explode( '.', $topic ); |
||
512 | |||
513 | update_post_meta( $this->id, '_topic', $topic ); |
||
514 | update_post_meta( $this->id, '_resource', $resource ); |
||
515 | update_post_meta( $this->id, '_event', $event ); |
||
516 | |||
517 | // custom topics are mapped to a single hook |
||
518 | if ( 'action' === $resource ) { |
||
519 | |||
520 | update_post_meta( $this->id, '_hooks', array( $event ) ); |
||
521 | |||
522 | } else { |
||
523 | |||
524 | // API topics have multiple hooks |
||
525 | update_post_meta( $this->id, '_hooks', $this->get_topic_hooks( $topic ) ); |
||
526 | } |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * Get the associated hook names for a topic. |
||
531 | * |
||
532 | * @since 2.2 |
||
533 | * @param string $topic |
||
534 | * @return array hook names |
||
535 | */ |
||
536 | private function get_topic_hooks( $topic ) { |
||
537 | |||
538 | $topic_hooks = array( |
||
539 | 'coupon.created' => array( |
||
540 | 'woocommerce_process_shop_coupon_meta', |
||
541 | 'woocommerce_api_create_coupon', |
||
542 | ), |
||
543 | 'coupon.updated' => array( |
||
544 | 'woocommerce_process_shop_coupon_meta', |
||
545 | 'woocommerce_api_edit_coupon', |
||
546 | ), |
||
547 | 'coupon.deleted' => array( |
||
548 | 'wp_trash_post', |
||
549 | ), |
||
550 | 'customer.created' => array( |
||
551 | 'user_register', |
||
552 | 'woocommerce_created_customer', |
||
553 | 'woocommerce_api_create_customer' |
||
554 | ), |
||
555 | 'customer.updated' => array( |
||
556 | 'profile_update', |
||
557 | 'woocommerce_api_edit_customer', |
||
558 | 'woocommerce_customer_save_address', |
||
559 | ), |
||
560 | 'customer.deleted' => array( |
||
561 | 'delete_user', |
||
562 | ), |
||
563 | 'order.created' => array( |
||
564 | 'woocommerce_checkout_order_processed', |
||
565 | 'woocommerce_process_shop_order_meta', |
||
566 | 'woocommerce_api_create_order', |
||
567 | ), |
||
568 | 'order.updated' => array( |
||
569 | 'woocommerce_process_shop_order_meta', |
||
570 | 'woocommerce_api_edit_order', |
||
571 | 'woocommerce_order_edit_status', |
||
572 | 'woocommerce_order_status_changed' |
||
573 | ), |
||
574 | 'order.deleted' => array( |
||
575 | 'wp_trash_post', |
||
576 | ), |
||
577 | 'product.created' => array( |
||
578 | 'woocommerce_process_product_meta', |
||
579 | 'woocommerce_api_create_product', |
||
580 | ), |
||
581 | 'product.updated' => array( |
||
582 | 'woocommerce_process_product_meta', |
||
583 | 'woocommerce_api_edit_product', |
||
584 | ), |
||
585 | 'product.deleted' => array( |
||
586 | 'wp_trash_post', |
||
587 | ), |
||
588 | ); |
||
589 | |||
590 | $topic_hooks = apply_filters( 'woocommerce_webhook_topic_hooks', $topic_hooks, $this ); |
||
591 | |||
592 | return isset( $topic_hooks[ $topic ] ) ? $topic_hooks[ $topic ] : array(); |
||
593 | } |
||
594 | |||
595 | /** |
||
596 | * Send a test ping to the delivery URL, sent when the webhook is first created. |
||
597 | * |
||
598 | * @since 2.2 |
||
599 | * @return bool|WP_Error |
||
600 | */ |
||
601 | public function deliver_ping() { |
||
602 | $args = array( |
||
603 | 'user-agent' => sprintf( 'WooCommerce/%s Hookshot (WordPress/%s)', WC_VERSION, $GLOBALS['wp_version'] ), |
||
604 | 'body' => "webhook_id={$this->id}", |
||
605 | ); |
||
606 | |||
607 | $test = wp_safe_remote_post( $this->get_delivery_url(), $args ); |
||
608 | $response_code = wp_remote_retrieve_response_code( $test ); |
||
609 | |||
610 | View Code Duplication | if ( is_wp_error( $test ) ) { |
|
611 | return new WP_Error( 'error', sprintf( __( 'Error: Delivery URL cannot be reached: %s', 'woocommerce' ), $test->get_error_message() ) ); |
||
612 | } |
||
613 | |||
614 | if ( 200 !== $response_code ) { |
||
615 | return new WP_Error( 'error', sprintf( __( 'Error: Delivery URL returned response code: %s', 'woocommerce' ), absint( $response_code ) ) ); |
||
616 | } |
||
617 | |||
618 | return true; |
||
619 | } |
||
620 | |||
621 | /** |
||
622 | * Get the webhook status: |
||
623 | * |
||
624 | * + `active` - delivers payload. |
||
625 | * + `paused` - does not deliver payload, paused by admin. |
||
626 | * + `disabled` - does not delivery payload, paused automatically due to. |
||
627 | * consecutive failures. |
||
628 | * |
||
629 | * @since 2.2 |
||
630 | * @return string status |
||
631 | */ |
||
632 | public function get_status() { |
||
633 | |||
634 | View Code Duplication | switch ( $this->get_post_data()->post_status ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
635 | |||
636 | case 'publish': |
||
637 | $status = 'active'; |
||
638 | break; |
||
639 | |||
640 | case 'draft': |
||
641 | $status = 'paused'; |
||
642 | break; |
||
643 | |||
644 | case 'pending': |
||
645 | $status = 'disabled'; |
||
646 | break; |
||
647 | |||
648 | default: |
||
649 | $status = 'paused'; |
||
650 | } |
||
651 | |||
652 | return apply_filters( 'woocommerce_webhook_status', $status, $this->id ); |
||
653 | } |
||
654 | |||
655 | /** |
||
656 | * Get the webhook i18n status. |
||
657 | * |
||
658 | * @return string |
||
659 | */ |
||
660 | public function get_i18n_status() { |
||
661 | $status = $this->get_status(); |
||
662 | $statuses = wc_get_webhook_statuses(); |
||
663 | |||
664 | return isset( $statuses[ $status ] ) ? $statuses[ $status ] : $status; |
||
665 | } |
||
666 | |||
667 | /** |
||
668 | * Update the webhook status, see get_status() for valid statuses. |
||
669 | * |
||
670 | * @since 2.2 |
||
671 | * @param $status |
||
672 | */ |
||
673 | public function update_status( $status ) { |
||
674 | global $wpdb; |
||
675 | |||
676 | View Code Duplication | switch ( $status ) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
677 | |||
678 | case 'active' : |
||
679 | $post_status = 'publish'; |
||
680 | break; |
||
681 | |||
682 | case 'paused' : |
||
683 | $post_status = 'draft'; |
||
684 | break; |
||
685 | |||
686 | case 'disabled' : |
||
687 | $post_status = 'pending'; |
||
688 | break; |
||
689 | |||
690 | default : |
||
691 | $post_status = 'draft'; |
||
692 | break; |
||
693 | } |
||
694 | |||
695 | $wpdb->update( $wpdb->posts, array( 'post_status' => $post_status ), array( 'ID' => $this->id ) ); |
||
696 | clean_post_cache( $this->id ); |
||
697 | } |
||
698 | |||
699 | /** |
||
700 | * Set the delivery URL. |
||
701 | * |
||
702 | * @since 2.2 |
||
703 | * @param string $url |
||
704 | */ |
||
705 | public function set_delivery_url( $url ) { |
||
706 | if ( update_post_meta( $this->id, '_delivery_url', esc_url_raw( $url, array( 'http', 'https' ) ) ) ) { |
||
707 | update_post_meta( $this->id, '_webhook_pending_delivery', true ); |
||
708 | } |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Get the delivery URL. |
||
713 | * |
||
714 | * @since 2.2 |
||
715 | * @return string |
||
716 | */ |
||
717 | public function get_delivery_url() { |
||
718 | |||
719 | return apply_filters( 'woocommerce_webhook_delivery_url', $this->delivery_url, $this->id ); |
||
720 | } |
||
721 | |||
722 | /** |
||
723 | * Set the secret used for generating the HMAC-SHA256 signature. |
||
724 | * |
||
725 | * @since 2.2 |
||
726 | * @param string $secret |
||
727 | */ |
||
728 | public function set_secret( $secret ) { |
||
729 | |||
730 | update_post_meta( $this->id, '_secret', $secret ); |
||
731 | } |
||
732 | |||
733 | /** |
||
734 | * Get the secret used for generating the HMAC-SHA256 signature. |
||
735 | * |
||
736 | * @since 2.2 |
||
737 | * @return string |
||
738 | */ |
||
739 | public function get_secret() { |
||
740 | return apply_filters( 'woocommerce_webhook_secret', $this->secret, $this->id ); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Get the friendly name for the webhook. |
||
745 | * |
||
746 | * @since 2.2 |
||
747 | * @return string |
||
748 | */ |
||
749 | public function get_name() { |
||
750 | return apply_filters( 'woocommerce_webhook_name', $this->get_post_data()->post_title, $this->id ); |
||
751 | } |
||
752 | |||
753 | /** |
||
754 | * Get the webhook topic, e.g. `order.created`. |
||
755 | * |
||
756 | * @since 2.2 |
||
757 | * @return string |
||
758 | */ |
||
759 | public function get_topic() { |
||
760 | return apply_filters( 'woocommerce_webhook_topic', $this->topic, $this->id ); |
||
761 | } |
||
762 | |||
763 | /** |
||
764 | * Get the hook names for the webhook. |
||
765 | * |
||
766 | * @since 2.2 |
||
767 | * @return array hook names |
||
768 | */ |
||
769 | public function get_hooks() { |
||
770 | return apply_filters( 'woocommerce_webhook_hooks', $this->hooks, $this->id ); |
||
771 | } |
||
772 | |||
773 | /** |
||
774 | * Get the resource for the webhook, e.g. `order`. |
||
775 | * |
||
776 | * @since 2.2 |
||
777 | * @return string |
||
778 | */ |
||
779 | public function get_resource() { |
||
780 | return apply_filters( 'woocommerce_webhook_resource', $this->resource, $this->id ); |
||
781 | } |
||
782 | |||
783 | /** |
||
784 | * Get the event for the webhook, e.g. `created`. |
||
785 | * |
||
786 | * @since 2.2 |
||
787 | * @return string |
||
788 | */ |
||
789 | public function get_event() { |
||
790 | return apply_filters( 'woocommerce_webhook_event', $this->event, $this->id ); |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Get the failure count. |
||
795 | * |
||
796 | * @since 2.2 |
||
797 | * @return int |
||
798 | */ |
||
799 | public function get_failure_count() { |
||
800 | return intval( $this->failure_count ); |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * Get the user ID for this webhook. |
||
805 | * |
||
806 | * @since 2.2 |
||
807 | * @return int|string user ID |
||
808 | */ |
||
809 | public function get_user_id() { |
||
810 | return $this->get_post_data()->post_author; |
||
811 | } |
||
812 | |||
813 | /** |
||
814 | * Get the post data for the webhook. |
||
815 | * |
||
816 | * @since 2.2 |
||
817 | * @return null|WP_Post |
||
818 | */ |
||
819 | public function get_post_data() { |
||
820 | return $this->post_data; |
||
821 | } |
||
822 | |||
823 | } |
||
824 |
Adding a
@return
annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.Please refer to the PHP core documentation on constructors.