_blogId = get_current_blog_id(); $this->_metaFieldCreatedGmt = "_a2c_wh_cart_{$this->_blogId}_created_gmt"; $this->_metaFieldUpdatedGmt = "_a2c_wh_cart_{$this->_blogId}_updated_gmt"; $this->_metaFieldCartId = "_a2c_wh_cart_{$this->_blogId}_id"; if (version_compare(WooCommerce::instance()->version, '3.1.0', '>=')) { $this->_metaFieldPersistentCart = "_woocommerce_persistent_cart_{$this->_blogId}"; } else { $this->_metaFieldPersistentCart = '_woocommerce_persistent_cart'; } } /** * @return WH_Helper */ public static function instance() { return self::$_instance ?: self::$_instance = new self; } /** * @param $meta_id * @param $object_id * @param $meta_key */ public function beforePersistentCartUpdate($meta_id, $object_id, $meta_key) { if ($meta_key === $this->_metaFieldPersistentCart) { $this->_cartHash = md5(wp_json_encode($this->_loadCartFromDb())); } } /** * @param $meta_id * @param $object_id * @param $meta_key */ public function persistentCartUpdated($meta_id, $object_id, $meta_key) { if ($meta_key === $this->_metaFieldPersistentCart && $this->_cartHash !== md5(wp_json_encode(WooCommerce::instance()->cart->get_cart_for_session())) ) { $this->resolveEvent(true); } } /** * @return array */ private function _loadCartFromDb() { $saved_cart = array(); if (apply_filters('woocommerce_persistent_cart_enabled', true)) { $saved_cart_meta = get_user_meta(get_current_user_id(), $this->_metaFieldPersistentCart, true); if (isset($saved_cart_meta['cart'])) { $saved_cart = array_filter((array)$saved_cart_meta['cart']); } } return $saved_cart; } /** * @param array $resources * * @return array */ public function registerWebhookResource($resources) { $resources[] = 'basket'; return $resources; } /** * @param array $topics * * @return array */ public function addWebhookTopics($topics) { $topics = array_merge( $topics, array( 'basket.created' => __('Basket Created', 'a2c_wh'), 'basket.updated' => __('Basket Updated', 'a2c_wh'), 'basket.deleted' => __('Basket Deleted', 'a2c_wh'), ) ); return $topics; } /** * @return int */ private function _getCustomerId() { if ($this->_customerId === null) { $this->_customerId = (int)get_current_user_id(); } return $this->_customerId; } /** * @param bool $isDelete * * @return array */ private function _webhookData($isDelete = false) { $webhookData = array( 'blog_id' => $this->_blogId, 'cart_id' => $this->_getCartId(), 'session_key' => WooCommerce::instance()->session->get_customer_id(), 'customer' => $this->_prepareCustomerData(), self::SESSION_FIELD_CREATED_GMT => WooCommerce::instance()->session->get(self::SESSION_FIELD_CREATED_GMT), self::SESSION_FIELD_UPDATED_GMT => WooCommerce::instance()->session->get(self::SESSION_FIELD_UPDATED_GMT), ); if (!$isDelete) { $webhookData['cart'] = $this->_cartDataToArray(); } return $webhookData; } /** * @return array */ private function _cartDataToArray() { new WC_Cart_Totals(WooCommerce::instance()->cart); $items = []; foreach (WooCommerce::instance()->cart->get_cart_contents() as $key => $item) { $items[] = $this->_prepareItemData($item, $key); } return $items; } /** * @param array $item * @param string $basketItemId * * @return array */ private function _prepareItemData($item, $basketItemId) { $itemData = $item['data']; /** * @var WC_Product $itemData */ $data = array( 'id' => $basketItemId, 'product_id' => $itemData->get_id(), 'sku' => $itemData->get_sku(), 'name' => $itemData->get_name(), 'variant_id' => $item['variation_id'] ?: null, 'weight' => $itemData->get_weight(), 'length' => $itemData->get_length(), 'width' => $itemData->get_width(), 'height' => $itemData->get_height(), 'quantity' => $item['quantity'], 'price' => $itemData->get_price(), 'subtotal' => $item['line_subtotal'], 'subtotal_tax' => $item['line_subtotal_tax'], 'total' => $item['line_total'], 'total_tax' => $item['line_tax'], ); return $data; } /** * @return array */ private function _prepareCustomerData() { $customer = WooCommerce::instance()->customer; $data = $customer->get_data(); if ($data['id'] != 0) { $data = array_intersect_key( $data, array_flip(array('id', 'email', 'first_name', 'last_name', 'billing', 'shipping')) ); } else { $customer = WooCommerce::instance()->session->get('customer'); $data = array( 'id' => $customer['id'], 'email' => $customer['email'], 'first_name' => $customer['first_name'], 'last_name' => $customer['last_name'], 'billing' => array( 'first_name' => $customer['first_name'], 'last_name' => $customer['last_name'], 'company' => $customer['company'], 'address_1' => isset($customer['address_1']) ? $customer['address_1'] : $customer['address'], 'address_2' => $customer['address_2'], 'city' => $customer['city'], 'postcode' => $customer['postcode'], 'state' => $customer['state'], 'country' => $customer['country'], 'email' => $customer['email'], 'phone' => $customer['phone'], ), 'shipping' => array( 'first_name' => $customer['shipping_first_name'], 'last_name' => $customer['shipping_last_name'], 'company' => $customer['shipping_company'], 'address_1' => isset($customer['shipping_address_1']) ? $customer['shipping_address_1'] : $customer['shipping_address'], 'address_2' => $customer['shipping_address_2'], 'city' => $customer['shipping_city'], 'postcode' => $customer['shipping_postcode'], 'state' => $customer['shipping_state'], 'country' => $customer['shipping_country'], ), ); } return $data; } /** * @return void */ public function cartCreated() { if (!$this->_isHookThrown) { $time = time(); $cartId = $this->_generateCartId(); if (($customerId = $this->_getCustomerId()) !== 0) { update_user_meta($customerId, $this->_metaFieldUpdatedGmt, $time); update_user_meta($customerId, $this->_metaFieldCreatedGmt, $time); update_user_meta($customerId, $this->_metaFieldCartId, $cartId); } WooCommerce::instance()->session->set(self::SESSION_FIELD_UPDATED_GMT, $time); WooCommerce::instance()->session->set(self::SESSION_FIELD_CREATED_GMT, $time); WooCommerce::instance()->session->set(self::SESSION_FIELD_CART_ID, $cartId); do_action('a2c_wh_cart_created_action', $this->_webhookData()); $this->_isHookThrown = true; } } /** * @return void */ public function cartUpdated() { if (!$this->_isHookThrown) { $this->_updateCartDate(); do_action('a2c_wh_cart_updated_action', $this->_webhookData()); $this->_isHookThrown = true; } } /** * @param $arg */ public function cartEmptied() { if (!$this->_isHookThrown) { $this->_updateCartDate(); do_action('a2c_wh_cart_emptied_action', $this->_webhookData()); $this->_isHookThrown = true; } } /** * @return void */ private function _updateCartDate() { $time = time(); if ($this->_getCustomerId() !== 0) { update_user_meta($this->_getCustomerId(), $this->_metaFieldUpdatedGmt, $time); } WooCommerce::instance()->session->set(self::SESSION_FIELD_UPDATED_GMT, $time); } /** * @param $arg */ public function cartDeleted() { if (!$this->_isHookThrown && !empty($this->_getCartId())) { if ($this->_getCustomerId() !== 0) { delete_user_meta($this->_getCustomerId(), $this->_metaFieldCreatedGmt); delete_user_meta($this->_getCustomerId(), $this->_metaFieldUpdatedGmt); delete_user_meta($this->_getCustomerId(), $this->_metaFieldCartId); } WooCommerce::instance()->session->__unset(self::SESSION_FIELD_CREATED_GMT); WooCommerce::instance()->session->__unset(self::SESSION_FIELD_UPDATED_GMT); WooCommerce::instance()->session->__unset(self::SESSION_FIELD_CART_ID); do_action('a2c_wh_cart_deleted_action', $this->_webhookData(true)); $this->_isHookThrown = true; $this->_cartId = null; } } /** * @param mixed $arg * * @return mixed */ public function resolveEvent($arg) { if (empty(WooCommerce::instance()->cart->get_cart_contents())) { $this->cartEmptied(); } else { if ($this->_getCustomerId() !== 0) { if (empty(get_user_meta($this->_getCustomerId(), $this->_metaFieldCreatedGmt, true))) { $this->cartCreated(); } else { $this->cartUpdated(); } } elseif (empty(WooCommerce::instance()->session->get(self::SESSION_FIELD_CREATED_GMT))) { $this->cartCreated(); } else { $this->cartUpdated(); } } return $arg; } /** * @param array $topics * * @return array */ public function registerWebhookTopicHooks($topics) { $topics['basket.updated'] = array( 'a2c_wh_cart_updated_action', 'a2c_wh_cart_emptied_action', ); $topics['basket.created'] = array( 'a2c_wh_cart_created_action', ); $topics['basket.deleted'] = array( 'a2c_wh_cart_deleted_action', ); return $topics; } /** * @param mixed $payload * @param string $resource * @param mixed $resource_id * * @return mixed */ public function buildWebhookPayload($payload, $resource, $resource_id) { if ($resource === 'basket') { $payload = $resource_id; } return $payload; } /** * @return string|null */ private function _getCartId() { if ($this->_cartId !== null) { return $this->_cartId; } $cartId = WooCommerce::instance()->session->get(self::SESSION_FIELD_CART_ID, false); if (!$cartId && ($userId = get_current_user_id()) !== 0) { $cartId = get_user_meta($userId, $this->_metaFieldCartId, true); } if ($cartId) { $this->_cartId = $cartId; } return $this->_cartId; } /** * @return string */ private function _generateCartId() { require_once ABSPATH . 'wp-includes/class-phpass.php'; $hasher = new PasswordHash(8, false); $this->_cartId = md5($hasher->get_random_bytes(32)); return $this->_cartId; } /** * @param WC_Order $order * @return void */ public function setCartIdForOrder($order) { $order->add_meta_data(self::SESSION_FIELD_CART_ID, $this->_getCartId()); $this->cartDeleted(); } /** * @return void */ public static function deactivate() { update_option(self::OPTION_NAME_ACTIVE, false); } /** * @return void */ public static function uninstall() { /** * @global $wpdb wpdb Database Access Abstraction Object */ global $wpdb; $wpdb->query('DELETE FROM `' . $wpdb->prefix . 'usermeta` WHERE `meta_key` LIKE "_a2c_wh_cart_%"'); delete_option(self::OPTION_NAME_VERSION); delete_option(self::OPTION_NAME_ACTIVE); } /** * @return void */ public static function activate() { if (class_exists('WooCommerce')) { $version = WooCommerce::instance()->version; } if (empty($version) || version_compare($version, '2.6') === -1) { echo '

'.__('Woocommerce 2.6+ is required. ', self::LANG_DOMAIN).'

'; @trigger_error(__('Woocommerce 2.6+ is required. ', self::LANG_DOMAIN), E_USER_ERROR); } update_option(self::OPTION_NAME_VERSION, self::VERSION, false); update_option(self::OPTION_NAME_ACTIVE, true, false); } }