Edit File by line
/home/zeestwma/richards.../wp-conte.../plugins/woocomme.../src/Internal/FraudPro...
File: DecisionHandler.php
<?php
[0] Fix | Delete
/**
[1] Fix | Delete
* DecisionHandler class file.
[2] Fix | Delete
*/
[3] Fix | Delete
[4] Fix | Delete
declare( strict_types=1 );
[5] Fix | Delete
[6] Fix | Delete
namespace Automattic\WooCommerce\Internal\FraudProtection;
[7] Fix | Delete
[8] Fix | Delete
defined( 'ABSPATH' ) || exit;
[9] Fix | Delete
[10] Fix | Delete
/**
[11] Fix | Delete
* Handles fraud protection decision application.
[12] Fix | Delete
*
[13] Fix | Delete
* This class is responsible for:
[14] Fix | Delete
* - Applying extension override filters for whitelisting
[15] Fix | Delete
* - Coordinating with SessionClearanceManager to apply decisions
[16] Fix | Delete
*
[17] Fix | Delete
* @since 10.5.0
[18] Fix | Delete
* @internal This class is part of the internal API and is subject to change without notice.
[19] Fix | Delete
*/
[20] Fix | Delete
class DecisionHandler {
[21] Fix | Delete
[22] Fix | Delete
/**
[23] Fix | Delete
* Session clearance manager instance.
[24] Fix | Delete
*
[25] Fix | Delete
* @var SessionClearanceManager
[26] Fix | Delete
*/
[27] Fix | Delete
private SessionClearanceManager $session_manager;
[28] Fix | Delete
[29] Fix | Delete
/**
[30] Fix | Delete
* Initialize with dependencies.
[31] Fix | Delete
*
[32] Fix | Delete
* @internal
[33] Fix | Delete
*
[34] Fix | Delete
* @param SessionClearanceManager $session_manager The session clearance manager instance.
[35] Fix | Delete
*/
[36] Fix | Delete
final public function init( SessionClearanceManager $session_manager ): void {
[37] Fix | Delete
$this->session_manager = $session_manager;
[38] Fix | Delete
}
[39] Fix | Delete
[40] Fix | Delete
/**
[41] Fix | Delete
* Apply a fraud protection decision.
[42] Fix | Delete
*
[43] Fix | Delete
* This method processes a decision from the API, applies any override filters,
[44] Fix | Delete
* validates the result, and updates the session status accordingly.
[45] Fix | Delete
*
[46] Fix | Delete
* The input decision is expected to be pre-validated by ApiClient.
[47] Fix | Delete
*
[48] Fix | Delete
* The decision flow:
[49] Fix | Delete
* 1. Apply the `woocommerce_fraud_protection_decision` filter for overrides
[50] Fix | Delete
* 2. Validate the filtered decision (third-party filters may return invalid values)
[51] Fix | Delete
* 3. Update session status via SessionClearanceManager
[52] Fix | Delete
*
[53] Fix | Delete
* @since 10.5.0
[54] Fix | Delete
*
[55] Fix | Delete
* @param string $decision The decision from the API (allow, block).
[56] Fix | Delete
* @param array<string, mixed> $session_data The session data that was sent to the API.
[57] Fix | Delete
* @return string The final applied decision after any filter overrides.
[58] Fix | Delete
*/
[59] Fix | Delete
public function apply_decision( string $decision, array $session_data ): string {
[60] Fix | Delete
// Validate input decision and fail open if invalid.
[61] Fix | Delete
if ( ! $this->is_valid_decision( $decision ) ) {
[62] Fix | Delete
FraudProtectionController::log(
[63] Fix | Delete
'warning',
[64] Fix | Delete
sprintf( 'Invalid decision "%s" received. Defaulting to "allow".', $decision ),
[65] Fix | Delete
array( 'session_data' => $session_data )
[66] Fix | Delete
);
[67] Fix | Delete
$decision = ApiClient::DECISION_ALLOW;
[68] Fix | Delete
}
[69] Fix | Delete
[70] Fix | Delete
$original_decision = $decision;
[71] Fix | Delete
[72] Fix | Delete
/**
[73] Fix | Delete
* Filters the fraud protection decision before it is applied.
[74] Fix | Delete
*
[75] Fix | Delete
* This filter allows extensions to override fraud protection decisions
[76] Fix | Delete
* to implement custom whitelisting logic. Common use cases:
[77] Fix | Delete
* - Whitelist specific users (e.g., admins, trusted customers)
[78] Fix | Delete
* - Whitelist specific conditions (e.g., certain IP ranges, logged-in users)
[79] Fix | Delete
* - Integrate with external fraud detection services
[80] Fix | Delete
*
[81] Fix | Delete
* Note: This filter can only change the decision to ApiClient::VALID_DECISIONS.
[82] Fix | Delete
* Any other value will be rejected and the original decision will be used.
[83] Fix | Delete
*
[84] Fix | Delete
* @since 10.5.0
[85] Fix | Delete
*
[86] Fix | Delete
* @param string $decision The decision from the API (allow, block).
[87] Fix | Delete
* @param array<string, mixed> $session_data The session data that was analyzed.
[88] Fix | Delete
*/
[89] Fix | Delete
$decision = apply_filters( 'woocommerce_fraud_protection_decision', $decision, $session_data );
[90] Fix | Delete
[91] Fix | Delete
// Validate filtered decision (third-party filters may return invalid values).
[92] Fix | Delete
if ( ! $this->is_valid_decision( $decision ) ) {
[93] Fix | Delete
FraudProtectionController::log(
[94] Fix | Delete
'warning',
[95] Fix | Delete
sprintf( 'Filter `woocommerce_fraud_protection_decision` returned invalid decision "%s". Using original decision "%s".', $decision, $original_decision ),
[96] Fix | Delete
array(
[97] Fix | Delete
'original_decision' => $original_decision,
[98] Fix | Delete
'filtered_decision' => $decision,
[99] Fix | Delete
'session_data' => $session_data,
[100] Fix | Delete
)
[101] Fix | Delete
);
[102] Fix | Delete
$decision = $original_decision;
[103] Fix | Delete
}
[104] Fix | Delete
[105] Fix | Delete
// Log if decision was overridden.
[106] Fix | Delete
if ( $decision !== $original_decision ) {
[107] Fix | Delete
FraudProtectionController::log(
[108] Fix | Delete
'info',
[109] Fix | Delete
sprintf( 'Decision overridden by filter `woocommerce_fraud_protection_decision`: "%s" -> "%s"', $original_decision, $decision ),
[110] Fix | Delete
array(
[111] Fix | Delete
'original_decision' => $original_decision,
[112] Fix | Delete
'final_decision' => $decision,
[113] Fix | Delete
'session_data' => $session_data,
[114] Fix | Delete
)
[115] Fix | Delete
);
[116] Fix | Delete
}
[117] Fix | Delete
[118] Fix | Delete
// Apply the decision to the session.
[119] Fix | Delete
$this->update_session_status( $decision );
[120] Fix | Delete
[121] Fix | Delete
return $decision;
[122] Fix | Delete
}
[123] Fix | Delete
[124] Fix | Delete
/**
[125] Fix | Delete
* Check if a decision value is valid.
[126] Fix | Delete
*
[127] Fix | Delete
* @param mixed $decision The decision to validate.
[128] Fix | Delete
* @return bool True if valid, false otherwise.
[129] Fix | Delete
*/
[130] Fix | Delete
private function is_valid_decision( $decision ): bool {
[131] Fix | Delete
if ( ! is_string( $decision ) ) {
[132] Fix | Delete
return false;
[133] Fix | Delete
}
[134] Fix | Delete
return in_array( $decision, ApiClient::VALID_DECISIONS, true );
[135] Fix | Delete
}
[136] Fix | Delete
[137] Fix | Delete
/**
[138] Fix | Delete
* Update the session status based on the decision.
[139] Fix | Delete
*
[140] Fix | Delete
* Important: Once a session is blocked, it stays blocked until explicitly reset.
[141] Fix | Delete
* This prevents race conditions where emptying the cart (done during block_session)
[142] Fix | Delete
* causes subsequent fraud checks to return "allow" (due to lower cart value),
[143] Fix | Delete
* which would incorrectly unblock the session.
[144] Fix | Delete
*
[145] Fix | Delete
* @param string $decision The validated decision to apply.
[146] Fix | Delete
* @return void
[147] Fix | Delete
*/
[148] Fix | Delete
private function update_session_status( string $decision ): void {
[149] Fix | Delete
// Don't overwrite a blocked session with an allow decision.
[150] Fix | Delete
// Once blocked, a session should stay blocked until explicitly reset.
[151] Fix | Delete
if ( ApiClient::DECISION_ALLOW === $decision && $this->session_manager->is_session_blocked() ) {
[152] Fix | Delete
FraudProtectionController::log(
[153] Fix | Delete
'info',
[154] Fix | Delete
'Preserving blocked session status. Allow decision not applied to already-blocked session.'
[155] Fix | Delete
);
[156] Fix | Delete
return;
[157] Fix | Delete
}
[158] Fix | Delete
[159] Fix | Delete
switch ( $decision ) {
[160] Fix | Delete
case ApiClient::DECISION_ALLOW:
[161] Fix | Delete
$this->session_manager->allow_session();
[162] Fix | Delete
break;
[163] Fix | Delete
[164] Fix | Delete
case ApiClient::DECISION_BLOCK:
[165] Fix | Delete
$this->session_manager->block_session();
[166] Fix | Delete
break;
[167] Fix | Delete
}
[168] Fix | Delete
}
[169] Fix | Delete
}
[170] Fix | Delete
[171] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function