Failed Conditions
Push — develop ( b0c738...9276f5 )
by Remco
04:33 queued 11s
created

Gateway::update_status()   B

Complexity

Conditions 10
Paths 41

Size

Total Lines 52
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 10
eloc 21
nc 41
nop 1
dl 0
loc 52
ccs 0
cts 29
cp 0
crap 110
rs 7.6666
c 4
b 0
f 0

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
use Pronamic\WordPress\Pay\Payments\PaymentStatus;
12
13
/**
14
 * Title: Buckaroo gateway
15
 * Description:
16
 * Copyright: 2005-2021 Pronamic
17
 * Company: Pronamic
18
 *
19
 * @author Remco Tolsma
20
 * @version 2.0.4
21
 * @since 1.0.0
22
 */
23
class Gateway extends Core_Gateway {
24
	/**
25
	 * 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...
26
	 *
27
	 * @var Client
28
	 */
29
	protected $client;
30
31
	/**
32
	 * Constructs and initializes an Buckaroo gateway
33
	 *
34
	 * @param Config $config Config.
35
	 */
36
	public function __construct( Config $config ) {
37
		parent::__construct( $config );
38
39
		$this->set_method( self::METHOD_HTTP_REDIRECT );
40
41
		// Supported features.
42
		$this->supports = array(
43
			'payment_status_request',
44
			'refunds',
45
			'webhook',
46
			'webhook_log',
47
			'webhook_no_config',
48
		);
49
	}
50
51
	/**
52
	 * Get issuers.
53
	 *
54
	 * @since 1.2.4
55
	 * @see Core_Gateway::get_issuers()
56
	 */
57
	public function get_issuers() {
58
		$groups = array();
59
60
		$object = $this->request( 'GET', 'Transaction/Specification/ideal?serviceVersion=2' );
61
62
		foreach ( $object->Actions as $action ) {
63
			if ( 'Pay' === $action->Name ) {
64
				foreach ( $action->RequestParameters as $request_parameter ) {
65
					if ( 'issuer' === $request_parameter->Name ) {
66
						foreach ( $request_parameter->ListItemDescriptions as $item ) {
67
							if ( ! array_key_exists( $item->GroupName, $groups ) ) {
68
								$groups[ $item->GroupName ] = array(
69
									'name'    => $item->GroupName,
70
									'options' => array(),
71
								);
72
							}
73
74
							$groups[ $item->GroupName ]['options'][ $item->Value ] = $item->Description;
75
						}
76
					}
77
				}
78
			}
79
		}
80
81
		return $groups;
82
	}
83
84
	/**
85
	 * Get supported payment methods
86
	 *
87
	 * @see Core_Gateway::get_supported_payment_methods()
88
	 */
89
	public function get_supported_payment_methods() {
90
		return array(
91
			Core_PaymentMethods::BANK_TRANSFER,
92
			Core_PaymentMethods::BANCONTACT,
93
			Core_PaymentMethods::CREDIT_CARD,
94
			Core_PaymentMethods::GIROPAY,
95
			Core_PaymentMethods::IDEAL,
96
			Core_PaymentMethods::PAYPAL,
97
			Core_PaymentMethods::SOFORT,
98
		);
99
	}
100
101
	/**
102
	 * Start
103
	 *
104
	 * @param Payment $payment Payment.
105
	 *
106
	 * @see Core_Gateway::start()
107
	 */
108
	public function start( Payment $payment ) {
109
		/**
110
		 * Currency.
111
		 */
112
		$currency_code = $payment->get_total_amount()->get_currency()->get_alphabetic_code();
113
114
		if ( null === $currency_code ) {
115
			throw new \InvalidArgumentException( 'Can not start payment with empty currency code.' );
116
		}
117
118
		/**
119
		 * Push URL.
120
		 */
121
		$push_url = \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' );
122
123
		/**
124
		 * Filters the Buckaroo push URL.
125
		 *
126
		 * If you want to debug the Buckaroo report URL you can use this filter
127
		 * to override the push URL. You could for example use a service like
128
		 * https://webhook.site/ to inspect the push requests from Buckaroo.
129
		 *
130
		 * @param string $push_url Buckaroo push URL.
131
		 */
132
		$push_url = \apply_filters( 'pronamic_pay_buckaroo_push_url', $push_url );
133
134
		/**
135
		 * JSON Transaction.
136
		 *
137
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
138
		 */
139
		$data = (object) array(
140
			'Currency'                  => $currency_code,
141
			/**
142
			 * The debit amount for the request. This is in decimal format,
143
			 * with a point as the decimal separator. For example, if the
144
			 * currency is specified as EUR, sending “1” will mean that 1 euro
145
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
146
			 * Please note, a transaction must have either a debit amount or a
147
			 * credit amount and it cannot have both.
148
			 * 
149
			 * @link https://dev.buckaroo.nl/Apis
150
			 */
151
			'AmountDebit'               => $payment->get_total_amount()->number_format( null, '.', '' ),
0 ignored issues
show
Bug introduced by
The method number_format() does not exist on Pronamic\WordPress\Money\TaxedMoney. Did you maybe mean format()? ( Ignorable by Annotation )

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

151
			'AmountDebit'               => $payment->get_total_amount()->/** @scrutinizer ignore-call */ number_format( null, '.', '' ),

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

345
			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...
346
				$data->Services->ServiceList[] = (object) array(
347
					'Action' => 'Pay',
348
					'Name'   => 'bancontactmrcash',
349
				);
350
351
				break;
352
			/**
353
			 * Payment method Giropay.
354
			 *
355
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/giropay#pay
356
			 */
357
			case Core_PaymentMethods::GIROPAY:
358
				$data->Services->ServiceList[] = (object) array(
359
					'Action' => 'Pay',
360
					'Name'   => 'giropay',
361
				);
362
363
				break;
364
			/**
365
			 * Payment method PayPal.
366
			 *
367
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/paypal#pay
368
			 */
369
			case Core_PaymentMethods::PAYPAL:
370
				$data->Services->ServiceList[] = (object) array(
371
					'Action' => 'Pay',
372
					'Name'   => 'paypal',
373
				);
374
375
				break;
376
			/**
377
			 * Payment method Sofort.
378
			 *
379
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort#pay
380
			 */
381
			case Core_PaymentMethods::SOFORT:
382
				$data->Services->ServiceList[] = (object) array(
383
					'Action' => 'Pay',
384
					'Name'   => 'sofortueberweisung',
385
				);
386
387
				break;
388
		}
389
390
		/**
391
		 * Request.
392
		 */
393
		$object = $this->request( 'POST', 'Transaction', $data );
394
395
		/**
396
		 * Buckaroo keys.
397
		 *
398
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionResponse
399
		 */
400
		if ( \property_exists( $object, 'Key' ) ) {
401
			$payment->set_transaction_id( $object->Key );
402
		}
403
404
		if ( \property_exists( $object, 'PaymentKey' ) ) {
405
			$payment->set_meta( 'buckaroo_transaction_payment_key', $object->PaymentKey );
406
		}
407
408
		/**
409
		 * Request Errors.
410
		 *
411
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
412
		 */
413
		if ( \property_exists( $object, 'RequestErrors' ) && null !== $object->RequestErrors ) {
414
			$exception = null;
415
416
			foreach ( $object->RequestErrors as $errors ) {
417
				foreach ( $errors as $error ) {
418
					// Add exception.
419
					$exception = new \Exception( $error->ErrorMessage, 0, $exception );
420
				}
421
			}
422
423
			if ( null !== $exception ) {
424
				throw $exception;
425
			}
426
		}
427
428
		/**
429
		 * Required Action.
430
		 */
431
		if ( null !== $object->RequiredAction ) {
432
			if ( 'Redirect' !== $object->RequiredAction->Name ) {
433
				throw new \Exception(
434
					\sprintf(
435
						'Unsupported Buckaroo action: %s',
436
						$object->RequiredAction->Name
437
					)
438
				);
439
			}
440
441
			// Set action URL.
442
			if ( \property_exists( $object->RequiredAction, 'RedirectURL' ) ) {
443
				$payment->set_action_url( $object->RequiredAction->RedirectURL );
444
			}
445
		}
446
447
		// Failure.
448
		if ( \property_exists( $object, 'Status' ) && \property_exists( $object->Status, 'Code' ) ) {
449
			$status = Statuses::transform( (string) $object->Status->Code->Code );
450
451
			if ( PaymentStatus::FAILURE === $status ) {
452
				throw new \Exception(
453
					\sprintf(
454
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
455
						__( 'Unable to create payment at gateway: %1$s%2$s', 'pronamic_ideal' ),
456
						$object->Status->Code->Description,
457
						\property_exists( $object->Status, 'SubCode' ) ? ' – ' . $object->Status->SubCode->Description : ''
458
					)
459
				);
460
			}
461
		}
462
	}
463
464
	/**
465
	 * JSON API Request.
466
	 *
467
	 * @param string      $method   HTTP request method.
468
	 * @param string      $endpoint JSON API endpoint.
469
	 * @param object|null $data     Data.
470
	 */
471
	public function request( $method, $endpoint, $data = null ) {
472
		$host = 'checkout.buckaroo.nl';
473
474
		if ( self::MODE_TEST === $this->config->mode ) {
475
			$host = 'testcheckout.buckaroo.nl';
476
		}
477
478
		/**
479
		 * Authentication.
480
		 *
481
		 * 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.
482
		 *
483
		 * @link https://dev.buckaroo.nl/Apis/Description/json
484
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Authentication
485
		 */
486
		$website_key         = $this->config->website_key;
487
		$request_http_method = $method;
488
		$request_uri         = $host . '/json/' . $endpoint;
489
		$request_timestamp   = \strval( \time() );
490
		$nonce               = \wp_generate_password( 32 );
491
		$request_content     = null === $data ? '' : \wp_json_encode( $data );
492
493
		$values = \implode(
494
			'',
495
			array(
496
				$website_key,
497
				$request_http_method,
498
				\strtolower( \rawurlencode( $request_uri ) ),
499
				$request_timestamp,
500
				$nonce,
501
				\
502
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
503
				null === $data ? '' : \base64_encode( \md5( $request_content, true ) ),
0 ignored issues
show
Bug introduced by
It seems like $request_content can also be of type false; however, parameter $string of md5() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

503
				null === $data ? '' : \base64_encode( \md5( /** @scrutinizer ignore-type */ $request_content, true ) ),
Loading history...
504
			)
505
		);
506
507
		$hash = \hash_hmac( 'sha256', $values, $this->config->secret_key, true );
508
509
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
510
		$hmac = \base64_encode( $hash );
511
512
		$authorization = \sprintf(
513
			'hmac %s:%s:%s:%s',
514
			$this->config->website_key,
515
			$hmac,
516
			$nonce,
517
			$request_timestamp
518
		);
519
520
		$response = \Pronamic\WordPress\Http\Facades\Http::request(
521
			'https://' . $request_uri,
522
			array(
523
				'method'  => $request_http_method,
524
				'headers' => array(
525
					'Authorization' => $authorization,
526
					'Content-Type'  => 'application/json',
527
				),
528
				'body'    => $request_content,
529
			)
530
		);
531
532
		$object = $response->json();
533
534
		/**
535
		 * OK.
536
		 */
537
		return $object;
538
	}
539
540
	/**
541
	 * Update status of the specified payment
542
	 *
543
	 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-Status-transactionKey
544
	 * @param Payment $payment Payment.
545
	 */
546
	public function update_status( Payment $payment ) {
547
		$transaction_key = $payment->get_transaction_id();
548
549
		if ( empty( $transaction_key ) ) {
550
			return;
551
		}
552
553
		$result = $this->request( 'GET', 'Transaction/Status/' . $transaction_key );
554
555
		$payment->set_status( Statuses::transform( \strval( $result->Status->Code->Code ) ) );
556
557
		/**
558
		 * Consumer bank details.
559
		 */
560
		$consumer_bank_details = $payment->get_consumer_bank_details();
561
562
		if ( null === $consumer_bank_details ) {
563
			$consumer_bank_details = new BankAccountDetails();
564
565
			$payment->set_consumer_bank_details( $consumer_bank_details );
566
		}
567
568
		/**
569
		 * Services.
570
		 */
571
		foreach ( $result->Services as $service ) {
572
			foreach ( $service->Parameters as $parameter ) {
573
				if ( 'consumerName' === $parameter->Name ) {
574
					$consumer_bank_details->set_name( $parameter->Value );
575
				}
576
577
				if ( 'consumerIBAN' === $parameter->Name ) {
578
					$consumer_bank_details->set_iban( $parameter->Value );
579
				}
580
581
				if ( 'consumerBIC' === $parameter->Name ) {
582
					$consumer_bank_details->set_bic( $parameter->Value );
583
				}
584
			}
585
		}
586
587
		/**
588
		 * Refunds.
589
		 *
590
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-RefundInfo-transactionKey
591
		 */
592
		$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_key );
593
594
		if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
595
			$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
596
597
			$payment->set_refunded_amount( $refunded_amount );
598
		}
