Test Setup Failed
Push — master ( 81dce0...a438b2 )
by Reüel
07:10 queued 11s
created

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\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
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 Pronamic_WP_Pay_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 Pronamic_WP_Pay_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
			'AmountDebit'               => $payment->get_total_amount()->get_value(),
142
			'Description'               => $payment->get_description(),
143
			'Invoice'                   => Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment ),
144
			'ReturnURL'                 => $payment->get_return_url(),
145
			'ReturnURLCancel'           => \add_query_arg(
146
				'buckaroo_return_url_cancel',
147
				true,
148
				$payment->get_return_url()
149
			),
150
			'ReturnURLError'            => \add_query_arg(
151
				'buckaroo_return_url_error',
152
				true,
153
				$payment->get_return_url()
154
			),
155
			'ReturnURLReject'           => \add_query_arg(
156
				'buckaroo_return_url_reject',
157
				true,
158
				$payment->get_return_url()
159
			),
160
			/**
161
			 * Push URL.
162
			 *
163
			 * When provided, this push URL overrides all the push URLs as configured in the payment plaza under websites for the associated website key
164
			 *
165
			 * @link https://dev.buckaroo.nl/Apis
166
			 */
167
			'PushURL'                   => $push_url,
168
			/**
169
			 * Push URL Failure.
170
			 *
171
			 * 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.
172
			 *
173
			 * @link https://dev.buckaroo.nl/Apis
174
			 */
175
			'PushURLFailure'            => $push_url,
176
			/**
177
			 * Services.
178
			 *
179
			 * Specifies which service (can be a payment method and/or additional service) is being called upon in the request.
180
			 *
181
			 * @link https://dev.buckaroo.nl/Apis
182
			 */
183
			'Services'                  => (object) array(
184
				'ServiceList' => array(),
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:
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
		/**
386
		 * Request Errors.
387
		 *
388
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/POST-json-Transaction
389
		 */
390
		if ( \property_exists( $object, 'RequestErrors' ) && null !== $object->RequestErrors ) {
391
			$exception = null;
392
393
			foreach ( $object->RequestErrors as $errors ) {
394
				foreach ( $errors as $error ) {
395
					// Add exception.
396
					$exception = new \Exception( $error->ErrorMessage, 0, $exception );
397
				}
398
			}
399
400
			if ( null !== $exception ) {
401
				throw $exception;
402
			}
403
		}
404
405
		/**
406
		 * Required Action.
407
		 */
408
		if ( 'Redirect' !== $object->RequiredAction->Name ) {
409
			throw new \Exception(
410
				\sprintf(
411
					'Unsupported Buckaroo action: %s',
412
					$object->RequiredAction->Name
413
				)
414
			);
415
		}
416
417
		$payment->set_action_url( $object->RequiredAction->RedirectURL );
418
419
		/**
420
		 * Buckaroo keys.
421
		 *
422
		 * @link https://testcheckout.buckaroo.nl/json/Docs/ResourceModel?modelName=TransactionResponse
423
		 */
424
		if ( \property_exists( $object, 'Key' ) ) {
425
			$payment->set_transaction_id( $object->Key );
426
		}
427
428
		if ( \property_exists( $object, 'PaymentKey' ) ) {
429
			$payment->set_meta( 'buckaroo_transaction_payment_key', $object->PaymentKey );
430
		}
431
	}
432
433
	/**
434
	 * JSON API Request.
435
	 *
436
	 * @param string      $method   HTTP request method.
437
	 * @param string      $endpoint JSON API endpoint.
438
	 * @param object|null $data     Data.
439
	 */
440
	public function request( $method, $endpoint, $data = null ) {
441
		$host = 'checkout.buckaroo.nl';
442
443
		if ( self::MODE_TEST === $this->config->mode ) {
444
			$host = 'testcheckout.buckaroo.nl';
445
		}
446
447
		/**
448
		 * Authentication.
449
		 *
450
		 * 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.
451
		 *
452
		 * @link https://dev.buckaroo.nl/Apis/Description/json
453
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Authentication
454
		 */
455
		$website_key         = $this->config->website_key;
456
		$request_http_method = $method;
457
		$request_uri         = $host . '/json/' . $endpoint;
458
		$request_timestamp   = \strval( \time() );
459
		$nonce               = \wp_generate_password( 32 );
460
		$request_content     = null === $data ? '' : \wp_json_encode( $data );
461
462
		$values = \implode(
463
			'',
464
			array(
465
				$website_key,
466
				$request_http_method,
467
				\strtolower( \rawurlencode( $request_uri ) ),
468
				$request_timestamp,
469
				$nonce,
470
				\
471
				// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
472
				null === $data ? '' : \base64_encode( \md5( $request_content, true ) ),
0 ignored issues
show
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

472
				null === $data ? '' : \base64_encode( \md5( /** @scrutinizer ignore-type */ $request_content, true ) ),
Loading history...
473
			)
474
		);
475
476
		$hash = \hash_hmac( 'sha256', $values, $this->config->secret_key, true );
477
478
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
479
		$hmac = \base64_encode( $hash );
480
481
		$authorization = \sprintf(
482
			'hmac %s:%s:%s:%s',
483
			$this->config->website_key,
484
			$hmac,
485
			$nonce,
486
			$request_timestamp
487
		);
488
489
		$response = \Pronamic\WordPress\Http\Facades\Http::request(
490
			'https://' . $request_uri,
491
			array(
492
				'method'  => $request_http_method,
493
				'headers' => array(
494
					'Authorization' => $authorization,
495
					'Content-Type'  => 'application/json',
496
				),
497
				'body'    => $request_content,
498
			)
499
		);
500
501
		$object = $response->json();
502
503
		/**
504
		 * OK.
505
		 */
506
		return $object;
507
	}
