Issues (12)

src/Gateway.php (2 issues)

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\Payments\Payment;
10
use Pronamic\WordPress\Pay\Payments\PaymentStatus;
11
use WP_Error;
12
13
/**
14
 * Title: Buckaroo gateway
15
 * Description:
16
 * Copyright: 2005-2022 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
	 * Constructs and initializes an Buckaroo gateway
26
	 *
27
	 * @param Config $config Config.
28
	 */
29
	public function __construct( Config $config ) {
30
		parent::__construct( $config );
31
32
		$this->set_method( self::METHOD_HTTP_REDIRECT );
33
34
		// Supported features.
35
		$this->supports = array(
36
			'payment_status_request',
37
			'refunds',
38
			'webhook',
39
			'webhook_log',
40
			'webhook_no_config',
41
		);
42
	}
43
44
	/**
45
	 * Get issuers.
46
	 *
47
	 * @since 1.2.4
48
	 * @see Core_Gateway::get_issuers()
49
	 */
50
	public function get_issuers() {
51
		$groups = array();
52
53
		// Check non-empty keys in configuration.
54
		if ( empty( $this->config->website_key ) || empty( $this->config->secret_key ) ) {
55
			return $groups;
56
		}
57
58
		// Get iDEAL issuers.
59
		try {
60
			$object = $this->request( 'GET', 'Transaction/Specification/ideal?serviceVersion=2' );
61
		} catch ( \Exception $e ) {
62
			$this->set_error( new WP_Error( 'buckaroo_error', $e->getMessage() ) );
63
64
			return $groups;
65
		}
66
67
		if ( \property_exists( $object, 'Actions' ) ) {
68
			foreach ( $object->Actions as $action ) {
69
				// Check action name.
70
				if ( 'Pay' !== $action->Name ) {
71
					continue;
72
				}
73
74
				foreach ( $action->RequestParameters as $request_parameter ) {
75
					// Check request parameter name.
76
					if ( 'issuer' !== $request_parameter->Name ) {
77
						continue;
78
					}
79
80
					foreach ( $request_parameter->ListItemDescriptions as $item ) {
81
						// Make sure to add group.
82
						if ( ! array_key_exists( $item->GroupName, $groups ) ) {
83
							$groups[ $item->GroupName ] = array(
84
								'name'    => $item->GroupName,
85
								'options' => array(),
86
							);
87
						}
88
89
						// Add issuer to group.
90
						$groups[ $item->GroupName ]['options'][ $item->Value ] = $item->Description;
91
					}
92
				}
93
			}
94
		}
95
96
		return $groups;
97
	}
98
99
	/**
100
	 * Get supported payment methods
101
	 *
102
	 * @see Core_Gateway::get_supported_payment_methods()
103
	 */
104
	public function get_supported_payment_methods() {
105
		return array(
106
			Core_PaymentMethods::AMERICAN_EXPRESS,
107
			Core_PaymentMethods::BANK_TRANSFER,
108
			Core_PaymentMethods::BANCONTACT,
109
			Core_PaymentMethods::CREDIT_CARD,
110
			Core_PaymentMethods::GIROPAY,
111
			Core_PaymentMethods::IDEAL,
112
			Core_PaymentMethods::MAESTRO,
113
			Core_PaymentMethods::MASTERCARD,
114
			Core_PaymentMethods::PAYPAL,
115
			Core_PaymentMethods::SOFORT,
116
			Core_PaymentMethods::V_PAY,
117
			Core_PaymentMethods::VISA,
118
		);
119
	}
120
121
	/**
122
	 * Start
123
	 *
124
	 * @param Payment $payment Payment.
125
	 *
126
	 * @see Core_Gateway::start()
127
	 */
