Files
AutoBooking/autobooking-geo-restrict.php
T

243 lines
12 KiB
PHP

<?php
/**
* Plugin Name: AutoBooking Geo Restrict
* Description: Permite operar solo en países habilitados por el admin. Cada usuario solo
* puede usar la app dentro del país donde está físicamente (GPS). Si el país
* no está en la lista de permitidos, acceso bloqueado.
* Version: 1.1.0
* Author: AutoBooking
*/
if ( ! defined( 'ABSPATH' ) ) exit;
/* ──────────────────────────────────────────────────────────────
BOUNDING BOXES por país
Formato: 'CODE' => [lat_min, lat_max, lng_min, lng_max]
Se pueden agregar más países desde el admin sin tocar el código.
─────────────────────────────────────────────────────────────── */
function ab_get_country_boxes() {
return [
'US' => [
[ 24.5, 49.5, -125.0, -66.9 ], // Contiguous USA
[ 51.2, 71.5, -180.0, -129.9 ], // Alaska
[ 18.9, 22.2, -160.2, -154.8 ], // Hawaii
],
'CO' => [[ 1.5, 13.4, -79.0, -66.8 ]],
'MX' => [[ 14.5, 32.7, -118.4, -86.7 ]],
'CA' => [[ 41.7, 83.1, -141.0, -52.6 ]],
'GB' => [[ 49.9, 60.9, -8.2, 1.8 ]],
'ES' => [[ 35.9, 43.8, -9.3, 4.3 ]],
'AR' => [[-55.0, -21.8, -73.6, -53.6 ]],
'BR' => [[-33.7, 5.3, -73.9, -34.8 ]],
'CL' => [[-55.9, -17.5, -75.6, -66.9 ]],
'PE' => [[-18.4, -0.0, -81.3, -68.7 ]],
'EC' => [[ -5.0, 1.5, -81.0, -75.2 ]],
'VE' => [[ 0.6, 12.2, -73.3, -59.8 ]],
];
}
/* ──────────────────────────────────────────────────────────────
HELPER — detecta el país de unas coordenadas
Retorna el código ISO 2 del país si está en las boxes,
o null si no coincide con ningún país configurado.
─────────────────────────────────────────────────────────────── */
function ab_get_country_from_coords( $lat, $lng ) {
$lat = (float) $lat;
$lng = (float) $lng;
if ( ! $lat || ! $lng ) return null;
foreach ( ab_get_country_boxes() as $code => $boxes ) {
foreach ( $boxes as $box ) {
if ( $lat >= $box[0] && $lat <= $box[1] && $lng >= $box[2] && $lng <= $box[3] ) {
return $code;
}
}
}
return null;
}
/* ──────────────────────────────────────────────────────────────
HELPER — verifica si un país está en la lista de permitidos
─────────────────────────────────────────────────────────────── */
function ab_is_country_allowed( $country_code ) {
if ( ! $country_code ) return false;
$allowed = get_option( 'ab_allowed_countries', [ 'US' ] );
if ( is_string( $allowed ) ) $allowed = json_decode( $allowed, true ) ?: [ 'US' ];
return in_array( strtoupper( $country_code ), array_map( 'strtoupper', (array) $allowed ), true );
}
/* ──────────────────────────────────────────────────────────────
IP GEOLOCATION — verifica país por IP como backup del GPS
─────────────────────────────────────────────────────────────── */
function ab_get_country_from_ip( $ip ) {
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) return null;
$cache_key = 'ab_geo_ip_' . md5( $ip );
$cached = get_transient( $cache_key );
if ( $cached !== false ) return $cached ?: null;
$response = wp_remote_get( 'https://ip-api.com/json/' . rawurlencode( $ip ) . '?fields=countryCode,status', ['timeout'=>5] );
if ( is_wp_error( $response ) ) { set_transient( $cache_key, '', 3600 ); return null; }
$body = json_decode( wp_remote_retrieve_body( $response ), true );
$code = ( ! empty( $body['status'] ) && $body['status'] === 'success' ) ? strtoupper( $body['countryCode'] ?? '' ) : '';
set_transient( $cache_key, $code, 86400 );
return $code ?: null;
}
function ab_get_request_ip() {
if ( function_exists( 'abs_get_ip' ) ) return abs_get_ip();
$ip = '';
if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
$parts = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] ); $ip = trim( $parts[0] );
}
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
}
return sanitize_text_field( $ip );
}
/* ──────────────────────────────────────────────────────────────
ENDPOINT PÚBLICO — GET /autobooking/v1/geo/check?lat=&lng=
El JS lo llama antes de cualquier acción crítica.
Retorna si el país está permitido Y cuál es el país detectado.
─────────────────────────────────────────────────────────────── */
add_action( 'rest_api_init', function () {
// Check de coordenadas
register_rest_route( 'autobooking/v1', '/geo/check', [
'methods' => 'GET',
'callback' => function ( WP_REST_Request $req ) {
$lat = (float) $req->get_param( 'lat' );
$lng = (float) $req->get_param( 'lng' );
$country = ab_get_country_from_coords( $lat, $lng );
$allowed = ab_is_country_allowed( $country );
if ( $allowed && $country ) {
$ip_country = ab_get_country_from_ip( ab_get_request_ip() );
if ( $ip_country && $ip_country !== $country ) {
return rest_ensure_response(['allowed'=>false,'country'=>$country,'message'=>'Ubicación inconsistente. Verifica tu conexión.']);
}
}
return rest_ensure_response( [
'allowed' => $allowed,
'country' => $country,
'message' => $allowed ? '' : get_option(
'ab_geo_blocked_msg',
'AutoBooking is not available in your current location.'
),
] );
},
'permission_callback' => '__return_true',
'args' => [
'lat' => [ 'required' => true, 'type' => 'number' ],
'lng' => [ 'required' => true, 'type' => 'number' ],
],
] );
// Leer configuración geo (para el admin dashboard)
register_rest_route( 'autobooking/v1', '/admin/geo-settings', [
'methods' => 'GET',
'callback' => function () {
$allowed = get_option( 'ab_allowed_countries', [ 'US' ] );
if ( is_string( $allowed ) ) $allowed = json_decode( $allowed, true ) ?: [ 'US' ];
return rest_ensure_response( [
'allowed_countries' => array_values( $allowed ),
'available_countries' => array_keys( ab_get_country_boxes() ),
'blocked_message' => get_option( 'ab_geo_blocked_msg', 'AutoBooking is not available in your current location.' ),
] );
},
'permission_callback' => function () {
return current_user_can( 'manage_autobooking' );
},
] );
// Guardar configuración geo (desde el admin dashboard)
register_rest_route( 'autobooking/v1', '/admin/geo-settings/save', [
'methods' => 'POST',
'callback' => function ( WP_REST_Request $req ) {
$body = $req->get_json_params();
$allowed = array_map( 'strtoupper', array_filter( (array) ( $body['allowed_countries'] ?? [] ), 'is_string' ) );
$msg = sanitize_textarea_field( $body['blocked_message'] ?? '' );
if ( empty( $allowed ) ) {
return new WP_Error( 'invalid', 'Debe haber al menos un país permitido.', [ 'status' => 400 ] );
}
update_option( 'ab_allowed_countries', array_values( $allowed ) );
if ( $msg ) update_option( 'ab_geo_blocked_msg', $msg );
return rest_ensure_response( [ 'ok' => true, 'allowed_countries' => $allowed ] );
},
'permission_callback' => function () {
return current_user_can( 'manage_autobooking' );
},
] );
} );
/* ──────────────────────────────────────────────────────────────
INTERCEPTOR REST — bloquea endpoints sensibles fuera de países permitidos
─────────────────────────────────────────────────────────────── */
add_filter( 'rest_dispatch_request', function ( $result, WP_REST_Request $request, $route, $handler ) {
if ( $request->get_method() !== 'POST' ) return $result;
$body = $request->get_json_params() ?: [];
$lat = 0;
$lng = 0;
/* Conductor se pone online */
if ( preg_match( '#^/autobooking/v1/driver/status$#', $route ) ) {
if ( ! empty( $body['online'] ) ) {
$lat = floatval( $body['lat'] ?? 0 );
$lng = floatval( $body['lng'] ?? 0 );
if ( $lat && $lng ) {
$country = ab_get_country_from_coords( $lat, $lng );
if ( ! ab_is_country_allowed( $country ) ) {
return new WP_Error(
'geo_restricted',
get_option( 'ab_geo_blocked_msg', 'AutoBooking is not available in your current location.' ),
[ 'status' => 403, 'country' => $country ]
);
}
}
}
return $result;
}
/* Pasajero solicita viaje */
if ( preg_match( '#^/autobooking/v1/passenger/(request|book|ride)$#', $route ) ) {
$lat = floatval( $body['origin_lat'] ?? $body['pickup_lat'] ?? 0 );
$lng = floatval( $body['origin_lng'] ?? $body['pickup_lng'] ?? 0 );
if ( $lat && $lng ) {
$country = ab_get_country_from_coords( $lat, $lng );
if ( ! ab_is_country_allowed( $country ) ) {
return new WP_Error(
'geo_restricted',
get_option( 'ab_geo_blocked_msg', 'AutoBooking is not available in your current location.' ),
[ 'status' => 403, 'country' => $country ]
);
}
}
return $result;
}
/* Corporate reserva viaje */
if ( preg_match( '#^/autobooking/v1/corporate/(book|trip|request)$#', $route ) ) {
$lat = floatval( $body['origin_lat'] ?? 0 );
$lng = floatval( $body['origin_lng'] ?? 0 );
if ( $lat && $lng ) {
$country = ab_get_country_from_coords( $lat, $lng );
if ( ! ab_is_country_allowed( $country ) ) {
return new WP_Error(
'geo_restricted',
get_option( 'ab_geo_blocked_msg', 'AutoBooking is not available in your current location.' ),
[ 'status' => 403, 'country' => $country ]
);
}
}
return $result;
}
return $result;
}, 10, 4 );