Passed
Push — develop ( 39fd78...365009 )
by Remco
05:12
created

Gateway   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 150
dl 0
loc 369
ccs 0
cts 199
cp 0
rs 8.96
c 0
b 0
f 0
wmc 43

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 2
A get_supported_payment_methods() 0 8 1
A get_client_error() 0 10 2
B handle_merchant_order_status_changed() 0 40 7
A maybe_update_access_token() 0 29 5
B update_status() 0 42 6
C start() 0 138 17
A handle_notification() 0 8 3

How to fix   Complexity   

Complex Class

Complex classes like Gateway often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Gateway, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Gateway
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2018 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay\Gateways\OmniKassa2
9
 */
10
11
namespace Pronamic\WordPress\Pay\Gateways\OmniKassa2;
12
13
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
14
use Pronamic\WordPress\Pay\Core\PaymentMethods;
15
use Pronamic\WordPress\Pay\Payments\Payment;
16
17
/**
18
 * Gateway
19
 *
20
 * @author  Remco Tolsma
21
 * @version 2.1.1
22
 * @since   1.0.0
23
 */
24
class Gateway extends Core_Gateway {
25
	/**
26
	 * Client.
27
	 *
28
	 * @var Client
29
	 */
30
	private $client;
31
32
	/**
33
	 * Constructs and initializes an OmniKassa 2.0 gateway.
34
	 *
35
	 * @param Config $config Config.
36
	 */
37
	public function __construct( Config $config ) {
38
		parent::__construct( $config );
39
40
		$this->set_method( self::METHOD_HTTP_REDIRECT );
41
42
		// Client.
43
		$this->client = new Client();
44
45
		$url = Client::URL_PRODUCTION;
46
47
		if ( self::MODE_TEST === $config->mode ) {
48
			$url = Client::URL_SANDBOX;
49
		}
50
51
		$this->client->set_url( $url );
52
		$this->client->set_refresh_token( $config->refresh_token );
53
		$this->client->set_signing_key( $config->signing_key );
54
	}
55
56
	/**
57
	 * Get supported payment methods.
58
	 *
59
	 * @see \Pronamic_WP_Pay_Gateway::get_supported_payment_methods()
60
	 * @return array
61
	 */
62
	public function get_supported_payment_methods() {
63
		return array(
64
			PaymentMethods::AFTERPAY,
65
			PaymentMethods::BANCONTACT,
66
			PaymentMethods::CREDIT_CARD,
67
			PaymentMethods::IDEAL,
68
			PaymentMethods::MAESTRO,
69
			PaymentMethods::PAYPAL,
70
		);
71
	}
72
73
	/**
74
	 * Start.
75
	 *
76
	 * @see Core_Gateway::start()
77
	 *
78
	 * @param Payment $payment Payment.
79
	 */
80
	public function start( Payment $payment ) {
81
		// Merchant order ID.
82
		$merchant_order_id = $payment->format_string( $this->config->order_id );
83
84
		$payment->set_meta( 'omnikassa_2_merchant_order_id', $merchant_order_id );
85
86
		// New order.
87
		$merchant_return_url = $payment->get_return_url();
88
		$merchant_return_url = apply_filters( 'pronamic_pay_omnikassa_2_merchant_return_url', $merchant_return_url );
89
90
		$order = new Order(
91
			$merchant_order_id,
92
			MoneyTransformer::transform( $payment->get_total_amount() ),
93
			$merchant_return_url
94
		);
95
96
		// Shipping address.
97
		$order->set_shipping_detail( AddressTransformer::transform( $payment->get_shipping_address() ) );
98
99
		// Billing address.
100
		$order->set_billing_detail( AddressTransformer::transform( $payment->get_billing_address() ) );
101
102
		// Customer information.
103
		$customer = $payment->get_customer();
104
105
		if ( null !== $customer ) {
106
			// Language.
107
			$language = $customer->get_language();
108
109
			if ( null !== $language ) {
110
				$order->set_language( strtoupper( $language ) );
111
			}
112
113
			// Customer information.
114
			$customer_information = new CustomerInformation();
115
116
			$customer_information->set_email_address( $customer->get_email() );
117
			$customer_information->set_date_of_birth( $customer->get_birth_date() );
118
			$customer_information->set_gender( Gender::transform( $customer->get_gender() ) );
119
			$customer_information->set_telephone_number( $customer->get_phone() );
120
121
			if ( null !== $customer->get_name() ) {
122
				$customer_information->set_initials( $customer->get_name()->get_initials() );
123
			}
124
125
			$order->set_customer_information( $customer_information );
126
		}
127
128
		// Payment brand.
129
		$payment_brand = PaymentBrands::transform( $payment->get_method() );
130
131
		$order->set_payment_brand( $payment_brand );
132
133
		if ( null !== $payment_brand ) {
134
			// Payment brand force should only be set if payment brand is not empty.
135
			$order->set_payment_brand_force( PaymentBrandForce::FORCE_ONCE );
136
		}
137
138
		// Description.
139
		$order->set_description( substr( $payment->get_description(), 0, 35 ) );
140
141
		// Lines.
142
		if ( null !== $payment->get_lines() ) {
143
			$order_items = $order->new_items();
144
145
			$i = 1;
146
147
			foreach ( $payment->get_lines() as $line ) {
148
				/* translators: %s: item index */
149
				$name = sprintf( __( 'Item %s', 'pronamic_ideal' ), $i++ );
150
151
				if ( null !== $line->get_name() && '' !== $line->get_name() ) {
152
					$name = $line->get_name();
153
				}
154
155
				$item = $order_items->new_item(
156
					substr( $name, 0, 50 ),
157
					$line->get_quantity(),
158
					// The amount in cents, including VAT, of the item each, see below for more details.
159
					MoneyTransformer::transform( $line->get_unit_price() ),
160
					ProductCategories::transform( $line->get_type() )
161
				);
162
163
				$item->set_id( $line->get_id() );
164
165
				// Description.
166
				$description = $line->get_description();
167
168
				if ( empty( $description ) && PaymentBrands::AFTERPAY === $payment_brand ) {
169
					/*
170
					 * The `OrderItem.description` field is documentated as `0..1` (optional),
171
					 * but for AfterPay payments it is required.
172
					 *
173
					 * @link https://github.com/wp-pay-gateways/omnikassa-2/tree/feature/post-pay/documentation#error-5024
174
					 */
175
					$description = $name;
176
				}
177
178
				if ( null !== $description ) {
179
					$description = substr( $description, 0, 100 );
180
				}
181
182
				$item->set_description( $description );
183
184
				$tax_amount = $line->get_unit_price()->get_tax_amount();
185
186
				if ( null !== $tax_amount ) {
187
					// The VAT of the item each, see below for more details.
188
					$item->set_tax( MoneyTransformer::transform( $tax_amount ) );
189
				}
190
			}
191
		}
192
193
		// Maybe update access token.
194
		$this->maybe_update_access_token();
195
196
		// Handle errors.
197
		if ( $this->get_client_error() ) {
198
			return;
199
		}
200
201
		// Announce order.
202
		$response = $this->client->order_announce( $this->config, $order );
203
204
		// Handle errors.
205
		if ( $this->get_client_error() ) {
206
			return;
207
		}
208
209
		if ( false === $response ) {
210
			return;
211
		}
212
213
		if ( ! $response->is_valid( $this->config->signing_key ) ) {
214
			return;
215
		}
216
217
		$payment->set_action_url( $response->get_redirect_url() );
218
	}
219
220
	/**
221
	 * Update status of the specified payment.
222
	 *
223
	 * @param Payment $payment Payment.
224
	 */
225
	public function update_status( Payment $payment ) {
226
		if ( ! ReturnParameters::contains( $_GET ) ) { // WPCS: CSRF ok.
227
			return;
228
		}
229
230
		$parameters = ReturnParameters::from_array( $_GET ); // WPCS: CSRF ok.
231
232
		// Note.
233
		$note_values = array(
234
			'order_id'  => $parameters->get_order_id(),
235
			'status'    => $parameters->get_status(),
236
			'signature' => $parameters->get_signature(),
237
			'valid'     => $parameters->is_valid( $this->config->signing_key ) ? 'true' : 'false',
238
		);
239
240
		$note = '';
241
242
		$note .= '<p>';
243
		$note .= __( 'OmniKassa 2.0 return URL requested:', 'pronamic_ideal' );
244
		$note .= '</p>';
245
246
		$note .= '<dl>';
247
248
		foreach ( $note_values as $key => $value ) {
249
			$note .= sprintf( '<dt>%s</dt>', esc_html( $key ) );
250
			$note .= sprintf( '<dd>%s</dd>', esc_html( $value ) );
251
		}
252
253
		$note .= '</dl>';
254
255
		$payment->add_note( $note );
256
257
		// Validate.
258
		if ( ! $parameters->is_valid( $this->config->signing_key ) ) {
259
			return;
260
		}
261
262
		// Status.
263
		$pronamic_status = Statuses::transform( $parameters->get_status() );
264
265
		if ( null !== $pronamic_status ) {
266
			$payment->set_status( $pronamic_status );
267
		}
268
	}
269
270
	/**
271
	 * Handle notification.
272
	 *
273
	 * @param Notification $notification Notification.
274
	 *
275
	 * @return void
276
	 */
277
	public function handle_notification( Notification $notification ) {
278
		if ( ! $notification->is_valid( $this->config->signing_key ) ) {
279
			return;
280
		}
281
282
		switch ( $notification->get_event_name() ) {
283
			case 'merchant.order.status.changed':
284
				$this->handle_merchant_order_status_changed( $notification );
285
		}
286
	}
287
288
	/**
289
	 * Handle `merchant.order.status.changed` event.
290
	 *
291
	 * @param Notification $notification Notification.
292
	 *
293
	 * @return void
294
	 */
295
	private function handle_merchant_order_status_changed( Notification $notification ) {
296
		do {
297
			$order_results = $this->client->get_order_results( $notification->get_authentication() );
298
299
			if ( false === $order_results ) {
300
				return;
301
			}
302
303
			if ( ! $order_results->is_valid( $this->config->signing_key ) ) {
304
				return;
305
			}
306
307
			foreach ( $order_results as $order_result ) {
308
				$payment = get_pronamic_payment_by_meta( '_pronamic_payment_omnikassa_2_merchant_order_id', $order_result->get_merchant_order_id() );
309
310
				if ( empty( $payment ) ) {
311
					continue;
312
				}
313
314
				$payment->set_transaction_id( $order_result->get_omnikassa_order_id() );
315
316
				$pronamic_status = Statuses::transform( $order_result->get_order_status() );
317
318
				if ( null !== $pronamic_status ) {
319
					$payment->set_status( $pronamic_status );
320
				}
321
322
				// Note.
323
				$note = '';
324
325
				$note .= '<p>';
326
				$note .= __( 'OmniKassa 2.0 webhook URL requested:', 'pronamic_ideal' );
327
				$note .= '</p>';
328
				$note .= '<pre>';
329
				$note .= wp_json_encode( $order_result->get_json(), JSON_PRETTY_PRINT );
330
				$note .= '</pre>';
331
332
				$payment->add_note( $note );
333
334
				$payment->save();
335
			}
336
		} while ( $order_results->more_available() );
337
	}
338
339
	/**
340
	 * Maybe update access token.
341
	 *
342
	 * @return void
343
	 */
344
	private function maybe_update_access_token() {
345
		if ( $this->config->is_access_token_valid() ) {
346
			return;
347
		}
348
349
		$data = $this->client->get_access_token_data();
350
351
		if ( ! is_object( $data ) ) {
352
			return;
353
		}
354
355
		if ( isset( $data->token ) ) {
356
			$this->config->access_token = $data->token;
357
358
			update_post_meta( $this->config->post_id, '_pronamic_gateway_omnikassa_2_access_token', $data->token );
359
		}
360
361
		/*
362
		 * @codingStandardsIgnoreStart
363
		 *
364
		 * Ignore coding standards because of sniff WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
365
		 */
366
		if ( isset( $data->validUntil ) ) {
367
			$this->config->access_token_valid_until = $data->validUntil;
368
369
			update_post_meta(
370
				$this->config->post_id,
371
				'_pronamic_gateway_omnikassa_2_access_token_valid_until',
372
				$data->validUntil
373
			);
374
		}
375
		// @codingStandardsIgnoreEnd
376
	}
377
378
	/**
379
	 * Get client error.
380
	 *
381
	 * @return \WP_Error|bool
382
	 */
383
	private function get_client_error() {
384
		$error = $this->client->get_error();
385
386
		if ( is_wp_error( $error ) ) {
387
			$this->error = $error;
388
389
			return $error;
390
		}
391
392
		return false;
393
	}
394
}
395