/** * 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) { global $wc_braintree; $order = $this->get_order($order_id); try { /* processing subscription */ if ($wc_braintree->is_subscriptions_active() && WC_Subscriptions_Order::order_contains_subscription($order_id)) { return $this->process_subscription_payment($order); /* processing pre-order */ } elseif ($wc_braintree->is_pre_orders_active() && WC_Pre_Orders_Order::order_contains_pre_order($order_id)) { return $this->process_pre_order_payment($order); /* processing regular product */ } else { return parent::process_payment($order_id); } } catch (WC_Gateway_Braintree_Exception $e) { // mark order as failed, which adds an order note for the admin and displays a generic "payment error" to the customer $this->mark_order_as_failed($order, $e->getMessage()); // add detailed debugging information $this->add_debug_message($e->getErrors()); } catch (Braintree_Exception_Authorization $e) { $this->mark_order_as_failed($order, __('Authorization failed, ensure that your API key is correct and has permissions to create transactions.', WC_Braintree::TEXT_DOMAIN)); } catch (Exception $e) { $this->mark_order_as_failed($order, sprintf(__('Error Type %s', WC_Braintree::TEXT_DOMAIN), get_class($e))); } }
/** * Check if order contains pre-orders. * * @param int $order_id * @return bool */ protected function order_contains_pre_order($order_id) { return class_exists('WC_Pre_Orders_Order') && WC_Pre_Orders_Order::order_contains_pre_order($order_id); }
/** * Process the payment * * @param int $order_id * @return array */ public function process_payment($order_id) { // Processing subscription if (class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($order_id)) { return $this->process_subscription($order_id); // Processing pre-order } elseif (class_exists('WC_Pre_Orders_Order') && WC_Pre_Orders_Order::order_contains_pre_order($order_id)) { return $this->process_pre_order($order_id); // Processing regular product } else { return parent::process_payment($order_id); } }
/** * Marks the order as being a pre order if it contains pre order products in * case an order gets added manually from the administration panel * * @since 1.0.4 * @param int $order_id id of the newly saved order * @return void */ public function check_manual_order_for_pre_order_products($order_id) { // Make sure we are in the administration panel and we're saving an order if (!is_admin() || !isset($_POST['post_type']) || 'shop_order' != $_POST['post_type']) { return; } $order = new WC_Order($order_id); // Check if the order hasn't been processed already if (WC_Pre_Orders_Order::order_contains_pre_order($order)) { return; } // Order has not been processed yet (or doesn't contain pre orders) $contains_pre_orders = false; foreach ($order->get_items() as $item) { if ('line_item' == $item['type']) { $product = get_product($item['item_meta']['_product_id'][0]); if ('yes' == $product->wc_pre_orders_enabled) { // Set correct flags for this order, making it a pre order update_post_meta($order_id, '_wc_pre_orders_is_pre_order', 1); update_post_meta($order_id, '_wc_pre_orders_when_charged', $product->wc_pre_orders_when_to_charge); return; } } } }
/** * Handle the pre-order initial payment/tokenization, or defer back to the normal payment * processing flow * * @since 4.1.0 * @see SV_WC_Payment_Gateway::process_payment() * @param boolean $result the result of this pre-order payment process * @param int $order_id the order identifier * @return true|array true to process this payment as a regular transaction, otherwise * return an array containing keys 'result' and 'redirect' */ public function process_payment($result, $order_id) { if (WC_Pre_Orders_Order::order_contains_pre_order($order_id) && WC_Pre_Orders_Order::order_requires_payment_tokenization($order_id)) { $order = $this->get_gateway()->get_order($order_id); try { // using an existing tokenized payment method if (isset($order->payment->token) && $order->payment->token) { // save the tokenized card info for completing the pre-order in the future $this->get_gateway()->add_transaction_data($order); } else { // otherwise tokenize the payment method $order = $this->get_gateway()->create_payment_token($order); } // mark order as pre-ordered / reduce order stock WC_Pre_Orders_Order::mark_order_as_pre_ordered($order); // empty cart WC()->cart->empty_cart(); // redirect to thank you page return array('result' => 'success', 'redirect' => $this->get_gateway()->get_return_url($order)); } catch (SV_WC_Payment_Gateway_Exception $e) { $this->get_gateway()->mark_order_as_failed($order, sprintf(__('Pre-Order Tokenization attempt failed (%s)', 'woocommerce-plugin-framework'), $this->get_gateway()->get_method_title(), $e->getMessage())); } } // processing regular product return $result; }
/** * Automatically change the pre-order status when the order status changes * 1) Change the pre-order status to 'active' when the order status changes to 'pre-ordered' * 2) Change the pre-order status to 'active' when the order status changes to 'on-hold' -- this ensures the pre-orders using * a gateway that does not call WC_Order::payment_complete() like BACS or Cheque will still show on the 'Manage Pre-Orders' page * 3) Change the pre-order status to 'cancelled' when the order status changes to 'cancelled' and the order contains a pre-order * * @since 1.0 * @param int $order_id post ID of the order * @param string $old_order_status the prior order status * @param string $new_order_status the new order status */ public function auto_update_pre_order_status($order_id, $old_order_status, $new_order_status) { // change to 'active' when changing order status to 'pre-ordered' if ('pre-ordered' === $new_order_status) { $this->update_pre_order_status($order_id, 'active'); } // change to 'active when changing order status to on-hold if ('on-hold' === $new_order_status && $this->order_contains_pre_order($order_id)) { $this->update_pre_order_status($order_id, 'active'); } // change to 'cancelled' when changing order status to 'cancelled', except when the pre-order status is already cancelled. this prevents sending double emails when bulk-cancelling pre-orders if ('cancelled' === $new_order_status && WC_Pre_Orders_Order::order_contains_pre_order($order_id) && 'cancelled' !== get_post_meta($order_id, '_wc_pre_orders_status', true)) { $this->update_pre_order_status($order_id, 'cancelled'); } }
/** * Process the transaction after receiving the token from Amazon * * @since 2.0 * @param WC_Order $order the WC Order object */ protected function process_transaction(WC_Order $order) { /* processing subscription */ if (wc_amazon_fps()->is_subscriptions_active() && WC_Subscriptions_Order::order_contains_subscription($order)) { // set the initial payment total $order->amazon_order_total = WC_Subscriptions_Order::get_total_initial_payment($order); // if there is a free trial, mark the order as paid, otherwise process it if (0 == $order->amazon_order_total) { $this->mark_order_as_processing($order); } else { parent::process_transaction($order); } /* processing pre-order */ } elseif (wc_amazon_fps()->is_pre_orders_active() && WC_Pre_Orders_Order::order_contains_pre_order($order) && WC_Pre_Orders_Order::order_requires_payment_tokenization($order)) { // mark order as pre-ordered / reduce order stock WC_Pre_Orders_Order::mark_order_as_pre_ordered($order); /* processing regular product (or a pre-order charged upfront) */ } else { parent::process_transaction($order); } }
/** * Maybe cancel pre order when product is trashed * * @param int $product_id Product ID * @return void */ public function maybe_cancel_pre_order_product_trashed($product_id) { global $wpdb; $orders = $wpdb->get_results($wpdb->prepare("\n\t\t\t\tSELECT order_items.order_id\n\t\t\t\tFROM {$wpdb->prefix}woocommerce_order_items AS order_items\n\t\t\t\t\tLEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS order_itemmeta\n\t\t\t\t\tON order_itemmeta.order_item_id = order_items.order_item_id\n\t\t\t\tWHERE order_itemmeta.meta_key = '_product_id'\n\t\t\t\tAND order_itemmeta.meta_value = %d\n\t\t\t", $product_id)); foreach ($orders as $order_data) { $order = new WC_Order($order_data->order_id); if (WC_Pre_Orders_Order::order_contains_pre_order($order) && WC_Pre_Orders_Manager::can_pre_order_be_changed_to('cancelled', $order)) { WC_Pre_Orders_Order::update_pre_order_status($order, 'cancelled'); } } }
/** * updates order status to pre-ordered for orders that are charged upfront. This handles gateways that don't call * payment_complete(). Unfortunately status changes show like pending->processing/completed->pre-ordered * * @since 1.0 * @param int $order_id the post ID of the order * @return string */ public function update_manual_payment_complete_order_status($order_id) { $order = new WC_Order($order_id); // don't update status for non pre-order orders if (!WC_Pre_Orders_Order::order_contains_pre_order($order)) { return; } // don't update if pre-order will be charged upon release if (WC_Pre_Orders_Order::order_will_be_charged_upon_release($order)) { return; } // change order status to pre-ordered $order->update_status('pre-ordered'); }
/** * Prevent order stock reduction when WC_Order::payment_complete() is called for a pre-order charged upon release. * Because order stock for pre-orders charged upon release is reduced during initial checkout, this prevents stock from * being reduced twice. * * @since 1.0 * @param bool $reduce_stock whether to reduce stock for the order or not * @param int $order_id the post ID of the order * @return bool true if the order stock should be reduced, false otherwise */ public function maybe_prevent_payment_complete_order_stock_reduction($reduce_stock, $order_id) { $order = new WC_Order($order_id); if (WC_Pre_Orders_Order::order_contains_pre_order($order) && WC_Pre_Orders_Order::order_will_be_charged_upon_release($order)) { $reduce_stock = false; } return $reduce_stock; }
/** * Handle the pre-order initial payment/tokenization, or defer back to the normal payment * processing flow * * @since 1.0 * @see SV_WC_Payment_Gateway::process_payment() * @param boolean $result the result of this pre-order payment process * @param int $order_id the order identifier * @return true|array true to process this payment as a regular transaction, otherwise * return an array containing keys 'result' and 'redirect' * @throws SV_WC_Payment_Gateway_Feature_Unsupported_Exception if pre-orders are not supported by this gateway or its current configuration */ public function process_pre_order_payment($result, $order_id) { if (!$this->supports_pre_orders()) { throw new SV_WC_Payment_Gateway_Feature_Unsupported_Exception('Pre-Orders not supported by gateway'); } if (WC_Pre_Orders_Order::order_contains_pre_order($order_id) && WC_Pre_Orders_Order::order_requires_payment_tokenization($order_id)) { $order = $this->get_order($order_id); try { // using an existing tokenized payment method if (isset($order->payment->token) && $order->payment->token) { // save the tokenized card info for completing the pre-order in the future $this->add_transaction_data($order); } else { // otherwise tokenize the payment method $order = $this->create_payment_token($order); } // mark order as pre-ordered / reduce order stock WC_Pre_Orders_Order::mark_order_as_pre_ordered($order); // empty cart SV_WC_Plugin_Compatibility::WC()->cart->empty_cart(); // redirect to thank you page return array('result' => 'success', 'redirect' => $this->get_return_url($order)); } catch (Exception $e) { $this->mark_order_as_failed($order, sprintf(_x('Pre-Order Tokenization attempt failed (%s)', 'Supports direct payment method pre-orders', $this->text_domain), $this->get_method_title(), $e->getMessage())); } } // processing regular product return $result; }
/** * Process the payment * * @param int $order_id * @return array */ public function process_payment($order_id, $retry = true) { // Processing subscription if (function_exists('wcs_order_contains_subscription') && (wcs_order_contains_subscription($order_id) || wcs_is_subscription($order_id) || wcs_order_contains_renewal($order_id))) { return $this->process_subscription($order_id, $retry); // Processing pre-order } elseif (class_exists('WC_Pre_Orders_Order') && WC_Pre_Orders_Order::order_contains_pre_order($order_id)) { return $this->process_pre_order($order_id, $retry); // Processing regular product } else { return parent::process_payment($order_id, $retry); } }