Failed Conditions
Push — develop ( 7405d6...baa419 )
by Reüel
06:09
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
			$request->set_billing_address( $billing_address );
218
		}
219
220
		// Delivery address.
221
		if ( null !== $payment->get_shipping_address() ) {
222
			$address = $payment->get_shipping_address();
223
224
			$delivery_address = new Address( $address->get_country_code() );
225
226
			$delivery_address->set_city( $address->get_city() );
227
			$delivery_address->set_house_number_or_name( sprintf( '%s %s', $address->get_house_number(), $address->get_house_number_addition() ) );
228
			$delivery_address->set_postal_code( $address->get_postal_code() );
229
			$delivery_address->set_state_or_province( $address->get_region() );
230
			$delivery_address->set_street( $address->get_street_name() );
231
232
			$request->set_delivery_address( $delivery_address );
233
		}
234
235
		// Lines.
236
		$lines = $payment->get_lines();
237
238
		if ( null !== $lines ) {
239
			$line_items = $request->new_items();
240
241
			$i = 1;
242
243
			foreach ( $lines as $line ) {
244
				// Description.
245
				$description = $line->get_description();
246
247
				// Use line item name as fallback for description.
248
				if ( null === $description ) {
249
					/* translators: %s: item index */
250
					$description = sprintf( __( 'Item %s', 'pronamic_ideal' ), $i ++ );
251
252
					if ( null !== $line->get_name() && '' !== $line->get_name() ) {
253
						$description = $line->get_name();
254
					}
255
				}
256
257
				$item = $line_items->new_item(
258
					$description,
259
					$line->get_quantity(),
260
					$line->get_total_amount()->get_including_tax()->get_minor_units()
261
				);
262
263
				$item->set_amount_excluding_tax( $line->get_total_amount()->get_excluding_tax()->get_minor_units() );
264
265
				$item->set_id( $line->get_id() );
266
267
				// Tax amount.
268
				$tax_amount = $line->get_unit_price()->get_tax_amount();
269
270
				if ( null !== $tax_amount ) {
271
					$item->set_tax_amount( $line->get_total_amount()->get_tax_amount()->get_minor_units() );
272
					$item->set_tax_percentage( (int) $line->get_total_amount()->get_tax_percentage() * 100 );
273
				}
274
			}
275
		}
276
277
		// Create payment or payment session.
278
		if ( $request instanceof PaymentRequest ) {
279
			$result = $this->client->create_payment( $request );
280
		} else {
281
			$result = $this->client->create_payment_session( $request );
282
		}
283
284
		// Handle errors.
285
		if ( ! $result ) {
286
			$this->error = $this->client->get_error();
287
288
			return;
289
		}
290
291
		// Set checkout meta for Web SDK redirect.
292
		if ( isset( $result->paymentSession ) ) {
293
			$payment->set_meta(
294
				'adyen_checkout',
295
				array(
296
					'sdk_version' => self::SDK_VERSION,
297
					'payload'     => $result->paymentSession,
298
				)
299
			);
300
		}
301
302
		// Set transaction ID.
303
		if ( isset( $result->pspReference ) ) {
304
			$payment->set_transaction_id( $result->pspReference );
305
		}
306
307
		// Set action URL.
308
		$action_url = add_query_arg(
309
			array(
310
				'payment_redirect' => $payment->get_id(),
311
				'key'              => $payment->key,
312
			),
313
			home_url( '/' )
314
		);
315
316
		if ( isset( $result->redirect->url ) ) {
317
			$action_url = $result->redirect->url;
318
		}
319
320
		$payment->set_action_url( $action_url );
321
	}
322
323
	/**
324
	 * Payment redirect.
325
	 *
326
	 * @param Payment $payment Payment.
327
	 *
328
	 * @return void
329
	 */
