Failed Conditions
Push — feature/json-api ( 29c948...881637 )
by Remco
05:02
created

Gateway::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 4
b 0
f 0
nc 1
nop 1
dl 0
loc 12
ccs 0
cts 9
cp 0
crap 2
rs 10
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.
0 ignored issues
show
Bug introduced by
The type Pronamic\WordPress\Pay\Gateways\Buckaroo\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
		 * Push URL.
119
		 */
120
		$push_url = \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' );
121
122
		/**
123
		 * Filters the Buckaroo push URL.
124
		 *
125
		 * If you want to debug the Buckaroo report URL you can use this filter
126
		 * to override the push URL. You could for example use a service like
127
		 * https://webhook.site/ to inspect the push requests from Buckaroo.
128
		 *
129
		 * @param string $push_url Buckaroo push URL.
130
		 */
131
		$push_url = \apply_filters( 'pronamic_pay_buckaroo_push_url', $push_url );
132
133
		/**
134
		 * JSON Transaction.
135
		 *
136
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
137
		 */
138
		$data = (object) array(
139
			'Currency'        => $currency_code,
140
			'AmountDebit'     => $payment->get_total_amount()->get_value(),
141
			'Invoice'         => Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment ),
142
			'ReturnURL'       => $payment->get_return_url(),
143
			'ReturnURLCancel' => \add_query_arg(
144
				'buckaroo_return_url_cancel',
145
				true,
146
				$payment->get_return_url()
147
			),
148
			'ReturnURLError'  => \add_query_arg(
149
				'buckaroo_return_url_error',
150
				true,
151
				$payment->get_return_url()
152
			),
153
			'ReturnURLReject' => \add_query_arg(
154
				'buckaroo_return_url_reject',
155
				true,
156
				$payment->get_return_url()
157
			),
158
			/**
159
			 * Push URL.
160
			 * 
161
			 * When provided, this push URL overrides all the push URLs as configured in the payment plaza under websites for the associated website key
162
			 *
163
			 * @link https://dev.buckaroo.nl/Apis
164
			 */
165
			'PushURL'         => $push_url,
166
			/**
167
			 * Push URL Failure.
168
			 * 
169
			 * 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.
170
			 *
171
			 * @link https://dev.buckaroo.nl/Apis
172
			 */
173
			'PushURLFailure'  => $push_url,
174
			/**
175
			 * Services.
176
			 *
177
			 * Specifies which service (can be a payment method and/or additional service) is being called upon in the request.
178
			 *
179
			 * @link https://dev.buckaroo.nl/Apis
180
			 */
181
			'Services'        => (object) array(
182
				'ServiceList' => array(
183
					
184
				),
185
			),
186
			/**
187
			 * Continue On Incomplete.
188
			 * 
189
			 * Specifies if a redirecturl to a payment form will be returned to
190
			 * which a customer should be sent if no paymentmethod is selected
191
			 * or if any required parameter which the customer may provide is
192
			 * missing or incorrect. Possible Values:
193
			 * 
194
			 * · No: This is the default. The request will fail if not all the
195
			 * needed information is provided.
196
			 * 
197
			 * · RedirectToHTML: A redirect to the HTML gateway is provided if
198
			 * a recoverable problems are detected in the request. The customer
199
			 * can then provide the needed information there.
200
			 * 
201
			 * @link https://dev.buckaroo.nl/Apis
202
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
203
			 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ContinueOnIncomplete
204
			 */
205
			'ContinueOnIncomplete'      => 'RedirectToHTML',
206
			/**
207
			 * Services Excluded For Client.
208
			 * 
209
			 * If no primary service is provided and ContinueOnIncomplete is
210
			 * set, this list of comma separated servicescodes can be used to
211
			 * limit the number of services from which the customer may choose
212
			 * once he is redirected to the payment form. Services which are
213
			 * entered in this field are not selectable.
214
			 * This field is optional.
215
			 * 
216
			 * @link https://dev.buckaroo.nl/Apis
217
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
218
			 */