128
	public function start( Payment $payment ) {
129
		/**
130
		 * Currency.
131
		 */
132
		$currency_code = $payment->get_total_amount()->get_currency()->get_alphabetic_code();
133
134
		/**
135
		 * Push URL.
136
		 */
137
		$push_url = \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' );
138
139
		/**
140
		 * Filters the Buckaroo push URL.
141
		 *
142
		 * If you want to debug the Buckaroo report URL you can use this filter
143
		 * to override the push URL. You could for example use a service like
144
		 * https://webhook.site/ to inspect the push requests from Buckaroo.
145
		 *
146
		 * @param string $push_url Buckaroo push URL.
147
		 */
148
		$push_url = \apply_filters( 'pronamic_pay_buckaroo_push_url', $push_url );
149
150
		/**
151
		 * JSON Transaction.
152
		 *
153
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
154
		 */
155
		$data = (object) array(
156
			'Currency'                  => $currency_code,
157
			/**
158
			 * The debit amount for the request. This is in decimal format,
159
			 * with a point as the decimal separator. For example, if the
160
			 * currency is specified as EUR, sending “1” will mean that 1 euro
161
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
162
			 * Please note, a transaction must have either a debit amount or a
163
			 * credit amount and it cannot have both.
164
			 *
165
			 * @link https://dev.buckaroo.nl/Apis
166
			 */
167
			'AmountDebit'               => $payment->get_total_amount()->number_format( null, '.', '' ),
168
			'Description'               => $payment->get_description(),
169
			'Invoice'                   => Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment ),
170
			'ReturnURL'                 => $payment->get_return_url(),
171
			'ReturnURLCancel'           => \add_query_arg(
172
				'buckaroo_return_url_cancel',
173
				true,
174
				$payment->get_return_url()
175
			),
176
			'ReturnURLError'            => \add_query_arg(
177
				'buckaroo_return_url_error',
178
				true,
179
				$payment->get_return_url()
180
			),
181
			'ReturnURLReject'           => \add_query_arg(
182
				'buckaroo_return_url_reject',
183
				true,
184
				$payment->get_return_url()
185
			),
186
			/**
187
			 * Push URL.
188
			 *
189
			 * When provided, this push URL overrides all the push URLs as configured in the payment plaza under websites for the associated website key
190
			 *
191
			 * @link https://dev.buckaroo.nl/Apis
192
			 */
193
			'PushURL'                   => $push_url,
194
			/**
195
			 * Push URL Failure.
196
			 *
197
			 * 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.
198
			 *
199
			 * @link https://dev.buckaroo.nl/Apis
200
			 */
201
			'PushURLFailure'            => $push_url,
202
			/**
203
			 * Services.
204
			 *
205
			 * Specifies which service (can be a payment method and/or additional service) is being called upon in the request.
206
			 *
207
			 * @link https://dev.buckaroo.nl/Apis
208
			 */
209
			'Services'                  => (object) array(
210
				'ServiceList' => array(),
211
			),
212
			/**
213
			 * Continue On Incomplete.
214
			 *
215
			 * Specifies if a redirecturl to a payment form will be returned to
216
			 * which a customer should be sent if no paymentmethod is selected
217
			 * or if any required parameter which the customer may provide is
218
			 * missing or incorrect. Possible Values:
219
			 *
220
			 * · No: This is the default. The request will fail if not all the
221
			 * needed information is provided.
222
			 *
223
			 * · RedirectToHTML: A redirect to the HTML gateway is provided if
224
			 * a recoverable problems are detected in the request. The customer
225
			 * can then provide the needed information there.
226
			 *
227
			 * @link https://dev.buckaroo.nl/Apis
228
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
229
			 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ContinueOnIncomplete
230
			 */
231
			'ContinueOnIncomplete'      => 'RedirectToHTML',
232
			/**
233
			 * Services Excluded For Client.
234
			 *
235
			 * If no primary service is provided and ContinueOnIncomplete is
236
			 * set, this list of comma separated servicescodes can be used to
237
			 * limit the number of services from which the customer may choose
238
			 * once he is redirected to the payment form. Services which are
239
			 * entered in this field are not selectable.
240
			 * This field is optional.
241
			 *
242
			 * @link https://dev.buckaroo.nl/Apis
243
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
244
			 */
245
			'ServicesExcludedForClient' => $this->config->get_excluded_services(),
246
			/**
247
			 * Custom parameters.
248
			 *
249
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
250
			 */
251
			'CustomParameters'          => array(
252
				(object) array(
253
					'Name'  => 'pronamic_payment_id',
254
					'Value' => $payment->get_id(),
255
				),
256
			),
257
		);
