Failed Conditions
Push — develop ( 9276f5...e8fded )
by Reüel
06:29
created

src/Gateway.php (1 issue)

Labels
Severity
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
	 * 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
		$object = $this->request( 'GET', 'Transaction/Specification/ideal?serviceVersion=2' );
54
55
		foreach ( $object->Actions as $action ) {
56
			if ( 'Pay' === $action->Name ) {
57
				foreach ( $action->RequestParameters as $request_parameter ) {
58
					if ( 'issuer' === $request_parameter->Name ) {
59
						foreach ( $request_parameter->ListItemDescriptions as $item ) {
60
							if ( ! array_key_exists( $item->GroupName, $groups ) ) {
61
								$groups[ $item->GroupName ] = array(
62
									'name'    => $item->GroupName,
63
									'options' => array(),
64
								);
65
							}
66
67
							$groups[ $item->GroupName ]['options'][ $item->Value ] = $item->Description;
68
						}
69
					}
70
				}
71
			}
72
		}
73
74
		return $groups;
75
	}
76
77
	/**
78
	 * Get supported payment methods
79
	 *
80
	 * @see Core_Gateway::get_supported_payment_methods()
81
	 */
82
	public function get_supported_payment_methods() {
83
		return array(
84
			Core_PaymentMethods::BANK_TRANSFER,
85
			Core_PaymentMethods::BANCONTACT,
86
			Core_PaymentMethods::CREDIT_CARD,
87
			Core_PaymentMethods::GIROPAY,
88
			Core_PaymentMethods::IDEAL,
89
			Core_PaymentMethods::PAYPAL,
90
			Core_PaymentMethods::SOFORT,
91
		);
92
	}
93
94
	/**
95
	 * Start
96
	 *
97
	 * @param Payment $payment Payment.
98
	 *
99
	 * @see Core_Gateway::start()
100
	 */
101
	public function start( Payment $payment ) {
102
		/**
103
		 * Currency.
104
		 */
105
		$currency_code = $payment->get_total_amount()->get_currency()->get_alphabetic_code();
106
107
		if ( null === $currency_code ) {
108
			throw new \InvalidArgumentException( 'Can not start payment with empty currency code.' );
109
		}
110
111
		/**
112
		 * Push URL.
113
		 */
114
		$push_url = \rest_url( Integration::REST_ROUTE_NAMESPACE . '/push' );
115
116
		/**
117
		 * Filters the Buckaroo push URL.
118
		 *
119
		 * If you want to debug the Buckaroo report URL you can use this filter
120
		 * to override the push URL. You could for example use a service like
121
		 * https://webhook.site/ to inspect the push requests from Buckaroo.
122
		 *
123
		 * @param string $push_url Buckaroo push URL.
124
		 */
125
		$push_url = \apply_filters( 'pronamic_pay_buckaroo_push_url', $push_url );
126
127
		/**
128
		 * JSON Transaction.
129
		 *
130
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
131
		 */
132
		$data = (object) array(
133
			'Currency'                  => $currency_code,
134
			/**
135
			 * The debit amount for the request. This is in decimal format,
136
			 * with a point as the decimal separator. For example, if the
137
			 * currency is specified as EUR, sending “1” will mean that 1 euro
138
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
139
			 * Please note, a transaction must have either a debit amount or a
140
			 * credit amount and it cannot have both.
141
			 *
142
			 * @link https://dev.buckaroo.nl/Apis
143
			 */
144
			'AmountDebit'               => $payment->get_total_amount()->number_format( null, '.', '' ),
0 ignored issues
show
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

144
			'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...
145
			'Description'               => $payment->get_description(),
146
			'Invoice'                   => Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment ),
147
			'ReturnURL'                 => $payment->get_return_url(),
148
			'ReturnURLCancel'           => \add_query_arg(
149
				'buckaroo_return_url_cancel',
150
				true,
151
				$payment->get_return_url()
152
			),
153
			'ReturnURLError'            => \add_query_arg(
154
				'buckaroo_return_url_error',
155
				true,
156
				$payment->get_return_url()
157
			),
158
			'ReturnURLReject'           => \add_query_arg(
159
				'buckaroo_return_url_reject',
160
				true,
161
				$payment->get_return_url()
162
			),
163
			/**
164
			 * Push URL.
165
			 *
166
			 * When provided, this push URL overrides all the push URLs as configured in the payment plaza under websites for the associated website key
167
			 *
168
			 * @link https://dev.buckaroo.nl/Apis
169
			 */
170
			'PushURL'                   => $push_url,
171
			/**
172
			 * Push URL Failure.
173
			 *
174
			 * 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.
175
			 *
176
			 * @link https://dev.buckaroo.nl/Apis
177
			 */
178
			'PushURLFailure'            => $push_url,
179
			/**
180
			 * Services.
181
			 *
182
			 * Specifies which service (can be a payment method and/or additional service) is being called upon in the request.
183
			 *
184
			 * @link https://dev.buckaroo.nl/Apis
185
			 */
186
			'Services'                  => (object) array(
187
				'ServiceList' => array(),
188
			),
189
			/**
190
			 * Continue On Incomplete.
191
			 *
192
			 * Specifies if a redirecturl to a payment form will be returned to
193
			 * which a customer should be sent if no paymentmethod is selected
194
			 * or if any required parameter which the customer may provide is
195
			 * missing or incorrect. Possible Values:
196
			 *
197
			 * · No: This is the default. The request will fail if not all the
198
			 * needed information is provided.
199
			 *
200
			 * · RedirectToHTML: A redirect to the HTML gateway is provided if
201
			 * a recoverable problems are detected in the request. The customer
202
			 * can then provide the needed information there.
203
			 *
204
			 * @link https://dev.buckaroo.nl/Apis
205
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
206
			 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ContinueOnIncomplete
207
			 */
208
			'ContinueOnIncomplete'      => 'RedirectToHTML',
209
			/**
210
			 * Services Excluded For Client.
211
			 *
212
			 * If no primary service is provided and ContinueOnIncomplete is
213
			 * set, this list of comma separated servicescodes can be used to
214
			 * limit the number of services from which the customer may choose
215
			 * once he is redirected to the payment form. Services which are
216
			 * entered in this field are not selectable.
217
			 * This field is optional.
218
			 *
219
			 * @link https://dev.buckaroo.nl/Apis
220
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
221
			 */
222
			'ServicesExcludedForClient' => $this->config->get_excluded_services(),
223
			/**
224
			 * Custom parameters.
225
			 *
226
			 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
227
			 */
228
			'CustomParameters'          => array(
229
				(object) array(
230
					'Name'  => 'pronamic_payment_id',
231
					'Value' => $payment->get_id(),
232
				),
233
			),
234
		);
