Failed Conditions
Push — develop ( 74d5f5...f78307 )
by Remco
14:50 queued 08:54
created

src/Gateway.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * Gateway
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2019 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 Pronamic\WordPress\Pay\WebhookManager;
0 ignored issues
show
The type Pronamic\WordPress\Pay\WebhookManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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