258
259
		/**
260
		 * Client IP.
261
		 *
262
		 * In this field the IP address of the customer (or employee) for which
263
		 * the action is being performed can be passed. Please note, If this
264
		 * field is not sent to our gateway, your server IP address will be
265
		 * used as the clientIP. This may result in unwanted behaviour for
266
		 * anti-fraud checks. Also, certain payment methods perform checks on
267
		 * the IP address, if an IP address is overused, the request could be
268
		 * blocked. This field is sent in the following format, where
269
		 * type 0 = IPv4 and type 1 = IPv6:
270
		 * "ClientIP": { "Type": 0, "Address": "0.0.0.0" },
271
		 *
272
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
273
		 * @link https://stackoverflow.com/questions/1448871/how-to-know-which-version-of-the-internet-protocol-ip-a-client-is-using-when-c/1448901
274
		 */
275
		$customer = $payment->get_customer();
276
277
		if ( null !== $customer ) {
278
			$ip_address = $customer->get_ip_address();
279
280
			if ( null !== $ip_address ) {
281
				$data->ClientIP = (object) array(
282
					'Type'    => false === \strpos( $ip_address, ':' ) ? 0 : 1,
283
					'Address' => $ip_address,
284
				);
285
			}
286
		}
287
288
		/**
289
		 * Payment method.
290
		 *
291
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
292
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServicesRequest
293
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServiceRequest
294
		 */
295
		switch ( $payment->get_payment_method() ) {
296
			/**
297
			 * Payment method American Express.
298
			 * 
299
			 * @link 
300
			 */
301
			case Core_PaymentMethods::AMERICAN_EXPRESS:
302
				$data->Services->ServiceList[] = (object) array(
303
					'Action' => 'Pay',
304
					'Name'   => PaymentMethods::AMERICAN_EXPRESS,
305
				);
306
307
				break;
308
			/**
309
			 * Payment method creditcard.
310
			 *
311
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#pay
312
			 */
313
			case Core_PaymentMethods::CREDIT_CARD:
314
				$data->Services->ServiceList[] = (object) array(
315
					'Action' => 'Pay',
316
					'Name'   => PaymentMethods::AMERICAN_EXPRESS,
317
				);
318
319
				$data->Services->ServiceList[] = (object) array(
320
					'Action' => 'Pay',
321
					'Name'   => PaymentMethods::MAESTRO,
322
				);
323
324
				$data->Services->ServiceList[] = (object) array(
325
					'Action' => 'Pay',
326
					'Name'   => PaymentMethods::MASTERCARD,
327
				);
328
329
				$data->Services->ServiceList[] = (object) array(
330
					'Action' => 'Pay',
331
					'Name'   => PaymentMethods::VISA,
332
				);
333
334
				break;
335
			/**
336
			 * Payment method iDEAL.
337
			 *
338
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal#pay
339
			 */
340
			case Core_PaymentMethods::IDEAL:
341
				$data->Services->ServiceList[] = (object) array(
342
					'Action'     => 'Pay',
343
					'Name'       => 'ideal',
344
					'Parameters' => array(
345
						array(
346
							'Name'  => 'issuer',
347
							'Value' => $payment->get_meta( 'issuer' ),
348
						),
349
					),
350
				);
351
352
				break;
353
			/**
354
			 * Payment method transfer.
355
			 *
356
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/transfer#pay
357
			 */
358
			case Core_PaymentMethods::BANK_TRANSFER:
359
				$data->Services->ServiceList[] = (object) array(
360
					'Action' => 'Pay',
361
					'Name'   => 'transfer',
362
				);
363
364
				break;
365
			/**
366
			 * Payment method Bancontact.
367
			 *
368
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/bancontact#pay
369
			 */
370
			case Core_PaymentMethods::BANCONTACT:
371
			case Core_PaymentMethods::MISTER_CASH:
372
				$data->Services->ServiceList[] = (object) array(
373
					'Action' => 'Pay',
374
					'Name'   => 'bancontactmrcash',
375
				);
376
377
				break;
378
			/**
379
			 * Payment method Maestro.
380
			 * 
381
			 * @link 
382
			 */
383
			case Core_PaymentMethods::MAESTRO:
384
				$data->Services->ServiceList[] = (object) array(
385
					'Action' => 'Pay',
386
					'Name'   => PaymentMethods::MAESTRO,
387
				);
388
389
				break;
390
			/**
391
			 * Payment method Mastercard.
392
			 * 
393
			 * @link 
394
			 */
395
			case Core_PaymentMethods::MASTERCARD:
396
				$data->Services->ServiceList[] = (object) array(
397
					'Action' => 'Pay',
398
					'Name'   => PaymentMethods::MASTERCARD,
399
				);
400
401
				break;
402
			/**
403
			 * Payment method Giropay.
404
			 *
405
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/giropay#pay
406
			 */
407
			case Core_PaymentMethods::GIROPAY:
408
				$data->Services->ServiceList[] = (object) array(
409
					'Action' => 'Pay',
410
					'Name'   => 'giropay',
411
				);
412
413
				break;
414
			/**
415
			 * Payment method PayPal.
416
			 *
417
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/paypal#pay
418
			 */
419
			case Core_PaymentMethods::PAYPAL:
420
				$data->Services->ServiceList[] = (object) array(
421
					'Action' => 'Pay',
422
					'Name'   => 'paypal',
423
				);
424
425
				break;
426
			/**
427
			 * Payment method Sofort.
428
			 *
429
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort#pay
430
			 */
431
			case Core_PaymentMethods::SOFORT:
432
				$data->Services->ServiceList[] = (object) array(
433
					'Action' => 'Pay',
434
					'Name'   => 'sofortueberweisung',
435
				);
436
437
				break;
438
			/**
439
			 * Payment method V PAY.
440
			 * 
441
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#top
442
			 */
443
			case Core_PaymentMethods::V_PAY:
444
				$data->Services->ServiceList[] = (object) array(
445
					'Action' => 'Pay',
446
					'Name'   => PaymentMethods::V_PAY,
447
				);
448
449
				break;
450
			/**
451
			 * Payment method Visa.
452
			 * 
453
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#top
454
			 */
455
			case Core_PaymentMethods::VISA:
456
				$data->Services->ServiceList[] = (object) array(
457
					'Action' => 'Pay',
458
					'Name'   => PaymentMethods::VISA,
459
				);
460
461
				break;
462
		}
