Failed Conditions
Push — develop ( 2fda54...ea14ca )
by Remco
03:37
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
			$request->set_shopper_ip( $payment->get_customer()->get_ip_address() );
177
			$request->set_shopper_statement( $payment->get_customer()->get_gender() );
178
			$request->set_shopper_locale( $payment->get_customer()->get_locale() );
179
			$request->set_shopper_reference( $payment->get_customer()->get_user_id() );
180
			$request->set_telephone_number( $payment->get_customer()->get_phone() );
181
182
			if ( null !== $payment->get_customer()->get_name() ) {
183
				$shopper_name = new ShopperName(
184
					$payment->get_customer()->get_name()->get_first_name(),
185
					$payment->get_customer()->get_name()->get_middle_name(),
186
					$payment->get_customer()->get_name()->get_last_name()
187
				);
188
189
				$request->set_shopper_name( $shopper_name );
190
			}
191
		}
192
193
		// Lines.
194
		$lines = $payment->get_lines();
195
196
		if ( null !== $lines ) {
197
			$line_items = $request->new_items();
198
199
			$i = 1;
200
201
			foreach ( $lines as $line ) {
202
				/* translators: %s: item index */
203
				$name = sprintf( __( 'Item %s', 'pronamic_ideal' ), $i ++ );
204
205
				if ( null !== $line->get_name() && '' !== $line->get_name() ) {
206
					$name = $line->get_name();
207
				}
208
209
				$item = $line_items->new_item(
210
					DataHelper::shorten( $name, 50 ),
211
					$line->get_quantity(),
212
					// The amount in cents, including VAT, of the item each, see below for more details.
213
					AmountTransformer::transform( $line->get_unit_price() ),
214
					$line->get_type()
215
				);
216
217
				$item->set_id( $line->get_id() );
218
219
				// Description.
220
				$description = $line->get_description();
221
222
				if ( null !== $description ) {
223
					$description = DataHelper::shorten( $description, 100 );
224
				}
225
226
				$item->set_description( $description );
227
228
				$tax_amount = $line->get_unit_price()->get_tax_amount();
229
230
				if ( null !== $tax_amount ) {
231
					// The VAT of the item each, see below for more details.
232
					$item->set_tax( AmountTransformer::transform( $tax_amount ) );
233
				}
234
			}
235
		}
236
237
		// Create payment or payment session.
238
		if ( $request instanceof PaymentRequest ) {
239
			$result = $this->client->create_payment( $request );
240
		} else {
241
			$result = $this->client->create_payment_session( $request );
242
		}
243
244
		// Handle errors.
245
		if ( ! $result ) {
246
			$this->error = $this->client->get_error();
247
248
			return;
249
		}
250
251
		// Load checkout view for payment sessions.
252
		if ( isset( $result->paymentSession ) ) {
253
			$url = sprintf(
254
				'https://checkoutshopper-%s.adyen.com/checkoutshopper/assets/js/sdk/checkoutSDK.%s.min.js',
255
				( self::MODE_TEST === $this->config->mode ? 'test' : 'live' ),
256
				self::SDK_VERSION
257
			);
258
259
			wp_register_script(
260
				'pronamic-pay-adyen-checkout',
261
				$url,
262
				array(),
263
				self::SDK_VERSION,
264
				false
265
			);
266
267
			// No cache.
268
			Util::no_cache();
269
270
			$payment_session = $result->paymentSession;
271
272
			$context = ( self::MODE_TEST === $this->config->mode ? 'test' : 'live' );
273
274
			require __DIR__ . '/../views/checkout.php';
275
276
			exit;
277
		}
278
279
		// Set transaction ID.
280
		if ( isset( $result->pspReference ) ) {
281
			$payment->set_transaction_id( $result->pspReference );
282
		}
283
284
		// Set redirect URL.
285
		if ( isset( $result->redirect->url ) ) {
286
			$payment->set_action_url( $result->redirect->url );
287
		}
288
	}
289
290
	/**
291
	 * Update status of the specified payment.
292
	 *
293
	 * @param Payment $payment Payment.
294
	 *
295
	 * @return void
296
	 */
