<?php
/**
 * The webhook class
 *
 * Used to define webhook related functions
 *
 *
 * @since      9.9
 * @package    CartBounty Pro - Save and recover abandoned carts for WooCommerce
 * @subpackage CartBounty Pro - Save and recover abandoned carts for WooCommerce/includes
 * @author     Streamline.lv
 */
class CartBounty_Pro_Webhook{

	/**
	 * The admin handler that manages the plugin's settings, options, and backend functionality.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Admin    $admin    Provides methods to control and extend the plugin's admin area.
	 */
	protected $admin = null;

	/**
	 * The public handler that manages the plugin's settings, options, and frontend functionality.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Public    $public    Provides methods to control and extend the plugin's public area.
	 */
	protected $public = null;

	/**
	 * The coupons manager responsible for handling coupons.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Coupons    $coupons    Provides methods to create, apply, and manage discount coupons.
	 */
	protected $coupons = null;

	/**
	 * Get the admin handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Admin
	 */
	protected function admin(){
		
		if( $this->admin === null ){
			$this->admin = new CartBounty_Pro_Admin( CARTBOUNTY_PRO_PLUGIN_NAME_SLUG, CARTBOUNTY_PRO_VERSION_NUMBER );
		}

		return $this->admin;
	}

	/**
	 * Get the public handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Public
	 */
	protected function public(){
		
		if( $this->public === null ){
			$this->public = new CartBounty_Pro_Public( CARTBOUNTY_PRO_PLUGIN_NAME_SLUG, CARTBOUNTY_PRO_VERSION_NUMBER );
		}

		return $this->public;
	}

	/**
	 * Get the coupon handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Coupons
	 */
	protected function coupons(){
		
		if( $this->coupons === null ){
			$this->coupons = new CartBounty_Pro_Coupons();
		}

		return $this->coupons;
	}

	/**
	 * Starting automated sending of abandoned carts to webhook 
	 *
	 * @since    9.9
	 */
	public function auto_send(){
		$admin = $this->admin();

		if( !$admin->check_license() || !class_exists('WooCommerce') ) return; //Exit if license key not valid or if WooCommerce is not activated

		if( $this->api_valid() ){ //If webhook connected and enabled
			$this->put_carts();
		}
	}

	/**
	 * Method retrieves saved webhook URL
	 *
	 * @since    9.9
	 * @return 	 String
	 */
	public function get_webhook_url(){
		$url = esc_attr( get_option( 'cartbounty_pro_webhook_url' ) ); //Retrieve webhook URL from database
		return $url;
	}

	/**
	 * Method returns array of webhook actions
	 *
	 * @since    9.9
	 * @return 	 Array
	 */
	public function get_webhook_actions(){
		$actions = array(
			'test_send_cart' 		=> esc_html( 'Add cart', 'woo-save-abandoned-carts' ),
			'test_update_cart' 		=> esc_html( 'Update cart', 'woo-save-abandoned-carts' ),
			'test_new_order' 		=> esc_html( 'New order', 'woo-save-abandoned-carts' ),
			'test_delete_cart' 		=> esc_html( 'Delete cart', 'woo-save-abandoned-carts' )
		);
		return $actions;
	}

	/**
	* Test the Webhook URL
	*
	* @since    9.9
	* @return 	Boolean
	*/
	public function credentials_test(){
		$result = $this->send_webhook( array(), apply_filters( 'cartbounty_pro_webhook_method_get', 'GET' ), 3 );
		return $result;
	}