235
236
		/**
237
		 * Client IP.
238
		 *
239
		 * In this field the IP address of the customer (or employee) for which
240
		 * the action is being performed can be passed. Please note, If this
241
		 * field is not sent to our gateway, your server IP address will be
242
		 * used as the clientIP. This may result in unwanted behaviour for
243
		 * anti-fraud checks. Also, certain payment methods perform checks on
244
		 * the IP address, if an IP address is overused, the request could be
245
		 * blocked. This field is sent in the following format, where
246
		 * type 0 = IPv4 and type 1 = IPv6:
247
		 * "ClientIP": { "Type": 0, "Address": "0.0.0.0" },
248
		 *
249
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
250
		 * @link https://stackoverflow.com/questions/1448871/how-to-know-which-version-of-the-internet-protocol-ip-a-client-is-using-when-c/1448901
251
		 */
252
		$customer = $payment->get_customer();
253
254
		if ( null !== $customer ) {
255
			$ip_address = $customer->get_ip_address();
256
257
			if ( null !== $ip_address ) {
258
				$data->ClientIP = (object) array(
259
					'Type'    => false === \strpos( $ip_address, ':' ) ? 0 : 1,
260
					'Address' => $ip_address,
261
				);
262
			}
263
		}
264
265
		/**
266
		 * Payment method.
267
		 *
268
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
269
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServicesRequest
270
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=ServiceRequest
271
		 */
272
		$payment_method = $payment->get_method();
