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