463
464
		/**
465
		 * Request.
466
		 */
467
		$object = $this->request( 'POST', 'Transaction', $data );
468
469
		/**
470
		 * Buckaroo keys.
471
		 *
472
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionResponse
473
		 */
474
		if ( \property_exists( $object, 'Key' ) ) {
475
			$payment->set_transaction_id( $object->Key );
476
		}
477
478
		if ( \property_exists( $object, 'PaymentKey' ) ) {
479
			$payment->set_meta( 'buckaroo_transaction_payment_key', $object->PaymentKey );
480
		}
481
482
		/**
483
		 * Request Errors.
484
		 *
485
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
486
		 */
487
		if ( \property_exists( $object, 'RequestErrors' ) && null !== $object->RequestErrors ) {
488
			$exception = null;
489
490
			foreach ( $object->RequestErrors as $errors ) {
491
				foreach ( $errors as $error ) {
492
					// Add exception.
493
					$exception = new \Exception( $error->ErrorMessage, 0, $exception );
494
				}
495
			}
496
497
			if ( null !== $exception ) {
498
				throw $exception;
499
			}
500
		}
501
502
		/**
503
		 * Required Action.
504
		 */
505
		if ( null !== $object->RequiredAction ) {
506
			if ( 'Redirect' !== $object->RequiredAction->Name ) {
507
				throw new \Exception(
508
					\sprintf(
509
						'Unsupported Buckaroo action: %s',
510
						$object->RequiredAction->Name
511
					)
512
				);
513
			}
514
515
			// Set action URL.
516
			if ( \property_exists( $object->RequiredAction, 'RedirectURL' ) ) {
517
				$payment->set_action_url( $object->RequiredAction->RedirectURL );
518
			}
519
		}
520
521
		// Failure.
522
		if ( \property_exists( $object, 'Status' ) && \property_exists( $object->Status, 'Code' ) ) {
523
			$status = Statuses::transform( (string) $object->Status->Code->Code );
524
525
			if ( PaymentStatus::FAILURE === $status ) {
526
				throw new \Exception(
527
					\sprintf(
528
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
529
						__( 'Unable to create payment at gateway: %1$s%2$s', 'pronamic_ideal' ),
530
						$object->Status->Code->Description,
531
						\property_exists( $object->Status, 'SubCode' ) ? ' – ' . $object->Status->SubCode->Description : ''
532
					)
533
				);
534
			}
535
		}
536
	}
537
538
	/**
539
	 * JSON API Request.
540
	 *
541
	 * @param string      $method   HTTP request method.
542
	 * @param string      $endpoint JSON API endpoint.
543
	 * @param object|null $data     Data.
544
	 * @return object
545
	 */
