2014-07-30 20:23:22 +00:00
< ? php
/**
2017-08-17 00:20:02 +00:00
* Webhook
2014-07-30 20:23:22 +00:00
*
2015-11-03 13:31:20 +00:00
* This class handles storing and retrieving webhook data from the associated .
2014-07-30 20:23:22 +00:00
*
* Webhooks are enqueued to their associated actions , delivered , and logged .
*
2017-08-17 00:20:02 +00:00
* @ version 3.2 . 0
* @ package WooCommerce / Webhooks
* @ category Webhooks
* @ since 2.2 . 0
* @ author Automattic
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
if ( ! defined ( 'ABSPATH' ) ) {
exit ; // Exit if accessed directly.
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
include_once ( 'legacy/class-wc-legacy-webhook.php' );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
/**
* Webhook class .
*/
class WC_Webhook extends WC_Legacy_Webhook {
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Stores webhook data .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ var array
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
protected $data = array (
2017-08-17 00:40:31 +00:00
'date_created' => null ,
'date_modified' => null ,
2017-08-17 20:54:31 +00:00
'status' => 'disabled' ,
2017-08-17 00:40:31 +00:00
'delivery_url' => '' ,
'secret' => '' ,
'name' => '' ,
'topic' => '' ,
'hooks' => '' ,
'resource' => '' ,
'event' => '' ,
'failure_count' => 0 ,
'user_id' => 0 ,
'api_version' => 2 ,
'pending_delivery' => false ,
2017-08-17 00:20:02 +00:00
);
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Load webhook data based on how WC_Webhook is called .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ param WC_Webhook | int $data Webhook ID or data .
* @ throws Exception If webhook cannot be read / found and $data is set .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function __construct ( $data = 0 ) {
parent :: __construct ( $data );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
if ( $data instanceof WC_Webhook ) {
$this -> set_id ( absint ( $data -> get_id () ) );
} elseif ( is_numeric ( $data ) ) {
$this -> set_id ( $data );
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
$this -> data_store = WC_Data_Store :: load ( 'webhook' );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
// If we have an ID, load the user from the DB.
if ( $this -> get_id () ) {
try {
$this -> data_store -> read ( $this );
} catch ( Exception $e ) {
$this -> set_id ( 0 );
$this -> set_object_read ( true );
}
} else {
$this -> set_object_read ( true );
}
}
2014-07-30 20:23:22 +00:00
/**
2015-11-03 13:31:20 +00:00
* Enqueue the hooks associated with the webhook .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
2014-07-30 20:23:22 +00:00
*/
public function enqueue () {
2014-12-17 03:44:35 +00:00
$hooks = $this -> get_hooks ();
2014-12-17 03:48:29 +00:00
$url = $this -> get_delivery_url ();
2014-12-17 03:44:35 +00:00
2014-12-17 03:48:29 +00:00
if ( is_array ( $hooks ) && ! empty ( $url ) ) {
2014-12-17 03:44:35 +00:00
foreach ( $hooks as $hook ) {
2014-12-16 22:16:38 +00:00
add_action ( $hook , array ( $this , 'process' ) );
}
2014-07-30 20:23:22 +00:00
}
}
/**
2015-11-03 13:31:20 +00:00
* Process the webhook for delivery by verifying that it should be delivered .
* and scheduling the delivery ( in the background by default , or immediately ) .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param mixed $arg The first argument provided from the associated hooks .
2014-07-30 20:23:22 +00:00
*/
public function process ( $arg ) {
2017-08-17 00:20:02 +00:00
// Verify that webhook should be processed for delivery.
2014-07-30 20:23:22 +00:00
if ( ! $this -> should_deliver ( $arg ) ) {
return ;
}
2017-08-17 00:20:02 +00:00
// Webhooks are processed in the background by default
2014-07-30 20:23:22 +00:00
// so as to avoid delays or failures in delivery from affecting the
2017-08-17 00:20:02 +00:00
// user who triggered it.
2016-04-01 07:05:24 +00:00
if ( apply_filters ( 'woocommerce_webhook_deliver_async' , true , $this , $arg ) ) {
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
// Deliver in background.
wp_schedule_single_event ( time (), 'woocommerce_deliver_webhook_async' , array ( $this -> get_id (), $arg ) );
2014-07-30 20:23:22 +00:00
} else {
2017-08-17 00:20:02 +00:00
// Deliver immediately.
2014-07-30 20:23:22 +00:00
$this -> deliver ( $arg );
}
}
/**
2015-11-03 13:31:20 +00:00
* Helper to check if the webhook should be delivered , as some hooks .
2014-07-30 20:23:22 +00:00
* ( like `wp_trash_post` ) will fire for every post type , not just ours .
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param mixed $arg First hook argument .
* @ return bool True if webhook should be delivered , false otherwise .
2014-07-30 20:23:22 +00:00
*/
private function should_deliver ( $arg ) {
2016-03-03 12:52:58 +00:00
$should_deliver = true ;
$current_action = current_action ();
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
// Only active webhooks can be delivered.
if ( 'active' !== $this -> get_status () ) {
2016-03-03 12:52:58 +00:00
$should_deliver = false ;
2017-04-27 18:44:57 +00:00
} elseif ( in_array ( $current_action , array ( 'delete_post' , 'wp_trash_post' , 'untrashed_post' ), true ) ) {
// Only deliver deleted/restored event for coupons, orders, and products.
2017-08-17 00:20:02 +00:00
if ( isset ( $GLOBALS [ 'post_type' ] ) && ! in_array ( $GLOBALS [ 'post_type' ], array ( 'shop_coupon' , 'shop_order' , 'product' ), true ) ) {
2016-11-24 18:07:32 +00:00
$should_deliver = false ;
}
2014-07-30 20:23:22 +00:00
2016-11-24 18:07:32 +00:00
// Check if is delivering for the correct resource.
2017-02-02 20:52:00 +00:00
if ( isset ( $GLOBALS [ 'post_type' ] ) && str_replace ( 'shop_' , '' , $GLOBALS [ 'post_type' ] ) !== $this -> get_resource () ) {
2016-11-24 18:07:32 +00:00
$should_deliver = false ;
}
2017-08-17 00:20:02 +00:00
} elseif ( 'delete_user' === $current_action ) {
2014-07-30 20:23:22 +00:00
$user = get_userdata ( absint ( $arg ) );
2017-08-17 00:20:02 +00:00
// Only deliver deleted customer event for users with customer role.
if ( ! $user || ! in_array ( 'customer' , ( array ) $user -> roles , true ) ) {
2016-03-03 12:52:58 +00:00
$should_deliver = false ;
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
} elseif ( 'order' === $this -> get_resource () && ! in_array ( get_post_type ( absint ( $arg ) ), wc_get_order_types ( 'order-webhooks' ), true ) ) {
// Only if the custom order type has chosen to exclude order webhooks from triggering along with its own webhooks.
2016-03-03 12:52:58 +00:00
$should_deliver = false ;
2015-05-12 04:01:23 +00:00
2016-05-27 10:35:16 +00:00
} elseif ( 0 === strpos ( $current_action , 'woocommerce_process_shop' ) || 0 === strpos ( $current_action , 'woocommerce_process_product' ) ) {
2017-08-17 00:20:02 +00:00
// The `woocommerce_process_shop_*` and `woocommerce_process_product_*` hooks
2016-05-27 10:35:16 +00:00
// fire for create and update of products and orders, so check the post
2017-08-17 00:20:02 +00:00
// creation date to determine the actual event.
2015-05-27 15:46:44 +00:00
$resource = get_post ( absint ( $arg ) );
2017-08-17 00:20:02 +00:00
// Drafts don't have post_date_gmt so calculate it here.
2016-08-12 10:53:40 +00:00
$gmt_date = get_gmt_from_date ( $resource -> post_date );
2017-08-17 00:20:02 +00:00
// A resource is considered created when the hook is executed within 10 seconds of the post creation date.
2016-08-12 10:53:40 +00:00
$resource_created = ( ( time () - 10 ) <= strtotime ( $gmt_date ) );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
if ( 'created' === $this -> get_event () && ! $resource_created ) {
2016-03-03 12:52:58 +00:00
$should_deliver = false ;
2017-08-17 00:20:02 +00:00
} elseif ( 'updated' === $this -> get_event () && $resource_created ) {
2016-03-03 12:52:58 +00:00
$should_deliver = false ;
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
} // End if().
2014-07-30 20:23:22 +00:00
2016-03-03 12:52:58 +00:00
/*
2017-08-17 00:20:02 +00:00
* Let other plugins intercept deliver for some messages queue like rabbit / zeromq .
2016-03-03 12:52:58 +00:00
*/
return apply_filters ( 'woocommerce_webhook_should_deliver' , $should_deliver , $this , $arg );
2014-07-30 20:23:22 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Deliver the webhook payload using wp_safe_remote_request () .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
2016-04-28 17:45:13 +00:00
* @ param mixed $arg First hook argument .
2014-07-30 20:23:22 +00:00
*/
public function deliver ( $arg ) {
$payload = $this -> build_payload ( $arg );
2016-04-28 17:45:13 +00:00
// Setup request args.
2014-07-30 20:23:22 +00:00
$http_args = array (
'method' => 'POST' ,
'timeout' => MINUTE_IN_SECONDS ,
'redirection' => 0 ,
'httpversion' => '1.0' ,
'blocking' => true ,
'user-agent' => sprintf ( 'WooCommerce/%s Hookshot (WordPress/%s)' , WC_VERSION , $GLOBALS [ 'wp_version' ] ),
2017-08-17 00:20:02 +00:00
'body' => trim ( wp_json_encode ( $payload ) ),
'headers' => array (
'Content-Type' => 'application/json' ,
),
2014-07-30 20:23:22 +00:00
'cookies' => array (),
);
2017-08-17 00:20:02 +00:00
$http_args = apply_filters ( 'woocommerce_webhook_http_args' , $http_args , $arg , $this -> get_id () );
2014-07-30 20:23:22 +00:00
2016-04-28 17:45:13 +00:00
// Add custom headers.
2017-08-17 00:20:02 +00:00
$delivery_id = $this -> get_new_delivery_id ();
2016-04-28 17:45:13 +00:00
$http_args [ 'headers' ][ 'X-WC-Webhook-Source' ] = home_url ( '/' ); // Since 2.6.0.
2014-07-30 20:23:22 +00:00
$http_args [ 'headers' ][ 'X-WC-Webhook-Topic' ] = $this -> get_topic ();
$http_args [ 'headers' ][ 'X-WC-Webhook-Resource' ] = $this -> get_resource ();
$http_args [ 'headers' ][ 'X-WC-Webhook-Event' ] = $this -> get_event ();
$http_args [ 'headers' ][ 'X-WC-Webhook-Signature' ] = $this -> generate_signature ( $http_args [ 'body' ] );
2017-08-17 00:20:02 +00:00
$http_args [ 'headers' ][ 'X-WC-Webhook-ID' ] = $this -> get_id ();
$http_args [ 'headers' ][ 'X-WC-Webhook-Delivery-ID' ] = $delivery_id ;
2014-07-30 20:23:22 +00:00
$start_time = microtime ( true );
2016-04-28 17:45:13 +00:00
// Webhook away!
2015-05-20 16:03:37 +00:00
$response = wp_safe_remote_request ( $this -> get_delivery_url (), $http_args );
2014-07-30 20:23:22 +00:00
$duration = round ( microtime ( true ) - $start_time , 5 );
$this -> log_delivery ( $delivery_id , $http_args , $response , $duration );
2017-08-17 00:20:02 +00:00
do_action ( 'woocommerce_webhook_delivery' , $http_args , $response , $duration , $arg , $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
2016-11-23 00:59:55 +00:00
/**
* Get Legacy API payload .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-23 00:59:55 +00:00
* @ param string $resource Resource type .
* @ param int $resource_id Resource ID .
* @ param string $event Event type .
* @ return array
*/
private function get_legacy_api_payload ( $resource , $resource_id , $event ) {
// Include & load API classes.
WC () -> api -> includes ();
WC () -> api -> register_resources ( new WC_API_Server ( '/' ) );
switch ( $resource ) {
2017-11-08 12:46:23 +00:00
case 'coupon' :
2016-11-23 00:59:55 +00:00
$payload = WC () -> api -> WC_API_Coupons -> get_coupon ( $resource_id );
break ;
2017-11-08 12:46:23 +00:00
case 'customer' :
2016-11-23 00:59:55 +00:00
$payload = WC () -> api -> WC_API_Customers -> get_customer ( $resource_id );
break ;
2017-11-08 12:46:23 +00:00
case 'order' :
2016-11-23 00:59:55 +00:00
$payload = WC () -> api -> WC_API_Orders -> get_order ( $resource_id , null , apply_filters ( 'woocommerce_webhook_order_payload_filters' , array () ) );
break ;
2017-11-08 12:46:23 +00:00
case 'product' :
2016-11-23 00:59:55 +00:00
// Bulk and quick edit action hooks return a product object instead of an ID.
if ( 'updated' === $event && is_a ( $resource_id , 'WC_Product' ) ) {
$resource_id = $resource_id -> get_id ();
}
$payload = WC () -> api -> WC_API_Products -> get_product ( $resource_id );
break ;
// Custom topics include the first hook argument.
2017-11-08 12:46:23 +00:00
case 'action' :
2016-11-23 00:59:55 +00:00
$payload = array (
'action' => current ( $this -> get_hooks () ),
'arg' => $resource_id ,
);
break ;
2017-11-08 12:46:23 +00:00
default :
2016-11-23 00:59:55 +00:00
$payload = array ();
break ;
}
return $payload ;
}
/**
* Get WP API integration payload .
*
2017-03-15 16:36:53 +00:00
* @ since 3.0 . 0
2016-11-23 00:59:55 +00:00
* @ param string $resource Resource type .
* @ param int $resource_id Resource ID .
* @ param string $event Event type .
* @ return array
*/
private function get_wp_api_payload ( $resource , $resource_id , $event ) {
2017-04-02 06:07:09 +00:00
$version_suffix = 'wp_api_v1' === $this -> get_api_version () ? '_V1' : '' ;
2016-11-23 00:59:55 +00:00
switch ( $resource ) {
2017-11-08 12:46:23 +00:00
case 'coupon' :
case 'customer' :
case 'order' :
case 'product' :
2017-04-02 06:07:09 +00:00
$class = 'WC_REST_' . ucfirst ( $resource ) . 's' . $version_suffix . '_Controller' ;
2016-11-23 00:59:55 +00:00
$request = new WP_REST_Request ( 'GET' );
2017-11-08 12:46:23 +00:00
$controller = new $class ();
2016-11-23 00:59:55 +00:00
// Bulk and quick edit action hooks return a product object instead of an ID.
if ( 'product' === $resource && 'updated' === $event && is_a ( $resource_id , 'WC_Product' ) ) {
$resource_id = $resource_id -> get_id ();
}
$request -> set_param ( 'id' , $resource_id );
$result = $controller -> get_item ( $request );
$payload = isset ( $result -> data ) ? $result -> data : array ();
break ;
// Custom topics include the first hook argument.
2017-11-08 12:46:23 +00:00
case 'action' :
2016-11-23 00:59:55 +00:00
$payload = array (
'action' => current ( $this -> get_hooks () ),
'arg' => $resource_id ,
);
break ;
2017-11-08 12:46:23 +00:00
default :
2016-11-23 00:59:55 +00:00
$payload = array ();
break ;
}
return $payload ;
}
2014-07-30 20:23:22 +00:00
/**
2015-11-03 13:31:20 +00:00
* Build the payload data for the webhook .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param mixed $resource_id First hook argument , typically the resource ID .
* @ return mixed Payload data .
2014-07-30 20:23:22 +00:00
*/
2017-04-24 11:22:47 +00:00
public function build_payload ( $resource_id ) {
2017-08-17 00:20:02 +00:00
// Build the payload with the same user context as the user who created
2014-07-30 20:23:22 +00:00
// the webhook -- this avoids permission errors as background processing
2017-08-17 00:20:02 +00:00
// runs with no user context.
2014-07-30 20:23:22 +00:00
$current_user = get_current_user_id ();
wp_set_current_user ( $this -> get_user_id () );
$resource = $this -> get_resource ();
$event = $this -> get_event ();
2016-11-23 00:59:55 +00:00
// If a resource has been deleted, just include the ID.
2017-08-17 00:20:02 +00:00
if ( 'deleted' === $event ) {
2014-07-30 20:23:22 +00:00
$payload = array (
'id' => $resource_id ,
);
} else {
2017-04-04 00:41:00 +00:00
if ( in_array ( $this -> get_api_version (), array ( 'wp_api_v1' , 'wp_api_v2' ), true ) ) {
2016-11-23 00:59:55 +00:00
$payload = $this -> get_wp_api_payload ( $resource , $resource_id , $event );
} else {
$payload = $this -> get_legacy_api_payload ( $resource , $resource_id , $event );
2014-07-30 20:23:22 +00:00
}
}
2016-11-23 00:59:55 +00:00
// Restore the current user.
2014-07-30 20:23:22 +00:00
wp_set_current_user ( $current_user );
2017-08-17 00:20:02 +00:00
return apply_filters ( 'woocommerce_webhook_payload' , $payload , $resource , $resource_id , $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Generate a base64 - encoded HMAC - SHA256 signature of the payload body so the .
* recipient can verify the authenticity of the webhook . Note that the signature .
* is calculated after the body has already been encoded ( JSON by default ) .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $payload Payload data to hash .
* @ return string
2014-07-30 20:23:22 +00:00
*/
public function generate_signature ( $payload ) {
2017-08-17 00:20:02 +00:00
$hash_algo = apply_filters ( 'woocommerce_webhook_hash_algorithm' , 'sha256' , $payload , $this -> get_id () );
2014-07-30 20:23:22 +00:00
return base64_encode ( hash_hmac ( $hash_algo , $payload , $this -> get_secret (), true ) );
}
/**
2015-11-03 13:31:20 +00:00
* Create a new comment for log the delivery request / response and .
* return the ID for inclusion in the webhook request .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ todo Update to use log system .
* @ since 2.2 . 0
* @ return int
2014-07-30 20:23:22 +00:00
*/
public function get_new_delivery_id () {
$comment_data = apply_filters ( 'woocommerce_new_webhook_delivery_data' , array (
'comment_author' => __ ( 'WooCommerce' , 'woocommerce' ),
2017-11-08 12:46:23 +00:00
'comment_author_email' => sanitize_email ( sprintf ( '%s@%s' , strtolower ( __ ( 'WooCommerce' , 'woocommerce' ) ), isset ( $_SERVER [ 'HTTP_HOST' ] ) ? str_replace ( 'www.' , '' , wp_unslash ( $_SERVER [ 'HTTP_HOST' ] ) ) : 'noreply.com' ) ), // @codingStandardsIgnoreLine
2017-08-17 00:20:02 +00:00
'comment_post_ID' => $this -> get_id (),
2014-07-30 20:23:22 +00:00
'comment_agent' => 'WooCommerce Hookshot' ,
'comment_type' => 'webhook_delivery' ,
'comment_parent' => 0 ,
'comment_approved' => 1 ,
2017-08-17 00:20:02 +00:00
), $this -> get_id () );
2014-07-30 20:23:22 +00:00
$comment_id = wp_insert_comment ( $comment_data );
return $comment_id ;
}
/**
2015-11-03 13:31:20 +00:00
* Log the delivery request / response .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ todo Update to use log system .
* @ since 2.2 . 0
* @ param int $delivery_id Previously created comment ID .
* @ param array $request Request data .
* @ param array | WP_Error $response Response data .
* @ param float $duration Request duration .
2014-07-30 20:23:22 +00:00
*/
public function log_delivery ( $delivery_id , $request , $response , $duration ) {
2017-08-17 00:20:02 +00:00
// Save request data.
2014-07-30 20:23:22 +00:00
add_comment_meta ( $delivery_id , '_request_method' , $request [ 'method' ] );
2017-08-17 00:20:02 +00:00
add_comment_meta ( $delivery_id , '_request_headers' , array_merge ( array (
'User-Agent' => $request [ 'user-agent' ],
), $request [ 'headers' ] ) );
2017-06-05 18:25:29 +00:00
add_comment_meta ( $delivery_id , '_request_body' , wp_slash ( $request [ 'body' ] ) );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
// Parse response.
2014-07-30 20:23:22 +00:00
if ( is_wp_error ( $response ) ) {
$response_code = $response -> get_error_code ();
$response_message = $response -> get_error_message ();
2016-07-25 11:52:20 +00:00
$response_headers = array ();
$response_body = '' ;
2014-07-30 20:23:22 +00:00
} else {
$response_code = wp_remote_retrieve_response_code ( $response );
$response_message = wp_remote_retrieve_response_message ( $response );
$response_headers = wp_remote_retrieve_headers ( $response );
$response_body = wp_remote_retrieve_body ( $response );
}
2017-08-17 00:20:02 +00:00
// Save response data.
2014-07-30 20:23:22 +00:00
add_comment_meta ( $delivery_id , '_response_code' , $response_code );
add_comment_meta ( $delivery_id , '_response_message' , $response_message );
add_comment_meta ( $delivery_id , '_response_headers' , $response_headers );
add_comment_meta ( $delivery_id , '_response_body' , $response_body );
2017-08-17 00:20:02 +00:00
// Save duration.
2014-07-30 20:23:22 +00:00
add_comment_meta ( $delivery_id , '_duration' , $duration );
2017-08-17 00:20:02 +00:00
// Set a summary for quick display.
2014-07-30 20:23:22 +00:00
$args = array (
'comment_ID' => $delivery_id ,
'comment_content' => sprintf ( 'HTTP %s %s: %s' , $response_code , $response_message , $response_body ),
);
wp_update_comment ( $args );
2017-08-17 00:20:02 +00:00
// Track failures.
2014-07-30 20:23:22 +00:00
if ( intval ( $response_code ) >= 200 && intval ( $response_code ) < 300 ) {
2017-08-17 00:20:02 +00:00
delete_post_meta ( $this -> get_id (), '_failure_count' );
2014-07-30 20:23:22 +00:00
} else {
$this -> failed_delivery ();
}
2017-08-17 00:20:02 +00:00
// Keep the 25 most recent delivery logs.
$log = wp_count_comments ( $this -> get_id () );
2014-07-30 20:23:22 +00:00
if ( $log -> total_comments > apply_filters ( 'woocommerce_max_webhook_delivery_logs' , 25 ) ) {
global $wpdb ;
2017-11-08 12:46:23 +00:00
$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 -> get_id () ) ); // @codingStandardsIgnoreLine
2014-07-31 15:52:16 +00:00
if ( $comment_id ) {
wp_delete_comment ( $comment_id , true );
}
2014-07-30 20:23:22 +00:00
}
}
/**
2015-11-03 13:31:20 +00:00
* Track consecutive delivery failures and automatically disable the webhook .
* if more than 5 consecutive failures occur . A failure is defined as a .
* non - 2 xx response .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
2014-07-30 20:23:22 +00:00
*/
private function failed_delivery () {
$failures = $this -> get_failure_count ();
if ( $failures > apply_filters ( 'woocommerce_max_webhook_delivery_failures' , 5 ) ) {
$this -> update_status ( 'disabled' );
} else {
2017-08-17 00:20:02 +00:00
update_post_meta ( $this -> get_id (), '_failure_count' , ++ $failures );
2014-07-30 20:23:22 +00:00
}
}
/**
2015-11-03 13:31:20 +00:00
* Get the delivery logs for this webhook .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ todo Update to use log system .
* @ since 2.2 . 0
2014-07-30 20:23:22 +00:00
* @ return array
*/
public function get_delivery_logs () {
$args = array (
2017-08-17 00:20:02 +00:00
'post_id' => $this -> get_id (),
2014-07-30 20:23:22 +00:00
'status' => 'approve' ,
'type' => 'webhook_delivery' ,
);
remove_filter ( 'comments_clauses' , array ( 'WC_Comments' , 'exclude_webhook_comments' ), 10 , 1 );
$logs = get_comments ( $args );
add_filter ( 'comments_clauses' , array ( 'WC_Comments' , 'exclude_webhook_comments' ), 10 , 1 );
$delivery_logs = array ();
foreach ( $logs as $log ) {
$log = $this -> get_delivery_log ( $log -> comment_ID );
2017-11-08 12:46:23 +00:00
$delivery_logs [] = ( ! empty ( $log ) ? $log : array () );
2014-07-30 20:23:22 +00:00
}
return $delivery_logs ;
}
/**
* Get the delivery log specified by the ID . The delivery log includes :
*
* + duration
* + summary
* + request method / url
* + request headers / body
* + response code / message / headers / body
*
2017-08-17 00:20:02 +00:00
* @ todo Update to use log system .
2014-07-30 20:23:22 +00:00
* @ since 2.2
2017-08-17 00:20:02 +00:00
* @ param int $delivery_id Delivery ID .
2014-07-30 20:23:22 +00:00
* @ return bool | array false if invalid delivery ID , array of log data otherwise
*/
public function get_delivery_log ( $delivery_id ) {
$log = get_comment ( $delivery_id );
2017-08-17 00:20:02 +00:00
// Valid comment and ensure delivery log belongs to this webhook.
if ( is_null ( $log ) || $log -> comment_post_ID !== $this -> get_id () ) {
2014-07-30 20:23:22 +00:00
return false ;
}
$delivery_log = array (
'id' => intval ( $delivery_id ),
'duration' => get_comment_meta ( $delivery_id , '_duration' , true ),
'summary' => $log -> comment_content ,
'request_method' => get_comment_meta ( $delivery_id , '_request_method' , true ),
'request_url' => $this -> get_delivery_url (),
'request_headers' => get_comment_meta ( $delivery_id , '_request_headers' , true ),
'request_body' => get_comment_meta ( $delivery_id , '_request_body' , true ),
'response_code' => get_comment_meta ( $delivery_id , '_response_code' , true ),
'response_message' => get_comment_meta ( $delivery_id , '_response_message' , true ),
'response_headers' => get_comment_meta ( $delivery_id , '_response_headers' , true ),
'response_body' => get_comment_meta ( $delivery_id , '_response_body' , true ),
'comment' => $log ,
);
2017-08-17 00:20:02 +00:00
return apply_filters ( 'woocommerce_webhook_delivery_log' , $delivery_log , $delivery_id , $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Send a test ping to the delivery URL , sent when the webhook is first created .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
2015-12-04 12:47:30 +00:00
* @ return bool | WP_Error
2014-07-30 20:23:22 +00:00
*/
public function deliver_ping () {
$args = array (
'user-agent' => sprintf ( 'WooCommerce/%s Hookshot (WordPress/%s)' , WC_VERSION , $GLOBALS [ 'wp_version' ] ),
2017-08-17 00:20:02 +00:00
'body' => 'webhook_id=' . $this -> get_id (),
2014-07-30 20:23:22 +00:00
);
2015-12-04 12:47:30 +00:00
$test = wp_safe_remote_post ( $this -> get_delivery_url (), $args );
$response_code = wp_remote_retrieve_response_code ( $test );
if ( is_wp_error ( $test ) ) {
2017-08-17 00:20:02 +00:00
/* translators: error message */
2015-12-09 15:20:53 +00:00
return new WP_Error ( 'error' , sprintf ( __ ( 'Error: Delivery URL cannot be reached: %s' , 'woocommerce' ), $test -> get_error_message () ) );
2015-12-04 12:47:30 +00:00
}
if ( 200 !== $response_code ) {
2017-08-17 00:20:02 +00:00
/* translators: error message */
2015-12-09 15:20:53 +00:00
return new WP_Error ( 'error' , sprintf ( __ ( 'Error: Delivery URL returned response code: %s' , 'woocommerce' ), absint ( $response_code ) ) );
2015-12-04 12:47:30 +00:00
}
2017-08-17 00:20:02 +00:00
delete_post_meta ( $this -> get_id (), '_webhook_pending_delivery' );
2017-06-16 12:56:50 +00:00
2015-12-04 12:47:30 +00:00
return true ;
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
/*
|--------------------------------------------------------------------------
| Getters
|--------------------------------------------------------------------------
*/
/**
* Get the friendly name for the webhook .
*
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return string
*/
public function get_name ( $context = 'view' ) {
return apply_filters ( 'woocommerce_webhook_name' , $this -> get_prop ( 'name' , $context ), $this -> get_id () );
}
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Get the webhook status .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* - 'active' - delivers payload .
* - 'paused' - does not deliver payload , paused by admin .
* - 'disabled' - does not delivery payload , paused automatically due to consecutive failures .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
2014-07-30 20:23:22 +00:00
* @ return string status
*/
2017-08-17 00:20:02 +00:00
public function get_status ( $context = 'view' ) {
return apply_filters ( 'woocommerce_webhook_status' , $this -> get_prop ( 'status' , $context ), $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:40:31 +00:00
/**
* Get webhopk created date .
*
* @ since 3.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return WC_DateTime | null Object if the date is set or null if there is no date .
*/
public function get_date_created ( $context = 'view' ) {
return $this -> get_prop ( 'date_created' , $context );
}
/**
* Get webhopk modified date .
*
* @ since 3.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return WC_DateTime | null Object if the date is set or null if there is no date .
*/
public function get_date_modified ( $context = 'view' ) {
return $this -> get_prop ( 'date_modified' , $context );
}
2014-12-16 17:29:37 +00:00
/**
2017-08-17 00:20:02 +00:00
* Get the secret used for generating the HMAC - SHA256 signature .
2014-12-16 17:29:37 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
2014-12-16 17:29:37 +00:00
* @ return string
*/
2017-08-17 00:20:02 +00:00
public function get_secret ( $context = 'view' ) {
return apply_filters ( 'woocommerce_webhook_secret' , $this -> get_prop ( 'secret' , $context ), $this -> get_id () );
2014-12-16 17:29:37 +00:00
}
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Get the webhook topic , e . g . `order.created` .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return string
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function get_topic ( $context = 'view' ) {
return apply_filters ( 'woocommerce_webhook_topic' , $this -> get_prop ( 'topic' , $context ), $this -> get_id () );
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
/**
* Get the delivery URL .
*
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return string
*/
public function get_delivery_url ( $context = 'view' ) {
return apply_filters ( 'woocommerce_webhook_delivery_url' , $this -> get_prop ( 'delivery_url' , $context ), $this -> get_id () );
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
/**
* Get the user ID for this webhook .
*
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return int
*/
public function get_user_id ( $context = 'view' ) {
return $this -> get_prop ( 'user_id' , $context );
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
/**
* API version .
*
* @ since 3.0 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return string
*/
public function get_api_version ( $context = 'view' ) {
$version = $this -> get_prop ( 'api_version' , $context );
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
return 0 < $version ? 'wp_api_v' . $version : 'legacy_v3' ;
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:20:02 +00:00
/**
* Get the failure count .
*
* @ since 2.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return int
*/
public function get_failure_count ( $context = 'view' ) {
return $this -> get_prop ( 'failure_count' , $context );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Get pending delivery .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.2 . 0
* @ param string $context What the value is for .
* Valid values are 'view' and 'edit' .
* @ return bool
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function get_pending_delivery ( $context = 'view' ) {
return $this -> get_prop ( 'pending_delivery' , $context );
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
/*
|--------------------------------------------------------------------------
| Setters
|--------------------------------------------------------------------------
*/
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Set webhook name .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.2 . 0
* @ param string $name Webhook name .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_name ( $name ) {
$this -> set_prop ( 'name' , $name );
}
2014-07-30 20:23:22 +00:00
2017-08-17 00:40:31 +00:00
/**
2017-08-17 03:08:32 +00:00
* Set webhook created date .
2017-08-17 00:40:31 +00:00
*
* @ since 3.2 . 0
* @ param string | integer | null $date UTC timestamp , or ISO 8601 DateTime .
* If the DateTime string has no timezone or offset ,
* WordPress site timezone will be assumed .
* Null if their is no date .
*/
public function set_date_created ( $date = null ) {
$this -> set_date_prop ( 'date_created' , $date );
}
/**
2017-08-17 03:08:32 +00:00
* Set webhook modified date .
2017-08-17 00:40:31 +00:00
*
* @ since 3.2 . 0
* @ param string | integer | null $date UTC timestamp , or ISO 8601 DateTime .
* If the DateTime string has no timezone or offset ,
* WordPress site timezone will be assumed .
* Null if their is no date .
*/
public function set_date_modified ( $date = null ) {
$this -> set_date_prop ( 'date_modified' , $date );
}
2017-08-17 00:20:02 +00:00
/**
* Set status .
*
* @ since 3.2 . 0
* @ param string $status Status .
*/
public function set_status ( $status ) {
2017-08-17 21:36:42 +00:00
if ( ! array_key_exists ( $status , wc_get_webhook_statuses () ) ) {
2017-08-17 20:54:31 +00:00
$status = 'disabled' ;
}
2017-08-17 00:20:02 +00:00
$this -> set_prop ( 'status' , $status );
2014-07-30 20:23:22 +00:00
}
/**
2015-11-03 13:31:20 +00:00
* Set the secret used for generating the HMAC - SHA256 signature .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $secret Secret .
2014-07-30 20:23:22 +00:00
*/
public function set_secret ( $secret ) {
2017-08-17 00:20:02 +00:00
$this -> set_prop ( 'secret' , $secret );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set the webhook topic and associated hooks .
* The topic resource & event are also saved separately .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $topic Webhook topic .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_topic ( $topic ) {
2017-08-17 20:57:09 +00:00
$topic = wc_clean ( $topic );
2017-08-17 21:36:42 +00:00
if ( ! wc_is_webhook_valid_topic ( $topic ) ) {
2017-08-17 20:57:09 +00:00
$topic = '' ;
}
2017-08-17 00:20:02 +00:00
$this -> set_prop ( 'topic' , $topic );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set the delivery URL .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ todo trigger webhook when set new delivery URL .
* @ since 2.2 . 0
* @ param string $url Delivery URL .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_delivery_url ( $url ) {
$this -> set_prop ( 'delivery_url' , esc_url_raw ( $url , array ( 'http' , 'https' ) ) );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set user ID .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.2 . 0
* @ param int $user_id User ID .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_user_id ( $user_id ) {
$this -> set_prop ( 'user_id' , ( int ) $user_id );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set API version .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.0 . 0
2017-08-17 15:18:32 +00:00
* @ param int | string $version REST API version .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_api_version ( $version ) {
2017-08-17 15:18:32 +00:00
if ( ! is_numeric ( $version ) ) {
$version = $this -> data_store -> get_api_version_number ( $version );
}
$this -> set_prop ( 'api_version' , ( int ) $version );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set pending delivery .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.2 . 0
* @ param bool $pending_delivery Set true if is pending for delivery .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_pending_delivery ( $pending_delivery ) {
$this -> set_prop ( 'pending_delivery' , ( bool ) $pending_delivery );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Set failure count .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 3.2 . 0
* @ param bool $failure_count Total of failures .
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function set_failure_count ( $failure_count ) {
$this -> set_prop ( 'failure_count' , intval ( $failure_count ) );
2014-07-30 20:23:22 +00:00
}
2017-08-17 00:20:02 +00:00
/*
|--------------------------------------------------------------------------
| Non - CRUD Getters
|--------------------------------------------------------------------------
*/
2014-07-30 20:23:22 +00:00
/**
2017-08-17 00:20:02 +00:00
* Get the associated hook names for a topic .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ param string $topic Topic name .
* @ return array
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
private function get_topic_hooks ( $topic ) {
$topic_hooks = array (
'coupon.created' => array (
'woocommerce_process_shop_coupon_meta' ,
'woocommerce_new_coupon' ,
),
'coupon.updated' => array (
'woocommerce_process_shop_coupon_meta' ,
'woocommerce_update_coupon' ,
),
'coupon.deleted' => array (
'wp_trash_post' ,
),
'coupon.restored' => array (
'untrashed_post' ,
),
'customer.created' => array (
'user_register' ,
'woocommerce_created_customer' ,
'woocommerce_new_customer' ,
),
'customer.updated' => array (
'profile_update' ,
'woocommerce_update_customer' ,
),
'customer.deleted' => array (
'delete_user' ,
),
'order.created' => array (
'woocommerce_process_shop_order_meta' ,
'woocommerce_new_order' ,
),
'order.updated' => array (
'woocommerce_process_shop_order_meta' ,
'woocommerce_update_order' ,
),
'order.deleted' => array (
'wp_trash_post' ,
),
'order.restored' => array (
'untrashed_post' ,
),
'product.created' => array (
'woocommerce_process_product_meta' ,
'woocommerce_new_product' ,
'woocommerce_new_product_variation' ,
),
'product.updated' => array (
'woocommerce_process_product_meta' ,
'woocommerce_update_product' ,
'woocommerce_update_product_variation' ,
),
'product.deleted' => array (
'wp_trash_post' ,
),
'product.restored' => array (
'untrashed_post' ,
),
);
$topic_hooks = apply_filters ( 'woocommerce_webhook_topic_hooks' , $topic_hooks , $this );
return isset ( $topic_hooks [ $topic ] ) ? $topic_hooks [ $topic ] : array ();
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Get the hook names for the webhook .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ return array
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function get_hooks () {
if ( 'action' === $this -> get_resource () ) {
$hooks = array ( $this -> get_event () );
} else {
$hooks = $this -> get_topic_hooks ( $this -> get_topic () );
}
return apply_filters ( 'woocommerce_webhook_hooks' , $hooks , $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Get the resource for the webhook , e . g . `order` .
2014-07-30 20:23:22 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ return string
2014-07-30 20:23:22 +00:00
*/
2017-08-17 00:20:02 +00:00
public function get_resource () {
$topic = explode ( '.' , $this -> get_topic () );
return apply_filters ( 'woocommerce_webhook_resource' , $topic [ 0 ], $this -> get_id () );
2014-07-30 20:23:22 +00:00
}
2016-11-22 23:49:04 +00:00
/**
2017-08-17 00:20:02 +00:00
* Get the event for the webhook , e . g . `created` .
2016-11-22 23:49:04 +00:00
*
2017-08-17 00:20:02 +00:00
* @ since 2.2 . 0
* @ return string
2016-11-22 23:49:04 +00:00
*/
2017-08-17 00:20:02 +00:00
public function get_event () {
$topic = explode ( '.' , $this -> get_topic () );
2016-11-22 23:49:04 +00:00
2017-08-17 00:20:02 +00:00
return apply_filters ( 'woocommerce_webhook_event' , isset ( $topic [ 1 ] ) ? $topic [ 1 ] : '' , $this -> get_id () );
2016-11-22 23:49:04 +00:00
}
/**
2017-08-17 00:20:02 +00:00
* Get the webhook i18n status .
2016-11-22 23:49:04 +00:00
*
* @ return string
*/
2017-08-17 00:20:02 +00:00
public function get_i18n_status () {
$status = $this -> get_status ();
$statuses = wc_get_webhook_statuses ();
return isset ( $statuses [ $status ] ) ? $statuses [ $status ] : $status ;
2016-11-22 23:49:04 +00:00
}
2014-07-30 20:23:22 +00:00
}