feat: AutoBooking initial commit — PHP WordPress Plugin (REST API, wpdb, WP_User_Query)
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
<?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 );
|
||||
Reference in New Issue
Block a user