219
			'ServicesExcludedForClient' => $this->config->get_excluded_services(),
220
			/**
221
			 * Custom parameters.
222
			 * 
223
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
224
			 */
225
			'CustomParameters' => array(
226
				(object) array(
227
					'Name'  => 'pronamic_payment_id',
228
					'Value' => $payment->get_id(),
229
				),
230
			),
231
		);
232
233
		/**
234
		 * Client IP.
235
		 * 
236
		 * In this field the IP address of the customer (or employee) for which
237
		 * the action is being performed can be passed. Please note, If this
238
		 * field is not sent to our gateway, your server IP address will be
239
		 * used as the clientIP. This may result in unwanted behaviour for
240
		 * anti-fraud checks. Also, certain payment methods perform checks on
241
		 * the IP address, if an IP address is overused, the request could be
242
		 * blocked. This field is sent in the following format, where 
243
		 * type 0 = IPv4 and type 1 = IPv6: 
244
		 * "ClientIP": { "Type": 0, "Address": "0.0.0.0" },
245
		 * 
246
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
247
		 * @link https://stackoverflow.com/questions/1448871/how-to-know-which-version-of-the-internet-protocol-ip-a-client-is-using-when-c/1448901
248
		 */
249
		$customer = $payment->get_customer();
250
251
		if ( null !== $customer ) {
252
			$ip_address = $customer->get_ip_address();
253
254
			if ( null !== $ip_address ) {
255
				$data->ClientIP = (object) array(
256
					'Type'    => false === \strpos( $ip_address, ':' ) ? 0 : 1,
257
					'Address' => $ip_address,
258
				);
259
			}
260
		}
261
262
		/**
263
		 * Payment method.
264
		 * 
265
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
266
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServicesRequest
267
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServiceRequest
268
		 */
269
		$payment_method = $payment->get_method();
270
271
		switch ( $payment_method ) {
272
			/**
273
			 * Payment method creditcard.
274
			 * 
275
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#pay
276
			 */
277
			case Core_PaymentMethods::CREDIT_CARD:
278
				$data->Services->ServiceList[] = (object) array(
279
					'Action' => 'Pay',
280
					'Name'   => PaymentMethods::AMERICAN_EXPRESS,
281
				);
282
283
				$data->Services->ServiceList[] = (object) array(
284
					'Action' => 'Pay',
285
					'Name'   => PaymentMethods::MAESTRO,
286
				);
287
288
				$data->Services->ServiceList[] = (object) array(
289
					'Action' => 'Pay',
290
					'Name'   => PaymentMethods::MASTERCARD,
291
				);
292
293
				$data->Services->ServiceList[] = (object) array(
294
					'Action' => 'Pay',
295
					'Name'   => PaymentMethods::VISA,
296
				);
297
298
				break;
299
			/**
300
			 * Payment method iDEAL.
301
			 * 
302
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal#pay
303
			 */
304
			case Core_PaymentMethods::IDEAL:
305
				$data->Services->ServiceList[] = (object) array(
306
					'Action'     => 'Pay',
307
					'Name'       => 'ideal',
308
					'Parameters' => array(
309
						array(
310
							'Name'  => 'issuer',
311
							'Value' => $payment->get_issuer(),
312
						),
313
					),
314
				);
315
316
				break;
317
			/**
318
			 * Payment method transfer.
319
			 * 
320
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/transfer#pay
321
			 */
322
			case Core_PaymentMethods::BANK_TRANSFER:
323
				$data->Services->ServiceList[] = (object) array(
324
					'Action'     => 'Pay',
325
					'Name'       => 'transfer',
326
				);
327
328
				break;
329
			/**
330
			 * Payment method Bancontact.
331
			 * 
332
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/bancontact#pay
333
			 */
334
			case Core_PaymentMethods::BANCONTACT:
335
			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

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