Passed
Push — master ( 7a7a10...7ec057 )
by Reüel
08:25
created

Gateway::start()   F

Complexity

Conditions 18
Paths 1339

Size

Total Lines 148
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
cc 18
eloc 67
nc 1339
nop 1
dl 0
loc 148
ccs 0
cts 85
cp 0
crap 342
rs 0.7
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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