WooCommerce Plugin for payment method "Cash on Pickup"
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
11 KiB

4 years ago
  1. <?php
  2. /**
  3. * WooCommerce Cash On Pickup
  4. * Copyright (C) 2013-2014 Pinch Of Code. All rights reserved.
  5. * Copyright (C) 2017-2018 Marian Kadanka. All rights reserved.
  6. * Copyright (C) 2020 Björn Hase, Tentakelfabrik. All rights reserved.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. *
  20. */
  21. if (!defined('ABSPATH')) {
  22. exit; // Exit if accessed directly
  23. }
  24. /**
  25. * Main plugin class
  26. *
  27. * Provides a Cash on Pickup Payment Gateway.
  28. *
  29. * @class WooChop_Gateway_Cash_On_Pickup
  30. * @extends WC_Payment_Gateway
  31. */
  32. class WooChop_Gateway_Cash_On_Pickup extends WC_Payment_Gateway
  33. {
  34. const WOO_CHOP_ID = WOOCHOP_CASH_ON_PICKUP_ID;
  35. /**
  36. *
  37. *
  38. */
  39. public function __construct()
  40. {
  41. load_plugin_textdomain('woochop-cash-on-pickup', false, dirname(dirname(plugin_basename( __FILE__ ))).'/i18n/');
  42. // Setup general properties
  43. $this->setup_properties();
  44. // Load the settings
  45. $this->init_form_fields();
  46. $this->init_settings();
  47. // Get settings
  48. $this->enabled = $this->get_option('enabled');
  49. $this->title = $this->get_option('title');
  50. $this->description = $this->get_option('description');
  51. $this->instructions = $this->get_option('instructions');
  52. $this->enable_for_methods = $this->get_option('enable_for_methods', array() );
  53. $this->default_order_status = $this->get_option('default_order_status', apply_filters('wc_cop_default_order_status', 'on-hold'));
  54. $this->exclusive_for_local = $this->get_option('exclusive_for_local');
  55. $this->enable_for_virtual = $this->get_option('enable_for_virtual', 'yes') === 'yes' ? true : false;
  56. add_action('woocommerce_update_options_payment_gateways_'.$this->id, array($this, 'process_admin_options'));
  57. add_action('woocommerce_thankyou_' . $this->id, array($this, 'thankyou_page'));
  58. // Customer Emails
  59. add_action('woocommerce_email_before_order_table', array($this, 'email_instructions'), 10, 3);
  60. if (!is_admin()) {
  61. // Disable other payment methods if local pickup shippings
  62. if ('yes' === $this->enabled && 'yes' === $this->exclusive_for_local) {
  63. add_filter('woocommerce_available_payment_gateways', array($this, 'maybe_cop_only_if_local_pickup_shipping'));
  64. }
  65. }
  66. }
  67. /**
  68. * Setup general properties for the gateway.
  69. */
  70. protected function setup_properties() {
  71. $this->id = self::WOO_CHOP_ID;
  72. $this->icon = apply_filters('woocommerce_cop_icon', '');
  73. $this->method_title = __('Cash on pickup', 'woochop-cash-on-pickup');
  74. $this->method_description = __('Have your customers pay with cash on pickup.', 'woochop-cash-on-pickup');
  75. $this->has_fields = false;
  76. }
  77. /**
  78. * Get part of a string before :.
  79. *
  80. * Used for example in shipping methods ids where they take the format
  81. * method_id:instance_id
  82. *
  83. * @param string $string
  84. * @return string
  85. */
  86. private function get_string_before_colon( $string )
  87. {
  88. return trim(current(explode(':', (string) $string)));
  89. }
  90. /**
  91. * Check if every of the shipping methods is local pickup
  92. *
  93. * @param array $shipping_methods Shipping methods to check.
  94. * @return bool
  95. */
  96. private function only_local_pickups_selected( $shipping_methods )
  97. {
  98. // Local Pickup Plus fix
  99. unset($shipping_methods['undefined']);
  100. foreach($shipping_methods as $shipping_method) {
  101. if (strpos($shipping_method, 'local_pickup') === false) {
  102. return false;
  103. }
  104. }
  105. return true;
  106. }
  107. /**
  108. * COP will be the only payment method available if each of the shipping methods chosen is local pickup only
  109. *
  110. * @param array $gateways Payment methods to filter.
  111. * @return array of filtered methods
  112. */
  113. public function maybe_cop_only_if_local_pickup_shipping($gateways)
  114. {
  115. if (WC()->session) {
  116. $chosen_shipping_methods_session = WC()->session->get('chosen_shipping_methods');
  117. if ($chosen_shipping_methods_session && $this->only_local_pickups_selected($chosen_shipping_methods_session)) {
  118. if (isset($gateways[self::WOO_CHOP_ID])) {
  119. return array(self::WOO_CHOP_ID => $gateways[self::WOO_CHOP_ID]);
  120. } else {
  121. return array();
  122. }
  123. }
  124. }
  125. return $gateways;
  126. }
  127. /**
  128. * Initialise Gateway Settings Form Fields.
  129. */
  130. public function init_form_fields()
  131. {
  132. $shipping_methods = array();
  133. $order_statuses = array();
  134. if (is_admin()) {
  135. foreach (WC()->shipping->load_shipping_methods() as $method) {
  136. $shipping_methods[$method->id] = $method->get_method_title();
  137. }
  138. $statuses = function_exists('wc_get_order_statuses') ? wc_get_order_statuses() : array();
  139. foreach ($statuses as $status => $status_name) {
  140. $order_statuses[substr( $status, 3)] = $status_name;
  141. }
  142. }
  143. $this->form_fields = array(
  144. 'enabled' => array(
  145. 'title' => __('Enable/Disable', 'woocommerce'),
  146. 'label' => __('Enable cash on pickup', 'woochop-cash-on-pickup'),
  147. 'type' => 'checkbox',
  148. 'description' => '',
  149. 'default' => 'no',
  150. ),
  151. 'title' => array(
  152. 'title' => __('Title', 'woocommerce'),
  153. 'type' => 'text',
  154. 'description' => __('Payment method description that the customer will see on your checkout.', 'woocommerce'),
  155. 'default' => __('Cash on pickup', 'woochop-cash-on-pickup'),
  156. 'desc_tip' => true,
  157. ),
  158. 'description' => array(
  159. 'title' => __('Description', 'woocommerce'),
  160. 'type' => 'textarea',
  161. 'description' => __('Payment method description that the customer will see on your website.', 'woocommerce'),
  162. 'default' => __('Pay with cash on pickup.', 'woochop-cash-on-pickup'),
  163. 'desc_tip' => true,
  164. ),
  165. 'instructions' => array(
  166. 'title' => __('Instructions', 'woocommerce'),
  167. 'type' => 'textarea',
  168. 'description' => __('Instructions that will be added to the thank you page.', 'woocommerce'),
  169. 'default' => __('Pay with cash on pickup.', 'woochop-cash-on-pickup'),
  170. 'desc_tip' => true,
  171. ),
  172. 'enable_for_methods' => array(
  173. 'title' => __('Enable for shipping methods', 'woocommerce'),
  174. 'type' => 'multiselect',
  175. 'class' => 'chosen_select',
  176. 'css' => 'width: 450px;',
  177. 'default' => '',
  178. 'description' => __('If COP is only available for certain methods, set it up here. Leave blank to enable for all methods.', 'woochop-cash-on-pickup'),
  179. 'options' => $shipping_methods,
  180. 'desc_tip' => true,
  181. ),
  182. 'default_order_status' => array(
  183. 'title' => __('Default order status', 'woochop-cash-on-pickup'),
  184. 'type' => 'select',
  185. 'default' => apply_filters('wc_cop_default_order_status', 'on-hold'),
  186. 'options' => $order_statuses,
  187. ),
  188. 'exclusive_for_local' => array(
  189. 'title' => __('Disable other payment methods if local pickup', 'woochop-cash-on-pickup'),
  190. 'label' => __('Cash on pickup will be the only payment method available if local pickup is selected on checkout', 'woochop-cash-on-pickup'),
  191. 'type' => 'checkbox',
  192. 'description' => '',
  193. 'default' => 'no',
  194. ),
  195. 'enable_for_virtual' => array(
  196. 'title' => __('Accept for virtual orders', 'woocommerce'),
  197. 'label' => __('Accept COP if the order is virtual', 'woochop-cash-on-pickup'),
  198. 'type' => 'checkbox',
  199. 'default' => 'yes',
  200. ),
  201. );
  202. }
  203. /**
  204. * Check If The Gateway Is Available For Use.
  205. *
  206. * @return bool
  207. *
  208. */
  209. public function is_available()
  210. {
  211. $order = null;
  212. $needs_shipping = false;
  213. // Test if shipping is needed first
  214. if (WC()->cart && WC()->cart->needs_shipping()) {
  215. $needs_shipping = true;
  216. } elseif (is_page(wc_get_page_id('checkout')) && 0 < get_query_var('order-pay')) {
  217. $order_id = absint(get_query_var('order-pay'));
  218. $order = wc_get_order($order_id);
  219. // Test if order needs shipping.
  220. if (0 < sizeof($order->get_items())) {
  221. foreach ($order->get_items() as $item) {
  222. if (version_compare( WC_VERSION, '3.0', '<')) {
  223. $_product = $order->get_product_from_item($item);
  224. } else {
  225. $_product = $item->get_product();
  226. }
  227. if ($_product && $_product->needs_shipping()) {
  228. $needs_shipping = true;
  229. break;
  230. }
  231. }
  232. }
  233. }
  234. $needs_shipping = apply_filters('woocommerce_cart_needs_shipping', $needs_shipping);
  235. // Virtual order, with virtual disabled
  236. if (!$this->enable_for_virtual && ! $needs_shipping) {
  237. return false;
  238. }
  239. // Only apply if all packages are being shipped via chosen method, or order is virtual.
  240. if (!empty($this->enable_for_methods) && $needs_shipping) {
  241. $chosen_shipping_methods = array();
  242. if (is_object($order)) {
  243. $chosen_shipping_methods = array_unique(array_map(array($this, 'get_string_before_colon'), $order->get_shipping_methods()));
  244. } elseif ( $chosen_shipping_methods_session = WC()->session->get('chosen_shipping_methods')) {
  245. $chosen_shipping_methods = array_unique(array_map(array($this, 'get_string_before_colon'), $chosen_shipping_methods_session));
  246. }
  247. // Local Pickup Plus fix
  248. unset($chosen_shipping_methods['undefined']);
  249. if ( 0 < count(array_diff($chosen_shipping_methods, $this->enable_for_methods)) && !('yes' === $this->exclusive_for_local && $this->only_local_pickups_selected($chosen_shipping_methods))) {
  250. return false;
  251. }
  252. }
  253. return parent::is_available();
  254. }
  255. /**
  256. * Process the payment and return the result.
  257. *
  258. * @param int $order_id
  259. * @return array
  260. */
  261. public function process_payment($order_id)
  262. {
  263. $order = wc_get_order($order_id);
  264. $order->update_status(apply_filters('wc_cop_default_order_status', $this->default_order_status));
  265. // Reduce stock levels
  266. if (version_compare(WC_VERSION, '3.0', '>=')) {
  267. wc_reduce_stock_levels($order_id);
  268. } else {
  269. $order->reduce_order_stock();
  270. }
  271. // Remove cart
  272. WC()->cart->empty_cart();
  273. // Return thankyou redirect
  274. return array(
  275. 'result' => 'success',
  276. 'redirect' => $this->get_return_url($order),
  277. );
  278. }
  279. /**
  280. * Output for the order received page.
  281. */
  282. public function thankyou_page()
  283. {
  284. if ($this->instructions) {
  285. echo wp_kses_post(wpautop(wptexturize($this->instructions)));
  286. }
  287. }
  288. /**
  289. * Add content to the WC emails.
  290. *
  291. * @access public
  292. * @param WC_Order $order
  293. * @param bool $sent_to_admin
  294. * @param bool $plain_text
  295. */
  296. public function email_instructions($order, $sent_to_admin, $plain_text = false)
  297. {
  298. $payment_method = version_compare(WC_VERSION, '3.0', '>=') ? $order->get_payment_method() : $order->payment_method;
  299. if ($this->instructions && !$sent_to_admin && $this->id === $payment_method && $order->has_status($this->default_order_status)) {
  300. echo wp_kses_post(wpautop(wptexturize($this->instructions)).PHP_EOL);
  301. }
  302. }
  303. }