297
	public function update_status( Payment $payment ) {
298
		// Maybe process stored webhook notification.
299
		$this->maybe_handle_notification( $payment );
300
301
		// Process payload on return.
302
		if ( ! filter_has_var( INPUT_GET, 'payload' ) ) {
303
			return;
304
		}
305
306
		$status = null;
307
308
		$payload = filter_input( INPUT_GET, 'payload', FILTER_SANITIZE_STRING );
309
310
		switch ( $payment->get_method() ) {
311
			case PaymentMethods::IDEAL:
312
			case PaymentMethods::SOFORT:
313
				$result = $this->client->get_payment_details( $payload );
314
315
				break;
316
			default:
317
				$result = $this->client->get_payment_result( $payload );
318
		}
319
320
		if ( $result ) {
321
			$status = ResultCode::transform( $result->resultCode );
322
323
			$psp_reference = $result->pspReference;
324
		}
325
326
		// Handle errors.
327
		if ( empty( $status ) ) {
328
			$payment->set_status( Core_Statuses::FAILURE );
329
330
			$this->error = $this->client->get_error();
331
332
			return;
333
		}
334
335
		// Update status.
336
		$payment->set_status( $status );
337
338
		// Update transaction ID.
339
		if ( isset( $psp_reference ) ) {
340
			$payment->set_transaction_id( $psp_reference );
341
		}
342
	}
343
344
	/**
345
	 * Maybe handle notification.
346
	 *
347
	 * @param Payment $payment      Payment.
348
	 */
349
	public function maybe_handle_notification( Payment $payment ) {
350
		$notification = $payment->get_meta( 'adyen_notification' );
351
352
		if ( empty( $notification ) ) {
353
			return;
354
		}
355
356
		$notification = json_decode( $notification );
357
358
		if ( ! is_object( $notification ) ) {
359
			return;
360
		}
361
362
		switch ( $notification->eventCode ) {
363
			case EventCode::AUTHORIZATION:
364
				$this->handle_authorization_event( $payment, $notification );
365
366
				break;
367
		}
368
369
		$payment->set_meta( 'adyen_notification', null );
370
	}
371
372
	/**
373
	 * Handle authorization event.
374
	 *
375
	 * @param Payment $payment      Payment.
376
	 * @param object  $notification Notification.
377
	 */
378
	public function handle_authorization_event( Payment $payment, $notification ) {
379
		if ( ! is_object( $notification ) ) {
380
			return;
381
		}
382
383
		$success = $notification->success;
384
385
		if ( 'true' === $success ) {
386
			$status = Core_Statuses::SUCCESS;
387
		} else {
388
			$status = Core_Statuses::FAILURE;
389
390
			// Add note.
391
			$note = sprintf(
392
				/* translators: %s: failure reason message */
393
				__( 'Failure reason: %s.', 'pronamic_ideal' ),
394
				esc_html( $notification->reason )
395
			);
396
397
			$payment->add_note( $note );
398
		}
399
400
		$payment->set_status( $status );
401
	}
402
403
	/**
404
	 * Get available payment methods.
405
	 *
406
	 * @see Core_Gateway::get_available_payment_methods()
407
	 */
408
	public function get_available_payment_methods() {
409
		$payment_methods = array();
410
411
		// Get active payment methods for Adyen account.
412
		$methods = $this->client->get_payment_methods();
413
414
		if ( ! $methods ) {
415
			$this->error = $this->client->get_error();
416
417
			return $payment_methods;
418
		}
419
420
		// Transform to WordPress payment methods.
421
		foreach ( $methods as $method => $details ) {
422
			$payment_method = PaymentMethodType::transform_gateway_method( $method );
423
424
			if ( $payment_method ) {
425
				$payment_methods[] = $payment_method;
426
			}
427
		}
428
429
		$payment_methods = array_unique( $payment_methods );
430
431
		return $payment_methods;
432
	}
433
434
	/**
435
	 * Get issuers.
436
	 *
437
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
438
	 */
439
	public function get_issuers() {
440
		$groups = array();
441
442
		$payment_method = PaymentMethodType::transform( PaymentMethods::IDEAL );
443
444
		$result = $this->client->get_issuers( $payment_method );
445
446
		if ( ! $result ) {
447
			$this->error = $this->client->get_error();
448
449
			return $groups;
450
		}
451
452
		$groups[] = array(
453
			'options' => $result,
454
		);
455
456
		return $groups;
457
	}
458
}
459