Completed
Push — master ( 15aa29...17da96 )
by Claudio
18:39 queued 11s
created

includes/gateways/cod/class-wc-gateway-cod.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Class WC_Gateway_COD file.
4
 *
5
 * @package WooCommerce\Gateways
6
 */
7
8 1
if ( ! defined( 'ABSPATH' ) ) {
9
	exit; // Exit if accessed directly.
10
}
11
12
/**
13
 * Cash on Delivery Gateway.
14
 *
15
 * Provides a Cash on Delivery Payment Gateway.
16
 *
17
 * @class       WC_Gateway_COD
18
 * @extends     WC_Payment_Gateway
19
 * @version     2.1.0
20
 * @package     WooCommerce/Classes/Payment
21
 */
22
class WC_Gateway_COD extends WC_Payment_Gateway {
23
24
	/**
25
	 * Constructor for the gateway.
26
	 */
27 427
	public function __construct() {
28
		// Setup general properties.
29 427
		$this->setup_properties();
30
31
		// Load the settings.
32 427
		$this->init_form_fields();
33 427
		$this->init_settings();
34
35
		// Get settings.
36 427
		$this->title              = $this->get_option( 'title' );
37 427
		$this->description        = $this->get_option( 'description' );
38 427
		$this->instructions       = $this->get_option( 'instructions' );
39 427
		$this->enable_for_methods = $this->get_option( 'enable_for_methods', array() );
40 427
		$this->enable_for_virtual = $this->get_option( 'enable_for_virtual', 'yes' ) === 'yes';
41
42 427
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
43 427
		add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'thankyou_page' ) );
44 427
		add_filter( 'woocommerce_payment_complete_order_status', array( $this, 'change_payment_complete_order_status' ), 10, 3 );
45
46
		// Customer Emails.
47 427
		add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 );
48
	}
49
50
	/**
51
	 * Setup general properties for the gateway.
52
	 */
53 427
	protected function setup_properties() {
54 427
		$this->id                 = 'cod';
55 427
		$this->icon               = apply_filters( 'woocommerce_cod_icon', '' );
56 427
		$this->method_title       = __( 'Cash on delivery', 'woocommerce' );
57 427
		$this->method_description = __( 'Have your customers pay with cash (or by other means) upon delivery.', 'woocommerce' );
58 427
		$this->has_fields         = false;
59
	}
60
61
	/**
62
	 * Initialise Gateway Settings Form Fields.
63
	 */
64 427
	public function init_form_fields() {
65
66 427
		$options    = array();
67 427
		$data_store = WC_Data_Store::load( 'shipping-zone' );
68 427
		$raw_zones  = $data_store->get_zones();
69
70 427
		foreach ( $raw_zones as $raw_zone ) {
71
			$zones[] = new WC_Shipping_Zone( $raw_zone );
72
		}
73
74 427
		$zones[] = new WC_Shipping_Zone( 0 );
75
76 427
		foreach ( WC()->shipping()->load_shipping_methods() as $method ) {
77
78 427
			$options[ $method->get_method_title() ] = array();
79
80
			// Translators: %1$s shipping method name.
81 427
			$options[ $method->get_method_title() ][ $method->id ] = sprintf( __( 'Any &quot;%1$s&quot; method', 'woocommerce' ), $method->get_method_title() );
82
83 427
			foreach ( $zones as $zone ) {
84
85 427
				$shipping_method_instances = $zone->get_shipping_methods();
86
87 427
				foreach ( $shipping_method_instances as $shipping_method_instance_id => $shipping_method_instance ) {
88
89
					if ( $shipping_method_instance->id !== $method->id ) {
90
						continue;
91
					}
92
93
					$option_id = $shipping_method_instance->get_rate_id();
94
95
					// Translators: %1$s shipping method title, %2$s shipping method id.
96
					$option_instance_title = sprintf( __( '%1$s (#%2$s)', 'woocommerce' ), $shipping_method_instance->get_title(), $shipping_method_instance_id );
97
98
					// Translators: %1$s zone name, %2$s shipping method instance name.
99
					$option_title = sprintf( __( '%1$s &ndash; %2$s', 'woocommerce' ), $zone->get_id() ? $zone->get_zone_name() : __( 'Other locations', 'woocommerce' ), $option_instance_title );
100
101
					$options[ $method->get_method_title() ][ $option_id ] = $option_title;
102
				}
103
			}
104
		}
105
106 427
		$this->form_fields = array(
107
			'enabled'            => array(
108 427
				'title'       => __( 'Enable/Disable', 'woocommerce' ),
109 427
				'label'       => __( 'Enable cash on delivery', 'woocommerce' ),
110 427
				'type'        => 'checkbox',
111 427
				'description' => '',
112 427
				'default'     => 'no',
113
			),
114
			'title'              => array(
115 427
				'title'       => __( 'Title', 'woocommerce' ),
116 427
				'type'        => 'text',
117 427
				'description' => __( 'Payment method description that the customer will see on your checkout.', 'woocommerce' ),
118 427
				'default'     => __( 'Cash on delivery', 'woocommerce' ),
119
				'desc_tip'    => true,
120
			),
121
			'description'        => array(
122 427
				'title'       => __( 'Description', 'woocommerce' ),
123 427
				'type'        => 'textarea',
124 427
				'description' => __( 'Payment method description that the customer will see on your website.', 'woocommerce' ),
125 427
				'default'     => __( 'Pay with cash upon delivery.', 'woocommerce' ),
126
				'desc_tip'    => true,
127
			),
128
			'instructions'       => array(
129 427
				'title'       => __( 'Instructions', 'woocommerce' ),
130 427
				'type'        => 'textarea',
131 427
				'description' => __( 'Instructions that will be added to the thank you page.', 'woocommerce' ),
132 427
				'default'     => __( 'Pay with cash upon delivery.', 'woocommerce' ),
133
				'desc_tip'    => true,
134
			),
135
			'enable_for_methods' => array(
136 427
				'title'             => __( 'Enable for shipping methods', 'woocommerce' ),
137 427
				'type'              => 'multiselect',
138 427
				'class'             => 'wc-enhanced-select',
139 427
				'css'               => 'width: 400px;',
140 427
				'default'           => '',
141 427
				'description'       => __( 'If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.', 'woocommerce' ),
142 427
				'options'           => $options,
143
				'desc_tip'          => true,
144
				'custom_attributes' => array(
145 427
					'data-placeholder' => __( 'Select shipping methods', 'woocommerce' ),
146
				),
147
			),
148
			'enable_for_virtual' => array(
149 427
				'title'   => __( 'Accept for virtual orders', 'woocommerce' ),
150 427
				'label'   => __( 'Accept COD if the order is virtual', 'woocommerce' ),
151 427
				'type'    => 'checkbox',
152 427
				'default' => 'yes',
153
			),
154
		);
155
	}
