Test Failed
Push — feature/json-api ( 4d01fe...29c948 )
by Remco
06:47
created

Gateway::start()   F

Complexity

Conditions 17
Paths 181

Size

Total Lines 290
Code Lines 101

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 306

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 17
eloc 101
c 6
b 0
f 0
nc 181
nop 1
dl 0
loc 290
ccs 0
cts 176
cp 0
crap 306
rs 3.6333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Pronamic\WordPress\Pay\Gateways\Buckaroo;
4
5
use Pronamic\WordPress\Money\Money;
6
use Pronamic\WordPress\Pay\Banks\BankAccountDetails;
7
use Pronamic\WordPress\Pay\Core\Gateway as Core_Gateway;
8
use Pronamic\WordPress\Pay\Core\PaymentMethods as Core_PaymentMethods;
9
use Pronamic\WordPress\Pay\Core\Server;
10
use Pronamic\WordPress\Pay\Payments\Payment;
11
12
/**
13
 * Title: Buckaroo gateway
14
 * Description:
15
 * Copyright: 2005-2021 Pronamic
16
 * Company: Pronamic
17
 *
18
 * @author Remco Tolsma
19
 * @version 2.0.4
20
 * @since 1.0.0
21
 */
22
class Gateway extends Core_Gateway {
23
	/**
24
	 * Client.
25
	 *
26
	 * @var Client
27
	 */
28
	protected $client;
29
30
	/**
31
	 * Constructs and initializes an Buckaroo gateway
32
	 *
33
	 * @param Config $config Config.
34
	 */
35
	public function __construct( Config $config ) {
36
		parent::__construct( $config );
37
38
		$this->set_method( self::METHOD_HTTP_REDIRECT );
39
40
		// Supported features.
41
		$this->supports = array(
42
			'payment_status_request',
43
			'refunds',
44
			'webhook',
45
			'webhook_log',
46
			'webhook_no_config',
47
		);
48
	}
49
50
	/**
51
	 * Get issuers.
52
	 *
53
	 * @since 1.2.4
54
	 * @see Pronamic_WP_Pay_Gateway::get_issuers()
55
	 */
56
	public function get_issuers() {
57
		$groups = array();
58
59
		$object = $this->request( 'GET', 'Transaction/Specification/ideal?serviceVersion=2' );
60
61
		foreach ( $object->Actions as $action ) {
62
			if ( 'Pay' === $action->Name ) {
63
				foreach ( $action->RequestParameters as $request_parameter ) {
64
					if ( 'issuer' === $request_parameter->Name ) {
65
						foreach ( $request_parameter->ListItemDescriptions as $item ) {
66
							if ( ! array_key_exists( $item->GroupName, $groups ) ) {
67
								$groups[ $item->GroupName ] = array(
68
									'name'    => $item->GroupName,
69
									'options' => array(),
70
								);
71
							}
72
73
							$groups[ $item->GroupName ]['options'][ $item->Value ] = $item->Description;
74
						}
75
					}
76
				}
77
			}
78
		}
79
80
		return $groups;
81
	}
82
83
	/**
84
	 * Get supported payment methods
85
	 *
86
	 * @see Pronamic_WP_Pay_Gateway::get_supported_payment_methods()
87
	 */
88
	public function get_supported_payment_methods() {
89
		return array(
90
			Core_PaymentMethods::BANK_TRANSFER,
91
			Core_PaymentMethods::BANCONTACT,
92
			Core_PaymentMethods::CREDIT_CARD,
93
			Core_PaymentMethods::GIROPAY,
94
			Core_PaymentMethods::IDEAL,
95
			Core_PaymentMethods::PAYPAL,
96
			Core_PaymentMethods::SOFORT,
97
		);
98
	}
99
100
	/**
101
	 * Start
102
	 *
103
	 * @param Payment $payment Payment.
104
	 *
105
	 * @see Core_Gateway::start()
106
	 */
107
	public function start( Payment $payment ) {
108
		/**
109
		 * Currency.
110
		 */
111
		$currency_code = $payment->get_total_amount()->get_currency()->get_alphabetic_code();
112
113
		if ( null === $currency_code ) {
114
			throw new \InvalidArgumentException( 'Can not start payment with empty currency code.' );
115
		}
116
117
		/**
118
		 * JSON Transaction.
119
		 *
120
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
121
		 */
122
		$data = (object) array(
123
			'Currency'        => $currency_code,
124
			'AmountDebit'     => $payment->get_total_amount()->get_value(),
125
			'Invoice'         => Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment ),
126
			'ReturnURL'       => $payment->get_return_url(),
127
			'ReturnURLCancel' => \add_query_arg(
128
				'buckaroo_return_url_cancel',
129
				true,
130
				$payment->get_return_url()
131
			),
132
			'ReturnURLError'  => \add_query_arg(
133
				'buckaroo_return_url_error',
134
				true,
135
				$payment->get_return_url()
136
			),
137
			'ReturnURLReject' => \add_query_arg(
138
				'buckaroo_return_url_reject',
139
				true,
140
				$payment->get_return_url()
141
			),
142
			/**
143
			 * Push URL.
144
			 * 
145
			 * When provided, this push URL overrides all the push URLs as configured in the payment plaza under websites for the associated website key
146
			 *
147
			 * @link https://dev.buckaroo.nl/Apis
148
			 */
149
			'PushURL'         => \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' ),
150
			/**
151
			 * Push URL Failure.
152
			 * 
153
			 * When provided, this push URL overrides the push URL for failed transactions as configured in the payment plaza under websites for the associated website key.
154
			 *
155
			 * @link https://dev.buckaroo.nl/Apis
156
			 */
157
			'PushURLFailure'  => \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' ),
158
			/**
159
			 * Services.
160
			 *
161
			 * Specifies which service (can be a payment method and/or additional service) is being called upon in the request.
162
			 *
163
			 * @link https://dev.buckaroo.nl/Apis
164
			 */
165
			'Services'        => (object) array(
166
				'ServiceList' => array(
167
					
168
				),
169
			),
170
			/**
171
			 * Continue On Incomplete.
172
			 * 
173
			 * Specifies if a redirecturl to a payment form will be returned to
174
			 * which a customer should be sent if no paymentmethod is selected
175
			 * or if any required parameter which the customer may provide is
176
			 * missing or incorrect. Possible Values:
177
			 * 
178
			 * · No: This is the default. The request will fail if not all the
179
			 * needed information is provided.
180
			 * 
181
			 * · RedirectToHTML: A redirect to the HTML gateway is provided if
182
			 * a recoverable problems are detected in the request. The customer
183
			 * can then provide the needed information there.
184
			 * 
185
			 * @link https://dev.buckaroo.nl/Apis
186
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
187
			 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ContinueOnIncomplete
188
			 */
189
			'ContinueOnIncomplete'      => 'RedirectToHTML',
190
			/**
191
			 * Services Excluded For Client.
192
			 * 
193
			 * If no primary service is provided and ContinueOnIncomplete is
194
			 * set, this list of comma separated servicescodes can be used to
195
			 * limit the number of services from which the customer may choose
196
			 * once he is redirected to the payment form. Services which are
197
			 * entered in this field are not selectable.
198
			 * This field is optional.
199
			 * 
200
			 * @link https://dev.buckaroo.nl/Apis
201
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
202
			 */
203
			'ServicesExcludedForClient' => $this->config->get_excluded_services(),
204
			/**
205
			 * Custom parameters.
206
			 * 
207
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
208
			 */
209
			'CustomParameters' => array(
210
				(object) array(
211
					'Name'  => 'pronamic_payment_id',
212
					'Value' => $payment->get_id(),
213
				),
214
			),
215
		);
216
217
		/**
218
		 * Client IP.
219
		 * 
220
		 * In this field the IP address of the customer (or employee) for which
221
		 * the action is being performed can be passed. Please note, If this
222
		 * field is not sent to our gateway, your server IP address will be
223
		 * used as the clientIP. This may result in unwanted behaviour for
224
		 * anti-fraud checks. Also, certain payment methods perform checks on
225
		 * the IP address, if an IP address is overused, the request could be
226
		 * blocked. This field is sent in the following format, where 
227
		 * type 0 = IPv4 and type 1 = IPv6: 
228
		 * "ClientIP": { "Type": 0, "Address": "0.0.0.0" },
229
		 * 
230
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
231
		 * @link https://stackoverflow.com/questions/1448871/how-to-know-which-version-of-the-internet-protocol-ip-a-client-is-using-when-c/1448901
232
		 */
233
		$customer = $payment->get_customer();
234
235
		if ( null !== $customer ) {
236
			$ip_address = $customer->get_ip_address();
237
238
			if ( null !== $ip_address ) {
239
				$data->ClientIP = (object) array(
240
					'Type'    => false === \strpos( $ip_address, ':' ) ? 0 : 1,
241
					'Address' => $ip_address,
242
				);
243
			}
244
		}
245
246
		/**
247
		 * Payment method.
248
		 * 
249
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
250
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServicesRequest
251
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServiceRequest
252
		 */
253
		$payment_method = $payment->get_method();
254
255
		switch ( $payment_method ) {
256
			/**
257
			 * Payment method creditcard.
258
			 * 
259
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#pay
260
			 */
261
			case Core_PaymentMethods::CREDIT_CARD:
262
				$data->Services->ServiceList[] = (object) array(
263
					'Action' => 'Pay',
264
					'Name'   => PaymentMethods::AMERICAN_EXPRESS,
265
				);
266
267
				$data->Services->ServiceList[] = (object) array(
268
					'Action' => 'Pay',
269
					'Name'   => PaymentMethods::MAESTRO,
270
				);
271
272
				$data->Services->ServiceList[] = (object) array(
273
					'Action' => 'Pay',
274
					'Name'   => PaymentMethods::MASTERCARD,
275
				);
276
277
				$data->Services->ServiceList[] = (object) array(
278
					'Action' => 'Pay',
279
					'Name'   => PaymentMethods::VISA,
280
				);
281
282
				break;
283
			/**
284
			 * Payment method iDEAL.
285
			 * 
286
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal#pay
287
			 */
288
			case Core_PaymentMethods::IDEAL:
289
				$data->Services->ServiceList[] = (object) array(
290
					'Action'     => 'Pay',
291
					'Name'       => 'ideal',
292
					'Parameters' => array(
293
						array(
294
							'Name'  => 'issuer',
295
							'Value' => $payment->get_issuer(),
296
						),
297
					),
298
				);
299
300
				break;
301
			/**
302
			 * Payment method transfer.
303
			 * 
304
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/transfer#pay
305
			 */
306
			case Core_PaymentMethods::BANK_TRANSFER:
307
				$data->Services->ServiceList[] = (object) array(
308
					'Action'     => 'Pay',
309
					'Name'       => 'transfer',
310
				);
311
312
				break;
313
			/**
314
			 * Payment method Bancontact.
315
			 * 
316
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/bancontact#pay
317
			 */
318
			case Core_PaymentMethods::BANCONTACT:
319
			case Core_PaymentMethods::MISTER_CASH:
0 ignored issues
show
Deprecated Code introduced by
The constant Pronamic\WordPress\Pay\C...entMethods::MISTER_CASH has been deprecated: "Bancontact/Mister Cash" was renamed to just "Bancontact". ( Ignorable by Annotation )

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

319
			case /** @scrutinizer ignore-deprecated */ Core_PaymentMethods::MISTER_CASH:

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
320
				$data->Services->ServiceList[] = (object) array(
321
					'Action'     => 'Pay',
322
					'Name'       => 'bancontactmrcash',
323
				);
324
325
				break;
326
			/**
327
			 * Payment method Giropay.
328
			 * 
329
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/giropay#pay
330
			 */
331
			case Core_PaymentMethods::GIROPAY:
332
				$data->Services->ServiceList[] = (object) array(
333
					'Action'     => 'Pay',
334
					'Name'       => 'giropay',
335
				);
336
337
				break;
338
			/**
339
			 * Payment method PayPal.
340
			 * 
341
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/paypal#pay
342
			 */
343
			case Core_PaymentMethods::PAYPAL:
344
				$data->Services->ServiceList[] = (object) array(
345
					'Action'     => 'Pay',
346
					'Name'       => 'paypal',
347
				);
348
349
				break;
350
			/**
351
			 * Payment method Sofort.
352
			 * 
353
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort#pay
354
			 */
355
			case Core_PaymentMethods::SOFORT:
356
				$data->Services->ServiceList[] = (object) array(
357
					'Action'     => 'Pay',
358
					'Name'       => 'sofortueberweisung',
359
				);
360
361
				break;
362
		}
363
364
		/**
365
		 * Request.
366
		 */
367
		$object = $this->request( 'POST', 'Transaction', $data );
368
369
		if ( 'Redirect' !== $object->RequiredAction->Name ) {
370
			throw new \Exception(
371
				\sprintf(
372
					'Unsupported Buckaroo action: %s',
373
					$object->RequiredAction->Name
374
				)
375
			);
376
		}
377
378
		$payment->set_action_url( $object->RequiredAction->RedirectURL );
379
380
		/**
381
		 * Buckaroo keys.
382
		 * 
383
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionResponse
384
		 */
385
		$payment->set_meta( 'buckaroo_transaction_key', $object->Key );
386
		$payment->set_meta( 'buckaroo_transaction_payment_key', $object->PaymentKey );
387
388
		/**
389
		 * Transaction ID.
390
		 *
391
		 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal]
392
		 */
393
		foreach ( $object->Services as $service ) {
394
			foreach ( $service->Parameters as $parameter ) {
395
				if ( 'transactionId' === $parameter->Name ) {
396
					$payment->set_transaction_id( $parameter->Value );
397
				}
398
			}
399
		}
400
	}