508
509
	/**
510
	 * Update status of the specified payment
511
	 *
512
	 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-Status-transactionKey
513
	 * @param Payment $payment Payment.
514
	 */
515
	public function update_status( Payment $payment ) {
516
		$transaction_key = $payment->get_transaction_id();
517
518
		if ( empty( $transaction_key ) ) {
519
			return;
520
		}
521
522
		$result = $this->request( 'GET', 'Transaction/Status/' . $transaction_key );
523
524
		$payment->set_status( Statuses::transform( \strval( $result->Status->Code->Code ) ) );
525
526
		/**
527
		 * Consumer bank details.
528
		 */
529
		$consumer_bank_details = $payment->get_consumer_bank_details();
530
531
		if ( null === $consumer_bank_details ) {
532
			$consumer_bank_details = new BankAccountDetails();
533
534
			$payment->set_consumer_bank_details( $consumer_bank_details );
535
		}
536
537
		/**
538
		 * Services.
539
		 */
540
		foreach ( $result->Services as $service ) {
541
			foreach ( $service->Parameters as $parameter ) {
542
				if ( 'consumerName' === $parameter->Name ) {
543
					$consumer_bank_details->set_name( $parameter->Value );
544
				}
545
546
				if ( 'consumerIBAN' === $parameter->Name ) {
547
					$consumer_bank_details->set_iban( $parameter->Value );
548
				}
549
550
				if ( 'consumerBIC' === $parameter->Name ) {
551
					$consumer_bank_details->set_iban( $parameter->Value );
552
				}
553
			}
554
		}
555
556
		/**
557
		 * Refunds.
558
		 *
559
		 * @link https://testcheckout.buckaroo.nl/json/Docs/Api/GET-json-Transaction-RefundInfo-transactionKey
560
		 */
561
		$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_key );
562
563
		if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
564
			$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
565
566
			$payment->set_refunded_amount( $refunded_amount );
567
		}
568
	}
569
570
	/**
571
	 * Create refund.
572
	 *
573
	 * @param string $transaction_id Transaction ID.
574
	 * @param Money  $amount         Amount to refund.
575
	 * @param string $description    Refund reason.
576
	 * @return string
577
	 */
578
	public function create_refund( $transaction_id, Money $amount, $description = null ) {
579
		$original_transaction = $this->request( 'GET', 'Transaction/Status/' . $transaction_id );
580
581
		if ( ! \is_object( $original_transaction ) ) {
582
			throw new \Exception(
583
				sprintf(
584
					/* translators: %s: transaction key */
585
					__( 'Unable to create refund for transaction with transaction key: %s', 'pronamic_ideal' ),
586
					$transaction_id
587
				)
588
			);
589
		}
590
591
		$service_name = Util::get_transaction_service( $original_transaction );
592
593
		if ( null === $service_name ) {
594
			throw new \Exception(
595
				sprintf(
596
					/* translators: %s: transaction key */
597
					__( 'Unable to create refund for transaction without service name. Transaction key: %s', 'pronamic_ideal' ),
598
					$transaction_id
599
				)
600
			);
601
		}
602
603
		// Invoice.
604
		$payment = \get_pronamic_payment_by_transaction_id( $transaction_id );
605
606
		$invoice = null;
607
608
		if ( null !== $payment ) {
609
			$invoice = Util::get_invoice_number( (string) $this->config->get_invoice_number(), $payment );
610
		}
611
612
		// Refund request.
613
		$data = (object) array(
614
			'Channel'                => 'Web',
615
			'Currency'               => $amount->get_currency()->get_alphabetic_code(),
616
			'AmountCredit'           => (float) $amount->get_value(),
617
			'Invoice'                => $invoice,
618
			'OriginalTransactionKey' => $transaction_id,
619
			'Services'               => array(
620
				'ServiceList' => array(
621
					array(
622
						'Name'   => $service_name,
623
						'Action' => 'Refund',
624
					),
625
				),
626
			),
627
		);
628
629
		$refund = $this->request( 'POST', 'Transaction', $data );
630
631
		// Check refund object.
632
		if ( ! \is_object( $refund ) ) {
633
			return null;
634
		}
635
636
		// Check refund status.
637
		if ( \property_exists( $refund, 'Status' ) && \property_exists( $refund->Status, 'Code' ) ) {
638
			$status = Statuses::transform( (string) $refund->Status->Code->Code );
639
640
			if ( PaymentStatus::SUCCESS !== $status ) {
641
				throw new \Exception(
642
					\sprintf(
643
						/* translators: 1: payment provider name, 2: status message, 3: status sub message*/
644
						__( 'Unable to create refund at %1$s gateway: %2$s%3$s', 'pronamic_ideal' ),
645
						__( 'Buckaroo', 'pronamic_ideal' ),
646
						$refund->Status->Code->Description,
647
						\property_exists( $refund->Status, 'SubCode' ) ? ' – ' . $refund->Status->SubCode->Description : ''
648
					)
649
				);
650
			}
651
		}
652
653
		// Update payment refunded amount.
654
		if ( null !== $payment ) {
655
			$result = $this->request( 'GET', 'Transaction/RefundInfo/' . $transaction_id );
656
657
			if ( \property_exists( $result, 'RefundedAmount' ) && ! empty( $result->RefundedAmount ) ) {
658
				$refunded_amount = new Money( $result->RefundedAmount, $result->RefundCurrency );
659
660
				$payment->set_refunded_amount( $refunded_amount );
661
			}
662
		}
663
664
		// Return.
665
		$refund_id = \property_exists( $refund, 'Key' ) ? $refund->Key : null;
666
667
		return $refund_id;
668
	}
669
}
670