156
157
	/**
158
	 * Check If The Gateway Is Available For Use.
159
	 *
160
	 * @return bool
161
	 */
162 1
	public function is_available() {
163 1
		$order          = null;
164 1
		$needs_shipping = false;
165
166
		// Test if shipping is needed first.
167 1
		if ( WC()->cart && WC()->cart->needs_shipping() ) {
168
			$needs_shipping = true;
169 1
		} elseif ( is_page( wc_get_page_id( 'checkout' ) ) && 0 < get_query_var( 'order-pay' ) ) {
170
			$order_id = absint( get_query_var( 'order-pay' ) );
171
			$order    = wc_get_order( $order_id );
172
173
			// Test if order needs shipping.
174
			if ( 0 < count( $order->get_items() ) ) {
175
				foreach ( $order->get_items() as $item ) {
176
					$_product = $item->get_product();
177
					if ( $_product && $_product->needs_shipping() ) {
178
						$needs_shipping = true;
179
						break;
180
					}
181
				}
182
			}
183
		}
184
185 1
		$needs_shipping = apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
186
187
		// Virtual order, with virtual disabled.
188 1
		if ( ! $this->enable_for_virtual && ! $needs_shipping ) {
189
			return false;
190
		}
191
192
		// Only apply if all packages are being shipped via chosen method, or order is virtual.
193 1
		if ( ! empty( $this->enable_for_methods ) && $needs_shipping ) {
194
			$order_shipping_items            = is_object( $order ) ? $order->get_shipping_methods() : false;
195
			$chosen_shipping_methods_session = WC()->session->get( 'chosen_shipping_methods' );
196
197
			if ( $order_shipping_items ) {
198
				$canonical_rate_ids = $this->get_canonical_order_shipping_item_rate_ids( $order_shipping_items );
199
			} else {
200
				$canonical_rate_ids = $this->get_canonical_package_rate_ids( $chosen_shipping_methods_session );
201
			}
202
203
			if ( ! count( $this->get_matching_rates( $canonical_rate_ids ) ) ) {
204
				return false;
205
			}
206
		}
207
208 1
		return parent::is_available();
209
	}
210
211
	/**
212
	 * Converts the chosen rate IDs generated by Shipping Methods to a canonical 'method_id:instance_id' format.
213
	 *
214
	 * @since  3.4.0
215
	 *
216
	 * @param  array $order_shipping_items  Array of WC_Order_Item_Shipping objects.
217
	 * @return array $canonical_rate_ids    Rate IDs in a canonical format.
218
	 */
