Failed Conditions
Push — develop ( 6f22b8...619326 )
by Reüel
03:40
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\Adyen
9
 */
10
11
namespace Pronamic\WordPress\Pay\Gateways\Adyen;
12
13
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
14
use Pronamic\WordPress\Pay\Core\Statuses as Core_Statuses;
15
use Pronamic\WordPress\Pay\Core\PaymentMethods;
16
use Pronamic\WordPress\Pay\Core\Util;
17
use Pronamic\WordPress\Pay\Payments\Payment;
18
use Pronamic\WordPress\Pay\Plugin;
19
20
/**
21
 * Gateway
22
 *
23
 * @author  Remco Tolsma
24
 * @version 1.0.0
25
 * @since   1.0.0
26
 * @link    https://github.com/adyenpayments/php/blob/master/generatepaymentform.php
27
 */
28
class Gateway extends Core_Gateway {
29
	/**
30
	 * Slug of this gateway.
31
	 *
32
	 * @var string
33
	 */
34
	const SLUG = 'adyen';
35
36
	/**
37
	 * Web SDK version.
38
	 *
39
	 * @link https://docs.adyen.com/developers/checkout/web-sdk/release-notes-web-sdk
40
	 *
41
	 * @var string
42
	 */
43
	const SDK_VERSION = '1.9.2';
44
45
	/**
46
	 * Client.
47
	 *
48
	 * @var Client
49
	 */
50
	protected $client;
51
52
	/**
53
	 * Constructs and initializes an Adyen gateway.
54
	 *
55
	 * @param Config $config Config.
56
	 */
57
	public function __construct( Config $config ) {
58
		parent::__construct( $config );
59
60
		$this->set_method( self::METHOD_HTTP_REDIRECT );
61
		$this->set_slug( self::SLUG );
62
63
		$this->client = new Client( $config->api_key, $config->api_live_url_prefix );
64
		$this->client->set_merchant_account( $config->merchant_account );
65
		$this->client->set_mode( $config->mode );
66
67
		$this->supports = array(
68
			'payment_redirect',
69
		);
70
	}
71
72
	/**
73
	 * Get supported payment methods
74
	 *
75
	 * @see Core_Gateway::get_supported_payment_methods()
76
	 */
77
	public function get_supported_payment_methods() {
78
		return array(
79
			PaymentMethods::BANCONTACT,
80
			PaymentMethods::CREDIT_CARD,
81
			PaymentMethods::DIRECT_DEBIT,
82
			PaymentMethods::GIROPAY,
83
			PaymentMethods::IDEAL,
84
			PaymentMethods::MAESTRO,
85
			PaymentMethods::SOFORT,
86
		);
87
	}
88
89
	/**
90
	 * Start.
91
	 *
92
	 * @param Payment $payment Payment.
93
	 *
94
	 * @see Plugin::start()
95
	 */
96
	public function start( Payment $payment ) {
97
		// Amount.
98
		$amount = new Amount(
99
			$payment->get_total_amount()->get_currency()->get_alphabetic_code(),
100
			$payment->get_total_amount()->get_minor_units()
0 ignored issues
show
The method get_minor_units() does not exist on Pronamic\WordPress\Money\TaxedMoney. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
			$payment->get_total_amount()->/** @scrutinizer ignore-call */ get_minor_units()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
101
		);
102
103
		// Payment method. Take leap of faith for unknown payment methods.
104
		$type = PaymentMethodType::transform(
105
			$payment->get_method(),
106
			$payment->get_method()
107
		);
108
109
		$payment_method = new PaymentMethod( $type );
110
111
		switch ( $payment->get_method() ) {
112
			case PaymentMethods::IDEAL:
113
				$payment_method->issuer = $payment->get_issuer();
114
115
				break;
116
		}
117
118
		// Country.
119
		$locale = get_locale();
120
121
		if ( null !== $payment->get_customer() ) {
122
			$locale = $payment->get_customer()->get_locale();
123
		}
124
125
		$locale = explode( '_', $locale );
126
127
		$country_code = strtoupper( substr( $locale[1], 0, 2 ) );
128
129
		// Create payment or payment session request.
130
		switch ( $payment->get_method() ) {
131
			case PaymentMethods::IDEAL:
132
			case PaymentMethods::SOFORT:
133
				// API integration.
134
				$request = new PaymentRequest(
135
					$amount,
136
					$this->config->merchant_account,
137
					$payment->get_id(),
138
					$payment->get_return_url(),
139
					$payment_method
140
				);
141
142
				$request->set_country_code( $country_code );
143
144
				break;
145
			default:
146
				// Web SDK integration.
147
				$request = new PaymentSessionRequest(
148
					$amount,
149
					$this->config->merchant_account,
150
					$payment->get_id(),
151
					$payment->get_return_url(),
152
					$country_code
153
				);
154
155
				$request->set_origin( home_url() );
156
				$request->set_sdk_version( self::SDK_VERSION );
157
158
				// Set allowed payment methods.
159
				$allowed_methods = array( $type );
160
161
				// Add all available payment methods if no payment method is given.
162
				if ( empty( $type ) ) {
163
					$allowed_methods = array();
164
165
					foreach ( $this->get_available_payment_methods() as $method ) {
166
						$allowed_methods[] = PaymentMethodType::transform( $method );
167
					}
168
				}
169
170
				$request->set_allowed_payment_methods( $allowed_methods );
171
		}
172
173
		// Channel.
174
		$request->set_channel( 'Web' );
175
176
		// Shopper.
177
		$request->set_shopper_statement( $payment->get_description() );
178
179
		if ( null !== $payment->get_customer() ) {
180
			$customer = $payment->get_customer();
181
182
			$request->set_shopper_ip( $customer->get_ip_address() );
183
			$request->set_shopper_statement( $customer->get_gender() );
184
			$request->set_shopper_locale( $customer->get_locale() );
185
			$request->set_shopper_reference( $customer->get_user_id() );
186
			$request->set_telephone_number( $customer->get_phone() );
187
188
			// Shopper name.
189
			if ( null !== $customer->get_name() ) {
190
				$shopper_name = new Name(
191
					$customer->get_name()->get_first_name(),
192
					$customer->get_name()->get_last_name(),
193
					GenderTransformer::transform( $customer->get_gender() )
194
				);
195
196
				$request->set_shopper_name( $shopper_name );
197
			}
198
199
			// Date of birth.
200
			if ( null !== $customer->get_birth_date() ) {
201
				$request->set_date_of_birth( $customer->get_birth_date()->format( 'YYYY-MM-DD' ) );
202
			}
203
		}
204
205
		// Billing address.
206
		if ( null !== $payment->get_billing_address() ) {
207
			$address = $payment->get_billing_address();
208
209
			$billing_address = new Address( $address->get_country_code() );
210
211
			$billing_address->set_city( $address->get_city() );
212
			$billing_address->set_house_number_or_name( sprintf( '%s %s', $address->get_house_number(), $address->get_house_number_addition() ) );
213
			$billing_address->set_postal_code( $address->get_postal_code() );
214
			$billing_address->set_state_or_province( $address->get_region() );
215
			$billing_address->set_street( $address->get_street_name() );
216
		}
217
218
		// Lines.
219
		$lines = $payment->get_lines();
220
221
		if ( null !== $lines ) {
222
			$line_items = $request->new_items();
223
224
			$i = 1;
225
226
			foreach ( $lines as $line ) {
227
				// Description.
228
				$description = $line->get_description();
229
230
				// Use line item name as fallback for description.
231
				if ( null === $description ) {
232
					/* translators: %s: item index */
233
					$description = sprintf( __( 'Item %s', 'pronamic_ideal' ), $i ++ );
234
235
					if ( null !== $line->get_name() && '' !== $line->get_name() ) {
236
						$description = $line->get_name();
237
					}
238
				}
239
240
				$item = $line_items->new_item(
241
					$description,
242
					$line->get_quantity(),
243
					$line->get_total_amount()->get_including_tax()->get_minor_units()
244
				);
245
246
				$item->set_id( $line->get_id() );
247
248
				// Tax amount.
249
				$tax_amount = $line->get_unit_price()->get_tax_amount();
250
251
				if ( null !== $tax_amount ) {
252
					$item->set_tax_amount( $line->get_total_amount()->get_tax_amount()->get_minor_units() );
253
					$item->set_tax_percentage( (int) $line->get_total_amount()->get_tax_percentage() * 100 );
254
				}
255
			}
256
		}
257
258
		// Create payment or payment session.
259
		if ( $request instanceof PaymentRequest ) {
260
			$result = $this->client->create_payment( $request );
261
		} else {
262
			$result = $this->client->create_payment_session( $request );
263
		}
264
265
		// Handle errors.
266
		if ( ! $result ) {
267
			$this->error = $this->client->get_error();
268
269
			return;
270
		}
271
272
		// Set checkout meta for Web SDK redirect.
273
		if ( isset( $result->paymentSession ) ) {
274
			$payment->set_meta(
275
				'adyen_checkout',
276
				array(
277
					'sdk_version' => self::SDK_VERSION,
278
					'payload'     => $result->paymentSession,
279
				)
280
			);
281
		}
282
283
		// Set transaction ID.
284
		if ( isset( $result->pspReference ) ) {
285
			$payment->set_transaction_id( $result->pspReference );
286
		}
287
288
		// Set action URL.
289
		$action_url = add_query_arg(
290
			array(
291
				'payment_redirect' => $payment->get_id(),
292
				'key'              => $payment->key,
293
			),
294
			home_url( '/' )
295
		);
296
297
		if ( isset( $result->redirect->url ) ) {
298
			$action_url = $result->redirect->url;
299
		}
300
301
		$payment->set_action_url( $action_url );
302
	}
303
304
	/**
305
	 * Payment redirect.
306
	 *
307
	 * @param Payment $payment Payment.
308
	 *
309
	 * @return void
310
	 */
311
	public function payment_redirect( Payment $payment ) {
312
		$checkout = $payment->get_meta( 'adyen_checkout' );
313
314
		if ( empty( $checkout ) ) {
315
			return;
316
		}
317
318
		$url = sprintf(
319
			'https://checkoutshopper-%s.adyen.com/checkoutshopper/assets/js/sdk/checkoutSDK.%s.min.js',
320
			( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' ),
321
			$checkout['sdk_version']
322
		);
323
324
		wp_register_script(
325
			'pronamic-pay-adyen-checkout',
326
			$url,
327
			array(),
328
			$checkout['sdk_version'],
329
			false
330
		);
331
332
		// No cache.
333
		Util::no_cache();
334
335
		$context = ( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' );
336
337
		require __DIR__ . '/../views/checkout.php';
338
339
		$payment->set_meta( 'adyen_checkout', null );
340
341
		exit;
342
	}
343
344
	/**
345
	 * Update status of the specified payment.
346
	 *
347
	 * @param Payment $payment Payment.
348
	 *
349
	 * @return void
350
	 */
351
	public function update_status( Payment $payment ) {
352
		// Maybe process stored webhook notification.
353
		$this->maybe_handle_notification( $payment );
354
355
		// Process payload on return.
356
		if ( ! filter_has_var( INPUT_GET, 'payload' ) ) {
357
			return;
358
		}
359
360
		$status = null;
361
362
		$payload = filter_input( INPUT_GET, 'payload', FILTER_SANITIZE_STRING );
363
364
		switch ( $payment->get_method() ) {
365
			case PaymentMethods::IDEAL:
366
			case PaymentMethods::SOFORT:
367
				$result = $this->client->get_payment_details( $payload );
368
369
				break;
370
			default:
371
				$result = $this->client->get_payment_result( $payload );
372
		}
373
374
		if ( $result ) {
375
			$status = ResultCode::transform( $result->resultCode );
376
377
			$psp_reference = $result->pspReference;
378
		}
379
380
		// Handle errors.
381
		if ( empty( $status ) ) {
382
			$payment->set_status( Core_Statuses::FAILURE );
383
384
			$this->error = $this->client->get_error();
385
386
			return;
387
		}
388
389
		// Update status.
390
		$payment->set_status( $status );
391
392
		// Update transaction ID.
393
		if ( isset( $psp_reference ) ) {
394
			$payment->set_transaction_id( $psp_reference );
395
		}
396
	}
397
398
	/**
399
	 * Maybe handle notification.
400
	 *
401
	 * @param Payment $payment      Payment.
402
	 */
403
	public function maybe_handle_notification( Payment $payment ) {
404
		$notification = $payment->get_meta( 'adyen_notification' );
405
406
		if ( empty( $notification ) ) {
407
			return;
408
		}
409
410
		$notification = json_decode( $notification );
411
412
		if ( ! is_object( $notification ) ) {
413
			return;
414
		}
415
416
		switch ( $notification->eventCode ) {
417
			case EventCode::AUTHORIZATION:
418
				$this->handle_authorization_event( $payment, $notification );
419
420
				break;
421
		}
422
423
		$payment->set_meta( 'adyen_notification', null );
424
	}
425
426
	/**
427
	 * Handle authorization event.
428
	 *
429
	 * @param Payment $payment      Payment.
430
	 * @param object  $notification Notification.
431
	 */
432
	public function handle_authorization_event( Payment $payment, $notification ) {
433
		if ( ! is_object( $notification ) ) {
434
			return;
435
		}
436
437
		$success = $notification->success;
438
439
		if ( 'true' === $success ) {
440
			$status = Core_Statuses::SUCCESS;
441
		} else {
442
			$status = Core_Statuses::FAILURE;
443
444
			// Add note.
445
			$note = sprintf(
446
				/* translators: %s: failure reason message */
447
				__( 'Failure reason: %s.', 'pronamic_ideal' ),
448
				esc_html( $notification->reason )
449
			);
450
451
			$payment->add_note( $note );
452
		}
453
454
		$payment->set_status( $status );
455
	}
456
457
	/**
458
	 * Get available payment methods.
459
	 *
460
	 * @see Core_Gateway::get_available_payment_methods()
461
	 */
462
	public function get_available_payment_methods() {
463
		$payment_methods = array();
464
465
		// Get active payment methods for Adyen account.
466
		$methods = $this->client->get_payment_methods();
467
468
		if ( ! $methods ) {
469
			$this->error = $this->client->get_error();
470
471
			return $payment_methods;
472
		}
473
474
		// Transform to WordPress payment methods.
475
		foreach ( $methods as $method => $details ) {
476
			$payment_method = PaymentMethodType::transform_gateway_method( $method );
477
478
			if ( $payment_method ) {
479
				$payment_methods[] = $payment_method;
480
			}
481
		}
482
483
		$payment_methods = array_unique( $payment_methods );
484
485
		return $payment_methods;
486
	}
487
488
	/**
489
	 * Get issuers.
490
	 *
491
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
492
	 */
493
	public function get_issuers() {
494
		$groups = array();
495
496
		$payment_method = PaymentMethodType::transform( PaymentMethods::IDEAL );
497
498
		$result = $this->client->get_issuers( $payment_method );
499
500
		if ( ! $result ) {
501
			$this->error = $this->client->get_error();
502
503
			return $groups;
504
		}
505
506
		$groups[] = array(
507
			'options' => $result,
508
		);
509
510
		return $groups;
511
	}
512
}
513