273
274
		switch ( $payment_method ) {
275
			/**
276
			 * Payment method creditcard.
277
			 *
278
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/creditcards#pay
279
			 */
280
			case Core_PaymentMethods::CREDIT_CARD:
281
				$data->Services->ServiceList[] = (object) array(
282
					'Action' => 'Pay',
283
					'Name'   => PaymentMethods::AMERICAN_EXPRESS,
284
				);
285
286
				$data->Services->ServiceList[] = (object) array(
287
					'Action' => 'Pay',
288
					'Name'   => PaymentMethods::MAESTRO,
289
				);
290
291
				$data->Services->ServiceList[] = (object) array(
292
					'Action' => 'Pay',
293
					'Name'   => PaymentMethods::MASTERCARD,
294
				);
295
296
				$data->Services->ServiceList[] = (object) array(
297
					'Action' => 'Pay',
298
					'Name'   => PaymentMethods::VISA,
299
				);
300
301
				break;
302
			/**
303
			 * Payment method iDEAL.
304
			 *
305
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/ideal#pay
306
			 */
307
			case Core_PaymentMethods::IDEAL:
308
				$data->Services->ServiceList[] = (object) array(
309
					'Action'     => 'Pay',
310
					'Name'       => 'ideal',
311
					'Parameters' => array(
312
						array(
313
							'Name'  => 'issuer',
314
							'Value' => $payment->get_issuer(),
315
						),
316
					),
317
				);
318
319
				break;
320
			/**
321
			 * Payment method transfer.
322
			 *
323
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/transfer#pay
324
			 */
325
			case Core_PaymentMethods::BANK_TRANSFER:
326
				$data->Services->ServiceList[] = (object) array(
327
					'Action' => 'Pay',
328
					'Name'   => 'transfer',
329
				);
330
331
				break;
332
			/**
333
			 * Payment method Bancontact.
334
			 *
335
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/bancontact#pay
336
			 */
337
			case Core_PaymentMethods::BANCONTACT:
338
			case Core_PaymentMethods::MISTER_CASH:
339
				$data->Services->ServiceList[] = (object) array(
340
					'Action' => 'Pay',
341
					'Name'   => 'bancontactmrcash',
342
				);
343
344
				break;
345
			/**
346
			 * Payment method Giropay.
347
			 *
348
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/giropay#pay
349
			 */
350
			case Core_PaymentMethods::GIROPAY:
351
				$data->Services->ServiceList[] = (object) array(
352
					'Action' => 'Pay',
353
					'Name'   => 'giropay',
354
				);
355
356
				break;
357
			/**
358
			 * Payment method PayPal.
359
			 *
360
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/paypal#pay
361
			 */
362
			case Core_PaymentMethods::PAYPAL:
363
				$data->Services->ServiceList[] = (object) array(
364
					'Action' => 'Pay',
365
					'Name'   => 'paypal',
366
				);
367
368
				break;
369
			/**
370
			 * Payment method Sofort.
371
			 *
372
			 * @link https://dev.buckaroo.nl/PaymentMethods/Description/sofort#pay
373
			 */
374
			case Core_PaymentMethods::SOFORT:
375
				$data->Services->ServiceList[] = (object) array(
376
					'Action' => 'Pay',
377
					'Name'   => 'sofortueberweisung',
378
				);
379
380
				break;
381
		}
382
383
		/**
384
		 * Request.
385
		 */
386
		$object = $this->request( 'POST', 'Transaction', $data );
387
388
		/**
389
		 * Buckaroo keys.
390
		 *
391
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionResponse
392
		 */
393
		if ( \property_exists( $object, 'Key' ) ) {
394
			$payment->set_transaction_id( $object->Key );
395
		}
396
397
		if ( \property_exists( $object, 'PaymentKey' ) ) {
398
			$payment->set_meta( 'buckaroo_transaction_payment_key', $object->PaymentKey );
399
		}
400
401
		/**
402
		 * Request Errors.
403
		 *
404
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
405
		 */
406
		if ( \property_exists( $object, 'RequestErrors' ) && null !== $object->RequestErrors ) {
407
			$exception = null;
408
409
			foreach ( $object->RequestErrors as $errors ) {
410
				foreach ( $errors as $error ) {
411
					// Add exception.
412
					$exception = new \Exception( $error->ErrorMessage, 0, $exception );
413
				}
414
			}
415
416
			if ( null !== $exception ) {
417
				throw $exception;
418
			}
419
		}
420
421
		/**
422
		 * Required Action.
423
		 */
424
		if ( null !== $object->RequiredAction ) {
425
			if ( 'Redirect' !== $object->RequiredAction->Name ) {
426
				throw new \Exception(
427
					\sprintf(
428
						'Unsupported Buckaroo action: %s',
429
						$object->RequiredAction->Name
430
					)
431
				);
432
			}
433
434
			// Set action URL.
435
			if ( \property_exists( $object->RequiredAction, 'RedirectURL' ) ) {
436
				$payment->set_action_url( $object->RequiredAction->RedirectURL );
437
			}
438
		}