330
	public function payment_redirect( Payment $payment ) {
331
		$checkout = $payment->get_meta( 'adyen_checkout' );
332
333
		if ( empty( $checkout ) ) {
334
			return;
335
		}
336
337
		$url = sprintf(
338
			'https://checkoutshopper-%s.adyen.com/checkoutshopper/assets/js/sdk/checkoutSDK.%s.min.js',
339
			( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' ),
340
			$checkout['sdk_version']
341
		);
342
343
		wp_register_script(
344
			'pronamic-pay-adyen-checkout',
345
			$url,
346
			array(),
347
			$checkout['sdk_version'],
348
			false
349
		);
350
351
		// No cache.
352
		Util::no_cache();
353
354
		$context = ( self::MODE_TEST === $payment->get_mode() ? 'test' : 'live' );
355
356
		require __DIR__ . '/../views/checkout.php';
357
358
		exit;
359
	}
360
361
	/**
362
	 * Update status of the specified payment.
363
	 *
364
	 * @param Payment $payment Payment.
365
	 *
366
	 * @return void
367
	 */
368
	public function update_status( Payment $payment ) {
369
		// Maybe process stored webhook notification.
370
		$this->maybe_handle_notification( $payment );
371
372
		// Process payload on return.
373
		if ( ! filter_has_var( INPUT_GET, 'payload' ) ) {
374
			return;
375
		}
376
377
		$status = null;
378
379
		$payload = filter_input( INPUT_GET, 'payload', FILTER_SANITIZE_STRING );
380
381
		switch ( $payment->get_method() ) {
382
			case PaymentMethods::IDEAL:
383
			case PaymentMethods::SOFORT:
384
				$result = $this->client->get_payment_details( $payload );
385
386
				break;
387
			default:
388
				$result = $this->client->get_payment_result( $payload );
389
		}
390
391
		if ( $result ) {
392
			$status = ResultCode::transform( $result->resultCode );
393
394
			$psp_reference = $result->pspReference;
395
		}
396
397
		// Handle errors.
398
		if ( empty( $status ) ) {
399
			$payment->set_status( Core_Statuses::FAILURE );
400
401
			$this->error = $this->client->get_error();
402
403
			return;
404
		}
405
406
		// Update status.
407
		$payment->set_status( $status );
408
409
		// Update transaction ID.
410
		if ( isset( $psp_reference ) ) {
411
			$payment->set_transaction_id( $psp_reference );
412
		}
413
	}
414
415
	/**
416
	 * Maybe handle notification.
417
	 *
418
	 * @param Payment $payment      Payment.
419
	 */
420
	public function maybe_handle_notification( Payment $payment ) {
421
		$notification = $payment->get_meta( 'adyen_notification' );
422
423
		if ( empty( $notification ) ) {
424
			return;
425
		}
426
427
		$notification = json_decode( $notification );
428
429
		if ( ! is_object( $notification ) ) {
430
			return;
431
		}
432
433
		switch ( $notification->eventCode ) {
434
			case EventCode::AUTHORIZATION:
435
				$this->handle_authorization_event( $payment, $notification );
436
437
				break;
438
		}
439
440
		$payment->set_meta( 'adyen_notification', null );
441
	}
442
443
	/**
444
	 * Handle authorization event.
445
	 *
446
	 * @param Payment $payment      Payment.
447
	 * @param object  $notification Notification.
448
	 */
449
	public function handle_authorization_event( Payment $payment, $notification ) {
450
		if ( ! is_object( $notification ) ) {
451
			return;
452
		}
453
454
		$success = $notification->success;
455
456
		if ( 'true' === $success ) {
457
			$status = Core_Statuses::SUCCESS;
458
		} else {
459
			$status = Core_Statuses::FAILURE;
460
461
			// Add note.
462
			$note = sprintf(
463
				/* translators: %s: failure reason message */
464
				__( 'Failure reason: %s.', 'pronamic_ideal' ),
465
				esc_html( $notification->reason )
466
			);
467
468
			$payment->add_note( $note );
469
		}
470
471
		$payment->set_status( $status );
472
	}
473
474
	/**
475
	 * Get available payment methods.
476
	 *
477
	 * @see Core_Gateway::get_available_payment_methods()
478
	 */
479
	public function get_available_payment_methods() {
480
		$payment_methods = array();
481
482
		// Get active payment methods for Adyen account.
483
		$methods = $this->client->get_payment_methods();
484
485
		if ( ! $methods ) {
486
			$this->error = $this->client->get_error();
487
488
			return $payment_methods;
489
		}
490
491
		// Transform to WordPress payment methods.
492
		foreach ( $methods as $method => $details ) {
493
			$payment_method = PaymentMethodType::transform_gateway_method( $method );
494
495
			if ( $payment_method ) {
496
				$payment_methods[] = $payment_method;
497
			}
498
		}
499
500
		$payment_methods = array_unique( $payment_methods );
501
502
		return $payment_methods;
503
	}
504
505
	/**
506
	 * Get issuers.
507
	 *
508
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
509
	 */
510
	public function get_issuers() {
511
		$groups = array();
512
513
		$payment_method = PaymentMethodType::transform( PaymentMethods::IDEAL );
514
515
		$result = $this->client->get_issuers( $payment_method );
516
517
		if ( ! $result ) {
518
			$this->error = $this->client->get_error();
519
520
			return $groups;
521
		}
522
523
		$groups[] = array(
524
			'options' => $result,
525
		);
526
527
		return $groups;
528
	}
529
}
530