Failed Conditions
Push — develop ( 12001c...aa0b57 )
by Remco
03:08
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
68
	/**
69
	 * Get supported payment methods
70
	 *
71
	 * @see Core_Gateway::get_supported_payment_methods()
72
	 */
73
	public function get_supported_payment_methods() {
74
		return array(
75
			PaymentMethods::BANCONTACT,
76
			PaymentMethods::CREDIT_CARD,
77
			PaymentMethods::DIRECT_DEBIT,
78
			PaymentMethods::GIROPAY,
79
			PaymentMethods::IDEAL,
80
			PaymentMethods::MAESTRO,
81
			PaymentMethods::SOFORT,
82
		);
83
	}
84
85
	/**
86
	 * Start.
87
	 *
88
	 * @param Payment $payment Payment.
89
	 *
90
	 * @see Plugin::start()
91
	 */
92
	public function start( Payment $payment ) {
93
		// Amount.
94
		$amount = new Amount(
95
			$payment->get_total_amount()->get_currency()->get_alphabetic_code(),
96
			$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

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