439
440
		// Failure.
441
		if ( \property_exists( $object, 'Status' ) && \property_exists( $object->Status, 'Code' ) ) {
442
			$status = Statuses::transform( (string) $object->Status->Code->Code );
443
444
			if ( PaymentStatus::FAILURE === $status ) {
445
				throw new \Exception(
446
					\sprintf(
447
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
448
						__( 'Unable to create payment at gateway: %1$s%2$s', 'pronamic_ideal' ),
449
						$object->Status->Code->Description,
450
						\property_exists( $object->Status, 'SubCode' ) ? ' – ' . $object->Status->SubCode->Description : ''
451
					)
452
				);
453
			}
454
		}
455
	}
456
457
	/**
458
	 * JSON API Request.
459
	 *
460
	 * @param string      $method   HTTP request method.
461
	 * @param string      $endpoint JSON API endpoint.
462
	 * @param object|null $data     Data.
463
	 */
464
	public function request( $method, $endpoint, $data = null ) {
465
		$host = 'checkout.buckaroo.nl';
466
467
		if ( self::MODE_TEST === $this->config->mode ) {
468
			$host = 'testcheckout.buckaroo.nl';
469
		}
470
471
		/**
472
		 * Authentication.
473
		 *
474
		 * 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.
475
		 *
476
		 * @link https://dev.buckaroo.nl/Apis/Description/json
477
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Authentication
478
		 */
479
		$website_key         = $this->config->website_key;
480
		$request_http_method = $method;
481
		$request_uri         = $host . '/json/' . $endpoint;
482
		$request_timestamp   = \strval( \time() );
483
		$nonce               = \wp_generate_password( 32 );
484
		$request_content     = null === $data ? '' : \wp_json_encode( $data );
485
486
		$values = \implode(
487
			'',
488
			array(
489
				$website_key,
490
				$request_http_method,
491
				\strtolower( \rawurlencode( $request_uri ) ),
492
				$request_timestamp,
493
				$nonce,
494
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
495
				null === $data ? '' : \base64_encode( \md5( (string) $request_content, true ) ),
496
			)
497
		);
498
499
		$hash = \hash_hmac( 'sha256', $values, $this->config->secret_key, true );
500
501
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
502
		$hmac = \base64_encode( $hash );
503
504
		$authorization = \sprintf(
505
			'hmac %s:%s:%s:%s',
506
			$this->config->website_key,
507
			$hmac,
508
			$nonce,
509
			$request_timestamp
510
		);
511
512
		$response = \Pronamic\WordPress\Http\Facades\Http::request(
513
			'https://' . $request_uri,
514
			array(
515
				'method'  => $request_http_method,
516
				'headers' => array(
517
					'Authorization' => $authorization,
518
					'Content-Type'  => 'application/json',
519
				),
520
				'body'    => $request_content,
521
			)
522
		);
523
524
		$object = $response->json();
525
526
		/**
527
		 * OK.
528
		 */
529
		return $object;
530
	}
531
532
	/**
533
	 * Update status of the specified payment
534
	 *
535
	 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-Status-transactionKey
536
	 * @param Payment $payment Payment.
537
	 */
538
	public function update_status( Payment $payment ) {
539
		$transaction_key = $payment->get_transaction_id();
540
541
		if ( empty( $transaction_key ) ) {
542
			return;
543
		}
544
545
		$result = $this->request( 'GET', 'Transaction/Status/' . $transaction_key );
546
547
		$payment->set_status( Statuses::transform( \strval( $result->Status->Code->Code ) ) );
548
549
		/**
550
		 * Consumer bank details.
551
		 */
552
		$consumer_bank_details = $payment->get_consumer_bank_details();
553
554
		if ( null === $consumer_bank_details ) {
555
			$consumer_bank_details = new BankAccountDetails();
556
557
			$payment->set_consumer_bank_details( $consumer_bank_details );
558
		}
559
560
		/**
561
		 * Services.
562
		 */
563
		foreach ( $result->Services as $service ) {
564
			foreach ( $service->Parameters as $parameter ) {
565
				if ( 'consumerName' === $parameter->Name ) {
566
					$consumer_bank_details->set_name( $parameter->Value );
567
				}
568
569
				if ( 'consumerIBAN' === $parameter->Name ) {
570
					$consumer_bank_details->set_iban( $parameter->Value );
571
				}
572
573
				if ( 'consumerBIC' === $parameter->Name ) {
574
					$consumer_bank_details->set_bic( $parameter->Value );
575
				}
576
			}
577
		}
578
579
		/**
580
		 * Refunds.
581
		 *
582
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-RefundInfo-transactionKey
583
		 */
584
		$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_key );