401
402
	/**
403
	 * JSON API Request.
404
	 *
405
	 * @param string      $method   HTTP request method.
406
	 * @param string      $endpoint JSON API endpoint.
407
	 * @param object|null $data     Data.
408
	 */
409
	private function request( $method, $endpoint, $data = null ) {
410
		$host = 'checkout.buckaroo.nl';
411
412
		if ( self::MODE_TEST === $this->config->mode ) {
413
			$host = 'testcheckout.buckaroo.nl';
414
		}
415
416
		/**
417
		 * Authentication.
418
		 * 
419
		 * The HMAC SHA256 is calculated over a concatenated string (as raw data/binary/bytes) of the following values: WebsiteKey, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String. See the next table for more information about these values. Please note: the Base64 hash should be a string of 44 characters. If yours is longer, it is probably in hexadecimal format.
420
		 *
421
		 * @link https://dev.buckaroo.nl/Apis/Description/json
422
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Authentication
423
		 */
424
		$website_key         = $this->config->website_key;
425
		$request_http_method = $method;
426
		$request_uri         = $host . '/json/' . $endpoint;
427
		$request_timestamp   = \strval( \time() );
428
		$nonce               = \wp_generate_password( 32 );
429
		$request_content     = null === $data ? '' : \json_encode( $data );
430
431
		$values = \implode(
432
			'',
433
			array(
434
				$website_key,
435
				$request_http_method,
436
				\strtolower( \urlencode( $request_uri ) ),
437
				$request_timestamp,
438
				$nonce,
439
				null === $data ? '' : \base64_encode( \md5( $request_content, true ) ),
440
			)
441
		);
442
443
		$hash = \hash_hmac( 'sha256', $values, $this->config->secret_key, true );
444
445
		$hmac = \base64_encode( $hash );
446
447
		$authorization = \sprintf(
448
			'hmac %s:%s:%s:%s',
449
			$this->config->website_key,
450
			$hmac,
451
			$nonce,
452
			$request_timestamp
453
		);
454
455
		$response = \Pronamic\WordPress\Http\Facades\Http::request(
456
			'https://' . $request_uri,
457
			array(
458
				'method'  => $request_http_method,
459
				'headers' => array(
460
					'Authorization' => $authorization,
461
					'Content-Type'  => 'application/json',
462
				),
463
				'body'    => $request_content,
464
			)
465
		);
466
467
		$object = $response->json();
468
469
		/**
470
		 * Request Errors.
471
		 * 
472
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
473
		 */
474
		$exception = null;
475
476
		/**
477
		 * Channel errors.
478
		 * 
479
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionRequestResponseChannelError
480
		 */
481
		foreach ( $object->RequestErrors->ChannelErrors as $error ) {
482
			$exception = new \Exception( $error->ErrorMessage, 0, $exception );
483
		}
484
485
		/**
486
		 * Service errors.
487
		 * 
488
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionRequestResponseServiceError
489
		 */
490
		foreach ( $object->RequestErrors->ServiceErrors as $error ) {
491
			$exception = new \Exception( $error->ErrorMessage, 0, $exception );
492
		}
493
494
		/**
495
		 * Action errors.
496
		 * 
497
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionRequestResponseActionError
498
		 */
499
		foreach ( $object->RequestErrors->ActionErrors as $error ) {
500
			$exception = new \Exception( $error->ErrorMessage, 0, $exception );
501
		}
502
503
		/**
504
		 * Action errors.
505
		 * 
506
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionRequestResponseParameterError
507
		 */
508
		foreach ( $object->RequestErrors->ParameterErrors as $error ) {
509
			$exception = new \Exception( $error->ErrorMessage, 0, $exception );
510
		}
511
512
		/**
513
		 * Action errors.
514
		 * 
515
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionRequestResponseCustomParameterError
516
		 */
517
		foreach ( $object->RequestErrors->CustomParameterErrors as $error ) {
518
			$exception = new \Exception( $error->ErrorMessage, 0, $exception );
519
		}
520
521
		if ( null !== $exception ) {
522
			throw $exception;
523
		}
524
525
		/**
526
		 * OK.
527
		 */
528
		return $object;
529
	}
