whoami7 - Manager
:
/
home
/
dataiclx
/
www
/
wp-content
/
plugins
/
woocommerce
/
src
/
Internal
/
FraudProtection
/
Upload File:
files >> /home/dataiclx/www/wp-content/plugins/woocommerce/src/Internal/FraudProtection/ApiClient.php
<?php /** * ApiClient class file. */ declare( strict_types=1 ); namespace Automattic\WooCommerce\Internal\FraudProtection; use Automattic\Jetpack\Connection\Client as Jetpack_Connection_Client; defined( 'ABSPATH' ) || exit; /** * Handles communication with the WPCOM fraud protection endpoint. * * Uses Jetpack Connection for authenticated requests to the WPCOM endpoint * to get fraud protection decisions (allow, block, or challenge). * * This class implements a fail-open pattern: if the endpoint is unreachable, * times out, or returns an error, it returns an "allow" decision to ensure * legitimate transactions are never blocked due to service issues. * * @since 10.5.0 * @internal This class is part of the internal API and is subject to change without notice. */ class ApiClient { /** * Default timeout for API requests in seconds. */ private const DEFAULT_TIMEOUT = 30; /** * WPCOM API version. */ private const WPCOM_API_VERSION = '2'; /** * WPCOM fraud protection events endpoint path within Transact platform. */ private const EVENTS_ENDPOINT = 'transact/fraud_protection/events'; /** * Decision type: allow session. */ public const DECISION_ALLOW = 'allow'; /** * Decision type: block session. */ public const DECISION_BLOCK = 'block'; /** * Decision type: challenge session. */ public const DECISION_CHALLENGE = 'challenge'; /** * Valid decision values that can be returned by the API. * * @var array<string> */ public const VALID_DECISIONS = array( self::DECISION_ALLOW, self::DECISION_BLOCK, ); /** * Send a fraud protection event and get a decision from WPCOM endpoint. * * Implements fail-open pattern: if the endpoint is unreachable or times out, * returns "allow" decision and logs the error. * * @since 10.5.0 * * @param string $event_type Type of event being sent (e.g., 'cart_updated', 'checkout_started'). * @param array<string, mixed> $event_data Event data to send to the endpoint. * @return string Decision: "allow" or "block". */ public function send_event( string $event_type, array $event_data ): string { $payload = array_merge( array( 'event_type' => $event_type ), array_filter( $event_data, fn( $value ) => null !== $value ) ); FraudProtectionController::log( 'info', sprintf( 'Sending fraud protection event: %s', $event_type ), array( 'payload' => $payload ) ); $response = $this->make_request( 'POST', self::EVENTS_ENDPOINT, $payload ); if ( is_wp_error( $response ) ) { $error_data = $response->get_error_data() ?? array(); $error_data = is_array( $error_data ) ? $error_data : array( 'error' => $error_data ); FraudProtectionController::log( 'error', sprintf( 'Event track request failed: %s. Failing open with "allow" decision.', $response->get_error_message() ), $error_data ); return self::DECISION_ALLOW; } if ( ! isset( $response['decision'] ) ) { FraudProtectionController::log( 'error', 'Response missing "decision" field. Failing open with "allow" decision.', array( 'response' => $response ) ); return self::DECISION_ALLOW; } $decision = $response['decision']; if ( ! in_array( $decision, self::VALID_DECISIONS, true ) ) { FraudProtectionController::log( 'error', sprintf( 'Invalid decision value "%s". Failing open with "allow" decision.', $decision ), array( 'response' => $response ) ); return self::DECISION_ALLOW; } $session = is_array( $event_data['session'] ?? null ) ? $event_data['session'] : array(); $session_id = $session['session_id'] ?? 'unknown'; FraudProtectionController::log( 'info', sprintf( 'Fraud decision received: %s | Event: %s | Session: %s', $decision, $event_type, $session_id ), array( 'response' => $response ) ); return $decision; } /** * Make an HTTP request to a WPCOM endpoint via Jetpack Connection. * * @param string $method HTTP method (GET, POST, etc.). * @param string $path Endpoint path (relative to sites/{blog_id}/). * @param array<string, mixed> $payload Request payload. * @return array<string, mixed>|\WP_Error Parsed JSON response or WP_Error on failure. */ private function make_request( string $method, string $path, array $payload ) { if ( ! class_exists( Jetpack_Connection_Client::class ) ) { return new \WP_Error( 'jetpack_not_available', 'Jetpack Connection is not available' ); } $blog_id = $this->get_blog_id(); if ( ! $blog_id ) { return new \WP_Error( 'no_blog_id', 'Jetpack blog ID not found. Is the site connected to WordPress.com?' ); } $full_path = sprintf( 'sites/%d/%s', $blog_id, $path ); $body = \wp_json_encode( $payload ); if ( false === $body ) { return new \WP_Error( 'json_encode_error', 'Failed to encode payload', array( 'payload' => $payload ) ); } $response = Jetpack_Connection_Client::wpcom_json_api_request_as_blog( $full_path, self::WPCOM_API_VERSION, array( 'headers' => array( 'Content-Type' => 'application/json' ), 'method' => $method, 'timeout' => self::DEFAULT_TIMEOUT, ), $body, 'wpcom' ); if ( is_wp_error( $response ) ) { return $response; } /** * Type assertion for PHPStan - Jetpack returns array on success. * * @var array $response */ $response_code = wp_remote_retrieve_response_code( $response ); $response_body = wp_remote_retrieve_body( $response ); $data = json_decode( $response_body, true ); if ( $response_code >= 300 ) { return new \WP_Error( 'api_error', sprintf( 'Endpoint %s returned status code %d', "$method $path", $response_code ), array( 'response' => JSON_ERROR_NONE === json_last_error() ? $data : $response_body ) ); } if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) ) { return new \WP_Error( 'json_decode_error', sprintf( 'Failed to decode JSON response: %s', json_last_error_msg() ), array( 'response' => $response_body ) ); } return $data; } /** * Get the Jetpack blog ID. * * @return int|false Blog ID or false if not available. */ private function get_blog_id() { if ( ! class_exists( \Jetpack_Options::class ) ) { return false; } return \Jetpack_Options::get_option( 'id' ); } }
Copyright ©2021 || Defacer Indonesia