585
586
		if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
587
			$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
588
589
			$payment->set_refunded_amount( $refunded_amount );
590
		}
591
	}
592
593
	/**
594
	 * Create refund.
595
	 *
596
	 * @param string $transaction_id Transaction ID.
597
	 * @param Money  $amount         Amount to refund.
598
	 * @param string $description    Refund reason.
599
	 * @return null|string
600
	 */
601
	public function create_refund( $transaction_id, Money $amount, $description = null ) {
602
		$original_transaction = $this->request( 'GET', 'Transaction/Status/' . $transaction_id );
603
604
		if ( ! \is_object( $original_transaction ) ) {
605
			throw new \Exception(
606
				sprintf(
607
					/* translators: %s: transaction key */
608
					__( 'Unable to create refund for transaction with transaction key: %s', 'pronamic_ideal' ),
609
					$transaction_id
610
				)
611
			);
612
		}
613
614
		$service_name = Util::get_transaction_service( $original_transaction );
615
616
		if ( null === $service_name ) {
617
			throw new \Exception(
618
				sprintf(
619
					/* translators: %s: transaction key */
620
					__( 'Unable to create refund for transaction without service name. Transaction key: %s', 'pronamic_ideal' ),
621
					$transaction_id
622
				)
623
			);
624
		}
625
626
		// Invoice.
627
		$payment = \get_pronamic_payment_by_transaction_id( $transaction_id );
628
629
		$invoice = null;
630
631
		if ( null !== $payment ) {
632
			$invoice = Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment );
633
		}
634
635
		// Refund request.
636
		$data = (object) array(
637
			'Channel'                => 'Web',
638
			'Currency'               => $amount->get_currency()->get_alphabetic_code(),
639
			/**
640
			 * The credit amount for the request. This is in decimal format,
641
			 * with a point as the decimal separator. For example, if the
642
			 * currency is specified as EUR, sending “1” will mean that 1 euro
643
			 * will be paid. “1.00” is also 1 euro. “0.01” means 1 cent.
644
			 * Please note, a transaction must have either a debit amount or a
645
			 * credit amount and it cannot have both.
646
			 *
647
			 * @link https://dev.buckaroo.nl/Apis
648
			 */
649
			'AmountCredit'           => $amount->format( null, '.', '' ),
650
			'Invoice'                => $invoice,
651
			'OriginalTransactionKey' => $transaction_id,
652
			'Services'               => array(
653
				'ServiceList' => array(
654
					array(
655
						'Name'   => $service_name,
656
						'Action' => 'Refund',
657
					),
658
				),
659
			),
660
		);
661
662
		$refund = $this->request( 'POST', 'Transaction', $data );
663
664
		// Check refund object.
665
		if ( ! \is_object( $refund ) ) {
666
			return null;
667
		}
668
669
		// Check refund status.
670
		if ( \property_exists( $refund, 'Status' ) && \property_exists( $refund->Status, 'Code' ) ) {
671
			$status = Statuses::transform( (string) $refund->Status->Code->Code );
672
673
			if ( PaymentStatus::SUCCESS !== $status ) {
674
				throw new \Exception(
675
					\sprintf(
676
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
677
						__( 'Unable to create refund at %1$s gateway: %2$s%3$s', 'pronamic_ideal' ),
678
						__( 'Buckaroo', 'pronamic_ideal' ),
679
						$refund->Status->Code->Description,
680
						\property_exists( $refund->Status, 'SubCode' ) ? ' – ' . $refund->Status->SubCode->Description : ''
681
					)
682
				);
683
			}
684
		}
685
686
		// Update payment refunded amount.
687
		if ( null !== $payment ) {
688
			$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_id );
689
690
			if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
691
				$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
692
693
				$payment->set_refunded_amount( $refunded_amount );
694
			}
695
		}
696
697
		// Return.
698
		$refund_id = \property_exists( $refund, 'Key' ) ? $refund->Key : null;
699
700
		return $refund_id;
701
	}
702
}
703