/** * Disables the gateway under any of these conditions: * 1) If the cart does not contain a pre-order * 2) If the pre-order amount is charged upfront * 3) On the pay page * * @since 1.0 * @return bool */ public function is_available() { $is_available = true; // Backwards compatibility checking for payment page if (function_exists('is_checkout_pay_page')) { $pay_page = is_checkout_pay_page(); } else { $pay_page = is_page(woocommerce_get_page_id('pay')); } // on checkout page if (!$pay_page || defined('WOOCOMMERCE_CHECKOUT') && WOOCOMMERCE_CHECKOUT) { // not available if the cart does not contain a pre-order if (WC_Pre_Orders_Cart::cart_contains_pre_order()) { // not available when the pre-order amount is charged upfront if (WC_Pre_Orders_Product::product_is_charged_upfront(WC_Pre_Orders_Cart::get_pre_order_product())) { $is_available = false; } } else { $is_available = false; } } else { // not available on the pay page (for now) $is_available = false; } return $is_available; }
/** * Dispatch the email * * @since 1.0 */ public function trigger($args) { if (!empty($args)) { $defaults = array('order' => '', 'message' => ''); $args = wp_parse_args($args, $defaults); extract($args); if (!is_object($order)) { return; } $this->object = $order; $this->recipient = $this->object->billing_email; $this->message = $message; $this->availability_date = WC_Pre_Orders_Product::get_localized_availability_date(WC_Pre_Orders_Order::get_pre_order_product($this->object)); $this->find[] = '{order_date}'; $this->replace[] = date_i18n(woocommerce_date_format(), strtotime($this->object->order_date)); $this->find[] = '{release_date}'; $this->replace[] = $this->availability_date; $this->find[] = '{order_number}'; $this->replace[] = $this->object->get_order_number(); } if (!$this->is_enabled() || !$this->get_recipient()) { return; } $this->send($this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments()); }
/** * Conditionally remove any gateways that don't support pre-orders on the checkout page when the pre-order is charged * upon release. This is done because payment info is not required in this case so displaying gateways/payment fields * is not needed. * * @since 1.0 * @param array $available_gateways * @return array */ public function maybe_remove_unsupported_gateways($available_gateways) { // on checkout page if ((!is_page(woocommerce_get_page_id('pay')) || defined('WOOCOMMERCE_CHECKOUT') && WOOCOMMERCE_CHECKOUT) && WC_Pre_Orders_Cart::cart_contains_pre_order() && WC_Pre_Orders_Product::product_is_charged_upon_release(WC_Pre_Orders_Cart::get_pre_order_product())) { // remove any non-supported payment gateways foreach ($available_gateways as $gateway_id => $gateway) { if (!method_exists($gateway, 'supports') || false === $gateway->supports('pre-orders')) { unset($available_gateways[$gateway_id]); } } } return $available_gateways; }
/** * Force tokenization for pre-orders * * @since 4.1.0 * @see SV_WC_Payment_Gateway::tokenization_forced() * @param boolean $force_tokenization whether tokenization should be forced * @return boolean true if tokenization should be forced, false otherwise */ public function maybe_force_tokenization($force_tokenization) { // pay page with pre-order? $pay_page_pre_order = false; if ($this->get_gateway()->is_pay_page_gateway()) { $order_id = $this->get_gateway()->get_checkout_pay_page_order_id(); if ($order_id) { $pay_page_pre_order = WC_Pre_Orders_Order::order_contains_pre_order($order_id) && WC_Pre_Orders_Product::product_is_charged_upon_release(WC_Pre_Orders_Order::get_pre_order_product($order_id)); } } if (WC_Pre_Orders_Cart::cart_contains_pre_order() && WC_Pre_Orders_Product::product_is_charged_upon_release(WC_Pre_Orders_Cart::get_pre_order_product()) || $pay_page_pre_order) { // always tokenize the card for pre-orders that are charged upon release $force_tokenization = true; } return $force_tokenization; }
/** * Dispatch the email * * @since 1.0 */ public function trigger($order_id, $message) { if ($order_id) { $this->object = new WC_Order($order_id); $this->recipient = $this->object->billing_email; $this->message = $message; $this->find[] = '{order_date}'; $this->replace[] = date_i18n(woocommerce_date_format(), strtotime($this->object->order_date)); $this->find[] = '{release_date}'; $this->replace[] = WC_Pre_Orders_Product::get_localized_availability_date(WC_Pre_Orders_Order::get_pre_order_product($this->object)); $this->find[] = '{order_number}'; $this->replace[] = $this->object->get_order_number(); } if (!$this->is_enabled() || !$this->get_recipient()) { return; } $this->send($this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments()); }
/** * Disables the gateway under any of these conditions: * 1) If the cart does not contain a pre-order * 2) If the pre-order amount is charged upfront * 3) On the pay page * * @since 1.0 * @return bool */ public function is_available() { $is_available = parent::is_available(); // on checkout page if (!is_page(woocommerce_get_page_id('pay')) || defined('WOOCOMMERCE_CHECKOUT') && WOOCOMMERCE_CHECKOUT) { // not available if the cart does not contain a pre-order if (WC_Pre_Orders_Cart::cart_contains_pre_order()) { // not available when the pre-order amount is charged upfront if (WC_Pre_Orders_Product::product_is_charged_upfront(WC_Pre_Orders_Cart::get_pre_order_product())) { $is_available = false; } } else { $is_available = false; } } else { // not available on the pay page (for now) $is_available = false; } return $is_available; }
echo get_post_permalink($item['product_id']); ?> "> <?php echo $item['name']; ?> </a> </td> <td class="pre-order-status" style="text-align:left; white-space:nowrap;"> <?php echo WC_Pre_Orders_Order::get_pre_order_status_to_display($order); ?> </td> <td class="pre-order-release-date"> <?php echo WC_Pre_Orders_Product::get_localized_availability_date($item['product_id']); ?> </td> <td class="pre-order-actions order-actions"> <?php foreach ($actions[$order->id] as $key => $action) { ?> <a href="<?php echo esc_url($action['url']); ?> " class="button <?php echo sanitize_html_class($key); ?> "><?php echo esc_html($action['name']); ?>
/** * Save pre-order options * * @since 1.0 * @param int $post_id the ID of the product being saved */ public function save_product_tab_options($post_id) { // don't save any settings if there are active pre-orders if (WC_Pre_Orders_Product::product_has_active_pre_orders($post_id)) { return; } // pre-orders enabled if (isset($_POST['_wc_pre_orders_enabled']) && 'yes' === $_POST['_wc_pre_orders_enabled']) { update_post_meta($post_id, '_wc_pre_orders_enabled', 'yes'); } else { update_post_meta($post_id, '_wc_pre_orders_enabled', 'no'); } /* * Save the availability date/time * * The date/time a pre-order is released is saved as a unix timestamp adjusted for the site's timezone. For example, * when an admin sets a pre-order to be released on 2013-06-25 12pm EST (UTC-4), it is saved as a timestamp equivalent * to 2013-12-25 4pm UTC. This makes the pre-order release check much easier, as it's a simple timestamp comparison, * because the release datetime and the current time are both in UTC. */ if (!empty($_POST['_wc_pre_orders_availability_datetime'])) { try { // get datetime object from site timezone $datetime = new DateTime($_POST['_wc_pre_orders_availability_datetime'], new DateTimeZone(WC_Pre_Orders_Product::get_wp_timezone_string())); // get the unix timestamp (adjusted for the site's timezone already) $timestamp = $datetime->format('U'); // don't allow availability dates in the past if ($timestamp <= time()) { $timestamp = ''; } // set the availability datetime update_post_meta($post_id, '_wc_pre_orders_availability_datetime', $timestamp); } catch (Exception $e) { global $wc_pre_orders; $wc_pre_orders->log($e->getMessage()); } } else { delete_post_meta($post_id, '_wc_pre_orders_availability_datetime'); } // pre-order fee if (isset($_POST['_wc_pre_orders_fee']) && is_numeric($_POST['_wc_pre_orders_fee'])) { update_post_meta($post_id, '_wc_pre_orders_fee', $_POST['_wc_pre_orders_fee']); } // when to charge pre-order amount if (isset($_POST['_wc_pre_orders_when_to_charge']) && isset($_POST['_wc_pre_orders_enabled']) && 'yes' === $_POST['_wc_pre_orders_enabled']) { update_post_meta($post_id, '_wc_pre_orders_when_to_charge', 'upon_release' === $_POST['_wc_pre_orders_when_to_charge'] ? 'upon_release' : 'upfront'); } do_action('wc_pre_orders_save_product_options', $post_id); }
/** * Checks if the current cart contains a product with pre-orders enabled * * @since 1.0 * @return bool true if the cart contains a pre-order, false otherwise */ public static function cart_contains_pre_order() { global $woocommerce; $contains_pre_order = false; if (!empty($woocommerce->cart->cart_contents)) { foreach ($woocommerce->cart->cart_contents as $cart_item) { if (WC_Pre_Orders_Product::product_can_be_pre_ordered($cart_item['product_id'])) { $contains_pre_order = true; break; } } } return $contains_pre_order; }
/** * Adds a 'Release Date' line to pre-order product order items on the * thank-you page, emails, my account, etc * * @since 1.0 * @param array $items array of order item arrays * @param WC_Order $order order object * @return array of order item arrays */ public function add_product_release_date_item_meta($items, $order) { if (self::order_contains_pre_order($order)) { $name = get_option('wc_pre_orders_availability_date_cart_title_text'); foreach ($items as &$item) { if ('line_item' === $item['type']) { $product = get_product($item['product_id']); $pre_order_meta = apply_filters('wc_pre_orders_order_item_meta', WC_Pre_Orders_Product::get_localized_availability_date($product), $item, $order); if (!empty($pre_order_meta)) { $item['item_meta'][$name][0] = $pre_order_meta; } } } } return $items; }
/** * Process payment for an order: * 1) If the order contains a subscription, process the initial subscription payment (could be $0 if a free trial exists) * 2) If the order contains a pre-order, process the pre-order total (could be $0 if the pre-order is charged upon release) * 3) Otherwise use the parent::process_payment() method for regular product purchases * * @since 2.0 * @param int $order_id * @return array */ public function process_payment($order_id) { $order = $this->get_order($order_id); try { /* processing subscription */ if (wc_amazon_fps()->is_subscriptions_active() && WC_Subscriptions_Order::order_contains_subscription($order)) { // calculate the lifetime amount to be charged for the subscription, with a multiplier applied to account for upgrades / price increases $order->amazon_lifetime_subscription_total = $this->calculate_lifetime_subscription_total($order); // set a subscription-specific description $order->amazon_description = sprintf(__('%s - Subscription Order %s', WC_Amazon_FPS::TEXT_DOMAIN), esc_html(get_bloginfo('name')), $order->get_order_number()); $url = $this->get_api()->get_subscriptions_purchase_url($order); /* processing pre-order */ } elseif (wc_amazon_fps()->is_pre_orders_active() && WC_Pre_Orders_Order::order_contains_pre_order($order_id) && WC_Pre_Orders_Order::order_requires_payment_tokenization($order->id)) { // Amazon requires an expiration date for the token so use the pre-order release date + 6 months to account for pre-order delays $release_date = WC_Pre_Orders_Product::get_localized_availability_date(WC_Pre_Orders_Order::get_pre_order_product($order)); $order->amazon_pre_order_release_date = strtotime("{$release_date} +6 months"); $url = $this->get_api()->get_pre_order_purchase_url($order); /* processing regular product (or a pre-order charged upfront) */ } else { return parent::process_payment($order_id); } // add to log $this->log($url, 'request'); // redirect to Amazon return array('result' => 'success', 'redirect' => $url); } catch (Exception $e) { $this->mark_order_as_failed($order, $e->getMessage()); } }
/** * Since a cart may only contain a single pre-ordered product, this returns the pre-ordered product object or * null if the cart does not contain a pre-order * * @since 1.0 * @return object|null the pre-ordered product object, or null if the cart does not contain a pre-order */ public static function get_pre_order_product() { global $woocommerce; if (self::cart_contains_pre_order()) { foreach ($woocommerce->cart->cart_contents as $cart_item) { if (WC_Pre_Orders_Product::product_can_be_pre_ordered($cart_item['product_id'])) { // return the product object return get_product($cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id']); } } } else { // cart doesn't contain pre-order return null; } }
/** * Get column content, this is called once per column, per row item ($order) * returns the content to be rendered within that cell. * * @see WP_List_Table::single_row_columns() * @since 1.0 * @param WC_Order $order one row (item) in the table * @param string $column_name the column slug * @return string the column content */ public function column_default($order, $column_name) { switch ($column_name) { case 'status': $actions = array(); // base action url $action_url = add_query_arg('order_id[]', $order->id); // determine any available actions if (WC_Pre_Orders_Manager::can_pre_order_be_changed_to('cancelled', $order)) { $actions['cancel'] = sprintf('<a href="%s">%s</a>', add_query_arg('action', 'cancel', $action_url), __('Cancel', 'wc-pre-orders')); } $column_content = sprintf('<mark class="%s tips" data-tip="%s">%s</mark>', WC_Pre_Orders_Order::get_pre_order_status($order), WC_Pre_Orders_Order::get_pre_order_status_to_display($order), WC_Pre_Orders_Order::get_pre_order_status_to_display($order)); $column_content .= $this->row_actions($actions); break; case 'customer': if (0 !== $order->user_id) { $column_content = sprintf('<a href="%s">%s</a>', get_edit_user_link($order->user_id), $order->billing_email); } else { $column_content = $order->billing_email; } break; case 'product': $item = WC_Pre_Orders_Order::get_pre_order_item($order); $product_edit = get_edit_post_link($item['product_id']); $column_content = $product_edit ? sprintf('<a href="%s">%s</a>', $product_edit, $item['name']) : $item['name']; break; case 'order': $column_content = sprintf('<a href="%s">%s</a>', get_edit_post_link($order->id), sprintf(__('Order %s', 'wc-pre-orders'), $order->get_order_number())); break; case 'order_date': $column_content = date_i18n(woocommerce_date_format(), strtotime($order->order_date)); break; case 'availability_date': $product = WC_Pre_Orders_Order::get_pre_order_product($order); $column_content = WC_Pre_Orders_Product::get_localized_availability_date($product, '--'); break; default: $column_content = ''; break; } return $column_content; }
/** * Helper function to return a formatted pre-order order total, e.g. '$99 charged on Dec 1, 2014' * * @since 1.0 * @param string $total formatted order total to modify * @param object|int $product the product that the pre-order contains * @return string the new formatted order total */ public static function get_formatted_pre_order_total($total, $product) { if (!is_object($product)) { $product = get_product($product); } // get order total format if (WC_Pre_Orders_Product::product_is_charged_upon_release($product)) { $formatted_total = get_option('wc_pre_orders_upon_release_order_total_format'); } else { $formatted_total = get_option('wc_pre_orders_upfront_order_total_format'); } // bail if no format is set if (!$formatted_total) { return $total; } // add localized availability date if needed $formatted_total = str_replace('{availability_date}', WC_Pre_Orders_Product::get_localized_availability_date($product), $formatted_total); // add order total $formatted_total = str_replace('{order_total}', $total, $formatted_total); return apply_filters('wc_pre_orders_pre_order_order_total', $formatted_total, $product); }