feat(07-01): create captcha verification class
- Support for reCAPTCHA v2, v3, and hCaptcha - Server-side verification with wp_remote_post - Automatic script enqueuing based on provider - Widget rendering for all three providers - reCAPTCHA v3 score checking (>= 0.5) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
334
includes/class-captcha.php
Normal file
334
includes/class-captcha.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/**
|
||||
* Captcha Verification
|
||||
*
|
||||
* Handles reCAPTCHA v2, v3, and hCaptcha integration
|
||||
*
|
||||
* @package Umzugsliste
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captcha verification class
|
||||
*/
|
||||
class Umzugsliste_Captcha {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if captcha is enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_enabled() {
|
||||
$provider = $this->get_provider();
|
||||
return 'none' !== $provider && ! empty( $provider );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current captcha provider
|
||||
*
|
||||
* @return string none|recaptcha_v2|recaptcha_v3|hcaptcha
|
||||
*/
|
||||
public function get_provider() {
|
||||
return get_option( 'umzugsliste_captcha_provider', 'none' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_site_key() {
|
||||
return get_option( 'umzugsliste_captcha_site_key', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get secret key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_secret_key() {
|
||||
return get_option( 'umzugsliste_captcha_secret_key', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue captcha provider scripts
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
if ( ! $this->is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$provider = $this->get_provider();
|
||||
$site_key = $this->get_site_key();
|
||||
|
||||
if ( empty( $site_key ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch ( $provider ) {
|
||||
case 'recaptcha_v2':
|
||||
wp_enqueue_script(
|
||||
'recaptcha-v2',
|
||||
'https://www.google.com/recaptcha/api.js',
|
||||
array(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
break;
|
||||
|
||||
case 'recaptcha_v3':
|
||||
wp_enqueue_script(
|
||||
'recaptcha-v3',
|
||||
'https://www.google.com/recaptcha/api.js?render=' . $site_key,
|
||||
array(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
break;
|
||||
|
||||
case 'hcaptcha':
|
||||
wp_enqueue_script(
|
||||
'hcaptcha',
|
||||
'https://js.hcaptcha.com/1/api.js',
|
||||
array(),
|
||||
null,
|
||||
true
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render captcha widget in form
|
||||
*
|
||||
* @return string HTML for captcha widget
|
||||
*/
|
||||
public function render_widget() {
|
||||
if ( ! $this->is_enabled() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$provider = $this->get_provider();
|
||||
$site_key = $this->get_site_key();
|
||||
|
||||
if ( empty( $site_key ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
switch ( $provider ) {
|
||||
case 'recaptcha_v2':
|
||||
?>
|
||||
<div class="captcha-widget">
|
||||
<div class="g-recaptcha" data-sitekey="<?php echo esc_attr( $site_key ); ?>"></div>
|
||||
</div>
|
||||
<?php
|
||||
break;
|
||||
|
||||
case 'recaptcha_v3':
|
||||
?>
|
||||
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response">
|
||||
<script>
|
||||
grecaptcha.ready(function() {
|
||||
var form = document.getElementById('umzugsliste-form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
grecaptcha.execute('<?php echo esc_js( $site_key ); ?>', {action: 'submit'}).then(function(token) {
|
||||
document.getElementById('g-recaptcha-response').value = token;
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
break;
|
||||
|
||||
case 'hcaptcha':
|
||||
?>
|
||||
<div class="captcha-widget">
|
||||
<div class="h-captcha" data-sitekey="<?php echo esc_attr( $site_key ); ?>"></div>
|
||||
</div>
|
||||
<?php
|
||||
break;
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify captcha response
|
||||
*
|
||||
* @param array $post_data POST data from form submission
|
||||
* @return bool True if verified, false otherwise
|
||||
*/
|
||||
public function verify_response( $post_data ) {
|
||||
if ( ! $this->is_enabled() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$provider = $this->get_provider();
|
||||
|
||||
switch ( $provider ) {
|
||||
case 'recaptcha_v2':
|
||||
return $this->verify_recaptcha_v2( $post_data );
|
||||
|
||||
case 'recaptcha_v3':
|
||||
return $this->verify_recaptcha_v3( $post_data );
|
||||
|
||||
case 'hcaptcha':
|
||||
return $this->verify_hcaptcha( $post_data );
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify reCAPTCHA v2 response
|
||||
*
|
||||
* @param array $post_data POST data
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_recaptcha_v2( $post_data ) {
|
||||
$response = isset( $post_data['g-recaptcha-response'] ) ? $post_data['g-recaptcha-response'] : '';
|
||||
|
||||
if ( empty( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$secret_key = $this->get_secret_key();
|
||||
if ( empty( $secret_key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$verify_url = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
|
||||
$response = wp_remote_post(
|
||||
$verify_url,
|
||||
array(
|
||||
'body' => array(
|
||||
'secret' => $secret_key,
|
||||
'response' => $response,
|
||||
'remoteip' => $_SERVER['REMOTE_ADDR'],
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
return isset( $body['success'] ) && $body['success'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify reCAPTCHA v3 response
|
||||
*
|
||||
* @param array $post_data POST data
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_recaptcha_v3( $post_data ) {
|
||||
$response = isset( $post_data['g-recaptcha-response'] ) ? $post_data['g-recaptcha-response'] : '';
|
||||
|
||||
if ( empty( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$secret_key = $this->get_secret_key();
|
||||
if ( empty( $secret_key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$verify_url = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
|
||||
$response = wp_remote_post(
|
||||
$verify_url,
|
||||
array(
|
||||
'body' => array(
|
||||
'secret' => $secret_key,
|
||||
'response' => $response,
|
||||
'remoteip' => $_SERVER['REMOTE_ADDR'],
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
// Check success and score (must be >= 0.5)
|
||||
return isset( $body['success'] ) && $body['success'] && isset( $body['score'] ) && $body['score'] >= 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify hCaptcha response
|
||||
*
|
||||
* @param array $post_data POST data
|
||||
* @return bool
|
||||
*/
|
||||
private function verify_hcaptcha( $post_data ) {
|
||||
$response = isset( $post_data['h-captcha-response'] ) ? $post_data['h-captcha-response'] : '';
|
||||
|
||||
if ( empty( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$secret_key = $this->get_secret_key();
|
||||
if ( empty( $secret_key ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$verify_url = 'https://hcaptcha.com/siteverify';
|
||||
|
||||
$response = wp_remote_post(
|
||||
$verify_url,
|
||||
array(
|
||||
'body' => array(
|
||||
'secret' => $secret_key,
|
||||
'response' => $response,
|
||||
'remoteip' => $_SERVER['REMOTE_ADDR'],
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
||||
|
||||
return isset( $body['success'] ) && $body['success'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user