546
	public function request( $method, $endpoint, $data = null ) {
547
		$host = 'checkout.buckaroo.nl';
548
549
		if ( self::MODE_TEST === $this->config->mode ) {
550
			$host = 'testcheckout.buckaroo.nl';
551
		}
552
553
		/**
554
		 * Authentication.
555
		 *
556
		 * 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.
557
		 *
558
		 * @link https://dev.buckaroo.nl/Apis/Description/json
559
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Authentication
560
		 */
561
		$website_key         = $this->config->website_key;
562
		$request_http_method = $method;
563
		$request_uri         = $host . '/json/' . $endpoint;
564
		$request_timestamp   = \strval( \time() );
565
		$nonce               = \wp_generate_password( 32 );
566
		$request_content     = null === $data ? '' : \wp_json_encode( $data );
567
568
		$values = \implode(
569
			'',
570
			array(
571
				$website_key,
572
				$request_http_method,
573
				\strtolower( \rawurlencode( $request_uri ) ),
574
				$request_timestamp,
575
				$nonce,
576
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
577
				null === $data ? '' : \base64_encode( \md5( (string) $request_content, true ) ),
578
			)
579
		);
580
581
		$hash = \hash_hmac( 'sha256', $values, $this->config->secret_key, true );
582
583
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
584
		$hmac = \base64_encode( $hash );
585
586
		$authorization = \sprintf(
587
			'hmac %s:%s:%s:%s',
588
			$this->config->website_key,
589
			$hmac,
590
			$nonce,
591
			$request_timestamp
592
		);
593
594
		$response = \Pronamic\WordPress\Http\Facades\Http::request(
595
			'https://' . $request_uri,
596
			array(
597
				'method'  => $request_http_method,
598
				'headers' => array(
599
					'Authorization' => $authorization,
600
					'Content-Type'  => 'application/json',
601
				),
602
				'body'    => $request_content,
603
			)
604
		);
605
606
		try {
607
			$object = $response->json();
608
		} catch ( \Exception $e ) {
609
			// JSON error.
610
			$json_error = \json_last_error();
611
612
			// Check authorization error.
613
			if ( \JSON_ERROR_NONE !== $json_error && 400 === $response->status() ) {
614
				throw new \Exception( $response->body() );
615
			}
616
617
			// Re-throw original response exception.
618
			throw $e;
619
		}
620
621
		/**
622
		 * OK.
623
		 */
624
		return $object;
625
	}
626
627
	/**
628
	 * Update status of the specified payment
629
	 *
630
	 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-Status-transactionKey
631
	 * @param Payment $payment Payment.
632
	 */
633
	public function update_status( Payment $payment ) {
634
		$transaction_key = $payment->get_transaction_id();
635
636
		if ( empty( $transaction_key ) ) {
637
			return;
638
		}
639
640
		$result = $this->request( 'GET', 'Transaction/Status/' . $transaction_key );
641
642
		$payment->set_status( Statuses::transform( \strval( $result->Status->Code->Code ) ) );
643
644
		/**
645
		 * Consumer bank details.
646
		 */
647
		$consumer_bank_details = $payment->get_consumer_bank_details();
648
649
		if ( null === $consumer_bank_details ) {
650
			$consumer_bank_details = new BankAccountDetails();
651
652
			$payment->set_consumer_bank_details( $consumer_bank_details );
653
		}
654
655
		/**
656
		 * Services.
657
		 */
658
		foreach ( $result->Services as $service ) {
659
			foreach ( $service->Parameters as $parameter ) {
660
				if ( 'consumerName' === $parameter->Name ) {
661
					$consumer_bank_details->set_name( $parameter->Value );
662
				}
663
664
				if ( \in_array(
665
					$parameter->Name,
666
					array(
667
						/**
668
						 * Payment method iDEAL.
669
						 * 
670
						 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal
671
						 */
672
						'consumerIBAN',
673
						/**
674
						 * Payment method Sofort.
675
						 * 
676
						 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort
677
						 */
678
						'CustomerIBAN',
679
					),
680
					true
681
				) ) {
682
					$consumer_bank_details->set_iban( $parameter->Value );
683
				}
684
685
				if ( \in_array(
686
					$parameter->Name,
687
					array(
688
						/**
689
						 * Payment method iDEAL.
690
						 * 
691
						 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal
692
						 */
693
						'consumerName',
694
						/**
695
						 * Payment method Sofort.
696
						 * 
697
						 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort
698
						 */
699
						'CustomerBIC',
700
					),
701
					true
702
				) ) {
703
					$consumer_bank_details->set_bic( $parameter->Value );
704
				}
705
			}
706
		}
707
708
		/**
709
		 * Refunds.
710
		 *
711
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-RefundInfo-transactionKey
712
		 */
713
		$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_key );