	/**
	 * Method checks if webhook connection is valid
	 *
	 * @since    9.9
	 * @return 	 Boolean
	 */
	public function api_valid(){
		$admin = $this->admin();
		$status = false;
		$webhook_url = $this->get_webhook_url();

		if( !empty( $webhook_url ) ){ //If Webhook url is not empty

			$current_fingerprint = $admin->get_key_fingerprint( $webhook_url );
			$valid_fingerprint = $admin->get_cartbounty_transient( 'webhook_valid_fingerprint' ); //Last known valid fingerprint
			
			if( $valid_fingerprint === $current_fingerprint ){ //If webhook URL has not changed and was already validated before
				$status = true;

			}else{

				$invalid_fp = $admin->get_cartbounty_transient( 'webhook_invalid_fingerprint' ); //Last known invalid fingerprint

				if( $invalid_fp !== $current_fingerprint ){ //If URL has changed

					$invalid_until = (int)$admin->get_cartbounty_transient( 'webhook_api_invalid_until' );

					if( $invalid_until && time() < $invalid_until && $invalid_fp === $current_fingerprint ){ //Exit and return false if credentials have already failed before and they have not changed
						return $status;
					}

					if( (int)$this->credentials_test() ){ //In case we get a valid response from Webhook
						$admin->set_cartbounty_transient( 'webhook_valid_fingerprint', $current_fingerprint, 60*60*48 ); //Cache valid API fingerprint for 48 hours
						$admin->delete_cartbounty_transient( 'webhook_api_times_failed' );
						$admin->delete_cartbounty_transient( 'webhook_api_invalid_until' );
						$admin->delete_cartbounty_transient( 'webhook_invalid_fingerprint' );
						$status = true;

					}else{ //If we have connection issues - try and save response to limit how many times we try to connect
						$fail_count = (int)$admin->get_cartbounty_transient( 'webhook_api_times_failed' );
						$fail_count++;
						$admin->set_cartbounty_transient( 'webhook_api_times_failed', $fail_count, 60*60 ); //Remember failures for 1 hour

						if( $fail_count >= 3 ){
							$admin->set_cartbounty_transient( 'webhook_api_invalid_until', time() + 60*15, 60*15 ); //Invalidate requests for 15 minutes
							$admin->set_cartbounty_transient( 'webhook_invalid_fingerprint', $current_fingerprint, 60*15 ); //Cache invalid API fingerprint for 15 minutes
							$admin->delete_cartbounty_transient( 'webhook_valid_fingerprint' );
						}
					}
				}
			}
		}

		return $status;
	}

	/**
	 * Method returns webhook status
	 *
	 * @since    9.9
	 * @return 	 Array
	 */
	public function api_status(){
		$admin = $this->admin();
		$url = $this->get_webhook_url();
		$response = array();

		if( !empty( $url ) ){ //If the field is not empty
			
			if( $this->api_valid() ){
				$response['status'] = '1';
				$response['result'] = '';
				$response['message'] =  esc_html__( 'Connected', 'woo-save-abandoned-carts' );

			}else{
				$response['status'] = '0';
				$response['result'] = esc_html__( 'Error', 'woo-save-abandoned-carts' );
				$response['message'] = esc_html__( 'Please make sure webhook URL is correct', 'woo-save-abandoned-carts' );
			}

		}else{ //In case the webhook URL field is empty
			$response['status'] = '0';
			$response['result'] = '';
			$response['message'] = '';
			$admin->delete_cartbounty_transient( 'webhook_api_times_failed' );
			$admin->delete_cartbounty_transient( 'webhook_api_invalid_until' );
			$admin->delete_cartbounty_transient( 'webhook_valid_fingerprint' );
			$admin->delete_cartbounty_transient( 'webhook_invalid_fingerprint' );
		}

		return $response;
	}

	/**
	* Send Webhook
	* POST 		- Method used to create new or update existing abandoned cart row
	* DELETE 	- Method used to delete abandoned cart
	*
	* @since    9.9
	* @param    array      $data    		  	  		Data sent to webhook
	* @param    string     $method    		  			Type of action (POST, GET, PUSH, PATCH, DELETE etc.)
    * @param    integer    $timeout      				Maximum time allowed for connection
	* @return 	Boolean
	*/
	private function send_webhook( $data, $method = 'POST', $timeout = 20 ){

		if( $method == 'POST' ){
			$method = apply_filters( 'cartbounty_pro_webhook_method_post', 'POST' );
		}
		
		$result = false;
		$url = $this->get_webhook_url();
		
		if( !empty( $data ) ){ //If data is not empty, encode it for sending over
			$data = json_encode( $data );
		}

		$request = wp_safe_remote_post( $url, array(
			'headers'     => [
				'Content-Type' => 'application/json',
			],
			'method'		=> $method,
			'timeout'		=> $timeout,
			'body'			=> $data
		));

		if ( is_wp_error( $request ) ) {
			$error = array();
			$error['code'] = $request->get_error_code();
			$error['message'] = $request->get_error_message();
			$this->log_wh_errors( $error );

		} else {

			if( isset( $request['response']['code'] ) ){

				if( $request['response']['code'] == 200 ){
					$this->log_wh_errors( $request['response'], $extra = array(
						'method' => $method
					) );
					$result = true;

				}else{

					if( isset( $request['response'] ) ){
						$this->log_wh_errors( $request['response'], $extra = array(
							'method' => $method
						) );
					}
				}
			}
		}

		return $result;
	}