219
	private function get_canonical_order_shipping_item_rate_ids( $order_shipping_items ) {
220
221
		$canonical_rate_ids = array();
222
223
		foreach ( $order_shipping_items as $order_shipping_item ) {
224
			$canonical_rate_ids[] = $order_shipping_item->get_method_id() . ':' . $order_shipping_item->get_instance_id();
225
		}
226
227
		return $canonical_rate_ids;
228
	}
229
230
	/**
231
	 * Converts the chosen rate IDs generated by Shipping Methods to a canonical 'method_id:instance_id' format.
232
	 *
233
	 * @since  3.4.0
234
	 *
235
	 * @param  array $chosen_package_rate_ids Rate IDs as generated by shipping methods. Can be anything if a shipping method doesn't honor WC conventions.
236
	 * @return array $canonical_rate_ids  Rate IDs in a canonical format.
237
	 */
238
	private function get_canonical_package_rate_ids( $chosen_package_rate_ids ) {
239
240
		$shipping_packages  = WC()->shipping()->get_packages();
241
		$canonical_rate_ids = array();
242
243
		if ( ! empty( $chosen_package_rate_ids ) && is_array( $chosen_package_rate_ids ) ) {
244
			foreach ( $chosen_package_rate_ids as $package_key => $chosen_package_rate_id ) {
245
				if ( ! empty( $shipping_packages[ $package_key ]['rates'][ $chosen_package_rate_id ] ) ) {
246
					$chosen_rate          = $shipping_packages[ $package_key ]['rates'][ $chosen_package_rate_id ];
247
					$canonical_rate_ids[] = $chosen_rate->get_method_id() . ':' . $chosen_rate->get_instance_id();
248
				}
249
			}
250
		}
251
252
		return $canonical_rate_ids;
253
	}
254
255
	/**
256
	 * Indicates whether a rate exists in an array of canonically-formatted rate IDs that activates this gateway.
257
	 *
258
	 * @since  3.4.0
259
	 *
260
	 * @param array $rate_ids Rate ids to check.
261
	 * @return boolean
262
	 */
263
	private function get_matching_rates( $rate_ids ) {
264
		// First, match entries in 'method_id:instance_id' format. Then, match entries in 'method_id' format by stripping off the instance ID from the candidates.
265
		return array_unique( array_merge( array_intersect( $this->enable_for_methods, $rate_ids ), array_intersect( $this->enable_for_methods, array_unique( array_map( 'wc_get_string_before_colon', $rate_ids ) ) ) ) );
266
	}
267
268
	/**
269
	 * Process the payment and return the result.
270
	 *
271
	 * @param int $order_id Order ID.
272
	 * @return array
273
	 */
274 View Code Duplication
	public function process_payment( $order_id ) {
275
		$order = wc_get_order( $order_id );
276
277
		if ( $order->get_total() > 0 ) {
278
			// Mark as processing or on-hold (payment won't be taken until delivery).
279
			$order->update_status( apply_filters( 'woocommerce_cod_process_payment_order_status', $order->has_downloadable_item() ? 'on-hold' : 'processing', $order ), __( 'Payment to be made upon delivery.', 'woocommerce' ) );
280
		} else {
281
			$order->payment_complete();
282
		}
283
284
		// Remove cart.
285
		WC()->cart->empty_cart();
286
287
		// Return thankyou redirect.
288
		return array(
289
			'result'   => 'success',
290
			'redirect' => $this->get_return_url( $order ),
291
		);
292
	}
293
294
	/**
295
	 * Output for the order received page.
296
	 */
297
	public function thankyou_page() {
298
		if ( $this->instructions ) {
299
			echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) );
300
		}
301
	}
302
303
	/**
304
	 * Change payment complete order status to completed for COD orders.
305
	 *
306
	 * @since  3.1.0
307
	 * @param  string         $status Current order status.
308
	 * @param  int            $order_id Order ID.
309
	 * @param  WC_Order|false $order Order object.
310
	 * @return string
311
	 */
312 4
	public function change_payment_complete_order_status( $status, $order_id = 0, $order = false ) {
313 4
		if ( $order && 'cod' === $order->get_payment_method() ) {
314
			$status = 'completed';
315
		}
316 4
		return $status;
317
	}
318
319
	/**
320
	 * Add content to the WC emails.
321
	 *
322
	 * @access public
323
	 * @param WC_Order $order Order object.
324
	 * @param bool     $sent_to_admin  Sent to admin.
325
	 * @param bool     $plain_text Email format: plain text or HTML.
326
	 */
327
	public function email_instructions( $order, $sent_to_admin, $plain_text = false ) {
0 ignored issues
show
The parameter $plain_text is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
328 View Code Duplication
		if ( $this->instructions && ! $sent_to_admin && $this->id === $order->get_payment_method() ) {
329
			echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) . PHP_EOL );
330
		}
331
	}
332
}
333