599
	}
600
601
	/**
602
	 * Create refund.
603
	 *
604
	 * @param string $transaction_id Transaction ID.
605
	 * @param Money  $amount         Amount to refund.
606
	 * @param string $description    Refund reason.
607
	 * @return string
608
	 */
609
	public function create_refund( $transaction_id, Money $amount, $description = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $description is not used and could be removed. ( Ignorable by Annotation )

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

609
	public function create_refund( $transaction_id, Money $amount, /** @scrutinizer ignore-unused */ $description = null ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
610
		$original_transaction = $this->request( 'GET', 'Transaction/Status/' . $transaction_id );
611
612
		if ( ! \is_object( $original_transaction ) ) {
613
			throw new \Exception(
614
				sprintf(
615
					/* translators: %s: transaction key */
616
					__( 'Unable to create refund for transaction with transaction key: %s', 'pronamic_ideal' ),
617
					$transaction_id
618
				)
619
			);
620
		}
621
622
		$service_name = Util::get_transaction_service( $original_transaction );
623
624
		if ( null === $service_name ) {
625
			throw new \Exception(
626
				sprintf(
627
					/* translators: %s: transaction key */
628
					__( 'Unable to create refund for transaction without service name. Transaction key: %s', 'pronamic_ideal' ),
629
					$transaction_id
630
				)
631
			);
632
		}
633
634
		// Invoice.
635
		$payment = \get_pronamic_payment_by_transaction_id( $transaction_id );
636
637
		$invoice = null;
638
639
		if ( null !== $payment ) {
640
			$invoice = Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment );
641
		}
642
643
		// Refund request.
644
		$data = (object) array(
645
			'Channel'                => 'Web',
646
			'Currency'               => $amount->get_currency()->get_alphabetic_code(),
647
			/**
648
			 * The credit amount for the request. This is in decimal format,
649
			 * with a point as the decimal separator. For example, if the
650
			 * currency is specified as EUR, sending “1” will mean that 1 euro
651
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
652
			 * Please note, a transaction must have either a debit amount or a
653
			 * credit amount and it cannot have both.
654
			 * 
655
			 * @link https://dev.buckaroo.nl/Apis
656
			 */
657
			'AmountCredit'           => $amount->format( null, '.', '' ),
0 ignored issues
show
Unused Code introduced by
The call to Pronamic\WordPress\Money\Money::format() has too many arguments starting with '.'. ( Ignorable by Annotation )

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

657
			'AmountCredit'           => $amount->/** @scrutinizer ignore-call */ format( null, '.', '' ),

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
658
			'Invoice'                => $invoice,
659
			'OriginalTransactionKey' => $transaction_id,
660
			'Services'               => array(
661
				'ServiceList' => array(
662
					array(
663
						'Name'   => $service_name,
664
						'Action' => 'Refund',
665
					),
666
				),
667
			),
668
		);
669
670
		$refund = $this->request( 'POST', 'Transaction', $data );
671
672
		// Check refund object.
673
		if ( ! \is_object( $refund ) ) {
674
			return null;
675
		}
676
677
		// Check refund status.
678
		if ( \property_exists( $refund, 'Status' ) && \property_exists( $refund->Status, 'Code' ) ) {
679
			$status = Statuses::transform( (string) $refund->Status->Code->Code );
680
681
			if ( PaymentStatus::SUCCESS !== $status ) {
682
				throw new \Exception(
683
					\sprintf(
684
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
685
						__( 'Unable to create refund at %1$s gateway: %2$s%3$s', 'pronamic_ideal' ),
686
						__( 'Buckaroo', 'pronamic_ideal' ),
687
						$refund->Status->Code->Description,
688
						\property_exists( $refund->Status, 'SubCode' ) ? ' – ' . $refund->Status->SubCode->Description : ''
689
					)
690
				);
691
			}
692
		}
693
694
		// Update payment refunded amount.
695
		if ( null !== $payment ) {
696
			$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_id );
697
698
			if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
699
				$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
700
701
				$payment->set_refunded_amount( $refunded_amount );
702
			}
703
		}
704
705
		// Return.
706
		$refund_id = \property_exists( $refund, 'Key' ) ? $refund->Key : null;
707
708
		return $refund_id;
709
	}
710
}
711