PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.1.8
Pay with Vipps and MobilePay for WooCommerce v6.1.8
6.2.2 6.2.1 6.2.0 6.1.10 6.1.9 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1.0 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 5.4.3 5.4.2 5.4.1 5.4.0 5.3.4 All 184 releases
woo-vipps / recurring / includes / wc-vipps-recurring-api.php

wc-vipps-recurring-api.php in Pay with Vipps and MobilePay for WooCommerce 6.1.8, at recurring/includes/wc-vipps-recurring-api.php

681 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 defined( 'ABSPATH' ) || exit;
4
5 require_once( __DIR__ . '/wc-vipps-recurring-exceptions.php' );
6
7 /**
8 * Class WC_Vipps_Recurring_Api
9 */
10 class WC_Vipps_Recurring_Api {
11 public WC_Gateway_Vipps_Recurring $gateway;
12
13 /**
14 * Amount of days to retry a payment for when creating a charge
15 */
16 public int $retry_days = WC_VIPPS_RECURRING_RETRY_DAYS;
17
18
19 public function __construct( WC_Gateway_Vipps_Recurring $gateway ) {
20 $this->gateway = $gateway;
21 }
22
23 /**
24 * @return mixed|string|null
25 * @throws WC_Vipps_Recurring_Config_Exception
26 * @throws WC_Vipps_Recurring_Temporary_Exception
27 */
28 public function get_access_token( bool $force_fresh = false ) {
29 $stored = get_transient( '_vipps_recurring_token' );
30
31 if ( ! $force_fresh && $stored && $stored['expires_on'] > time() ) {
32 return $stored['access_token'];
33 }
34
35 $token = $this->get_access_token_from_vipps();
36
37 if ( ! $token ) {
38 return null;
39 }
40
41 set_transient( '_vipps_recurring_token', $token, $token['expires_in'] / 2 );
42
43 return $token['access_token'];
44 }
45
46 /**
47 * @throws WC_Vipps_Recurring_Config_Exception
48 * @throws WC_Vipps_Recurring_Temporary_Exception
49 */
50 private function get_access_token_from_vipps(): array {
51 try {
52 return $this->http_call( 'accessToken/get', 'POST' );
53 } catch ( WC_Vipps_Recurring_Temporary_Exception $e ) {
54 WC_Vipps_Recurring_Logger::log( 'Could not get Vipps/MobilePay access token ' . $e->getMessage() );
55
56 throw $e;
57 } catch ( Exception $e ) {
58 WC_Vipps_Recurring_Logger::log( 'Could not get Vipps/MobilePay access token ' . $e->getMessage() );
59
60 throw new WC_Vipps_Recurring_Config_Exception( $e->getMessage() );
61 }
62 }
63
64 public function generate_idempotency_key(): string {
65 return wp_generate_password( 24, false );
66 }
67
68 /**
69 * @throws WC_Vipps_Recurring_Config_Exception
70 * @throws WC_Vipps_Recurring_Exception
71 * @throws WC_Vipps_Recurring_Temporary_Exception
72 */
73 public function create_agreement( WC_Vipps_Agreement $agreement, string $idempotency_key ): array {
74 $token = $this->get_access_token();
75
76 $headers = [
77 'Authorization' => 'Bearer ' . $token,
78 'Idempotency-Key' => $idempotency_key,
79 ];
80
81 $data = apply_filters( 'wc_vipps_recurring_create_agreement_data', $agreement->to_array( true ) );
82
83 return $this->http_call( 'recurring/v3/agreements', 'POST', $data, $headers );
84 }
85
86 /**
87 * @throws WC_Vipps_Recurring_Config_Exception
88 * @throws WC_Vipps_Recurring_Exception
89 * @throws WC_Vipps_Recurring_Temporary_Exception
90 */
91 public function get_agreement( string $agreement_id ): WC_Vipps_Agreement {
92 $token = $this->get_access_token();
93
94 $headers = [
95 'Authorization' => 'Bearer ' . $token,
96 ];
97
98 return new WC_Vipps_Agreement( $this->http_call( 'recurring/v3/agreements/' . $agreement_id, 'GET', [], $headers ) );
99 }
100
101 /**
102 * @throws WC_Vipps_Recurring_Config_Exception
103 * @throws WC_Vipps_Recurring_Exception
104 * @throws WC_Vipps_Recurring_Temporary_Exception
105 */
106 public function update_agreement( string $agreement_id, WC_Vipps_Agreement $agreement, string $idempotency_key ): void {
107 $token = $this->get_access_token();
108
109 $headers = [
110 'Authorization' => 'Bearer ' . $token,
111 'Idempotency-Key' => $idempotency_key,
112 ];
113
114 $data = apply_filters( 'wc_vipps_recurring_update_agreement_data', $agreement->to_array() );
115
116 $this->http_call( 'recurring/v3/agreements/' . $agreement_id, 'PATCH', $data, $headers );
117 }
118
119 /**
120 * @throws Exception
121 */
122 private function get_price_from_agreement( WC_Vipps_Agreement $agreement ): ?int {
123 $amount = $agreement->pricing->amount;
124
125 if ( $agreement->campaign ) {
126 $now = new DateTime();
127 $end = $agreement->campaign->end;
128
129 if ( $end > $now ) {
130 $amount = $agreement->campaign->price;
131 }
132 }
133
134 return $amount;
135 }
136
137 /**
138 * @throws WC_Vipps_Recurring_Config_Exception
139 * @throws WC_Vipps_Recurring_Exception
140 * @throws WC_Vipps_Recurring_Temporary_Exception
141 */
142 public function cancel_agreement( string $agreement_id, string $idempotency_key ): void {
143 $agreement = $this->get_agreement( $agreement_id );
144 if ( $agreement->status !== WC_Vipps_Agreement::STATUS_ACTIVE ) {
145 return;
146 }
147
148 $token = $this->get_access_token();
149
150 $headers = [
151 'Authorization' => 'Bearer ' . $token,
152 'Idempotency-Key' => $idempotency_key,
153 ];
154
155 $data = apply_filters( 'wc_vipps_recurring_cancel_agreement_data', [
156 'status' => WC_Vipps_Agreement::STATUS_STOPPED,
157 ] );
158
159 $this->http_call( 'recurring/v3/agreements/' . $agreement_id, 'PATCH', $data, $headers );
160 }
161
162 /**
163 * @return mixed|string|null
164 * @throws WC_Vipps_Recurring_Config_Exception
165 * @throws WC_Vipps_Recurring_Exception
166 * @throws WC_Vipps_Recurring_Temporary_Exception
167 */
168 public function capture_reserved_charge( WC_Vipps_Agreement $agreement, WC_Vipps_Charge $charge, string $idempotency_key ) {
169 $token = $this->get_access_token();
170
171 $headers = [
172 'Authorization' => 'Bearer ' . $token,
173 'Idempotency-Key' => $idempotency_key,
174 ];
175
176 return $this->http_call( 'recurring/v3/agreements/' . $agreement->id . '/charges/' . $charge->id . '/capture', 'POST', $charge->to_array(), $headers );
177 }
178
179 /**
180 * @throws WC_Vipps_Recurring_Config_Exception
181 * @throws WC_Vipps_Recurring_Exception
182 * @throws WC_Vipps_Recurring_Temporary_Exception
183 * @throws Exception
184 */
185 public function create_charge( WC_Vipps_Agreement $agreement, string $idempotency_key, string $order_id, string $vipps_order_id, ?int $amount = null ): array {
186 $token = $this->get_access_token();
187
188 $headers = [
189 'Authorization' => 'Bearer ' . $token,
190 'Idempotency-Key' => $idempotency_key,
191 ];
192
193 if ( $amount === null ) {
194 $amount = $this->get_price_from_agreement( $agreement );
195 }
196
197 // minimum of 1 days
198 $due_at = date( 'Y-m-d', time() + 3600 * 24 );
199
200 $charge = ( new WC_Vipps_Charge() )->set_order_id( $vipps_order_id )
201 ->set_external_id( $order_id )
202 ->set_amount( $amount )
203 ->set_description( $agreement->product_name ?? get_bloginfo() )
204 ->set_transaction_type( WC_Vipps_Charge::TRANSACTION_TYPE_DIRECT_CAPTURE )
205 ->set_due( $due_at )
206 ->set_retry_days( $this->retry_days )
207 ->set_processing_mode( $this->retry_days > 0 ? WC_Vipps_Charge::PROCESSING_MODE_MULTIPLE_ATTEMPTS : WC_Vipps_Charge::PROCESSING_MODE_SINGLE_ATTEMPT );
208
209 $data = apply_filters( 'wc_vipps_recurring_create_charge_data', $charge->to_array(), $agreement->id, $order_id );
210
211 return $this->http_call( 'recurring/v3/agreements/' . $agreement->id . '/charges', 'POST', $data, $headers );
212 }
213
214 /**
215 * @throws WC_Vipps_Recurring_Config_Exception
216 * @throws WC_Vipps_Recurring_Exception
217 * @throws WC_Vipps_Recurring_Temporary_Exception
218 */
219 public function cancel_charge( string $agreement_id, string $charge_id, string $idempotency_key ): void {
220 $token = $this->get_access_token();
221
222 $headers = [
223 'Authorization' => 'Bearer ' . $token,
224 'Idempotency-Key' => $idempotency_key,
225 ];
226
227 $this->http_call( 'recurring/v3/agreements/' . $agreement_id . '/charges/' . $charge_id, 'DELETE', [], $headers );
228 }
229
230 /**
231 * @throws WC_Vipps_Recurring_Config_Exception
232 * @throws WC_Vipps_Recurring_Exception
233 * @throws WC_Vipps_Recurring_Temporary_Exception
234 */
235 public function get_charge( string $agreement_id, string $charge_id ): WC_Vipps_Charge {
236 $token = $this->get_access_token();
237
238 $headers = [
239 'Authorization' => 'Bearer ' . $token,
240 ];
241
242 return new WC_Vipps_Charge( $this->http_call( 'recurring/v3/agreements/' . $agreement_id . '/charges/' . $charge_id, 'GET', [], $headers ) );
243 }
244
245 /**
246 * @return mixed|string|null
247 * @throws WC_Vipps_Recurring_Config_Exception
248 * @throws WC_Vipps_Recurring_Exception
249 * @throws WC_Vipps_Recurring_Temporary_Exception
250 */
251 public function refund_charge( string $agreement_id, string $charge_id, ?int $amount = null, ?string $reason = null ) {
252 $token = $this->get_access_token();
253
254 $headers = [
255 'Authorization' => 'Bearer ' . $token,
256 'Idempotency-Key' => $this->generate_idempotency_key(),
257 ];
258
259 if ( $reason !== null && strlen( $reason ) > 99 ) {
260 $reason = mb_substr( $reason, 0, 90 );
261 }
262
263 $data = [
264 'description' => $reason ?: 'Refund',
265 ];
266
267 if ( $amount !== null ) {
268 $data = array_merge( $data, [
269 'amount' => $amount,
270 ] );
271 }
272
273 $data = apply_filters( 'wc_vipps_recurring_refund_charge_data', $data );
274
275 return $this->http_call( 'recurring/v3/agreements/' . $agreement_id . '/charges/' . $charge_id . '/refund', 'POST', $data, $headers );
276 }
277
278 /**
279 * @return WC_Vipps_Charge[]
280 * @throws WC_Vipps_Recurring_Config_Exception
281 * @throws WC_Vipps_Recurring_Exception
282 * @throws WC_Vipps_Recurring_Temporary_Exception
283 */
284 public function get_charges_for( string $agreement_id ): array {
285 $token = $this->get_access_token();
286
287 $headers = [
288 'Authorization' => 'Bearer ' . $token,
289 ];
290
291 return array_map( static function ( $charge ) {
292 return new WC_Vipps_Charge( $charge );
293 }, $this->http_call( 'recurring/v3/agreements/' . $agreement_id . '/charges', 'GET', [], $headers ) );
294 }
295
296 /**
297 * @throws WC_Vipps_Recurring_Config_Exception
298 * @throws WC_Vipps_Recurring_Exception
299 * @throws WC_Vipps_Recurring_Temporary_Exception
300 */
301 public function get_userinfo( WC_Vipps_Agreement $agreement ): array {
302 $token = $this->get_access_token();
303
304 $headers = [
305 'Authorization' => 'Bearer ' . $token,
306 ];
307
308 $endpoint = str_replace( $this->get_base_url(), '', $agreement->userinfo_url );
309
310 return $this->http_call( $endpoint, 'GET', [], $headers );
311 }
312
313 /**
314 * @return array
315 * @throws WC_Vipps_Recurring_Config_Exception
316 * @throws WC_Vipps_Recurring_Exception
317 * @throws WC_Vipps_Recurring_Temporary_Exception
318 */
319 public function get_webhooks( string $msn ): array {
320 $token = $this->get_access_token();
321
322 $headers = [
323 'Authorization' => 'Bearer ' . $token,
324 'Merchant-Serial-Number' => $msn,
325 ];
326
327 return $this->http_call( 'webhooks/v1/webhooks', 'GET', [], $headers );
328 }
329
330 /**
331 * @return array
332 * @throws WC_Vipps_Recurring_Config_Exception
333 * @throws WC_Vipps_Recurring_Exception
334 * @throws WC_Vipps_Recurring_Temporary_Exception
335 */
336 public function register_webhook( string $msn ): array {
337 $token = $this->get_access_token();
338
339 $headers = [
340 'Authorization' => 'Bearer ' . $token,
341 'Merchant-Serial-Number' => $msn,
342 ];
343
344 $callback_url = $this->gateway->webhook_callback_url();
345
346 return $this->http_call( 'webhooks/v1/webhooks', 'POST', [
347 'url' => $callback_url,
348 'events' => [
349 'recurring.agreement-activated.v1',
350 'recurring.agreement-rejected.v1',
351 'recurring.agreement-stopped.v1',
352 'recurring.agreement-expired.v1',
353 'recurring.charge-reserved.v1',
354 'recurring.charge-captured.v1',
355 'recurring.charge-canceled.v1',
356 'recurring.charge-failed.v1',
357 ]
358 ], $headers );
359 }
360
361 /**
362 * @param string $id
363 *
364 * @return array|string
365 * @throws WC_Vipps_Recurring_Config_Exception
366 * @throws WC_Vipps_Recurring_Exception
367 * @throws WC_Vipps_Recurring_Temporary_Exception
368 */
369 public function delete_webhook( string $msn, string $id ) {
370 $token = $this->get_access_token();
371
372 $headers = [
373 'Authorization' => 'Bearer ' . $token,
374 'Merchant-Serial-Number' => $msn,
375 ];
376
377 return $this->http_call( 'webhooks/v1/webhooks/' . $id, 'DELETE', [], $headers );
378 }
379
380 /**
381 * @throws WC_Vipps_Recurring_Config_Exception
382 * @throws WC_Vipps_Recurring_Exception
383 * @throws WC_Vipps_Recurring_Temporary_Exception
384 */
385 public function checkout_poll( string $endpoint ) {
386 if ( ! str_starts_with( $endpoint, 'http' ) ) {
387 $endpoint = 'checkout/v3/session/' . $endpoint;
388 }
389
390 $token = $this->get_access_token();
391
392 $headers = [
393 'Authorization' => 'Bearer ' . $token,
394 ];
395
396 return $this->http_call( $endpoint, 'GET', [], $headers );
397 }
398
399 /**
400 * @throws WC_Vipps_Recurring_Missing_Value_Exception
401 * @throws WC_Vipps_Recurring_Config_Exception
402 * @throws WC_Vipps_Recurring_Exception
403 * @throws WC_Vipps_Recurring_Temporary_Exception
404 */
405 public function checkout_initiate( WC_Vipps_Checkout_Session $checkout_session ): WC_Vipps_Checkout_Session_Response {
406 $token = $this->get_access_token();
407
408 $headers = [
409 'Authorization' => 'Bearer ' . $token,
410 ];
411
412 $response = $this->http_call( 'checkout/v3/session', 'POST', $checkout_session->to_array(), $headers );
413
414 return new WC_Vipps_Checkout_Session_Response( $response );
415 }
416
417 private function get_base_url(): string {
418 if ( $this->get_test_mode() ) {
419 return 'https://apitest.vipps.no';
420 }
421
422 return 'https://api.vipps.no';
423 }
424
425 private function get_test_mode(): bool {
426 return $this->gateway->get_option( 'test_mode' ) === "yes" || WC_VIPPS_RECURRING_TEST_MODE;
427 }
428
429 /**
430 * @return mixed|string|null
431 * @throws WC_Vipps_Recurring_Config_Exception
432 * @throws WC_Vipps_Recurring_Exception
433 * @throws WC_Vipps_Recurring_Temporary_Exception
434 */
435 private function http_call( string $endpoint, string $method, array $data = [], array $headers = [], $encoding = 'json' ) {
436 // WC_Vipps_Recurring_Logger::log( sprintf( "Calling Vipps API endpoint: [%s] %s", $method, $endpoint ) );
437
438 $url = $endpoint;
439 if ( ! str_starts_with( $endpoint, "http" ) ) {
440 $url = $this->get_base_url() . '/' . $endpoint;
441 }
442
443 $client_id = $this->gateway->get_option( "client_id" );
444 $secret_key = $this->gateway->get_option( "secret_key" );
445 $subscription_key = $this->gateway->get_option( "subscription_key" );
446 $merchant_serial_number = $this->gateway->get_option( "merchant_serial_number" );
447
448 if ( $this->get_test_mode() ) {
449 $client_id = $this->gateway->get_option( "test_client_id" );
450 $secret_key = $this->gateway->get_option( "test_secret_key" );
451 $subscription_key = $this->gateway->get_option( "test_subscription_key" );
452 $merchant_serial_number = $this->gateway->get_option( "test_merchant_serial_number" );
453 }
454
455 if ( ! $subscription_key || ! $secret_key || ! $client_id ) {
456 throw new WC_Vipps_Recurring_Config_Exception( __( 'Your Vipps/MobilePay Recurring Payments gateway is not correctly configured.', 'woo-vipps' ) );
457 }
458
459 $system_plugin_version = WC_VIPPS_RECURRING_VERSION;
460 $checkout_enabled = get_option( WC_Vipps_Recurring_Helper::OPTION_CHECKOUT_ENABLED, false );
461
462 if ( $checkout_enabled ) {
463 $system_plugin_version = $system_plugin_version . '/checkout';
464 }
465
466 $headers = array_merge( [
467 'client_id' => $client_id,
468 'client_secret' => $secret_key,
469 'Ocp-Apim-Subscription-Key' => $subscription_key,
470 'Merchant-Serial-Number' => $merchant_serial_number,
471 'Vipps-System-Name' => 'woocommerce',
472 'Vipps-System-Version' => get_bloginfo( 'version' ) . '/' . ( defined( 'WC_VERSION' ) ? WC_VERSION : '0.0.0' ),
473 'Vipps-System-Plugin-Name' => 'woo-vipps-recurring',
474 'Vipps-System-Plugin-Version' => $system_plugin_version
475 ], $headers );
476
477 if ( $encoding === 'url' || $method === 'GET' ) {
478 $data_encoded = http_build_query( $data );
479 } else {
480 $data_encoded = json_encode( $data );
481 }
482
483 $data_len = strlen( $data_encoded );
484 $headers['Connection'] = 'close';
485
486 if ( $method !== 'GET' ) {
487 $headers['Content-length'] = $data_len;
488 $headers['Content-type'] = $encoding === 'url' ? 'application/x-www-form-urlencoded' : 'application/json';
489 }
490
491 $args = [];
492 $args['method'] = $method;
493 $args['headers'] = $headers;
494
495 if ( $method !== 'GET' ) {
496 $args['body'] = $data_encoded;
497 }
498
499 if ( $method === 'GET' && $data_encoded ) {
500 $url .= "?$data_encoded";
501 }
502
503 $response = wp_remote_request( $url, $args );
504
505 // throw WP error as a WC_Vipps_Recurring_Exception if response is not valid
506 $default_error = '';
507 if ( is_wp_error( $response ) ) {
508 $default_error = "500 " . $response->get_error_message();
509 }
510
511 // Parse the result, converting it to exceptions if necessary
512 return $this->handle_http_response( $response, $args['body'] ?? "<empty>", $endpoint, $default_error );
513 }
514
515 /**
516 * @param $response
517 * @param $request_body
518 * @param $endpoint
519 * @param $default_error
520 *
521 * @return mixed|string|null
522 * @throws WC_Vipps_Recurring_Exception
523 * @throws WC_Vipps_Recurring_Temporary_Exception
524 */
525 private function handle_http_response( $response, $request_body, $endpoint, $default_error ) {
526 // no response from Vipps
527 if ( ! $response ) {
528 $error_msg = __( 'No response from Vipps/MobilePay', 'woo-vipps' );
529 WC_Vipps_Recurring_Logger::log( sprintf( 'HTTP Response Temporary Error: %s with request body: %s', $error_msg, $request_body ) );
530
531 throw new WC_Vipps_Recurring_Temporary_Exception( $error_msg );
532 }
533
534 $status = (int) wp_remote_retrieve_response_code( $response );
535
536 $body = wp_remote_retrieve_body( $response );
537 if ( $body ) {
538 $body = json_decode( $body, true );
539 }
540
541 // As long as the status code is less than 300 and greater than 199 we can return the body
542 if ( $status < 300 && $status > 199 ) {
543 return $body;
544 }
545
546 // A transport error or a retryable HTTP response does not tell us whether a
547 // state-changing request reached Vipps/MobilePay. Treat it as temporary so
548 // callers can retry with the same idempotency key instead of failing the order.
549 if ( $status === 0 || in_array( $status, [ 408, 425, 429 ], true ) || $status >= 500 ) {
550 $error_msg = trim( (string) $default_error );
551 if ( ! $error_msg ) {
552 $error_msg = sprintf( 'HTTP %d', $status );
553 }
554
555 $localized_msg = __( 'Vipps/MobilePay is temporarily unavailable. Please try again.', 'woo-vipps' );
556 if ( $status === 429 ) {
557 $localized_msg = __( "We hit Vipps/MobilePay's rate limit. Please try again shortly.", 'woo-vipps' );
558 }
559
560 $log_body = is_array( $body ) ? json_encode( $body ) : $body;
561 WC_Vipps_Recurring_Logger::log( sprintf( 'HTTP Response Temporary Error (%s): %s (%s) with request body: %s. The response was: %s', $status, $error_msg, $endpoint, $request_body, $log_body ) );
562
563 $exception = new WC_Vipps_Recurring_Temporary_Exception( $error_msg, $localized_msg );
564 $exception->response_code = $status;
565
566 throw $exception;
567 }
568
569 // error handling
570 $error_msg = $default_error ?? '';
571 $is_idempotent_error = false;
572 $is_merchant_not_allowed_error = false;
573 $is_url_validation_error = false;
574
575 if ( $body ) {
576 if ( isset( $body['message'] ) ) {
577 $error_msg = $body['message'];
578 }
579
580 if ( isset( $body['error_description'] ) ) {
581 $error_msg = $body['error_description'];
582 }
583
584 if ( isset( $body['title'] ) ) {
585 $error_msg = $body['title'];
586
587 if ( isset( $body['detail'] ) ) {
588 $error_msg .= ' ' . $body['detail'];
589 }
590 }
591
592 $error_msg = trim( $error_msg );
593
594 /*
595 "type":"https://example.com/validation-error",
596 "title":"Your request parameters didn't validate.",
597 "detail":"The request body contains one or more errors",
598 "instance":"123e4567-e89b-12d3-a456-426655440000",
599 "status":"400",
600 "extraDetails":[
601 {
602 "name":"amount",
603 "reason":"Must be a positive integer larger than 100"
604 },
605 {
606 "name":"URL",
607 "reason":"Must use HTTPS and validate according to the API specification"
608 }
609 ]
610 */
611
612
613 // todo: implement new logic
614 /*
615 {
616 "type": "about:blank",
617 "title": "Not Found",
618 "status": 404,
619 "detail": "Agreement not found.",
620 "instance": "/vipps-recurring-merchant-api/v3/agreements/agr_GqnvsH0",
621 "contextId": "32900600-1dd4-47dc-9a02-28c9e6491c65"
622 }
623 */
624
625 // if ( isset( $body['message'] ) ) {
626 // $error_msg = $body['message'];
627 // } elseif ( isset( $body['error'] ) ) {
628 // // access token
629 // $error_msg = $body['error'];
630 // } elseif ( is_array( $body ) ) {
631 // $error_msg = '';
632 // foreach ( $body as $entry ) {
633 // if ( isset( $entry['code'] ) ) {
634 // $error_msg .= $entry['field'] . ': ' . $entry['message'] . "\n";
635 //
636 // if ( $entry['code'] === 'idempotentkey.hash.mismatch' ) {
637 // $is_idempotent_error = true;
638 // }
639 //
640 // if ( $entry['code'] === 'merchant.not.allowed.for.recurring.operation' ) {
641 // $is_merchant_not_allowed_error = true;
642 // }
643 //
644 // if ( $entry['code'] === 'merchantagreementurl.apacheurlvalidation' ) {
645 // $is_url_validation_error = true;
646 // }
647 // }
648 // }
649 // } else {
650 // $error_msg = $body;
651 // }
652 }
653
654 $localized_msg = $error_msg;
655 if ( $is_merchant_not_allowed_error ) {
656 /* translators: Link to a GitHub readme about the error */
657 $localized_msg = sprintf( __( 'Recurring payments is not yet activated for this sale unit. Read more <a href="%s" target="_blank">here</a>', 'woo-vipps' ), 'https://developer.vippsmobilepay.com/docs/APIs/recurring-api/recurring-api-faq/#why-do-i-get-the-error-merchantnotallowedforrecurringoperation' );
658 }
659
660 if ( $is_url_validation_error ) {
661 $localized_msg = __( 'Your WordPress URL is not passing Merchant Agreement URL validation. Is your website publicly accessible?', 'woo-vipps' );
662 }
663
664 if ( is_array( $request_body ) ) {
665 $request_body = json_encode( $request_body );
666 }
667
668 if ( is_array( $body ) ) {
669 $body = json_encode( $body );
670 }
671
672 WC_Vipps_Recurring_Logger::log( sprintf( 'HTTP Response Error (%s): %s (%s) with request body: %s. The response was: %s', $status, $error_msg, $endpoint, $request_body, $body ) );
673
674 $exception = new WC_Vipps_Recurring_Exception( $error_msg, $localized_msg );
675 $exception->response_code = $status;
676 $exception->is_idempotent_error = $is_idempotent_error;
677
678 throw $exception;
679 }
680 }
681