714
715
		if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
716
			$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
717
718
			$payment->set_refunded_amount( $refunded_amount );
719
		}
720
	}
721
722
	/**
723
	 * Create refund.
724
	 *
725
	 * @param string $transaction_id Transaction ID.
726
	 * @param Money  $amount         Amount to refund.
727
	 * @param string $description    Refund reason.
728
	 * @return null|string
729
	 */
730
	public function create_refund( $transaction_id, Money $amount, $description = null ) {
0 ignored issues
show
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

730
	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...
731
		$original_transaction = $this->request( 'GET', 'Transaction/Status/' . $transaction_id );
732
733
		if ( ! \is_object( $original_transaction ) ) {
734
			throw new \Exception(
735
				sprintf(
736
					/* translators: %s: transaction key */
737
					__( 'Unable to create refund for transaction with transaction key: %s', 'pronamic_ideal' ),
738
					$transaction_id
739
				)
740
			);
741
		}
742
743
		$service_name = Util::get_transaction_service( $original_transaction );
744
745
		if ( null === $service_name ) {
746
			throw new \Exception(
747
				sprintf(
748
					/* translators: %s: transaction key */
749
					__( 'Unable to create refund for transaction without service name. Transaction key: %s', 'pronamic_ideal' ),
750
					$transaction_id
751
				)
752
			);
753
		}
754
755
		// Invoice.
756
		$payment = \get_pronamic_payment_by_transaction_id( $transaction_id );
757
758
		$invoice = null;
759
760
		if ( null !== $payment ) {
761
			$invoice = Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment );
762
		}
763
764
		// Refund request.
765
		$data = (object) array(
766
			'Channel'                => 'Web',
767
			'Currency'               => $amount->get_currency()->get_alphabetic_code(),
768
			/**
769
			 * The credit amount for the request. This is in decimal format,
770
			 * with a point as the decimal separator. For example, if the
771
			 * currency is specified as EUR, sending “1” will mean that 1 euro
772
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
773
			 * Please note, a transaction must have either a debit amount or a
774
			 * credit amount and it cannot have both.
775
			 *
776
			 * @link https://dev.buckaroo.nl/Apis
777
			 */
778
			'AmountCredit'           => $amount->format( null, '.', '' ),
0 ignored issues
show
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

778
			'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...
779
			'Invoice'                => $invoice,
780
			'OriginalTransactionKey' => $transaction_id,
781
			'Services'               => array(
782
				'ServiceList' => array(
783
					array(
784
						'Name'   => $service_name,
785
						'Action' => 'Refund',
786
					),
787
				),
788
			),
789
		);
790
791
		$refund = $this->request( 'POST', 'Transaction', $data );
792
793
		// Check refund object.
794
		if ( ! \is_object( $refund ) ) {
795
			return null;
796
		}
797
798
		// Check refund status.
799
		if ( \property_exists( $refund, 'Status' ) && \property_exists( $refund->Status, 'Code' ) ) {
800
			$status = Statuses::transform( (string) $refund->Status->Code->Code );
801
802
			if ( PaymentStatus::SUCCESS !== $status ) {
803
				throw new \Exception(
804
					\sprintf(
805
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
806
						__( 'Unable to create refund at %1$s gateway: %2$s%3$s', 'pronamic_ideal' ),
807
						__( 'Buckaroo', 'pronamic_ideal' ),
808
						$refund->Status->Code->Description,
809
						\property_exists( $refund->Status, 'SubCode' ) ? ' – ' . $refund->Status->SubCode->Description : ''
810
					)
811
				);
812
			}
813
		}
814
815
		// Update payment refunded amount.
816
		if ( null !== $payment ) {
817
			$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_id );
818
819
			if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
820
				$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
821
822
				$payment->set_refunded_amount( $refunded_amount );
823
			}
824
		}
825
826
		// Return.
827
		$refund_id = \property_exists( $refund, 'Key' ) ? $refund->Key : null;
828
829
		return $refund_id;
830
	}
831
}
832