	/**
	 * Function sends new or updates existing carts to webhook
	 *
	 * @since    9.9
	 */
	private function put_carts() {
		global $wpdb;
		$admin = $this->admin();
		$cart_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME;
		$time = $admin->get_time_intervals();
		$ready_carts = array();

		$carts = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT *
				FROM {$cart_table}
				WHERE time != wh_last_synced AND
				(type = %d OR type = %d) AND
				(email != '' OR phone != '') AND
				cart_contents != '' AND
				paused != 1 AND
				wh_excluded != 1 AND
				anonymized != 1 AND
				time < %s AND
				time > %s
				ORDER BY time DESC",
				$admin->get_cart_type('abandoned'),
				$admin->get_cart_type('recovered_pending'),
				$time['wh_sync_period'],
				$time['maximum_sync_period']
			)
		);

		if( $carts ){ //If we have abandoned carts that must be created or updated
			foreach( $carts as $key => $cart ){

				if( !$admin->has_excluded_items( $cart, 'webhook' ) ){ //If cart contents do not have excluded items
					$ready_carts[] = $cart;

				}else{
					$admin->exclude_cart( 'webhook', $cart ); //Exclude cart from further recovery
				 	unset( $carts[$key] ); //Remove current cart from cart array since it includes products that are excluded
				}
			}

			$prepared_carts = $this->prepare_carts( $ready_carts );
			$result = $this->send_webhook( $prepared_carts );

			if ( $result ) { //Successful
				foreach( $ready_carts as $key => $cart ){
					$wpdb->query(
						$wpdb->prepare( 
							"UPDATE {$cart_table}
							SET wh_last_synced = %s
							WHERE id = %d",
							$cart->time,
							$cart->id
						)
					);
				}
				

			}else{ //Exclude the cart from further sending
				foreach( $ready_carts as $key => $cart ){
					$admin->exclude_cart( 'webhook', $cart ); //Exclude cart from further recovery
					$this->log_wh_errors( $result, $extra = array(
						'id' => $cart->id
					));
				}
			}
		}
	}

	/**
	 * Preparing cart data for sending
	 *
	 * @since    9.9
	 * @param    array     $carts    		  Abandoned carts
	 * @param    boolean   $test    		  Whether this is a test or not
	 * @param    boolean   $order    		  Whether this is a request for new order
	 * @return 	 Array
	 */
	public function prepare_carts( $carts, $test = false, $order = false ){
		$admin = $this->admin();
		$coupons = $this->coupons();;
		$cart_data = array();

		if( !$test ){
			foreach ( $carts as $key => $cart ) {

				$coupon = array();
				$status = 'ordered';

				if( !$order ){
					$coupon = $coupons->get_coupon( 'webhook', 'cartbounty_pro_webhook_coupon', $step_nr = false, ( object )$cart ); //Coupon code creation
					$status = 'abandoned';
				}
				
				$checkout_url = $admin->create_cart_url( $cart->email, $cart->session_id, $cart->id, 'webhook' );
				$location_data = $admin->get_cart_location( $cart->location );
				$cart_content_lines = $this->prepare_lines( $cart->cart_contents ); //Creating lines from cart contents

				$cart_array = array(
					'id' 				=> $cart->id,
					'email' 			=> $cart->email,
					'phone' 			=> $cart->phone,
					'email_consent' 	=> $cart->email_consent,
					'phone_consent' 	=> $cart->phone_consent,
					'name' 				=> sanitize_text_field( wp_unslash( (string)( $cart->name ?? '' ) ) ),
					'surname' 			=> sanitize_text_field( wp_unslash( (string)( $cart->surname ?? '' ) ) ),
					'country'			=> $location_data['country'],
					'city'				=> stripslashes( $location_data['city'] ),
					'postcode'			=> stripslashes( $location_data['postcode'] ),
					'language' 			=> $cart->language,
					'ip_address' 		=> $cart->ip_address,
					'session_id' 		=> $cart->session_id,
					'cart_contents' 	=> $cart_content_lines,
					'cart_total'		=> $cart->cart_total,
					'currency'			=> $cart->currency,
					'coupon'			=> $coupon,
					'cart_url'			=> $checkout_url,
					'time'				=> $cart->time,
					'last_synced'		=> $cart->wh_last_synced,
					'cart_status'		=> $status,
					'store_name'		=> get_bloginfo( 'name' ),
					'store_url'			=> esc_url( get_bloginfo( 'url' ) )
				);

				$cart_data[] = ( object )$cart_array; //Turn cart array back into object and add it to array
			}

		}else{ //In case we are dealing with test data
			$cart_data = $carts;
		}

		return $cart_data;
	}

	/**
	 * Method prepares product lines
	 *
	 * @since    9.9
	 * @param    array       $cart_contents				Product array from abandoned cart
	 */
	private function prepare_lines( $cart_contents ){
		$admin = $this->admin();
		$cart_contents = $admin->get_saved_cart_contents( $cart_contents );

		if( is_array( $cart_contents ) ){
			$line_id = 1;
			$lines = array();

			foreach( $cart_contents as $cart_product ){ //Handling cart contents
				$product_id = $cart_product['product_id']; //Product ID
				$product_variant_id = $cart_product['product_variation_id']; //Product Variant ID
				$inventory_quantity = $cart_product['quantity']; //Quantity
				$product = wc_get_product( $product_id ); //Getting product data

				if( $product ){ //If we have a product
					$title = strip_tags( $product->get_name() );
					$url = get_permalink( $product->get_id() );
					$description = $product->get_short_description();
					$categories = strip_tags( wc_get_product_category_list( $product_id ) );
					$product_price = $admin->get_product_price( $cart_product );
					$image_url = $admin->get_product_thumbnail_url( $cart_product );

					if( empty( $product_variant_id ) ){
						$product_variant_id = $product_id; //Setting variation ID for simple products because it can't be null
					}

					$line = array(
						'name' 					=> $title,
						'price' 				=> $product_price,
						'quantity' 				=> $inventory_quantity,
						'product_id' 			=> $product_id,
						'product_variant_id' 	=> $product_variant_id,
						'categories' 			=> $categories,
						'description' 			=> $description,
						'product_url' 			=> $url,
						'image_url' 			=> $image_url
					);
					$lines[] = $line;
					$line_id++;

				}else{
					$admin->log( 'notice', sprintf( 'Webhook: WooCommerce product does not exist. ID: %d', esc_html( $product_id ) ) );
				}
			}

			return $lines;
		}
	}

	/**
	 * Method sends new order data to webhook
	 *
	 * @since    9.9
	 * @param    object     $cart            Abandoned cart that has just been turned into order
	 */
	public function sync_order( $cart ){

		if( !$this->api_valid() ) return;

		if( !$this->cart_synced( $cart->id ) ) return;

		$prepared_carts = $this->prepare_carts( array( $cart ), $test = false, $order = true );
		$result = $this->send_webhook( $prepared_carts );
	}

	/**
	 * Method sends abandoned cart delete request to webhook
	 *
	 * @since    9.9
	 * @return   Boolean
	 */
	public function delete_cart( $cart_id ){
		$deleted = false;
		$carts = array();

		if( !$this->api_valid() ) return;

		if( !is_array( $cart_id ) ){
			$cart_id = array( $cart_id );
		}

		foreach( $cart_id as $key => $id ){
			if( $this->cart_synced( $id ) ){
				$carts[] = array(
					'id' 				=> $id,
					'cart_status'		=> 'deleted',
					'store_name'		=> get_bloginfo( 'name' ),
					'store_url'			=> esc_url( get_bloginfo( 'url' ) )
				);
			}
		}

		if( !empty( $carts ) ){
			$result = $this->send_webhook( $carts, apply_filters( 'cartbounty_pro_webhook_method_delete', 'DELETE' ) );
			
			if( $result ){
				$deleted = true;
			}
		}

		return $deleted;
	}

	/**
	 * Method removes duplicate carts from webhook
	 *
	 * @since    9.5
	 * @param    array      $carts          Cart array
	 */
	public function delete_duplicate_carts( $carts ){

		if( !$this->api_valid() ) return;

		if( empty( $carts ) ) return; //Exit in case no carts provided

		$duplicate_carts = array();

		foreach( $carts as $key => $cart ) {
			if( $this->cart_synced( $cart->id ) ){
				$duplicate_carts[] = array(
					'id'				=> $cart->id,
					'cart_status'		=> 'deleted',
					'store_name'		=> get_bloginfo( 'name' ),
					'store_url'			=> esc_url( get_bloginfo( 'url' ) )
				);
			}
		}

		if( !empty( $duplicate_carts ) ){
			$result = $this->send_webhook( $duplicate_carts, apply_filters( 'cartbounty_pro_webhook_method_delete', 'DELETE' ) );
		}
	}

	/**
	 * Method returns information if abandoned cart has been sent to webhook
	 *
	 * @since    9.9
	 * @param    integer     $id - Abandoned cart ID
	 * @return 	 boolean
	 */
	public function cart_synced( $id ){
		global $wpdb;
		$cart_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME;
		$synced = false;

		$last_synced_time = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT wh_last_synced FROM $cart_table
				WHERE id = %d",
				intval( $id )
			)
		);

		if( $last_synced_time != '0000-00-00 00:00:00' ){ //If cart has been sent to webhook
			$synced = true;
		}

		return $synced;
	}

	/**
	* Test webhook data
	*
	* @since    9.9
	* @return   HTML
	*/
	public function test_webhook(){
		
		if( !isset( $_POST ) ) wp_send_json_error();

		$nonce = $_POST['nonce'];
		$operation = $_POST['operation'];
		$direction = is_rtl() ? 'rtl' : 'ltr';

		if ( check_ajax_referer( $operation, 'nonce', false ) == false ) { //If the request does not include our nonce security check, stop executing function
			wp_send_json_error('<span class="license-status license-inactive cartbounty-pro-response cartbounty-pro-no-border fadeOutSlow" dir="'. $direction .'"><i class="license-status-icon"><img src="'. esc_url( plugin_dir_url( __DIR__ ) ) . 'admin/assets/invalid-icon.svg" /></i>'. esc_html__( 'Looks like you are not allowed to do this.', 'woo-save-abandoned-carts' ) .'</span>' );
		}

		$test_data = $this->get_test_data( $operation );
		$carts = $test_data['carts'];
		$method = $test_data['method'];
		$prepared_carts = $this->prepare_carts( $carts, $test = true );
		$result = $this->send_webhook( $prepared_carts, $method );

		if( $result ){
			wp_send_json_success('<span class="license-status license-active cartbounty-pro-response cartbounty-pro-no-border fadeOutSlow" dir="'. $direction .'"><i class="license-status-icon"><img src="'. esc_url( plugin_dir_url( __DIR__ ) ) . 'admin/assets/active-icon.svg" /></i>'. esc_html__( 'Webhook successfully sent', 'woo-save-abandoned-carts' ) .'</span>' );
		}else{
			wp_send_json_error('<span class="license-status license-inactive cartbounty-pro-response cartbounty-pro-no-border fadeOutSlow" dir="'. $direction .'"><i class="license-status-icon"><img src="'. esc_url( plugin_dir_url( __DIR__ ) ) . 'admin/assets/invalid-icon.svg" /></i>'. esc_html__( 'Error sending test webhook.', 'woo-save-abandoned-carts' ) .'</span>' );
		}
	}

	/**
     * Returning webhook test data and method
     *
     * @since    9.9
     * @param    string     $action    		      Webhook action that needs to be performed
     * @return   array
     */
	public function get_test_data( $action ){
		$public = $this->public();
		$carts = array();
		$cart = array();
		$method = '';

		if( $action == 'test_send_cart' || $action == 'test_update_cart' || $action == 'test_new_order' ){
			$type = 'abandoned';
			$method = apply_filters( 'cartbounty_pro_webhook_method_post', 'POST' );
			$last_synced = '0000-00-00 00:00:00';
			$current_time = current_time( 'mysql', true );

			if( $action == 'test_new_order' ){
				$type = 'ordered';
			}

			if( $action == 'test_update_cart' ){
				$last_synced = $current_time;
			}

			$product_a = array(
				'name' => esc_html__( 'Product name', 'woo-save-abandoned-carts' ),
				'price' => 28.99,
				'quantity' => 1,
				'product_id' => 750,
				'product_variant_id' => 753,
				'categories' => esc_html__( 'Category A, Category B', 'woo-save-abandoned-carts' ),
				'description' => esc_html__( 'Product description', 'woo-save-abandoned-carts' ),
				'product_url' => esc_url( get_bloginfo( 'url' ) ),
				'image_url' => esc_url( $public->get_plugin_url() . 'public/assets/email-product-image-1.png' )
			);

			$product_b = array(
				'name' => esc_html__( 'Product name', 'woo-save-abandoned-carts' ),
				'price' => 73,
				'quantity' => 2,
				'product_id' => 853,
				'product_variant_id' => 853,
				'categories' => '',
				'description' => esc_html__( 'Product description', 'woo-save-abandoned-carts' ),
				'product_url' => esc_url( get_bloginfo( 'url' ) ),
				'image_url' => esc_url( $public->get_plugin_url() . 'public/assets/email-product-image-2.png' )
			);

			$cart = array(
				'id' 				=> 1,
				'email' 			=> get_option( 'admin_email' ),
				'phone' 			=> esc_html__( 'Phone number', 'woo-save-abandoned-carts' ),
				'email_consent' 	=> 0,
				'phone_consent' 	=> 0,
				'name' 				=> esc_html__( 'Name', 'woo-save-abandoned-carts' ),
				'surname' 			=> esc_html__( 'Surname', 'woo-save-abandoned-carts' ),
				'country'			=> 'US',
				'city'				=> esc_html__( 'City', 'woo-save-abandoned-carts' ),
				'postcode'			=> esc_html__( 'Postcode', 'woo-save-abandoned-carts' ),
				'language' 			=> 'en_US',
				'ip_address' 		=> '0.0.0.0',
				'session_id' 		=> esc_html__( 'Session ID', 'woo-save-abandoned-carts' ),
				'cart_contents' 	=> array( $product_a, $product_b ),
				'cart_total'		=> 101.99,
				'currency'			=> 'USD',
				'coupon'			=> array(
					'coupon_code' 			=> esc_html__( 'Coupon code', 'woo-save-abandoned-carts' ),
					'coupon_expiry' 		=> date( 'M d, Y', strtotime( $current_time . ' + 14 days' ) ),
					'coupon_description' 	=> esc_html__( 'Coupon description', 'woo-save-abandoned-carts' )
				),
				'cart_url'			=> esc_url( get_bloginfo( 'url' ) ),
				'time'				=> $current_time,
				'last_synced'		=> $last_synced,
				'cart_status'		=> $type,
				'store_name'		=> get_bloginfo( 'name' ),
				'store_url'			=> esc_url( get_bloginfo( 'url' ) )
			);

		}elseif( $action == 'test_delete_cart' ){
			$type = 'deleted';
			$method = apply_filters( 'cartbounty_pro_webhook_method_delete', 'DELETE' );

			$cart = array(
				'id' 				=> 1,
				'cart_status'		=> $type,
				'store_name'		=> get_bloginfo( 'name' ),
				'store_url'			=> esc_url( get_bloginfo( 'url' ) )
			);
		}

		$carts[] = ( object )$cart;

		return array(
			'carts'		=> $carts,
			'method'	=> $method
		);
	}

	/**
	* Function handles webhook error logging
	*
	* @since    9.9
	* @param    array     $error    		  Webhook error
	* @param    array     $extra    		  Additional information to output, optional
	*/
	private function log_wh_errors( $error, $extra = array() ){
		$additional_data = array();

		if( isset( $extra['id'] ) ){
			$additional_data[] = 'Cart: ' . $extra['id'];
		}

		if( isset( $extra['method'] ) ){
			$additional_data[] = 'Method: ' . $extra['method'];
		}

		if( isset( $error['code']) && $error['message'] ){
			$admin = $this->admin();
			$admin->log( 'notice', sprintf( 'Webhook: %s - %s. %s', esc_html( $error['code'] ), esc_html( $error['message'] ), esc_html( implode( ', ', $additional_data ) ) ) );
		}
	}
}