530
531
	/**
532
	 * Update status of the specified payment
533
	 *
534
	 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-Status-transactionKey
535
	 * @param Payment $payment Payment.
536
	 */
537
	public function update_status( Payment $payment ) {
538
		$transaction_key = $payment->get_meta( 'buckaroo_transaction_key' );
539
540
		if ( empty( $transaction_key ) ) {
541
			return;
542
		}
543
544
		$result = $this->request( 'GET', 'Transaction/Status/' . $transaction_key );
545
546
		$payment->set_status( Statuses::transform( \strval( $result->Status->Code->Code ) ) );
547
548
		/**
549
		 * Consumer bank details.
550
		 */
551
		$consumer_bank_details = $payment->get_consumer_bank_details();
552
553
		if ( null === $consumer_bank_details ) {
554
			$consumer_bank_details = new BankAccountDetails();
555
556
			$payment->set_consumer_bank_details( $consumer_bank_details );
557
		}
558
559
		/**
560
		 * Services.
561
		 */
562
		foreach ( $result->Services as $service ) {
563
			foreach ( $service->Parameters as $parameter ) {
564
				if ( 'transactionId' === $parameter->Name ) {
565
					$payment->set_transaction_id( $parameter->Value );
566
				}
567
568
				if ( 'consumerName' === $parameter->Name ) {
569
					$consumer_bank_details->set_name( $parameter->Value );
570
				}
571
572
				if ( 'consumerIBAN' === $parameter->Name ) {
573
					$consumer_bank_details->set_iban( $parameter->Value );
574
				}
575
576
				if ( 'consumerBIC' === $parameter->Name ) {
577
					$consumer_bank_details->set_iban( $parameter->Value );
578
				}
579
			}
580
		}
581
582
		/**
583
		 * Refunds.
584
		 *
585
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-RefundInfo-transactionKey
586
		 */
587
		$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_key );
588
589
		$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
590
591
		$payment->set_refunded_amount( $refunded_amount );
592
	}
593
}
594